//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// This file contains the declaration of the SYCL queue class, which /// schedules kernels on a device. /// //===----------------------------------------------------------------------===// #ifndef _LIBSYCL___IMPL_QUEUE_HPP #define _LIBSYCL___IMPL_QUEUE_HPP #include #include #include #include #include #include #include #include #include #include _LIBSYCL_BEGIN_NAMESPACE_SYCL class context; namespace detail { class QueueImpl; template struct CheckFunctionCallOperator { static_assert(std::integral_constant::value, "Second template parameter is required to be of function type"); }; template struct CheckFunctionCallOperator { private: template static constexpr auto check(T *) -> typename std::is_same>() .operator()(std::declval()...)), RetT>::type; template static constexpr std::false_type check(...); using type = decltype(check(0)); public: static constexpr bool value = type::value; }; } // namespace detail // SYCL 2020 4.6.5. Queue class. class _LIBSYCL_EXPORT queue { public: queue(const queue &rhs) = default; queue(queue &&rhs) = default; queue &operator=(const queue &rhs) = default; queue &operator=(queue &&rhs) = default; ~queue() = default; friend bool operator==(const queue &lhs, const queue &rhs) { return lhs.impl == rhs.impl; } friend bool operator!=(const queue &lhs, const queue &rhs) { return !(lhs == rhs); } /// Constructs a SYCL queue instance using the device returned by an instance /// of default_selector. /// /// \param propList is a list of properties for queue construction. explicit queue(const property_list &propList = {}) : queue(detail::SelectDevice(default_selector_v), detail::defaultAsyncHandler, propList) {} /// Constructs a SYCL queue instance with an async_handler using the device /// returned by an instance of default_selector. /// /// \param asyncHandler is a SYCL asynchronous exception handler. /// \param propList is a list of properties for queue construction. explicit queue(const async_handler &asyncHandler, const property_list &propList = {}) : queue(detail::SelectDevice(default_selector_v), asyncHandler, propList) {} /// Constructs a SYCL queue instance using the device identified by the /// device selector provided. /// \param deviceSelector is a SYCL 2020 Device Selector, a simple callable /// that takes a device and returns an int /// \param propList is a list of properties for queue construction. template < typename DeviceSelector, typename = detail::EnableIfDeviceSelectorIsInvocable> explicit queue(const DeviceSelector &deviceSelector, const property_list &propList = {}) : queue(detail::SelectDevice(deviceSelector), detail::defaultAsyncHandler, propList) {} /// Constructs a SYCL queue instance using the device identified by the /// device selector provided. /// \param deviceSelector is a SYCL 2020 Device Selector, a simple callable /// that takes a device and returns an int /// \param asyncHandler is a SYCL asynchronous exception handler. /// \param propList is a list of properties for queue construction. template < typename DeviceSelector, typename = detail::EnableIfDeviceSelectorIsInvocable> explicit queue(const DeviceSelector &deviceSelector, const async_handler &asyncHandler, const property_list &propList = {}) : queue(detail::SelectDevice(deviceSelector), asyncHandler, propList) {} /// Constructs a SYCL queue instance using the device provided. /// /// \param syclDevice is an instance of SYCL device. /// \param propList is a list of properties for queue construction. explicit queue(const device &syclDevice, const property_list &propList = {}) : queue(syclDevice, detail::defaultAsyncHandler, propList) {} /// Constructs a SYCL queue instance with an async_handler using the device /// provided. /// /// \param syclDevice is an instance of SYCL device. /// \param asyncHandler is a SYCL asynchronous exception handler. /// \param propList is a list of properties for queue construction. explicit queue(const device &syclDevice, const async_handler &asyncHandler, const property_list &propList = {}); /// \return the SYCL backend associated with this queue. backend get_backend() const noexcept; /// \return the associated SYCL context. context get_context() const; /// \return the SYCL device this queue was constructed with. device get_device() const; /// Equivalent to has_property(). /// /// \return true if and only if the queue is in order. bool is_in_order() const; /// Queries the queue for information. /// /// The return type depends on information being queried. template typename Param::return_type get_info() const; /// Queries the queue for SYCL backend-specific information. /// /// The return type depends on the information being queried. template typename Param::return_type get_backend_info() const; /// Blocks the calling thread until all commands previously submitted to this /// queue have completed. Synchronous errors are reported through SYCL /// exceptions. void wait(); /// Defines and invokes a SYCL kernel function as a lambda expression or a /// named function object type. /// /// \param kernelFunc is the kernel functor or lambda. /// \return an event that represents the status of the submitted kernel. template event single_task(const KernelType &kernelFunc) { return single_task({}, kernelFunc); } /// Defines and invokes a SYCL kernel function as a lambda expression or a /// named function object type. /// /// \param depEvent is an event that specifies the kernel dependency. /// \param kernelFunc is the kernel functor or lambda. /// \return an event that represents the status of the submitted kernel. template event single_task(event depEvent, const KernelType &kernelFunc) { return single_task({depEvent}, kernelFunc); } /// Defines and invokes a SYCL kernel function as a lambda expression or a /// named function object type. /// /// \param depEvents is a collection of events that specify the kernel /// dependencies. /// \param kernelFunc is the kernel functor or lambda. /// \return an event that represents the status of the submitted kernel. template event single_task(const std::vector &depEvents, const KernelType &kernelFunc) { static_assert( detail::CheckFunctionCallOperator, void()>::value, "Invalid kernel function signature."); setKernelParameters(depEvents); using NameT = typename detail::get_kernel_name_t::name; submitSingleTask(kernelFunc); return getLastEvent(); } /// Defines and invokes a SYCL kernel function as a lambda expression or a /// named function object type, for the specified range. /// /// \param numWorkItems specifies the global work space of the kernel. /// \param rest acts as if it was "const KernelType &KernelFunc". // TODO: Rest will represent reduction types once it is supported. template event parallel_for(range<1> numWorkItems, Rest &&...rest) { return parallel_for(numWorkItems, {}, std::forward(rest)...); } /// Defines and invokes a SYCL kernel function as a lambda expression or a /// named function object type, for the specified range. /// /// \param numWorkItems specifies the global work space of the kernel. /// \param rest acts as if it was "const KernelType &KernelFunc". // TODO: Rest will represent reduction types once it is supported. template event parallel_for(range<2> numWorkItems, Rest &&...rest) { return parallel_for(numWorkItems, {}, std::forward(rest)...); } /// Defines and invokes a SYCL kernel function as a lambda expression or a /// named function object type, for the specified range. /// /// \param numWorkItems specifies the global work space of the kernel. /// \param rest acts as if it was "const KernelType &KernelFunc". // TODO: Rest will represent reduction types once it is supported. template event parallel_for(range<3> numWorkItems, Rest &&...rest) { return parallel_for(numWorkItems, {}, std::forward(rest)...); } /// Defines and invokes a SYCL kernel function as a lambda expression or a /// named function object type, for the specified range. /// /// \param numWorkItems specifies the global work space of the kernel. /// \param depEvent adds a requirement that the action represented by depEvent /// must complete before executing this kernel. /// \param rest acts as if it was "const KernelType &KernelFunc". // TODO: Rest will represent reduction types once it is supported. template event parallel_for(range<1> numWorkItems, event depEvent, Rest &&...rest) { return parallel_for(numWorkItems, {depEvent}, std::forward(rest)...); } /// Defines and invokes a SYCL kernel function as a lambda expression or a /// named function object type, for the specified range. /// /// \param numWorkItems specifies the global work space of the kernel. /// \param depEvent adds a requirement that the action represented by depEvent /// must complete before executing this kernel. /// \param rest acts as if it was "const KernelType &KernelFunc". // TODO: Rest will represent reduction types once it is supported. template event parallel_for(range<2> numWorkItems, event depEvent, Rest &&...rest) { return parallel_for(numWorkItems, {depEvent}, std::forward(rest)...); } /// Defines and invokes a SYCL kernel function as a lambda expression or a /// named function object type, for the specified range. /// /// \param numWorkItems specifies the global work space of the kernel. /// \param depEvent adds a requirement that the action represented by depEvent /// must complete before executing this kernel. /// \param rest acts as if it was "const KernelType &KernelFunc". // TODO: Rest will represent reduction types once it is supported. template event parallel_for(range<3> numWorkItems, event depEvent, Rest &&...rest) { return parallel_for(numWorkItems, {depEvent}, std::forward(rest)...); } /// Defines and invokes a SYCL kernel function as a lambda expression or a /// named function object type, for the specified range. /// /// \param numWorkItems specifies the global work space of the kernel /// \param depEvents is a vector of events that specifies the kernel /// dependencies. /// \param rest acts as if it was "const KernelType &KernelFunc". // TODO: Rest will represent reduction types once it is supported. template event parallel_for(range<1> numWorkItems, const std::vector &depEvents, Rest &&...rest) { return parallelForImpl(numWorkItems, depEvents, std::forward(rest)...); } /// Defines and invokes a SYCL kernel function as a lambda expression or a /// named function object type, for the specified range. /// /// \param numWorkItems specifies the global work space of the kernel /// \param depEvents is a vector of events that specifies the kernel /// dependencies. /// \param rest acts as if it was "const KernelType &KernelFunc". // TODO: Rest will represent reduction types once it is supported. template event parallel_for(range<2> numWorkItems, const std::vector &depEvents, Rest &&...rest) { return parallelForImpl(numWorkItems, depEvents, std::forward(rest)...); } /// Defines and invokes a SYCL kernel function as a lambda expression or a /// named function object type, for the specified range. /// /// \param numWorkItems specifies the global work space of the kernel /// \param depEvents is a vector of events that specifies the kernel /// dependencies. /// \param rest acts as if it was "const KernelType &KernelFunc". // TODO: Rest will represent reduction types once it is supported. template event parallel_for(range<3> numWorkItems, const std::vector &depEvents, Rest &&...rest) { return parallelForImpl(numWorkItems, depEvents, std::forward(rest)...); } private: template event parallelForImpl(range numWorkItems, const std::vector &depEvents, Rest &&...rest) { if constexpr (sizeof...(Rest) != 1) throw sycl::exception(errc::feature_not_supported, "Reductions are not supported"); setKernelParameters(depEvents, numWorkItems); using KernelType = std::decay_t>; using LambdaArgType = sycl::detail::lambda_arg_type>; static_assert( std::is_convertible_v, LambdaArgType> || std::is_convertible_v, LambdaArgType>, "Kernel argument of a sycl::parallel_for with sycl::range " "must be either sycl::item or be convertible from sycl::item"); using TranformedLambdaArgType = std::conditional_t< std::is_convertible_v, LambdaArgType>, item, std::conditional_t< std::is_convertible_v, LambdaArgType>, item, LambdaArgType>>; using NameT = typename detail::get_kernel_name_t::name; submitParallelFor(rest...); return getLastEvent(); } /// Name of this function is defined by compiler. It generates a call to this /// function in the host implementation of KernelFunc in submitSingleTask or /// submitParallelFor. /// \param KernelName the name of the kernel being invoked. /// \param args the kernel arguments for the kernel invocation. template void sycl_kernel_launch(const char *KernelName, Args &&...args) { static_assert( sizeof...(args) == 1, "sycl_kernel_launch expects only 2 arguments now: name of kernel and " "callable object passed to kernel invocation by the user."); auto FirstArg = std::get<0>(std::tie(args...)); submitKernelImpl(detail::getDeviceKernelInfo(KernelName), &FirstArg, sizeof(FirstArg)); } /// The sycl_kernel_entry_point attribute facilitates the generation of an /// offload kernel entry point function with parameters corresponding to the /// (potentially decomposed) kernel arguments and a body that executes the /// kernel (after reconstructing the arguments if required). #ifdef SYCL_LANGUAGE_VERSION # define _LIBSYCL_ENTRY_POINT_ATTR__(KernelName) \ [[clang::sycl_kernel_entry_point(KernelName)]] #else # define _LIBSYCL_ENTRY_POINT_ATTR__(KernelName) #endif // SYCL_LANGUAGE_VERSION /// Specifies the parameters and body of the generated offload kernel entry /// point for single_task invocations. On host, the compiler generates a call /// to sycl_kernel_launch instead of the KernelFunc invocation. template _LIBSYCL_ENTRY_POINT_ATTR__(KernelName) void submitSingleTask(const KernelType &KernelFunc) { KernelFunc(); } /// Specifies the parameters and body of the generated offload kernel entry /// point for parallel_for invocations. On host, the compiler generates a call /// to sycl_kernel_launch instead of the KernelFunc invocation. template _LIBSYCL_ENTRY_POINT_ATTR__(KernelName) void submitParallelFor(const KernelType &KernelFunc) { KernelFunc(detail::Builder::getElement(detail::declptr())); } #undef _LIBSYCL_ENTRY_POINT_ATTR__ /// Passes kernel parameters to the runtime. /// \param Events a collection of events representing dependencies of the /// kernel to submit. /// \param Range a unified view of the kernel execution range. void setKernelParameters(const std::vector &Events, const detail::UnifiedRangeView &Range = {}); /// Passes kernel arguments to runtime. /// If all the dependencies can be handled by the backend, the kernel is /// submitted to it directly in this call. /// \param KernelInfo the information for the kernel being invoked. /// \param ArgData a pointer to the kernel argument. /// \param ArgSize the size of the kernel argument. void submitKernelImpl(detail::DeviceKernelInfo &KernelInfo, void *ArgData, size_t ArgSize); /// \return an event representing last kernel invocation. event getLastEvent(); queue(const std::shared_ptr &Impl) : impl(Impl) {} std::shared_ptr impl; friend sycl::detail::ImplUtils; }; // class queue _LIBSYCL_END_NAMESPACE_SYCL template <> struct std::hash : public sycl::detail::HashBase {}; #endif // _LIBSYCL___IMPL_QUEUE_HPP