diff --git a/sycl/include/sycl/ext/oneapi/reduction.hpp b/sycl/include/sycl/ext/oneapi/reduction.hpp index 3005dc3417944..08598b2540cd5 100644 --- a/sycl/include/sycl/ext/oneapi/reduction.hpp +++ b/sycl/include/sycl/ext/oneapi/reduction.hpp @@ -124,17 +124,17 @@ __SYCL_EXPORT size_t reduComputeWGSize(size_t NWorkItems, size_t MaxWGSize, /// functions and representing users' reduction variable. /// The generic version of the class represents those reductions of those /// types and operations for which the identity value is not known. -/// The Algorithm template can be used to specialize reducers for different -/// reduction algorithms. The View template describes whether the reducer -/// owns its data or not: if View is 'true', then the reducer does not own -/// its data and instead provides a view of data allocated elsewhere (i.e. -/// via a reference or pointer member); if View is 'false', then the reducer -/// owns its data. With the current default reduction algorithm, the top-level -/// reducers that are passed to the user's lambda contain a private copy of -/// the reduction variable, whereas any reducer created by a subscript operator -/// contains a reference to a reduction variable allocated elsewhere. +/// The View template describes whether the reducer owns its data or not: if +/// View is 'true', then the reducer does not own its data and instead provides +/// a view of data allocated elsewhere (i.e. via a reference or pointer member); +/// if View is 'false', then the reducer owns its data. With the current default +/// reduction algorithm, the top-level reducers that are passed to the user's +/// lambda contain a private copy of the reduction variable, whereas any reducer +/// created by a subscript operator contains a reference to a reduction variable +/// allocated elsewhere. The Subst parameter is an implementation detail and is +/// used to spell out restrictions using 'enable_if'. template + bool View = false, typename Subst = void> class reducer; /// Helper class for accessing reducer-defined types in CRTP @@ -142,9 +142,8 @@ class reducer; template struct ReducerTraits; template -struct ReducerTraits< - reducer> { + bool View, typename Subst> +struct ReducerTraits> { using type = T; using op = BinaryOperation; static constexpr int dims = Dims; @@ -319,14 +318,13 @@ template class combiner { /// /// It stores a copy of the identity and binary operation associated with the /// reduction. -template +template class reducer< - T, BinaryOperation, Dims, Extent, Algorithm, View, + T, BinaryOperation, Dims, Extent, View, enable_if_t::value>> : public combiner< - reducer::value>>> { @@ -352,14 +350,13 @@ class reducer< /// /// It allows to reduce the size of the 'reducer' object by not holding /// the identity field inside it and allows to add a default constructor. -template +template class reducer< - T, BinaryOperation, Dims, Extent, Algorithm, View, + T, BinaryOperation, Dims, Extent, View, enable_if_t::value>> : public combiner< - reducer::value>>> { @@ -383,11 +380,10 @@ class reducer< /// Component of 'reducer' class for array reductions, representing a single /// element of the span (as returned by the subscript operator). -template -class reducer +class reducer> - : public combiner>> { public: reducer(T &Ref, BinaryOperation BOp) : MElement(Ref), MBinaryOp(BOp) {} @@ -401,13 +397,12 @@ class reducer +template class reducer< - T, BinaryOperation, Dims, Extent, Algorithm, View, + T, BinaryOperation, Dims, Extent, View, enable_if_t::value>> - : public combiner::value>>> { @@ -415,8 +410,7 @@ class reducer< reducer(const T &Identity, BinaryOperation BOp) : MValue(Identity), MIdentity(Identity), MBinaryOp(BOp) {} - reducer - operator[](size_t Index) { + reducer operator[](size_t Index) { return {MValue[Index], MBinaryOp}; } @@ -432,13 +426,12 @@ class reducer< /// Specialization of 'reducer' class for array reductions accepting a span /// in cases where the identity value is known. -template +template class reducer< - T, BinaryOperation, Dims, Extent, Algorithm, View, + T, BinaryOperation, Dims, Extent, View, enable_if_t::value>> - : public combiner::value>>> { @@ -448,8 +441,7 @@ class reducer< // SYCL 2020 revision 4 says this should be const, but this is a bug // see https://github.com/KhronosGroup/SYCL-Docs/pull/252 - reducer - operator[](size_t Index) { + reducer operator[](size_t Index) { return {MValue[Index], BinaryOperation()}; } @@ -504,78 +496,96 @@ template class reduction_impl_common { bool InitializeToIdentity; }; -/// Types representing specific reduction algorithms -/// Enables reduction_impl_algo to take additional algorithm-specific templates -template -class default_reduction_algorithm {}; +template struct is_rw_acc_t : public std::false_type {}; -/// Templated class for implementations of specific reduction algorithms -template -class reduction_impl_algo; +template +struct is_rw_acc_t> + : public std::true_type {}; + +template struct is_dw_acc_t : public std::false_type {}; + +template +struct is_dw_acc_t> + : public std::true_type {}; + +template struct is_placeholder_t : public std::false_type {}; + +template +struct is_placeholder_t> + : public std::true_type {}; + +// Used for determining dimensions for temporary storage (mainly). +template struct data_dim_t { + static constexpr int value = 1; +}; + +template +struct data_dim_t< + accessor> { + static constexpr int value = AccessorDims; +}; + +template struct get_red_t; +template struct get_red_t { + using type = T; +}; + +template +struct get_red_t< + accessor> { + using type = T; +}; -/// Original reduction algorithm is the default. It supports both USM and -/// accessors via a single class template -class reduction_impl_algo< - T, BinaryOperation, Dims, Extent, - default_reduction_algorithm> - : public reduction_impl_common { + typename RedOutVar> +class reduction_impl_algo : public reduction_impl_common { using base = reduction_impl_common; + using self = reduction_impl_algo; public: - using reducer_type = - reducer>; + using reducer_type = reducer; using result_type = T; using binary_operation = BinaryOperation; // Buffers and accessors always describe scalar reductions (i.e. Dims == 0) // The input buffer/accessor is allowed to have different dimensionality // AccessorDims also determines the dimensionality of some temp storage - static constexpr int accessor_dim = AccessorDims; - static constexpr int buffer_dim = (AccessorDims == 0) ? 1 : AccessorDims; - using rw_accessor_type = accessor::value; + static constexpr int buffer_dim = (accessor_dim == 0) ? 1 : accessor_dim; + static constexpr access::placeholder is_placeholder = + is_placeholder_t::value ? access::placeholder::true_t + : access::placeholder::false_t; + using rw_accessor_type = accessor>; - using dw_accessor_type = - accessor>; - static constexpr bool has_atomic_add_float64 = IsReduOptForAtomic64Add::value; static constexpr bool has_fast_atomics = IsReduOptForFastAtomicFetch::value; static constexpr bool has_fast_reduce = IsReduOptForFastReduce::value; - static constexpr bool is_usm = IsUSM; - static constexpr bool is_placeholder = - (IsPlaceholder == access::placeholder::true_t); + + static constexpr bool is_usm = std::is_same_v; + + static constexpr bool is_rw_acc = is_rw_acc_t::value; + static constexpr bool is_dw_acc = is_dw_acc_t::value; + static constexpr bool is_acc = is_rw_acc | is_dw_acc; + static_assert(!is_rw_acc || !is_dw_acc, "Can be only one at once!"); + static_assert(!is_usm || !is_acc, "Can be only one at once!"); static constexpr size_t dims = Dims; static constexpr size_t num_elements = Extent; reduction_impl_algo(const T &Identity, BinaryOperation BinaryOp, bool Init, - std::shared_ptr AccPointer) - : base(Identity, BinaryOp, Init), MRWAcc(AccPointer){}; - reduction_impl_algo(const T &Identity, BinaryOperation BinaryOp, bool Init, - std::shared_ptr AccPointer) - : base(Identity, BinaryOp, Init), MDWAcc(AccPointer){}; - reduction_impl_algo(const T &Identity, BinaryOperation BinaryOp, bool Init, - T *USMPointer) - : base(Identity, BinaryOp, Init), MUSMPointer(USMPointer){}; - - /// Associates the reduction accessor to user's memory with \p CGH handler - /// to keep the accessor alive until the command group finishes the work. - /// This function does not do anything for USM reductions. - void associateWithHandler(handler &CGH) { - if (MRWAcc) - CGH.associateWithHandler(MRWAcc.get(), access::target::device); - else if (MDWAcc) - CGH.associateWithHandler(MDWAcc.get(), access::target::device); - } + RedOutVar RedOut) + : base(Identity, BinaryOp, Init), MRedOut(std::move(RedOut)){}; /// Creates and returns a local accessor with the \p Size elements. /// By default the local accessor elements are of the same type as the @@ -594,32 +604,21 @@ class reduction_impl_algo< return {*MOutBufPtr, CGH}; } - /// Returns user's USM pointer passed to reduction for editing. - template - std::enable_if_t - getWriteMemForPartialReds(size_t, handler &) { - return getUSMPointer(); - } - - /// Returns user's accessor passed to reduction for editing if that is - /// the read-write accessor. Otherwise, create a new buffer and return - /// read-write accessor to it. - template - std::enable_if_t - getWriteMemForPartialReds(size_t, handler &CGH) { - if (MRWAcc) - return *MRWAcc; - return getWriteMemForPartialReds(1, CGH); - } - - /// Constructs a new temporary buffer to hold partial sums and returns - /// the accessor for that buffer. template - std::enable_if_t - getWriteMemForPartialReds(size_t Size, handler &CGH) { - MOutBufPtr = std::make_shared>(range<1>(Size)); - CGH.addReduction(MOutBufPtr); - return createHandlerWiredReadWriteAccessor(CGH, *MOutBufPtr); + auto getWriteMemForPartialReds(size_t Size, handler &CGH) { + // If there is only one WG we can avoid creation of temporary buffer with + // partial sums and write directly into user's reduction variable. + // + // Current implementation doesn't allow that in case of DW accessor used for + // reduction because C++ types for it and for temporary storage don't match, + // hence the second part of the check. + if constexpr (IsOneWG && !is_dw_acc) { + return MRedOut; + } else { + MOutBufPtr = std::make_shared>(range<1>(Size)); + CGH.addReduction(MOutBufPtr); + return createHandlerWiredReadWriteAccessor(CGH, *MOutBufPtr); + } } /// Returns an accessor accessing the memory that will hold the reduction @@ -629,9 +628,11 @@ class reduction_impl_algo< /// Otherwise, a new buffer is created and accessor to that buffer is /// returned. rw_accessor_type getWriteAccForPartialReds(size_t Size, handler &CGH) { - if (Size == 1 && MRWAcc != nullptr) { - associateWithHandler(CGH); - return *MRWAcc; + if constexpr (is_rw_acc) { + if (Size == 1) { + CGH.associateWithHandler(&MRedOut, access::target::device); + return MRedOut; + } } // Create a new output buffer and return an accessor to it. @@ -647,8 +648,12 @@ class reduction_impl_algo< template std::enable_if_t getReadWriteAccessorToInitializedMem(handler &CGH) { - if (!is_usm && !base::initializeToIdentity()) - return *MRWAcc; + if constexpr (is_rw_acc) { + if (!base::initializeToIdentity()) + return MRedOut; + } + assert(!(is_dw_acc && !base::initializeToIdentity()) && + "Unexpected condition!"); // TODO: Move to T[] in C++20 to simplify handling here // auto RWReduVal = std::make_shared(); @@ -675,22 +680,7 @@ class reduction_impl_algo< return {*CounterBuf, CGH}; } - bool hasUserDiscardWriteAccessor() { return MDWAcc != nullptr; } - - template - std::enable_if_t getUserReadWriteAccessor() { - return *MRWAcc; - } - - template - std::enable_if_t getUserDiscardWriteAccessor() { - return *MDWAcc; - } - - result_type *getUSMPointer() { - assert(is_usm && "Unexpected call of getUSMPointer()."); - return MUSMPointer; - } + RedOutVar &getUserRedVar() { return MRedOut; } static inline result_type *getOutPointer(const rw_accessor_type &OutAcc) { return OutAcc.get_pointer().get(); @@ -701,29 +691,27 @@ class reduction_impl_algo< } private: - template - std::enable_if_t - createHandlerWiredReadWriteAccessor(handler &CGH, BufferT Buffer) { - return {Buffer, CGH}; - } - - template - std::enable_if_t - createHandlerWiredReadWriteAccessor(handler &CGH, BufferT Buffer) { - rw_accessor_type Acc(Buffer); - CGH.require(Acc); - return Acc; + template + rw_accessor_type createHandlerWiredReadWriteAccessor(handler &CGH, + BufferT Buffer) { + // TODO: + // SYCL 2020: The accessor template parameter IsPlaceholder is allowed to be + // specified, but it has no bearing on whether the accessor instance is a + // placeholder. This is determined solely by the constructor used to create + // the instance. The associated type access::placeholder is also deprecated. + if constexpr (is_placeholder == access::placeholder::true_t) { + rw_accessor_type Acc(Buffer); + CGH.require(Acc); + return Acc; + } else { + return {Buffer, CGH}; + } } - /// User's accessor to where the reduction must be written. - std::shared_ptr MRWAcc; - std::shared_ptr MDWAcc; - std::shared_ptr> MOutBufPtr; - /// USM pointer referencing the memory to where the result of the reduction - /// must be written. Applicable/used only for USM reductions. - T *MUSMPointer = nullptr; + /// User's accessor/USM pointer to where the reduction must be written. + RedOutVar MRedOut; }; /// Predicate returning true if all template type parameters except the last one @@ -741,82 +729,22 @@ template struct AreAllButLastReductions { static constexpr bool value = !std::is_base_of>::value; }; - /// This class encapsulates the reduction variable/accessor, /// the reduction operator and an optional operator identity. template + typename RedOutVar> class reduction_impl : private reduction_impl_base, - public reduction_impl_algo { + public reduction_impl_algo { private: - using algo = reduction_impl_algo; + using algo = reduction_impl_algo; + using self = reduction_impl; -public: - using reducer_type = typename algo::reducer_type; - using rw_accessor_type = typename algo::rw_accessor_type; - using dw_accessor_type = typename algo::dw_accessor_type; + static constexpr bool is_known_identity = + sycl::detail::IsKnownIdentityOp::value; - // Only scalar and 1D array reductions are supported by SYCL 2020. - static_assert(Dims <= 1, "Multi-dimensional reductions are not supported."); - - /// SYCL-2020. - /// Constructs reduction_impl when the identity value is statically known. - template ::value> * = nullptr> - reduction_impl(buffer<_T, 1, AllocatorT> Buffer, handler &CGH, - bool InitializeToIdentity) - : algo(reducer_type::getIdentity(), BinaryOperation(), - InitializeToIdentity, std::make_shared(Buffer)) { - algo::associateWithHandler(CGH); - if (Buffer.size() != 1) - throw sycl::runtime_error(errc::invalid, - "Reduction variable must be a scalar.", - PI_ERROR_INVALID_VALUE); - } - - /// Constructs reduction_impl when the identity value is statically known. - template ::value> * = nullptr> - reduction_impl(rw_accessor_type &Acc) - : algo(reducer_type::getIdentity(), BinaryOperation(), false, - std::make_shared(Acc)) { - if (Acc.size() != 1) - throw sycl::runtime_error(errc::invalid, - "Reduction variable must be a scalar.", - PI_ERROR_INVALID_VALUE); - } - - /// Constructs reduction_impl when the identity value is statically known. - template ::value> * = nullptr> - reduction_impl(dw_accessor_type &Acc) - : algo(reducer_type::getIdentity(), BinaryOperation(), true, - std::make_shared(Acc)) { - if (Acc.size() != 1) - throw sycl::runtime_error(errc::invalid, - "Reduction variable must be a scalar.", - PI_ERROR_INVALID_VALUE); - } - - /// SYCL-2020. - /// Constructs reduction_impl when the identity value is statically known, - /// and user still passed the identity value. - template < - typename _T, typename AllocatorT, - enable_if_t::value> - * = nullptr> - reduction_impl(buffer<_T, 1, AllocatorT> Buffer, handler &CGH, - const T & /*Identity*/, BinaryOperation, - bool InitializeToIdentity) - : algo(reducer_type::getIdentity(), BinaryOperation(), - InitializeToIdentity, std::make_shared(Buffer)) { - algo::associateWithHandler(CGH); - if (Buffer.size() != 1) - throw sycl::runtime_error(errc::invalid, - "Reduction variable must be a scalar.", - PI_ERROR_INVALID_VALUE); + // TODO: Do we also need chooseBinOp? + static constexpr T chooseIdentity(const T &Identity) { // For now the implementation ignores the identity value given by user // when the implementation knows the identity. // The SPEC could prohibit passing identity parameter to operations with @@ -828,79 +756,57 @@ class reduction_impl // the existing user's program remains compilable and working correctly. // I.e. with this constructor here, adding more reduction operations to the // list of known operations does not break the existing programs. - } + if constexpr (is_known_identity) { + (void)Identity; + return reducer_type::getIdentity(); - /// Constructs reduction_impl when the identity value is statically known, - /// and user still passed the identity value. - template ::value> * = nullptr> - reduction_impl(rw_accessor_type &Acc, const T & /*Identity*/, BinaryOperation) - : algo(reducer_type::getIdentity(), BinaryOperation(), false, - std::make_shared(Acc)) { - if (Acc.size() != 1) - throw sycl::runtime_error(errc::invalid, - "Reduction variable must be a scalar.", - PI_ERROR_INVALID_VALUE); - // For now the implementation ignores the identity value given by user - // when the implementation knows the identity. - // The SPEC could prohibit passing identity parameter to operations with - // known identity, but that could have some bad consequences too. - // For example, at some moment the implementation may NOT know the identity - // for COMPLEX-PLUS reduction. User may create a program that would pass - // COMPLEX value (0,0) as identity for PLUS reduction. At some later moment - // when the implementation starts handling COMPLEX-PLUS as known operation - // the existing user's program remains compilable and working correctly. - // I.e. with this constructor here, adding more reduction operations to the - // list of known operations does not break the existing programs. + } else { + return Identity; + } } - /// Constructs reduction_impl when the identity value is statically known, - /// and user still passed the identity value. - template ::value> * = nullptr> - reduction_impl(dw_accessor_type &Acc, const T & /*Identity*/, BinaryOperation) - : algo(reducer_type::getIdentity(), BinaryOperation(), true, - std::make_shared(Acc)) { +public: + using algo::is_acc; + using algo::is_dw_acc; + using algo::is_rw_acc; + using algo::is_usm; + + using reducer_type = typename algo::reducer_type; + using rw_accessor_type = typename algo::rw_accessor_type; + + // Only scalar and 1D array reductions are supported by SYCL 2020. + static_assert(Dims <= 1, "Multi-dimensional reductions are not supported."); + + /// Constructs reduction_impl when the identity value is statically known. + template * = nullptr> + reduction_impl(RedOutVar &Acc) + : algo(reducer_type::getIdentity(), BinaryOperation(), is_dw_acc, Acc) { if (Acc.size() != 1) throw sycl::runtime_error(errc::invalid, "Reduction variable must be a scalar.", PI_ERROR_INVALID_VALUE); - // For now the implementation ignores the identity value given by user - // when the implementation knows the identity. - // The SPEC could prohibit passing identity parameter to operations with - // known identity, but that could have some bad consequences too. - // For example, at some moment the implementation may NOT know the identity - // for COMPLEX-PLUS reduction. User may create a program that would pass - // COMPLEX value (0,0) as identity for PLUS reduction. At some later moment - // when the implementation starts handling COMPLEX-PLUS as known operation - // the existing user's program remains compilable and working correctly. - // I.e. with this constructor here, adding more reduction operations to the - // list of known operations does not break the existing programs. } - /// SYCL-2020. - /// Constructs reduction_impl when the identity value is NOT known statically. - template < - typename _T, typename AllocatorT, - enable_if_t::value> - * = nullptr> - reduction_impl(buffer<_T, 1, AllocatorT> Buffer, handler &CGH, - const T &Identity, BinaryOperation BOp, - bool InitializeToIdentity) - : algo(Identity, BOp, InitializeToIdentity, - std::make_shared(Buffer)) { - algo::associateWithHandler(CGH); - if (Buffer.size() != 1) - throw sycl::runtime_error(errc::invalid, - "Reduction variable must be a scalar.", - PI_ERROR_INVALID_VALUE); - } + /// Constructs reduction_impl when the identity value is statically known. + /// The \param VarPtr is a USM pointer to memory, to where the computed + /// reduction value is added using BinaryOperation, i.e. it is expected that + /// the memory is pre-initialized with some meaningful value. + template * = nullptr> + reduction_impl(RedOutVar VarPtr, bool InitializeToIdentity = false) + : algo(reducer_type::getIdentity(), BinaryOperation(), + InitializeToIdentity, VarPtr) {} - /// Constructs reduction_impl when the identity value is unknown. - template ::value> * = nullptr> - reduction_impl(rw_accessor_type &Acc, const T &Identity, BinaryOperation BOp) - : algo(Identity, BOp, false, std::make_shared(Acc)) { + /// SYCL-2020. + /// Constructs reduction_impl when the identity value is statically known. + template * = + nullptr> + reduction_impl(RedOutVar &Acc, handler &CGH, bool InitializeToIdentity) + : algo(reducer_type::getIdentity(), BinaryOperation(), + InitializeToIdentity, Acc) { + associateWithHandler(CGH, &Acc, access::target::device); if (Acc.size() != 1) throw sycl::runtime_error(errc::invalid, "Reduction variable must be a scalar.", @@ -908,83 +814,44 @@ class reduction_impl } /// Constructs reduction_impl when the identity value is unknown. - template ::value> * = nullptr> - reduction_impl(dw_accessor_type &Acc, const T &Identity, BinaryOperation BOp) - : algo(Identity, BOp, true, std::make_shared(Acc)) { + template * = nullptr> + reduction_impl(RedOutVar &Acc, const T &Identity, BinaryOperation BOp) + : algo(chooseIdentity(Identity), BOp, is_dw_acc, Acc) { if (Acc.size() != 1) throw sycl::runtime_error(errc::invalid, "Reduction variable must be a scalar.", PI_ERROR_INVALID_VALUE); } - /// Constructs reduction_impl when the identity value is statically known. - /// The \param VarPtr is a USM pointer to memory, to where the computed - /// reduction value is added using BinaryOperation, i.e. it is expected that - /// the memory is pre-initialized with some meaningful value. - template ::value> * = nullptr> - reduction_impl(T *VarPtr, bool InitializeToIdentity = false) - : algo(reducer_type::getIdentity(), BinaryOperation(), - InitializeToIdentity, VarPtr) {} - - /// Constructs reduction_impl when the identity value is statically known, - /// and user still passed the identity value. /// The \param VarPtr is a USM pointer to memory, to where the computed /// reduction value is added using BinaryOperation, i.e. it is expected that /// the memory is pre-initialized with some meaningful value. - template ::value> * = nullptr> - reduction_impl(T *VarPtr, const T &Identity, BinaryOperation, + template * = nullptr> + reduction_impl(RedOutVar VarPtr, const T &Identity, BinaryOperation BOp, bool InitializeToIdentity = false) - : algo(Identity, BinaryOperation(), InitializeToIdentity, VarPtr) { - // For now the implementation ignores the identity value given by user - // when the implementation knows the identity. - // The SPEC could prohibit passing identity parameter to operations with - // known identity, but that could have some bad consequences too. - // For example, at some moment the implementation may NOT know the identity - // for COMPLEX-PLUS reduction. User may create a program that would pass - // COMPLEX value (0,0) as identity for PLUS reduction. At some later moment - // when the implementation starts handling COMPLEX-PLUS as known operation - // the existing user's program remains compilable and working correctly. - // I.e. with this constructor here, adding more reduction operations to the - // list of known operations does not break the existing programs. + : algo(chooseIdentity(Identity), BOp, InitializeToIdentity, VarPtr) {} + + /// For placeholder accessor. + template * = nullptr> + reduction_impl(RedOutVar &Acc, handler &CGH, const T &Identity, + BinaryOperation BOp, bool InitializeToIdentity) + : algo(chooseIdentity(Identity), BOp, InitializeToIdentity, Acc) { + associateWithHandler(CGH, &Acc, access::target::device); + if (Acc.size() != 1) + throw sycl::runtime_error(errc::invalid, + "Reduction variable must be a scalar.", + PI_ERROR_INVALID_VALUE); } - - /// Constructs reduction_impl when the identity value is unknown. - /// The \param VarPtr is a USM pointer to memory, to where the computed - /// reduction value is added using BinaryOperation, i.e. it is expected that - /// the memory is pre-initialized with some meaningful value. - template ::value> * = nullptr> - reduction_impl(T *VarPtr, const T &Identity, BinaryOperation BOp, - bool InitializeToIdentity = false) - : algo(Identity, BOp, InitializeToIdentity, VarPtr) {} - - /// Constructs reduction_impl when the identity value is statically known - template ::value> * = nullptr> - reduction_impl(span<_T, Extent> Span, bool InitializeToIdentity = false) - : algo(reducer_type::getIdentity(), BinaryOperation(), - InitializeToIdentity, Span.data()) {} - - /// Constructs reduction_impl when the identity value is statically known - /// and user passed an identity value anyway - template ::value> * = nullptr> - reduction_impl(span<_T, Extent> Span, const T & /* Identity */, - BinaryOperation BOp, bool InitializeToIdentity = false) - : algo(reducer_type::getIdentity(), BOp, InitializeToIdentity, - Span.data()) {} - - /// Constructs reduction_impl when the identity value is not statically known - template ::value> * = nullptr> - reduction_impl(span Span, const T &Identity, BinaryOperation BOp, - bool InitializeToIdentity = false) - : algo(Identity, BOp, InitializeToIdentity, Span.data()) {} }; +template +auto make_reduction(RedOutVar RedVar, RestTy &&...Rest) { + return reduction_impl::type, BinaryOp, Dims, + Extent, RedOutVar>{RedVar, + std::forward(Rest)...}; +} + /// A helper to pass undefined (sycl::detail::auto_name) names unmodified. We /// must do that to avoid name collisions. template