From debff926490a37f0677e3335510f527a11b3fafe Mon Sep 17 00:00:00 2001 From: Kseniya Tikhomirova Date: Fri, 6 Feb 2026 15:48:09 +0100 Subject: [PATCH] [SYCL] Add sycl::device initial implementation (#176972) This is part of the SYCL support upstreaming effort. The relevant RFCs can be found here: https://discourse.llvm.org/t/rfc-add-full-support-for-the-sycl-programming-model/74080 https://discourse.llvm.org/t/rfc-sycl-runtime-upstreaming/74479 Plan for next PR: E2E lit configs & test for get_platforms & get_devices impl context & USM free functions impl --------- Signed-off-by: Tikhomirova, Kseniya --- libsycl/docs/index.rst | 25 ++- libsycl/include/sycl/__impl/aspect.hpp | 43 ++++ libsycl/include/sycl/__impl/device.hpp | 183 ++++++++++++++++++ .../include/sycl/__impl/device_selector.hpp | 122 ++++++++++++ libsycl/include/sycl/__impl/info/device.hpp | 79 ++++++++ .../include/sycl/__impl/info/device_type.hpp | 35 ++++ libsycl/include/sycl/__impl/platform.hpp | 23 +++ libsycl/include/sycl/sycl.hpp | 2 + libsycl/src/CMakeLists.txt | 3 + libsycl/src/detail/device_impl.cpp | 55 ++++++ libsycl/src/detail/device_impl.hpp | 127 ++++++++++++ libsycl/src/detail/global_objects.cpp | 2 +- .../src/detail/offload/offload_topology.cpp | 65 +++++-- .../src/detail/offload/offload_topology.hpp | 64 ++---- libsycl/src/detail/offload/offload_utils.cpp | 31 ++- libsycl/src/detail/offload/offload_utils.hpp | 15 ++ libsycl/src/detail/platform_impl.cpp | 67 ++++++- libsycl/src/detail/platform_impl.hpp | 28 ++- libsycl/src/device.cpp | 104 ++++++++++ libsycl/src/device_selector.cpp | 118 +++++++++++ libsycl/src/platform.cpp | 19 +- libsycl/tools/sycl-ls/sycl-ls.cpp | 89 ++++++++- 22 files changed, 1218 insertions(+), 81 deletions(-) create mode 100644 libsycl/include/sycl/__impl/aspect.hpp create mode 100644 libsycl/include/sycl/__impl/device.hpp create mode 100644 libsycl/include/sycl/__impl/device_selector.hpp create mode 100644 libsycl/include/sycl/__impl/info/device.hpp create mode 100644 libsycl/include/sycl/__impl/info/device_type.hpp create mode 100644 libsycl/src/detail/device_impl.cpp create mode 100644 libsycl/src/detail/device_impl.hpp create mode 100644 libsycl/src/device.cpp create mode 100644 libsycl/src/device_selector.cpp diff --git a/libsycl/docs/index.rst b/libsycl/docs/index.rst index ce48743be3ae..03e083227ace 100644 --- a/libsycl/docs/index.rst +++ b/libsycl/docs/index.rst @@ -1,6 +1,6 @@ -===================== +=========================== SYCL runtime implementation -===================== +=========================== .. contents:: :local: @@ -8,7 +8,7 @@ SYCL runtime implementation .. _index: Current Status -======== +============== The implementation is in the very early stages of upstreaming. The first milestone is to get @@ -59,7 +59,7 @@ libsycl side: from the multi-architectural binaries Build steps -======== +=========== To build LLVM with libsycl runtime enabled the following script can be used. @@ -87,7 +87,22 @@ To build LLVM with libsycl runtime enabled the following script can be used. Limitations -======== +=========== Libsycl is not currently supported on Windows because it depends on liboffload which doesn't currently support Windows. + +TODO for added SYCL classes +=========================== + +* ``exception``: methods with context are not implemented, to add once context is ready +* ``platform``: deprecated info descriptor is not implemented (info::platform::extensions), to implement on RT level with ``device::get_info()`` +* ``device``: + + * ``get_info``: to find an efficient way to map descriptors to liboffload types, add other descriptors, add cache of info data + * ``has(aspect)``: same as get_info + * ``create_sub_devices``: partitioning is not supported by liboffload now, blocked + * ``has_extension``: deprecated API, to implement on RT level with ``device::has`` + +* device selection: to add compatibility with old SYCL 1.2.1 device selectors, still part of SYCL 2020 specification + diff --git a/libsycl/include/sycl/__impl/aspect.hpp b/libsycl/include/sycl/__impl/aspect.hpp new file mode 100644 index 000000000000..0a73dd621df9 --- /dev/null +++ b/libsycl/include/sycl/__impl/aspect.hpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBSYCL___IMPL_ASPECT_HPP +#define _LIBSYCL___IMPL_ASPECT_HPP + +#include + +#include + +_LIBSYCL_BEGIN_NAMESPACE_SYCL + +// SYCL 2020 4.6.4.5. Aspects. +enum class aspect : std::uint32_t { + cpu, + gpu, + accelerator, + custom, + emulated, + host_debuggable, + fp16, + fp64, + atomic64, + image, + online_compiler, + online_linker, + queue_profiling, + usm_device_allocations, + usm_host_allocations, + usm_atomic_host_allocations, + usm_shared_allocations, + usm_atomic_shared_allocations, + usm_system_allocations +}; + +_LIBSYCL_END_NAMESPACE_SYCL + +#endif // _LIBSYCL___IMPL_ASPECT_HPP diff --git a/libsycl/include/sycl/__impl/device.hpp b/libsycl/include/sycl/__impl/device.hpp new file mode 100644 index 000000000000..55b624f8fcbd --- /dev/null +++ b/libsycl/include/sycl/__impl/device.hpp @@ -0,0 +1,183 @@ +//===----------------------------------------------------------------------===// +// +// 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 2020 device class, which +/// represents a single SYCL device on which kernels can be executed. +/// +//===----------------------------------------------------------------------===// + +#ifndef _LIBSYCL___IMPL_DEVICE_HPP +#define _LIBSYCL___IMPL_DEVICE_HPP + +#include +#include +#include +#include + +#include +#include + +_LIBSYCL_BEGIN_NAMESPACE_SYCL + +class platform; + +namespace detail { +class DeviceImpl; +} // namespace detail + +// SYCL 2020 4.6.4. Device class. +class _LIBSYCL_EXPORT device { +public: + device(const device &rhs) = default; + + device(device &&rhs) = default; + + device &operator=(const device &rhs) = default; + + device &operator=(device &&rhs) = default; + + friend bool operator==(const device &lhs, const device &rhs) { + return lhs.impl == rhs.impl; + } + + friend bool operator!=(const device &lhs, const device &rhs) { + return !(lhs == rhs); + } + + /// Constructs a SYCL device instance using the default device (device chosen + /// by default device selector). + device(); + + /// Constructs a SYCL device instance using the device + /// identified by the provided device selector. + /// \param DeviceSelector is SYCL 2020 device selector, a simple callable that + /// takes a device and returns an int. + template < + typename DeviceSelector, + // `DeviceImpl` (used as a parameter in private ctor) is incomplete + // so would result in a error trying to instantiate + // `EnableIfDeviceSelectorIsInvocable` below. Filter it out + // before trying to do that. + typename = + std::enable_if_t>, + typename = detail::EnableIfDeviceSelectorIsInvocable> + explicit device(const DeviceSelector &deviceSelector) + : device(detail::SelectDevice(deviceSelector)) {} + + /// Returns the backend associated with this device. + /// + /// \return the backend associated with this device. + backend get_backend() const noexcept; + + /// Check if device is a CPU device. + /// + /// \return true if SYCL device is a CPU device. + bool is_cpu() const; + + /// Check if device is a GPU device. + /// + /// \return true if SYCL device is a GPU device. + bool is_gpu() const; + + /// Check if device is an accelerator device. + /// + /// \return true if SYCL device is an accelerator device. + bool is_accelerator() const; + + /// Get associated SYCL platform. + /// + /// \return The associated SYCL platform. + platform get_platform() const; + + /// Queries this SYCL device for information requested by the template + /// parameter param. + /// + /// \return device info of type described in 4.6.4.4. + template + detail::is_device_info_desc_t get_info() const; + + /// Queries this SYCL device for SYCL backend-specific information. + /// + /// The return type depends on information being queried. + template + typename detail::is_backend_info_desc::return_type + get_backend_info() const; + + /// Queries which optional features this device supports (if any). + /// + /// \return true if this device has the given aspect. + bool has(aspect asp) const; + + /// Partition device into sub devices. + /// + /// Available only when prop is info::partition_property::partition_equally. + /// If this SYCL device does not support + /// info::partition_property::partition_equally a feature_not_supported + /// exception will be thrown. + /// + /// \param ComputeUnits is a desired count of compute units in each sub + /// device. + /// \return sub devices partitioned from this SYCL device equally based on the + /// ComputeUnits parameter. + template + std::vector create_sub_devices(size_t ComputeUnits) const; + + /// Partition device into sub devices. + /// + /// Available only when prop is info::partition_property::partition_by_counts. + /// If this SYCL device does not support + /// info::partition_property::partition_by_counts a feature_not_supported + /// exception will be thrown. + /// + /// \param Counts is a std::vector of desired compute units in sub devices. + /// \return sub devices partitioned from this SYCL device by count sizes based + /// on the Counts parameter. + template + std::vector + create_sub_devices(const std::vector &Counts) const; + + /// Partition device into sub devices. + /// + /// Available only when prop is + /// info::partition_property::partition_by_affinity_domain. If this SYCL + /// device does not support + /// info::partition_property::partition_by_affinity_domain or the SYCL device + /// does not support provided info::affinity_domain provided a + /// feature_not_supported exception will be thrown. + /// + /// \param AffinityDomain is one of the values described in Table 4.20 of the + /// SYCL 2020 specification. + /// \return sub devices partitioned from this SYCL device by affinity domain + /// based on the AffinityDomain parameter. + template + std::vector + create_sub_devices(info::partition_affinity_domain AffinityDomain) const; + + /// Query available SYCL devices. + /// + /// \param deviceType is one of the values described in A.3 of the SYCL 2020 + /// specification. + /// \return all SYCL devices available in the system of the device type + /// specified. + static std::vector + get_devices(info::device_type deviceType = info::device_type::all); + +private: + device(detail::DeviceImpl &Impl) : impl(&Impl) {} + detail::DeviceImpl *impl; + + friend sycl::detail::ImplUtils; +}; // class device + +_LIBSYCL_END_NAMESPACE_SYCL + +template <> +struct std::hash : public sycl::detail::HashBase {}; + +#endif // _LIBSYCL___IMPL_DEVICE_HPP diff --git a/libsycl/include/sycl/__impl/device_selector.hpp b/libsycl/include/sycl/__impl/device_selector.hpp new file mode 100644 index 000000000000..00a5f0ec594b --- /dev/null +++ b/libsycl/include/sycl/__impl/device_selector.hpp @@ -0,0 +1,122 @@ +//===----------------------------------------------------------------------===// +// +// 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 standard device selectors +/// (SYCL 2020 4.6.1.1. Device selector). +/// +//===----------------------------------------------------------------------===// + +#ifndef _LIBSYCL___IMPL_DEVICE_SELECTOR_HPP +#define _LIBSYCL___IMPL_DEVICE_SELECTOR_HPP + +#include +#include + +#include + +_LIBSYCL_BEGIN_NAMESPACE_SYCL + +class device; + +namespace detail { + +// SYCL 2020 4.6.1.1. Device selector: +// The interface for a device selector is any object that meets the C++ named +// requirement Callable, taking a parameter of type const device & and returning +// a value that is implicitly convertible to int. +using DeviceSelectorInvocableType = std::function; + +template +using EnableIfDeviceSelectorIsInvocable = std::enable_if_t< + std::is_invocable_r_v>; + +/// Returns a SYCL device instance chosen by the device selector provided. +/// +/// \param DeviceSelector is SYCL 2020 device selector, a simple callable that +/// takes a device and returns an int. +/// \return device chosen by selector. +_LIBSYCL_EXPORT device +SelectDevice(const DeviceSelectorInvocableType &DeviceSelector); + +} // namespace detail + +/// Standard device selector to select SYCL device from any supported SYCL +/// backend based on an implementation-defined heuristic. +/// +/// \param Dev device to calculate the score for. +/// \return score value for the provided device. Further device selection is +/// based on score values. +_LIBSYCL_EXPORT int default_selector_v(const device &Dev); + +/// Standard device selector to select SYCL device from any supported SYCL +/// backend for which the device type is info::device_type::gpu. +/// +/// \param Dev device to calculate the score for. +/// \return score value for the provided device. Further device selection is +/// based on score values. +_LIBSYCL_EXPORT int gpu_selector_v(const device &Dev); + +/// Standard device selector to select SYCL device from any supported SYCL +/// backend for which the device type is info::device_type::cpu. +/// +/// \param Dev device to calculate the score for. +/// \return score value for the provided device. Further device selection is +/// based on score values. +_LIBSYCL_EXPORT int cpu_selector_v(const device &Dev); + +/// Standard device selector to select SYCL device from any supported SYCL +/// backend for which the device type is info::device_type::accelerator. +/// +/// \param Dev device to calculate the score for. +/// \return score value for the provided device. Further device selection is +/// based on score values. +_LIBSYCL_EXPORT int accelerator_selector_v(const device &Dev); + +/// Returns a selector object that selects a SYCL device from any supported SYCL +/// backend which contains all the requested aspects. +/// +/// \param RequireList requested aspects, i.e. for the specific device dev and +/// each aspect devAspect from RequireList dev.has(devAspect) equals true. +/// \param DenyList all the aspects that have to be avoided, i.e. for the +/// specific device dev and each aspect devAspect from denyList +/// dev.has(devAspect) equals false. +/// \return a selector object +_LIBSYCL_EXPORT detail::DeviceSelectorInvocableType +aspect_selector(const std::vector &RequireList, + const std::vector &DenyList = {}); + +/// Returns a selector object that selects a SYCL device from any supported SYCL +/// backend which contains all the requested aspects. +/// +/// \param AspectList requested aspects, i.e. for the specific device dev and +/// each aspect devAspect from AspectList dev.has(devAspect) equals true. +/// \return a selector object +template +detail::DeviceSelectorInvocableType aspect_selector(AspectListT... AspectList) { + std::vector RequireList; + RequireList.reserve(sizeof...(AspectList)); + (RequireList.emplace_back(AspectList), ...); + + return aspect_selector(RequireList, {}); +} + +/// Returns a selector object that selects a SYCL device from any supported SYCL +/// backend which contains all the requested aspects. +/// +/// \param AspectList requested aspects, i.e. for the specific device dev and +/// each aspect devAspect from AspectList dev.has(devAspect) equals true. +/// \return a selector object +template +detail::DeviceSelectorInvocableType aspect_selector() { + return aspect_selector({AspectList...}, {}); +} + +_LIBSYCL_END_NAMESPACE_SYCL + +#endif //_LIBSYCL___IMPL_DEVICE_SELECTOR_HPP diff --git a/libsycl/include/sycl/__impl/info/device.hpp b/libsycl/include/sycl/__impl/info/device.hpp new file mode 100644 index 000000000000..ffdb2d4b0efd --- /dev/null +++ b/libsycl/include/sycl/__impl/info/device.hpp @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// 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 SYCL 2020 device info types. +/// +//===----------------------------------------------------------------------===// + +#ifndef _LIBSYCL___IMPL_INFO_DEVICE_HPP +#define _LIBSYCL___IMPL_INFO_DEVICE_HPP + +#include +#include +#include +#include + +#include +#include + +_LIBSYCL_BEGIN_NAMESPACE_SYCL + +class device; +class platform; + +namespace detail { +template +using is_device_info_desc_t = typename is_info_desc::return_type; +} // namespace detail + +// SYCL 2020 A.3. Device information descriptors. +namespace info { + +enum class partition_property : std::uint32_t { + no_partition = 0, + partition_equally, + partition_by_counts, + partition_by_affinity_domain +}; + +enum class partition_affinity_domain : std::uint32_t { + not_applicable = 0, + numa, + L4_cache, + L3_cache, + L2_cache, + L1_cache, + next_partitionable +}; + +namespace device { +// SYCL 2020 4.6.4.4. Information descriptors. + +struct device_type : detail::info_desc_tag { + using return_type = sycl::info::device_type; +}; +struct name : detail::info_desc_tag { + using return_type = std::string; +}; +struct vendor : detail::info_desc_tag { + using return_type = std::string; +}; +struct driver_version : detail::info_desc_tag { + using return_type = std::string; +}; +struct platform : detail::info_desc_tag { + using return_type = sycl::platform; +}; + +} // namespace device +} // namespace info + +_LIBSYCL_END_NAMESPACE_SYCL + +#endif // _LIBSYCL___IMPL_INFO_DEVICE_HPP diff --git a/libsycl/include/sycl/__impl/info/device_type.hpp b/libsycl/include/sycl/__impl/info/device_type.hpp new file mode 100644 index 000000000000..90db65506385 --- /dev/null +++ b/libsycl/include/sycl/__impl/info/device_type.hpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBSYCL___IMPL_INFO_DEVICE_TYPE_HPP +#define _LIBSYCL___IMPL_INFO_DEVICE_TYPE_HPP + +#include + +#include + +_LIBSYCL_BEGIN_NAMESPACE_SYCL + +namespace info { + +// SYCL 2020 4.6.4.7.1. Device type. +enum class device_type : std::uint32_t { + cpu = 0, + gpu, + accelerator, + custom, + automatic, + host, // Deprecated by SYCL 2020 + all +}; + +} // namespace info + +_LIBSYCL_END_NAMESPACE_SYCL + +#endif // _LIBSYCL___IMPL_INFO_DEVICE_TYPE_HPP diff --git a/libsycl/include/sycl/__impl/platform.hpp b/libsycl/include/sycl/__impl/platform.hpp index c7915eaa224f..63ded9b6f440 100644 --- a/libsycl/include/sycl/__impl/platform.hpp +++ b/libsycl/include/sycl/__impl/platform.hpp @@ -15,9 +15,11 @@ #ifndef _LIBSYCL___IMPL_PLATFORM_HPP #define _LIBSYCL___IMPL_PLATFORM_HPP +#include #include #include #include +#include #include #include @@ -25,6 +27,8 @@ _LIBSYCL_BEGIN_NAMESPACE_SYCL +class device; + namespace detail { class PlatformImpl; } // namespace detail @@ -56,6 +60,16 @@ public: /// \return the backend associated with this platform. backend get_backend() const noexcept; + /// Returns all SYCL devices associated with this platform. + /// + /// If there are no devices that match given device + /// type, resulting vector is empty. + /// + /// \param DeviceType is a SYCL device type. + /// \return a vector of SYCL devices matching given device type. + std::vector + get_devices(info::device_type DeviceType = info::device_type::all) const; + /// Queries this SYCL platform for info. /// /// The return type depends on information being queried. @@ -69,6 +83,15 @@ public: typename detail::is_backend_info_desc::return_type get_backend_info() const; + /// Indicates if all of the SYCL devices on this platform have the + /// given aspect. + /// + /// \param Aspect is one of the values defined in SYCL 2020 Section 4.6.4.5. + /// + /// \return true if all of the SYCL devices on this platform have the + /// given aspect. + bool has(aspect Aspect) const; + /// Returns all SYCL platforms from all backends that are available in the /// system. /// diff --git a/libsycl/include/sycl/sycl.hpp b/libsycl/include/sycl/sycl.hpp index ef91ab238177..3e7f81092256 100644 --- a/libsycl/include/sycl/sycl.hpp +++ b/libsycl/include/sycl/sycl.hpp @@ -14,6 +14,8 @@ #ifndef _LIBSYCL_SYCL_HPP #define _LIBSYCL_SYCL_HPP +#include +#include #include #include diff --git a/libsycl/src/CMakeLists.txt b/libsycl/src/CMakeLists.txt index 7ee228c8f748..0a83f2ef3644 100644 --- a/libsycl/src/CMakeLists.txt +++ b/libsycl/src/CMakeLists.txt @@ -83,7 +83,10 @@ endfunction(add_sycl_rt_library) set(LIBSYCL_SOURCES "exception.cpp" "exception_list.cpp" + "device.cpp" + "device_selector.cpp" "platform.cpp" + "detail/device_impl.cpp" "detail/global_objects.cpp" "detail/platform_impl.cpp" "detail/offload/offload_utils.cpp" diff --git a/libsycl/src/detail/device_impl.cpp b/libsycl/src/detail/device_impl.cpp new file mode 100644 index 000000000000..de702cc4b783 --- /dev/null +++ b/libsycl/src/detail/device_impl.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include +#include + +_LIBSYCL_BEGIN_NAMESPACE_SYCL + +namespace detail { + +bool DeviceImpl::has(aspect Aspect) const { + switch (Aspect) { + case (aspect::cpu): + return isCPU(); + case (aspect::gpu): + return isGPU(); + case (aspect::accelerator): + return isAccelerator(); + case (aspect::custom): + return false; + case (aspect::emulated): + return false; + case (aspect::host_debuggable): + return false; + default: + // Other aspects are not implemented yet + return false; + } +} + +info::device_type DeviceImpl::getDeviceType() const { + return getInfo(); +} + +bool DeviceImpl::isCPU() const { + return getDeviceType() == info::device_type::cpu; +} + +bool DeviceImpl::isGPU() const { + return getDeviceType() == info::device_type::gpu; +} + +bool DeviceImpl::isAccelerator() const { + return getDeviceType() == info::device_type::accelerator; +} + +backend DeviceImpl::getBackend() const { return MPlatform.getBackend(); } + +} // namespace detail +_LIBSYCL_END_NAMESPACE_SYCL diff --git a/libsycl/src/detail/device_impl.hpp b/libsycl/src/detail/device_impl.hpp new file mode 100644 index 000000000000..5fd0893c9912 --- /dev/null +++ b/libsycl/src/detail/device_impl.hpp @@ -0,0 +1,127 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBSYCL_DEVICE_IMPL +#define _LIBSYCL_DEVICE_IMPL + +#include +#include + +#include +#include + +#include + +_LIBSYCL_BEGIN_NAMESPACE_SYCL +namespace detail { + +class DeviceImpl { + // Helper to limit DeviceImpl creation. It must be created in platform ctor + // only. Using tag instead of private ctor + friend class to allow make_unique + // usage and to align with classes which impl is shared_ptr<>. + struct PrivateTag { + explicit PrivateTag() = default; + }; + friend class PlatformImpl; + +public: + /// Constructs a SYCL device instance using the provided + /// offload device instance. + /// + /// \param Device is a raw offload library handle representing device. + /// \param Platform is a platform this device belongs to. + /// All device impls must be created in corresponding platform ctor. + explicit DeviceImpl(ol_device_handle_t Device, PlatformImpl &Platform, + PrivateTag) + : MOffloadDevice(Device), MPlatform(Platform) {} + + ~DeviceImpl() = default; + + /// Queries device type from offloading runtime + /// + /// \return device type of the device + info::device_type getDeviceType() const; + + /// Check if device is a CPU device + /// + /// \return true if SYCL device is a CPU device + bool isCPU() const; + + /// Check if device is a GPU device + /// + /// \return true if SYCL device is a GPU device + bool isGPU() const; + + /// Check if device is an accelerator device + /// + /// \return true if SYCL device is an accelerator device + bool isAccelerator() const; + + /// Returns the backend associated with this device. + /// + /// \return the sycl::backend associated with this device. + backend getBackend() const; + + /// Returns the implementation class object of platform associated with this + /// device. + /// + /// \return platform implementation object this device belongs to. + PlatformImpl &getPlatformImpl() const { return MPlatform; } + + /// Checks if this device supports aspect. + /// + /// \param Aspect to perform a check of. + /// \return true if this device has the given aspect. + bool has(aspect Aspect) const; + + /// Queries this device for information requested by the template parameter + /// param. + /// The return type depends on information being queried. + template typename Param::return_type getInfo() const { + using namespace info::device; + using Map = info_ol_mapping; + + constexpr ol_device_info_t olInfo = map_info_desc( + Map::M{OL_DEVICE_INFO_TYPE}, + Map::M{OL_DEVICE_INFO_NAME}, + Map::M{OL_DEVICE_INFO_VENDOR}, + Map::M{OL_DEVICE_INFO_DRIVER_VERSION}); + + size_t ExpectedSize = 0; + callAndThrow(olGetDeviceInfoSize, MOffloadDevice, olInfo, &ExpectedSize); + + if constexpr (std::is_same_v) { + std::string Result; + // liboffload counts null terminator in the size while std::string + // doesn't. + Result.resize(ExpectedSize - 1); + callAndThrow(olGetDeviceInfo, MOffloadDevice, olInfo, ExpectedSize, + Result.data()); + return Result; + } else if constexpr (olInfo == OL_DEVICE_INFO_TYPE) { + assert((sizeof(typename Param::return_type) == ExpectedSize) && + "Size of info descriptor reported by backend doesn't match with " + "expected."); + ol_device_type_t olType{}; + callAndThrow(olGetDeviceInfo, MOffloadDevice, olInfo, sizeof(olType), + &olType); + return convertDeviceTypeToSYCL(olType); + } else + static_assert(false && "Info descriptor is not properly supported"); + } + +private: + ol_device_handle_t MOffloadDevice = {}; + PlatformImpl &MPlatform; +}; + +} // namespace detail + +_LIBSYCL_END_NAMESPACE_SYCL + +#endif // _LIBSYCL_DEVICE_IMPL diff --git a/libsycl/src/detail/global_objects.cpp b/libsycl/src/detail/global_objects.cpp index fe29f5e7d72c..fa7274d13704 100644 --- a/libsycl/src/detail/global_objects.cpp +++ b/libsycl/src/detail/global_objects.cpp @@ -29,7 +29,7 @@ std::vector &getPlatformCache() { return PlatformCache; } -void shutdown() { +static void shutdown() { // No error reporting in shutdown std::ignore = olShutDown(); } diff --git a/libsycl/src/detail/offload/offload_topology.cpp b/libsycl/src/detail/offload/offload_topology.cpp index 624f987a5d1f..5e595e520a45 100644 --- a/libsycl/src/detail/offload/offload_topology.cpp +++ b/libsycl/src/detail/offload/offload_topology.cpp @@ -11,18 +11,57 @@ #include #include -#include _LIBSYCL_BEGIN_NAMESPACE_SYCL namespace detail { +// Platforms for this backend +range_view OffloadTopology::getPlatforms() const { + return {MPlatforms.data(), MPlatforms.size()}; +} + +// Devices for a specific platform (PlatformId is index into Platforms) +range_view +OffloadTopology::getDevices(size_t PlatformId) const { + if (PlatformId >= MDeviceRange.size()) { + return {nullptr, 0}; + } + return MDeviceRange[PlatformId]; +} + +void OffloadTopology::registerNewPlatformsAndDevices( + Platform2DevContainer &PlatformsAndDev) { + if (!PlatformsAndDev.size()) + return; + + // MDeviceRange is populated with iterators of MDevices. Allocate required + // space in advance to keep them valid. + MDevices.reserve(PlatformsAndDev.size()); + + for (auto &[Platform, NewDev] : PlatformsAndDev) { + MDevices.push_back(NewDev); + + // Platform is not unique within PlatformsAndDev but the container is sorted + if (MPlatforms.empty() || MPlatforms.back() != Platform) { + MPlatforms.push_back(Platform); + range_view R{&MDevices.back(), 1 /*Size == 1*/}; + MDeviceRange.push_back(R); + } else { + // Device is inserted already, just increment device count for the current + // platform + MDeviceRange.back().len++; + } + } +} + void discoverOffloadDevices() { callAndThrow(olInit); + // liboffload returns devices sorted by backend + platform. We rely on this + // behavior during device enumeration. using PerBackendDataType = - std::array, - OL_PLATFORM_BACKEND_LAST>; + std::array; PerBackendDataType Mapping; // olIterateDevices() calls the lambda for each device. Devices that fail @@ -31,17 +70,19 @@ void discoverOffloadDevices() { // first failure and interrupt iteration. callNoCheck( olIterateDevices, - [](ol_device_handle_t Dev, void *User) -> bool { - auto *Data = static_cast(User); - ol_platform_handle_t Plat = nullptr; - ol_result_t Res = callNoCheck( - olGetDeviceInfo, Dev, OL_DEVICE_INFO_PLATFORM, sizeof(Plat), &Plat); + [](ol_device_handle_t Dev, void *UserData) -> bool { + auto *Data = static_cast(UserData); + + ol_platform_handle_t Platform = nullptr; + ol_result_t Res = + callNoCheck(olGetDeviceInfo, Dev, OL_DEVICE_INFO_PLATFORM, + sizeof(Platform), &Platform); // If an error occurs, ignore the device and continue iteration. if (Res != OL_SUCCESS) return true; ol_platform_backend_t OlBackend = OL_PLATFORM_BACKEND_UNKNOWN; - Res = callNoCheck(olGetPlatformInfo, Plat, OL_PLATFORM_INFO_BACKEND, + Res = callNoCheck(olGetPlatformInfo, Platform, OL_PLATFORM_INFO_BACKEND, sizeof(OlBackend), &OlBackend); // If an error occurs, ignore the device and continue iteration. if (Res != OL_SUCCESS) @@ -58,9 +99,7 @@ void discoverOffloadDevices() { if (OlBackend >= OL_PLATFORM_BACKEND_LAST) return true; - auto &[Map, DevCount] = (*Data)[static_cast(OlBackend)]; - Map[Plat].push_back(Dev); - DevCount++; + (*Data)[static_cast(OlBackend)].push_back({Platform, Dev}); return true; }, &Mapping); @@ -69,7 +108,7 @@ void discoverOffloadDevices() { for (size_t I = 0; I < OL_PLATFORM_BACKEND_LAST; ++I) { OffloadTopology &Topo = OffloadTopologies[I]; Topo.setBackend(static_cast(I)); - Topo.registerNewPlatformsAndDevices(Mapping[I].first, Mapping[I].second); + Topo.registerNewPlatformsAndDevices(Mapping[I]); } } diff --git a/libsycl/src/detail/offload/offload_topology.hpp b/libsycl/src/detail/offload/offload_topology.hpp index dbd98f953b7e..4d811f1e444d 100644 --- a/libsycl/src/detail/offload/offload_topology.hpp +++ b/libsycl/src/detail/offload/offload_topology.hpp @@ -14,7 +14,6 @@ #include #include -#include #include _LIBSYCL_BEGIN_NAMESPACE_SYCL @@ -31,8 +30,8 @@ template struct range_view { size_t size() const { return len; } }; -using PlatformWithDevStorageType = - std::unordered_map>; +using Platform2DevContainer = + std::vector>; /// Contiguous global storage of platform handlers and device handles (grouped /// by platform) for a backend. @@ -45,71 +44,42 @@ struct OffloadTopology { /// \param B new backend value. void setBackend(ol_platform_backend_t B) { MBackend = B; } + /// Queries backend of this topology. + /// + /// \returns backend of this topology. + ol_platform_backend_t getBackend() const { return MBackend; } + /// Returns all platforms associated with this topology. /// /// \returns minimal span-like view to platforms associated with this /// topology. - range_view platforms() const { - return {MPlatforms.data(), MPlatforms.size()}; - } + range_view getPlatforms() const; /// Returns all devices associated with specific platform. /// - /// \param PlatformId platform_id is index into MPlatforms. + /// \param PlatformId is index into MPlatforms. /// /// \returns minimal span-like view to devices associated with specified /// platform. - range_view - devicesForPlatform(size_t PlatformId) const { - if (PlatformId >= MDevRangePerPlatformId.size()) { - assert(false && "Platform index exceeds number of platforms."); - return {nullptr, 0}; - } - return MDevRangePerPlatformId[PlatformId]; - } + range_view getDevices(size_t PlatformId) const; /// Register new platform and devices into this topology. /// - /// \param PlatformsAndDev associative container with platforms & devices. - /// \param TotalDevCount total device count for the platform. - void - registerNewPlatformsAndDevices(PlatformWithDevStorageType &PlatformsAndDev, - size_t TotalDevCount) { - if (!PlatformsAndDev.size()) - return; - - MPlatforms.reserve(PlatformsAndDev.size()); - MDevRangePerPlatformId.reserve(MPlatforms.size()); - MDevices.reserve(TotalDevCount); - - for (auto &[NewPlatform, NewDevs] : PlatformsAndDev) { - MPlatforms.push_back(NewPlatform); - range_view R{MDevices.data() + MDevices.size(), - NewDevs.size()}; - MDevices.insert(MDevices.end(), NewDevs.begin(), NewDevs.end()); - MDevRangePerPlatformId.push_back(R); - } - - assert(TotalDevCount == MDevices.size()); - } - - /// Queries backend of this topology. - /// - /// \returns backend of this topology. - ol_platform_backend_t backend() const { return MBackend; } + /// \param PlatformsAndDev collection of platforms & devices. + void registerNewPlatformsAndDevices(Platform2DevContainer &PlatformsAndDev); private: ol_platform_backend_t MBackend = OL_PLATFORM_BACKEND_UNKNOWN; // Platforms and devices belonging to this backend (flattened) std::vector MPlatforms; - std::vector MDevices; // sorted by platform + + // Devices are sorted by platform (guarantee from liboffload) + std::vector MDevices; // Vector holding range of devices for each platform (index is platform index - // within MPlatforms) - std::vector> - MDevRangePerPlatformId; // MDevRangePerPlatformId.size() == - // MPlatforms.size() + // within Platforms), so MDeviceRange.size() == MPlatforms.size() + std::vector> MDeviceRange; }; // Initialize the topologies by calling olIterateDevices. diff --git a/libsycl/src/detail/offload/offload_utils.cpp b/libsycl/src/detail/offload/offload_utils.cpp index ed3d19767221..9a2609daddce 100644 --- a/libsycl/src/detail/offload/offload_utils.cpp +++ b/libsycl/src/detail/offload/offload_utils.cpp @@ -56,8 +56,35 @@ backend convertBackend(ol_platform_backend_t Backend) { case OL_PLATFORM_BACKEND_AMDGPU: return backend::hip; default: - throw exception(make_error_code(errc::runtime), - "convertBackend: Unsupported backend"); + throw exception(make_error_code(errc::runtime), "Unsupported backend"); + } +} + +ol_device_type_t convertDeviceTypeToOL(info::device_type DeviceType) { + switch (DeviceType) { + case info::device_type::all: + return OL_DEVICE_TYPE_ALL; + case info::device_type::gpu: + return OL_DEVICE_TYPE_GPU; + case info::device_type::cpu: + return OL_DEVICE_TYPE_CPU; + case info::device_type::automatic: + return OL_DEVICE_TYPE_DEFAULT; + default: + throw exception(sycl::make_error_code(sycl::errc::runtime), + "Device type is not supported"); + } +} + +info::device_type convertDeviceTypeToSYCL(ol_device_type_t DeviceType) { + switch (DeviceType) { + case OL_DEVICE_TYPE_GPU: + return info::device_type::gpu; + case OL_DEVICE_TYPE_CPU: + return info::device_type::cpu; + default: + throw exception(sycl::make_error_code(sycl::errc::runtime), + "Device type is not supported"); } } diff --git a/libsycl/src/detail/offload/offload_utils.hpp b/libsycl/src/detail/offload/offload_utils.hpp index e6113e5479f9..f32326fb87fc 100644 --- a/libsycl/src/detail/offload/offload_utils.hpp +++ b/libsycl/src/detail/offload/offload_utils.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -85,6 +86,20 @@ void callAndThrow(FunctionType &Function, ArgsT &&...Args) { /// \returns sycl::backend matching specified liboffload backend. backend convertBackend(ol_platform_backend_t Backend); +/// Converts SYCL device type to liboffload type. +/// +/// \param DeviceType SYCL device type. +/// +/// \returns ol_device_type_t matching specified SYCL device type. +ol_device_type_t convertDeviceTypeToOL(info::device_type DeviceType); + +/// Converts liboffload device type to SYCL type. +/// +/// \param DeviceType liboffload device type. +/// +/// \returns SYCL device type matching specified liboffload device type. +info::device_type convertDeviceTypeToSYCL(ol_device_type_t DeviceType); + /// Helper to map SYCL information descriptors to OL__INFO_. /// /// Typical usage: diff --git a/libsycl/src/detail/platform_impl.cpp b/libsycl/src/detail/platform_impl.cpp index 28bf709d5c07..0116ad68d4bd 100644 --- a/libsycl/src/detail/platform_impl.cpp +++ b/libsycl/src/detail/platform_impl.cpp @@ -9,9 +9,13 @@ #include #include +#include #include #include +#include +#include + _LIBSYCL_BEGIN_NAMESPACE_SYCL namespace detail { @@ -19,6 +23,7 @@ namespace detail { PlatformImpl &PlatformImpl::getPlatformImpl(ol_platform_handle_t Platform) { auto &PlatformCache = getPlatformCache(); for (auto &PlatImpl : PlatformCache) { + assert(PlatImpl && "Platform impl can not be nullptr"); if (PlatImpl->getHandleRef() == Platform) return *PlatImpl; } @@ -32,10 +37,11 @@ PlatformImpl &PlatformImpl::getPlatformImpl(ol_platform_handle_t Platform) { const std::vector &PlatformImpl::getPlatforms() { [[maybe_unused]] static auto InitPlatformsOnce = []() { discoverOffloadDevices(); + auto &PlatformCache = getPlatformCache(); for (const auto &Topo : getOffloadTopologies()) { size_t PlatformIndex = 0; - for (const auto &OffloadPlatform : Topo.platforms()) { + for (const auto &OffloadPlatform : Topo.getPlatforms()) { PlatformCache.emplace_back(std::make_unique( OffloadPlatform, PlatformIndex++, PrivateTag{})); } @@ -53,6 +59,65 @@ PlatformImpl::PlatformImpl(ol_platform_handle_t Platform, size_t PlatformIndex, sizeof(Backend), &Backend); MBackend = convertBackend(Backend); MOffloadBackend = Backend; + + const auto &Topologies = getOffloadTopologies(); + auto RootTopologyIt = std::find_if( + Topologies.begin(), Topologies.end(), [&](const OffloadTopology &Topo) { + return Topo.getBackend() == MOffloadBackend; + }); + + assert(RootTopologyIt != Topologies.end() && + "Root topology for platform must always exist"); + auto DevRange = RootTopologyIt->getDevices(MOffloadPlatformIndex); + MRootDevices.reserve(DevRange.size()); + std::for_each(DevRange.begin(), DevRange.end(), + [&](const ol_device_handle_t &Device) { + MRootDevices.emplace_back(std::make_unique( + Device, *this, DeviceImpl::PrivateTag{})); + }); } + +const std::vector &PlatformImpl::getRootDevices() const { + return MRootDevices; +} + +bool PlatformImpl::has(aspect Aspect) const { + const auto &Devices = getRootDevices(); + return std::all_of( + Devices.begin(), Devices.end(), + [&Aspect](const DeviceImplUPtr &Device) { return Device->has(Aspect); }); +} + +void PlatformImpl::iterateDevices( + info::device_type DeviceType, + std::function callback) const { + // Early exit if host/custom/accelerator device is requested: + // - host device is deprecated and not required by the SYCL 2020 + // specification. + // - accelerator and custom devices are unsupported by liboffload. + if ((DeviceType == info::device_type::host) || + (DeviceType == info::device_type::custom) || + (DeviceType == info::device_type::accelerator)) + return; + + const auto &DeviceImpls = getRootDevices(); + assert(!DeviceImpls.empty() && + "Platform can't exist without at least one device."); + + // TODO: Need a way to get default device from liboffload. + // As a temporal solution just return the first device for DeviceType == + // automatic. + if (DeviceType == info::device_type::automatic) { + callback(DeviceImpls[0].get()); + return; + } + + bool KeepAll = DeviceType == info::device_type::all; + for (auto &Impl : DeviceImpls) { + if (KeepAll || DeviceType == Impl->getDeviceType()) + callback(Impl.get()); + } +} + } // namespace detail _LIBSYCL_END_NAMESPACE_SYCL diff --git a/libsycl/src/detail/platform_impl.hpp b/libsycl/src/detail/platform_impl.hpp index a17b5d70a182..e23ce6a49228 100644 --- a/libsycl/src/detail/platform_impl.hpp +++ b/libsycl/src/detail/platform_impl.hpp @@ -13,10 +13,12 @@ #include #include +#include #include #include +#include #include #include #include @@ -26,9 +28,15 @@ _LIBSYCL_BEGIN_NAMESPACE_SYCL namespace detail { +class DeviceImpl; + using PlatformImplUPtr = std::unique_ptr; +using DeviceImplUPtr = std::unique_ptr; class PlatformImpl { + // Helper to limit PlatformImpl creation. It must be created in getPlatforms + // only. Using tag instead of private ctor + friend class to allow make_unique + // usage and to align with classes which impl is shared_ptr<>. struct PrivateTag { explicit PrivateTag() = default; }; @@ -73,7 +81,16 @@ public: /// \return the PlatformImpl representing the offloading RT platform. static PlatformImpl &getPlatformImpl(ol_platform_handle_t Platform); - /// Queries this platform for info. + /// Indicates if all of the SYCL devices on this platform have the + /// given aspect. + /// + /// \param Aspect is one of the values defined in SYCL 2020 Section 4.6.4.5. + /// + /// \return true all of the SYCL devices on this platform have the + /// given aspect. + bool has(aspect Aspect) const; + + /// Queries this SYCL platform for info. /// /// The return type depends on information being queried. template typename Param::return_type getInfo() const { @@ -99,11 +116,20 @@ public: return Result; } + /// Calls "callback" with every root device of type == DeviceType associated + /// with this platform + void iterateDevices(info::device_type DeviceType, + std::function callback) const; + private: + const std::vector &getRootDevices() const; + ol_platform_handle_t MOffloadPlatform{}; size_t MOffloadPlatformIndex{}; ol_platform_backend_t MOffloadBackend{OL_PLATFORM_BACKEND_UNKNOWN}; backend MBackend{}; + + std::vector MRootDevices; }; } // namespace detail diff --git a/libsycl/src/device.cpp b/libsycl/src/device.cpp new file mode 100644 index 000000000000..db61d2ff3a22 --- /dev/null +++ b/libsycl/src/device.cpp @@ -0,0 +1,104 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include + +#include +#include + +#include + +_LIBSYCL_BEGIN_NAMESPACE_SYCL + +device::device() : device(default_selector_v) {} + +bool device::is_cpu() const { return impl->isCPU(); } + +bool device::is_gpu() const { return impl->isGPU(); } + +bool device::is_accelerator() const { return impl->isAccelerator(); } + +platform device::get_platform() const { + return detail::createSyclObjFromImpl(impl->getPlatformImpl()); +} + +backend device::get_backend() const noexcept { return impl->getBackend(); } + +std::vector device::get_devices(info::device_type DeviceType) { + std::vector Devices; + + // Not calling platform::get_devices to avoid multiple vector packing + for (auto &PlatformImpl : detail::PlatformImpl::getPlatforms()) { + assert(PlatformImpl && "PlatformImpl can not be nullptr"); + PlatformImpl->iterateDevices( + DeviceType, [&Devices](detail::DeviceImpl *DevImpl) { + assert(DevImpl && "Device impl can't be nullptr"); + Devices.push_back(detail::createSyclObjFromImpl(*DevImpl)); + }); + } + + return Devices; +} + +template +std::vector device::create_sub_devices(size_t ComputeUnits) const { + throw exception(make_error_code(errc::feature_not_supported), + "Partitioning is not supported."); +} + +template _LIBSYCL_EXPORT std::vector +device::create_sub_devices( + size_t ComputeUnits) const; + +template +std::vector +device::create_sub_devices(const std::vector &Counts) const { + throw exception(make_error_code(errc::feature_not_supported), + "Partitioning is not supported."); +} + +template _LIBSYCL_EXPORT std::vector +device::create_sub_devices( + const std::vector &Counts) const; + +template +std::vector device::create_sub_devices( + info::partition_affinity_domain AffinityDomain) const { + throw exception(make_error_code(errc::feature_not_supported), + "Partitioning is not supported."); +} + +template _LIBSYCL_EXPORT std::vector device::create_sub_devices< + info::partition_property::partition_by_affinity_domain>( + info::partition_affinity_domain AffinityDomain) const; + +bool device::has(aspect Aspect) const { return impl->has(Aspect); } + +template +detail::is_device_info_desc_t device::get_info() const { + return impl->getInfo(); +} + +template <> +_LIBSYCL_EXPORT detail::is_device_info_desc_t +device::get_info() const { + static_assert( + std::is_same_v); + return get_platform(); +} + +#define _LIBSYCL_EXPORT_GET_INFO(Desc) \ + template _LIBSYCL_EXPORT detail::is_device_info_desc_t \ + device::get_info() const; +_LIBSYCL_EXPORT_GET_INFO(device_type) +_LIBSYCL_EXPORT_GET_INFO(name) +_LIBSYCL_EXPORT_GET_INFO(vendor) +_LIBSYCL_EXPORT_GET_INFO(driver_version) +#undef _LIBSYCL_EXPORT_GET_INFO + +_LIBSYCL_END_NAMESPACE_SYCL diff --git a/libsycl/src/device_selector.cpp b/libsycl/src/device_selector.cpp new file mode 100644 index 000000000000..86e5f5657c6b --- /dev/null +++ b/libsycl/src/device_selector.cpp @@ -0,0 +1,118 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include +#include + +#include + +#include + +_LIBSYCL_BEGIN_NAMESPACE_SYCL + +static constexpr int MatchedTypeDefaultScore = 1000; +static constexpr int GPUDeviceDefaultScore = 500; +static constexpr int CPUDeviceDefaultScore = 300; +static constexpr int AccDeviceDefaultScore = 75; +static constexpr int RejectDeviceScore = -1; + +static int getDevicePreference(const device &Device) { + int Score = 0; + const auto &DeviceImpl = detail::getSyclObjImpl(Device); + + // TODO: increase score for devices with compatible program images. + + if (DeviceImpl->getBackend() == backend::level_zero) + Score += 50; + + return Score; +} + +_LIBSYCL_EXPORT int default_selector_v(const device &dev) { + int Score = getDevicePreference(dev); + + if (dev.is_gpu()) + Score += GPUDeviceDefaultScore; + else if (dev.is_cpu()) + Score += CPUDeviceDefaultScore; + else if (dev.is_accelerator()) + Score += AccDeviceDefaultScore; + + return Score; +} + +_LIBSYCL_EXPORT int gpu_selector_v(const device &Dev) { + return Dev.is_gpu() ? MatchedTypeDefaultScore + getDevicePreference(Dev) + : RejectDeviceScore; +} + +_LIBSYCL_EXPORT int cpu_selector_v(const device &Dev) { + return Dev.is_cpu() ? MatchedTypeDefaultScore + getDevicePreference(Dev) + : RejectDeviceScore; +} + +_LIBSYCL_EXPORT int accelerator_selector_v(const device &Dev) { + return Dev.is_accelerator() + ? MatchedTypeDefaultScore + getDevicePreference(Dev) + : RejectDeviceScore; +} + +_LIBSYCL_EXPORT detail::DeviceSelectorInvocableType +aspect_selector(const std::vector &RequireList, + const std::vector &DenyList) { + return [=](const sycl::device &Dev) { + // 4.6.1.1. Device selector: + // If no aspects are passed in, the generated selector behaves like + // default_selector_v. + if (RequireList.empty() && DenyList.empty()) + return default_selector_v(Dev); + + auto HasAspect = [&Dev](const aspect &Aspect) -> bool { + return Dev.has(Aspect); + }; + if (!std::all_of(RequireList.begin(), RequireList.end(), HasAspect)) + return RejectDeviceScore; + + if (std::any_of(DenyList.begin(), DenyList.end(), HasAspect)) + return RejectDeviceScore; + + return MatchedTypeDefaultScore + getDevicePreference(Dev); + }; +} + +namespace detail { + +_LIBSYCL_EXPORT device +SelectDevice(const DeviceSelectorInvocableType &DeviceSelector) { + int ChosenDeviceScore = RejectDeviceScore; + const device *ChosenDevice = nullptr; + + std::vector Devices = device::get_devices(); + for (const auto &Device : Devices) { + int CurrentDevScore = DeviceSelector(Device); + if (CurrentDevScore < 0) + continue; + + if ((ChosenDeviceScore < CurrentDevScore) || + ((ChosenDeviceScore == CurrentDevScore) && + (getDevicePreference(*ChosenDevice) < getDevicePreference(Device)))) { + ChosenDevice = &Device; + ChosenDeviceScore = CurrentDevScore; + } + } + + if (ChosenDevice != nullptr) + return *ChosenDevice; + + throw exception(make_error_code(errc::runtime), + "No device of requested type is available"); +} + +} // namespace detail + +_LIBSYCL_END_NAMESPACE_SYCL diff --git a/libsycl/src/platform.cpp b/libsycl/src/platform.cpp index 7f401583d669..c04c0a628177 100644 --- a/libsycl/src/platform.cpp +++ b/libsycl/src/platform.cpp @@ -8,10 +8,9 @@ #include +#include #include -#include - _LIBSYCL_BEGIN_NAMESPACE_SYCL backend platform::get_backend() const noexcept { return impl->getBackend(); } @@ -21,12 +20,24 @@ std::vector platform::get_platforms() { std::vector Platforms; Platforms.reserve(PlatformImpls.size()); for (auto &PlatformImpl : PlatformImpls) { - platform Platform = detail::createSyclObjFromImpl(*PlatformImpl); - Platforms.push_back(std::move(Platform)); + Platforms.emplace_back( + detail::createSyclObjFromImpl(*PlatformImpl.get())); } return Platforms; } +std::vector platform::get_devices(info::device_type DeviceType) const { + std::vector Devices; + impl->iterateDevices(DeviceType, [&Devices](detail::DeviceImpl *DevImpl) { + assert(DevImpl && "Device impl can't be nullptr"); + Devices.push_back(detail::createSyclObjFromImpl(*DevImpl)); + }); + + return Devices; +} + +bool platform::has(aspect Aspect) const { return impl->has(Aspect); } + template detail::is_platform_info_desc_t platform::get_info() const { return impl->getInfo(); diff --git a/libsycl/tools/sycl-ls/sycl-ls.cpp b/libsycl/tools/sycl-ls/sycl-ls.cpp index bd6c10899d74..2938d767a404 100644 --- a/libsycl/tools/sycl-ls/sycl-ls.cpp +++ b/libsycl/tools/sycl-ls/sycl-ls.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// // -// The "sycl-ls" utility lists all platforms discovered by SYCL. +// The "sycl-ls" utility lists all platforms and devices discovered by SYCL. // // There are two types of output: // concise (default) and @@ -36,14 +36,69 @@ inline std::string_view getBackendName(const backend &Backend) { return ""; } +std::string getDeviceTypeName(const device &Device) { + auto DeviceType = Device.get_info(); + switch (DeviceType) { + case info::device_type::cpu: + return "cpu"; + case info::device_type::gpu: + return "gpu"; + case info::device_type::host: + return "host"; + case info::device_type::accelerator: + return "accelerator"; + default: + return "unknown"; + } +} + +static void printDeviceInfo(const device &Device, bool Verbose, + const std::string &Prepend) { + auto DeviceName = Device.get_info(); + auto DeviceVendor = Device.get_info(); + auto DeviceDriverVersion = Device.get_info(); + + if (Verbose) { + std::cout << Prepend << "Type : " << getDeviceTypeName(Device) + << std::endl; + std::cout << Prepend << "Name : " << DeviceName << std::endl; + std::cout << Prepend << "Vendor : " << DeviceVendor << std::endl; + std::cout << Prepend << "Driver : " << DeviceDriverVersion + << std::endl; + } else { + std::cout << Prepend << ", " << DeviceName << " [" << DeviceDriverVersion + << "]" << std::endl; + } +} + +static void +printSelectorChoice(const detail::DeviceSelectorInvocableType &Selector, + const std::string &Prepend) { + try { + const auto &Device = device(Selector); + std::string DeviceTypeName = getDeviceTypeName(Device); + auto Platform = Device.get_info(); + auto PlatformName = Platform.get_info(); + printDeviceInfo(Device, false /*Verbose*/, + Prepend + DeviceTypeName + ", " + PlatformName); + } catch (const sycl::exception &Exception) { + std::string What = Exception.what(); + constexpr size_t MaxLength = 80; + // Truncate long string so it can fit in one-line + if (What.length() > MaxLength) + What = What.substr(0, MaxLength) + "..."; + std::cout << Prepend << What << std::endl; + } +} + int main(int argc, char **argv) { llvm::cl::opt Verbose( - "verbose", - llvm::cl::desc("Verbosely prints all the discovered platforms")); + "verbose", llvm::cl::desc("Verbosely prints all the discovered devices")); llvm::cl::alias VerboseShort("v", llvm::cl::desc("Alias for -verbose"), llvm::cl::aliasopt(Verbose)); llvm::cl::ParseCommandLineOptions( - argc, argv, "This program lists all backends discovered by SYCL"); + argc, argv, + "This program lists all backends and devices discovered by SYCL"); try { const auto &Platforms = platform::get_platforms(); @@ -55,8 +110,17 @@ int main(int argc, char **argv) { for (const auto &Platform : Platforms) { backend Backend = Platform.get_backend(); - std::cout << "[" << getBackendName(Backend) << ":" - << "unknown" << "]" << std::endl; + auto PlatformName = Platform.get_info(); + const auto &Devices = Platform.get_devices(); + + for (const auto &Device : Devices) { + std::cout << "[" << getBackendName(Backend) << ":" + << getDeviceTypeName(Device) << "]"; + std::cout << " "; + // Verbose parameter is set to false to print regular devices output + // first + printDeviceInfo(Device, false, PlatformName); + } } if (Verbose) { @@ -71,8 +135,19 @@ int main(int argc, char **argv) { std::cout << " Version : " << PlatformVersion << std::endl; std::cout << " Name : " << PlatformName << std::endl; std::cout << " Vendor : " << PlatformVendor << std::endl; - std::cout << " Devices : " << "unknown" << std::endl; + + const auto &Devices = Platform.get_devices(); + std::cout << " Devices : " << Devices.size() << std::endl; + for (const auto &Device : Devices) { + printDeviceInfo(Device, true, " "); + } } + + // Print built-in device selectors choice + printSelectorChoice(default_selector_v, "default_selector() : "); + printSelectorChoice(accelerator_selector_v, "accelerator_selector() : "); + printSelectorChoice(cpu_selector_v, "cpu_selector() : "); + printSelectorChoice(gpu_selector_v, "gpu_selector() : "); } } catch (sycl::exception &e) { std::cerr << "SYCL Exception encountered: " << e.what() << std::endl