[llvm] [SYCL] Add sycl::device initial implementation (PR #176972)
Kseniya Tikhomirova via llvm-commits
llvm-commits at lists.llvm.org
Tue Jan 20 09:20:50 PST 2026
https://github.com/KseniyaTikhomirova created https://github.com/llvm/llvm-project/pull/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
>From 372e12252f621ea8b20868fb6102f67f3fd4ff3e Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Wed, 19 Nov 2025 10:15:32 -0800
Subject: [PATCH] [SYCL] Add sycl::device initial implementation
Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
libsycl/docs/index.rst | 25 ++-
libsycl/include/sycl/__impl/aspect.hpp | 43 +++++
libsycl/include/sycl/__impl/device.hpp | 182 ++++++++++++++++++
.../include/sycl/__impl/device_selector.hpp | 122 ++++++++++++
libsycl/include/sycl/__impl/info/device.hpp | 80 ++++++++
.../include/sycl/__impl/info/device_type.hpp | 35 ++++
libsycl/include/sycl/__impl/platform.hpp | 24 +++
libsycl/include/sycl/sycl.hpp | 2 +
libsycl/src/CMakeLists.txt | 3 +
libsycl/src/detail/device_impl.cpp | 55 ++++++
libsycl/src/detail/device_impl.hpp | 122 ++++++++++++
libsycl/src/detail/global_objects.cpp | 3 +-
.../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 | 64 +++++-
libsycl/src/detail/platform_impl.hpp | 26 ++-
libsycl/src/device.cpp | 104 ++++++++++
libsycl/src/device_selector.cpp | 123 ++++++++++++
libsycl/src/platform.cpp | 19 +-
libsycl/tools/sycl-ls/sycl-ls.cpp | 88 ++++++++-
22 files changed, 1214 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 ce48743be3ae2..03e083227ace4 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<info::device::aspects>()``
+* ``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 0000000000000..0a73dd621df9a
--- /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 <sycl/__impl/detail/config.hpp>
+
+#include <cstdint>
+
+_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 0000000000000..84f7e20175f6d
--- /dev/null
+++ b/libsycl/include/sycl/__impl/device.hpp
@@ -0,0 +1,182 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <sycl/__impl/aspect.hpp>
+#include <sycl/__impl/backend.hpp>
+#include <sycl/__impl/device_selector.hpp>
+#include <sycl/__impl/info/device.hpp>
+
+#include <sycl/__impl/detail/config.hpp>
+#include <sycl/__impl/detail/obj_utils.hpp>
+
+_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 device selector provided.
+ /// \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<!std::is_same_v<DeviceSelector, detail::DeviceImpl>>,
+ typename = detail::EnableIfDeviceSelectorIsInvocable<DeviceSelector>>
+ 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. Information descriptors.
+ template <typename Param>
+ detail::is_device_info_desc_t<Param> get_info() const;
+
+ /// Queries this SYCL device for SYCL backend-specific information.
+ ///
+ /// The return type depends on information being queried.
+ template <typename Param>
+ typename detail::is_backend_info_desc<Param>::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 must be thrown.
+ ///
+ /// \param ComputeUnits is a desired count of compute units in each sub
+ /// device.
+ /// \return A vector class of sub devices partitioned from this SYCL
+ /// device equally based on the ComputeUnits parameter.
+ template <info::partition_property prop>
+ std::vector<device> 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 must be thrown.
+ ///
+ /// \param Counts is a std::vector of desired compute units in sub devices.
+ /// \return a std::vector of sub devices partitioned from this SYCL device by
+ /// count sizes based on the Counts parameter.
+ template <info::partition_property prop>
+ std::vector<device>
+ create_sub_devices(const std::vector<size_t> &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 info::affinity_domain provided a feature_not_supported
+ /// exception must be thrown.
+ ///
+ /// \param AffinityDomain is one of the values described in Table 4.20 of SYCL
+ /// Spec.
+ /// \return a vector class of sub devices partitioned from this SYCL
+ /// device by affinity domain based on the AffinityDomain parameter.
+ template <info::partition_property prop>
+ std::vector<device>
+ 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 SYCL Spec.
+ /// \return a std::vector containing all SYCL devices available in the system
+ /// of the device type specified.
+ static std::vector<device>
+ 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<sycl::device> : public sycl::detail::HashBase<sycl::device> {};
+
+#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 0000000000000..2f118367fc1e0
--- /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) included with all SYCL implementations.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL___IMPL_DEVICE_SELECTOR_HPP
+#define _LIBSYCL___IMPL_DEVICE_SELECTOR_HPP
+
+#include <sycl/__impl/aspect.hpp>
+#include <sycl/__impl/detail/config.hpp>
+
+#include <functional>
+
+_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<int(const sycl::device &)>;
+
+template <typename DeviceSelector>
+using EnableIfDeviceSelectorIsInvocable = std::enable_if_t<
+ std::is_invocable_r_v<int, DeviceSelector &, const device &>>;
+
+/// 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 SYCL 2020 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 SYCL 2020 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 SYCL 2020 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 SYCL 2020 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<aspect> &RequireList,
+ const std::vector<aspect> &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 <typename... AspectListT>
+detail::DeviceSelectorInvocableType aspect_selector(AspectListT... AspectList) {
+ std::vector<aspect> 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 <aspect... AspectList>
+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 0000000000000..d5eee410275bb
--- /dev/null
+++ b/libsycl/include/sycl/__impl/info/device.hpp
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <sycl/__impl/aspect.hpp>
+#include <sycl/__impl/detail/config.hpp>
+#include <sycl/__impl/info/desc_base.hpp>
+#include <sycl/__impl/info/device_type.hpp>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+
+class device;
+class platform;
+
+namespace detail {
+template <typename T>
+using is_device_info_desc_t = typename is_info_desc<T, device>::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<device_type, sycl::device> {
+ using return_type = sycl::info::device_type;
+};
+struct name : detail::info_desc_tag<name, sycl::device> {
+ using return_type = std::string;
+};
+struct vendor : detail::info_desc_tag<vendor, sycl::device> {
+ using return_type = std::string;
+};
+struct driver_version : detail::info_desc_tag<driver_version, sycl::device> {
+ using return_type = std::string;
+};
+struct platform : detail::info_desc_tag<platform, sycl::device> {
+ 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 0000000000000..90db655063859
--- /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 <sycl/__impl/detail/config.hpp>
+
+#include <cstdint>
+
+_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 c7915eaa224fe..7566eec89bc9e 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 <sycl/__impl/aspect.hpp>
#include <sycl/__impl/backend.hpp>
#include <sycl/__impl/detail/config.hpp>
#include <sycl/__impl/detail/obj_utils.hpp>
+#include <sycl/__impl/info/device_type.hpp>
#include <sycl/__impl/info/platform.hpp>
#include <memory>
@@ -25,6 +27,8 @@
_LIBSYCL_BEGIN_NAMESPACE_SYCL
+class device;
+
namespace detail {
class PlatformImpl;
} // namespace detail
@@ -56,6 +60,16 @@ class _LIBSYCL_EXPORT platform {
/// \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.
+ std::vector<device>
+ 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,16 @@ class _LIBSYCL_EXPORT platform {
typename detail::is_backend_info_desc<Param>::return_type
get_backend_info() const;
+ /// Indicates if all of the SYCL devices on this platform have the
+ /// given feature.
+ ///
+ /// \param Aspect is one of the values in Table 4.20 of the SYCL 2020
+ /// Provisional Spec.
+ ///
+ /// \return true if all of the SYCL devices on this platform have the
+ /// given feature.
+ 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 ef91ab2381770..3e7f81092256c 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 <sycl/__impl/device.hpp>
+#include <sycl/__impl/device_selector.hpp>
#include <sycl/__impl/exception.hpp>
#include <sycl/__impl/platform.hpp>
diff --git a/libsycl/src/CMakeLists.txt b/libsycl/src/CMakeLists.txt
index 7ee228c8f7485..0a83f2ef36443 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 0000000000000..de702cc4b7839
--- /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 <detail/device_impl.hpp>
+#include <detail/platform_impl.hpp>
+
+_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<info::device::device_type>();
+}
+
+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 0000000000000..bd4f3a99561dd
--- /dev/null
+++ b/libsycl/src/detail/device_impl.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL_DEVICE_IMPL
+#define _LIBSYCL_DEVICE_IMPL
+
+#include <sycl/__impl/detail/config.hpp>
+#include <sycl/__impl/device.hpp>
+
+#include <detail/offload/offload_utils.hpp>
+#include <detail/platform_impl.hpp>
+
+#include <OffloadAPI.h>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+namespace detail {
+
+class DeviceImpl {
+ 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> typename Param::return_type getInfo() const {
+ using namespace info::device;
+ using Map = info_ol_mapping<ol_device_info_t>;
+
+ constexpr ol_device_info_t olInfo = map_info_desc<Param, ol_device_info_t>(
+ Map::M<device_type>{OL_DEVICE_INFO_TYPE},
+ Map::M<name>{OL_DEVICE_INFO_NAME},
+ Map::M<vendor>{OL_DEVICE_INFO_VENDOR},
+ Map::M<driver_version>{OL_DEVICE_INFO_DRIVER_VERSION});
+
+ size_t ExpectedSize = 0;
+ callAndThrow(olGetDeviceInfoSize, MOffloadDevice, olInfo, &ExpectedSize);
+
+ if constexpr (std::is_same_v<typename Param::return_type, std::string>) {
+ std::string Result;
+ Result.resize(ExpectedSize - 1);
+ callAndThrow(olGetDeviceInfo, MOffloadDevice, olInfo, ExpectedSize,
+ Result.data());
+ return Result;
+ } else if constexpr (olInfo == OL_DEVICE_INFO_TYPE) {
+ assert((sizeof(DescType) == 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 fe29f5e7d72c0..06e44a36a21e2 100644
--- a/libsycl/src/detail/global_objects.cpp
+++ b/libsycl/src/detail/global_objects.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include <detail/device_impl.hpp>
#include <detail/global_objects.hpp>
#include <detail/platform_impl.hpp>
@@ -29,7 +30,7 @@ std::vector<PlatformImplUPtr> &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 624f987a5d1f4..d7cfa30678ca3 100644
--- a/libsycl/src/detail/offload/offload_topology.cpp
+++ b/libsycl/src/detail/offload/offload_topology.cpp
@@ -11,18 +11,57 @@
#include <detail/offload/offload_utils.hpp>
#include <array>
-#include <unordered_map>
_LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {
+// Platforms for this backend
+range_view<const ol_platform_handle_t> OffloadTopology::getPlatforms() const {
+ return {MPlatforms.data(), MPlatforms.size()};
+}
+
+// Devices for a specific platform (platform_id is index into Platforms)
+range_view<ol_device_handle_t>
+OffloadTopology::getDevices(size_t PlatformId) const {
+ if (PlatformId >= MDeviceRange.size()) {
+ assert(false && "Platform index exceeds number of platforms.");
+ return {nullptr, 0};
+ }
+ return MDeviceRange[PlatformId];
+}
+
+void OffloadTopology::registerNewPlatformsAndDevices(
+ Platform2DevContainer &PlatformsAndDev) {
+ if (!PlatformsAndDev.size())
+ return;
+
+ // MDevices reallocation is prevented to keep correct ranges in MDeviceRange
+ 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<ol_device_handle_t> 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<std::pair<PlatformWithDevStorageType, size_t /*DevCount*/>,
- OL_PLATFORM_BACKEND_LAST>;
+ std::array<Platform2DevContainer, OL_PLATFORM_BACKEND_LAST>;
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<PerBackendDataType *>(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<PerBackendDataType *>(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<size_t>(OlBackend)];
- Map[Plat].push_back(Dev);
- DevCount++;
+ (*Data)[static_cast<size_t>(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<ol_platform_backend_t>(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 dbd98f953b7e4..4d811f1e444d5 100644
--- a/libsycl/src/detail/offload/offload_topology.hpp
+++ b/libsycl/src/detail/offload/offload_topology.hpp
@@ -14,7 +14,6 @@
#include <OffloadAPI.h>
#include <cassert>
-#include <unordered_map>
#include <vector>
_LIBSYCL_BEGIN_NAMESPACE_SYCL
@@ -31,8 +30,8 @@ template <class T> struct range_view {
size_t size() const { return len; }
};
-using PlatformWithDevStorageType =
- std::unordered_map<ol_platform_handle_t, std::vector<ol_device_handle_t>>;
+using Platform2DevContainer =
+ std::vector<std::pair<ol_platform_handle_t, ol_device_handle_t>>;
/// 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<const ol_platform_handle_t> platforms() const {
- return {MPlatforms.data(), MPlatforms.size()};
- }
+ range_view<const ol_platform_handle_t> 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<const ol_device_handle_t>
- 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<ol_device_handle_t> 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<const ol_device_handle_t> 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<ol_platform_handle_t> MPlatforms;
- std::vector<ol_device_handle_t> MDevices; // sorted by platform
+
+ // Devices are sorted by platform (guarantee from liboffload)
+ std::vector<ol_device_handle_t> MDevices;
// Vector holding range of devices for each platform (index is platform index
- // within MPlatforms)
- std::vector<range_view<const ol_device_handle_t>>
- MDevRangePerPlatformId; // MDevRangePerPlatformId.size() ==
- // MPlatforms.size()
+ // within Platforms), so MDeviceRange.size() == MPlatforms.size()
+ std::vector<range_view<ol_device_handle_t>> 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 ed3d197672218..9a2609daddcee 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 e6113e5479f97..f32326fb87fc9 100644
--- a/libsycl/src/detail/offload/offload_utils.hpp
+++ b/libsycl/src/detail/offload/offload_utils.hpp
@@ -12,6 +12,7 @@
#include <sycl/__impl/backend.hpp>
#include <sycl/__impl/detail/config.hpp>
#include <sycl/__impl/exception.hpp>
+#include <sycl/__impl/info/device_type.hpp>
#include <OffloadAPI.h>
@@ -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_<HANDLE>_INFO_<SMTH>.
///
/// Typical usage:
diff --git a/libsycl/src/detail/platform_impl.cpp b/libsycl/src/detail/platform_impl.cpp
index 28bf709d5c074..5df86f7742c5d 100644
--- a/libsycl/src/detail/platform_impl.cpp
+++ b/libsycl/src/detail/platform_impl.cpp
@@ -9,9 +9,13 @@
#include <sycl/__impl/detail/config.hpp>
#include <sycl/__impl/detail/obj_utils.hpp>
+#include <detail/device_impl.hpp>
#include <detail/global_objects.hpp>
#include <detail/platform_impl.hpp>
+#include <algorithm>
+#include <memory>
+
_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<PlatformImplUPtr> &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<PlatformImpl>(
OffloadPlatform, PlatformIndex++, PrivateTag{}));
}
@@ -53,6 +59,62 @@ 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<DeviceImpl>(
+ Device, *this, DeviceImpl::PrivateTag{}));
+ });
+}
+
+const std::vector<DeviceImplUPtr> &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<void(DeviceImpl *)> callback) const {
+ // Early exit if host device is requested
+ if (DeviceType == info::device_type::host)
+ return;
+ if (DeviceType == info::device_type::custom)
+ return;
+ if (DeviceType == info::device_type::accelerator)
+ return;
+
+ const auto &DeviceImpls = getRootDevices();
+
+ // TODO: need an way to get default device from liboffload
+ // as temporal solution just return the first device for DeviceType ==
+ // automatic
+ bool KeepAll = DeviceType == info::device_type::all;
+ for (auto &Impl : DeviceImpls) {
+ if (DeviceType == info::device_type::automatic) {
+ callback(Impl.get());
+ return;
+ }
+
+ 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 a17b5d70a1828..45e473104985a 100644
--- a/libsycl/src/detail/platform_impl.hpp
+++ b/libsycl/src/detail/platform_impl.hpp
@@ -13,10 +13,12 @@
#include <sycl/__impl/detail/config.hpp>
#include <sycl/__impl/platform.hpp>
+#include <detail/device_impl.hpp>
#include <detail/offload/offload_utils.hpp>
#include <OffloadAPI.h>
+#include <functional>
#include <memory>
#include <string>
#include <type_traits>
@@ -26,7 +28,10 @@ _LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {
+class DeviceImpl;
+
using PlatformImplUPtr = std::unique_ptr<PlatformImpl>;
+using DeviceImplUPtr = std::unique_ptr<DeviceImpl>;
class PlatformImpl {
struct PrivateTag {
@@ -73,7 +78,17 @@ class PlatformImpl {
/// \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 feature.
+ ///
+ /// \param Aspect is one of the values in Table 4.20 of the SYCL 2020
+ /// Provisional Spec.
+ ///
+ /// \return true all of the SYCL devices on this platform have the
+ /// given feature.
+ bool has(aspect Aspect) const;
+
+ /// Queries this SYCL platform for info.
///
/// The return type depends on information being queried.
template <typename Param> typename Param::return_type getInfo() const {
@@ -99,11 +114,20 @@ class PlatformImpl {
return Result;
}
+ /// Calls "callback" with every root device of type == DeviceType associated
+ /// with this platform
+ void iterateDevices(info::device_type DeviceType,
+ std::function<void(DeviceImpl *)> callback) const;
+
private:
+ const std::vector<DeviceImplUPtr> &getRootDevices() const;
+
ol_platform_handle_t MOffloadPlatform{};
size_t MOffloadPlatformIndex{};
ol_platform_backend_t MOffloadBackend{OL_PLATFORM_BACKEND_UNKNOWN};
backend MBackend{};
+
+ std::vector<DeviceImplUPtr> MRootDevices;
};
} // namespace detail
diff --git a/libsycl/src/device.cpp b/libsycl/src/device.cpp
new file mode 100644
index 0000000000000..0d09907ba6ba8
--- /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 <sycl/__impl/device.hpp>
+
+#include <detail/device_impl.hpp>
+#include <detail/platform_impl.hpp>
+
+#include <algorithm>
+
+_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<platform>(impl->getPlatformImpl());
+}
+
+backend device::get_backend() const noexcept { return impl->getBackend(); }
+
+std::vector<device> device::get_devices(info::device_type DeviceType) {
+ std::vector<device> 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<device>(*DevImpl));
+ });
+ }
+
+ return Devices;
+}
+
+template <info::partition_property prop>
+std::vector<device> 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>
+device::create_sub_devices<info::partition_property::partition_equally>(
+ size_t ComputeUnits) const;
+
+template <info::partition_property prop>
+std::vector<device>
+device::create_sub_devices(const std::vector<size_t> &Counts) const {
+ throw exception(make_error_code(errc::feature_not_supported),
+ "Partitioning is not supported.");
+}
+
+template _LIBSYCL_EXPORT std::vector<device>
+device::create_sub_devices<info::partition_property::partition_by_counts>(
+ const std::vector<size_t> &Counts) const;
+
+template <info::partition_property prop>
+std::vector<device> 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> 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 <typename Param>
+detail::is_device_info_desc_t<Param> device::get_info() const {
+ return impl->getInfo<Param>();
+}
+
+template <>
+_LIBSYCL_EXPORT detail::is_device_info_desc_t<info::device::platform>
+device::get_info<info::device::platform>() const {
+ static_assert(
+ std::is_same_v<info::device::platform::return_type, sycl::platform>);
+ return get_platform();
+}
+
+#define _LIBSYCL_EXPORT_GET_INFO(Desc) \
+ template _LIBSYCL_EXPORT detail::is_device_info_desc_t<info::device::Desc> \
+ device::get_info<info::device::Desc>() 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 0000000000000..a5da59fad7dae
--- /dev/null
+++ b/libsycl/src/device_selector.cpp
@@ -0,0 +1,123 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <sycl/__impl/device.hpp>
+#include <sycl/__impl/device_selector.hpp>
+
+#include <detail/device_impl.hpp>
+
+#include <algorithm>
+
+_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 dev images
+
+ if (DeviceImpl->getBackend() == backend::level_zero)
+ Score += 50;
+
+ return Score;
+}
+
+_LIBSYCL_EXPORT int default_selector_v(const device &dev) {
+ int Score = 0;
+
+ if (dev.is_gpu())
+ Score += GPUDeviceDefaultScore;
+
+ if (dev.is_cpu())
+ Score += CPUDeviceDefaultScore;
+
+ if (dev.is_accelerator())
+ Score += AccDeviceDefaultScore;
+
+ Score += getDevicePreference(dev);
+
+ 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<aspect> &RequireList,
+ const std::vector<aspect> &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<device> 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 7f401583d6693..c04c0a6281774 100644
--- a/libsycl/src/platform.cpp
+++ b/libsycl/src/platform.cpp
@@ -8,10 +8,9 @@
#include <sycl/__impl/platform.hpp>
+#include <detail/device_impl.hpp>
#include <detail/platform_impl.hpp>
-#include <stdexcept>
-
_LIBSYCL_BEGIN_NAMESPACE_SYCL
backend platform::get_backend() const noexcept { return impl->getBackend(); }
@@ -21,12 +20,24 @@ std::vector<platform> platform::get_platforms() {
std::vector<platform> Platforms;
Platforms.reserve(PlatformImpls.size());
for (auto &PlatformImpl : PlatformImpls) {
- platform Platform = detail::createSyclObjFromImpl<platform>(*PlatformImpl);
- Platforms.push_back(std::move(Platform));
+ Platforms.emplace_back(
+ detail::createSyclObjFromImpl<platform>(*PlatformImpl.get()));
}
return Platforms;
}
+std::vector<device> platform::get_devices(info::device_type DeviceType) const {
+ std::vector<device> Devices;
+ impl->iterateDevices(DeviceType, [&Devices](detail::DeviceImpl *DevImpl) {
+ assert(DevImpl && "Device impl can't be nullptr");
+ Devices.push_back(detail::createSyclObjFromImpl<device>(*DevImpl));
+ });
+
+ return Devices;
+}
+
+bool platform::has(aspect Aspect) const { return impl->has(Aspect); }
+
template <typename Param>
detail::is_platform_info_desc_t<Param> platform::get_info() const {
return impl->getInfo<Param>();
diff --git a/libsycl/tools/sycl-ls/sycl-ls.cpp b/libsycl/tools/sycl-ls/sycl-ls.cpp
index bd6c10899d748..3d23ba40ee42d 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 & devices discovered by SYCL.
//
// There are two types of output:
// concise (default) and
@@ -36,14 +36,68 @@ inline std::string_view getBackendName(const backend &Backend) {
return "";
}
+std::string getDeviceTypeName(const device &Device) {
+ auto DeviceType = Device.get_info<info::device::device_type>();
+ 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 "fpga";
+ default:
+ return "unknown";
+ }
+}
+
+static void printDeviceInfo(const device &Device, bool Verbose,
+ const std::string &Prepend) {
+ auto DeviceName = Device.get_info<info::device::name>();
+ auto DeviceVendor = Device.get_info<info::device::vendor>();
+ auto DeviceDriverVersion = Device.get_info<info::device::driver_version>();
+
+ 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<info::device::platform>();
+ auto PlatformName = Platform.get_info<info::platform::name>();
+ printDeviceInfo(Device, false /*Verbose*/,
+ Prepend + DeviceTypeName + ", " + PlatformName);
+ } catch (const sycl::exception &Exception) {
+ std::string What = Exception.what();
+ constexpr size_t MaxLength = 50;
+ // 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<bool> 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 devices discovered by SYCL");
try {
const auto &Platforms = platform::get_platforms();
@@ -55,8 +109,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<info::platform::name>();
+ 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 +134,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
More information about the llvm-commits
mailing list