[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 <kseniya.tikhomirova@intel.com>
This commit is contained in:
Kseniya Tikhomirova 2026-02-06 15:48:09 +01:00 committed by GitHub
parent 4a6697f393
commit debff92649
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 1218 additions and 81 deletions

View File

@ -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

View File

@ -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

View File

@ -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 <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 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<!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.
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 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 <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 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 <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 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 <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 the SYCL 2020
/// specification.
/// \return 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

View File

@ -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 <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 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<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

View File

@ -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 <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>
_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

View File

@ -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

View File

@ -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 @@ 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<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,15 @@ public:
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 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.
///

View File

@ -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>

View File

@ -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"

View File

@ -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

View File

@ -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 <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 {
// 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> 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;
// 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

View File

@ -29,7 +29,7 @@ std::vector<PlatformImplUPtr> &getPlatformCache() {
return PlatformCache;
}
void shutdown() {
static void shutdown() {
// No error reporting in shutdown
std::ignore = olShutDown();
}

View File

@ -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 (PlatformId is index into Platforms)
range_view<ol_device_handle_t>
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<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]);
}
}

View File

@ -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.

View File

@ -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");
}
}

View File

@ -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:

View File

@ -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,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<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/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

View File

@ -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,9 +28,15 @@ _LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {
class DeviceImpl;
using PlatformImplUPtr = std::unique_ptr<PlatformImpl>;
using DeviceImplUPtr = std::unique_ptr<DeviceImpl>;
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> 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<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

104
libsycl/src/device.cpp Normal file
View File

@ -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

View File

@ -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 <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 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<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

View File

@ -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>();

View File

@ -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<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 "accelerator";
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 = 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<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 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<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 +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