Skip to content

Conversation

@KseniyaTikhomirova
Copy link
Contributor

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

The SYCL runtime is device-agnostic and uses liboffload for offloading to GPU. This commit adds a dependency on liboffload, implementation of platform::get_platforms, platform::get_backend and platform::get_info methods, initial implementation of sycl-ls tool for manual testing of added functionality.

Plan for next PR:

device/context impl, rest of platform
test infrastructure (depends on L0 liboffload plugin CI, our effort is joined) ABI tests

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

The SYCL runtime is device-agnostic and uses liboffload for offloading to GPU.
This commit adds a dependency on liboffload, implementation of platform::get_platforms, platform::get_backend and platform::get_info methods, initial implementation of sycl-ls tool for manual testing of added functionality.

Plan for next PR:

device/context impl, rest of platform
test infrastructure (depends on L0 liboffload plugin CI, our effort is joined)
ABI tests
@KseniyaTikhomirova
Copy link
Contributor Author

@tahonermann, @dvrogozh, @asudarsa, @aelovikov-intel, @sergey-semenov, @bader, @againull, @YuriPlyakhin, @vinser52

FYI, published for review.

Comment on lines +83 to +85
// Exceptions must be noexcept copy constructible, so cannot use std::string
// directly.
std::shared_ptr<std::string> MMessage;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great opportunity to start conversation with libcxx developers (@ldionne to start that, I think) on how/if we can share some of their implementation details. __libcpp_refstring would be perfect here.

Another potential opportunity (in a not so distant future PR) would be related to the implementation of std::shared_ptr. In a nutshell, some of the SYCL objects would probably be implemented (simplified) like this:

class event_impl; // defined inside libsycl.so, not exposed to the public headers
class event {
public:
  /* ... */
private:
  std::shared_ptr<event_impl> pImpl;
};

Ideally, we'd want to inherit event_impl from std::enable_shared_from_this. The problem we saw is that inheritance by itself slowed down construction of event_impl because even if know that we always create them via make_shared<event_iml> the implementation still had to initialize the std::enable_shared_from_this subobject with atomic operations resulting in a measurable overhead. If we could somehow use most the libc++'s implementation of these but with a way to communicate extra guarantees of how those objects are created and not to pay the price of these initialization atomics, that would be great, but I'm not sure if that's possible/worth maintenance efforts.

typename backend_traits<Backend>::template return_type<SYCLObjectT>;

namespace detail {
inline std::string_view get_backend_name(const backend &Backend) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to use something like _LIBCPP_HIDE_FROM_ABI here, if I understand the idea behind it correctly.

Copy link
Contributor Author

@KseniyaTikhomirova KseniyaTikhomirova Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would leave questions about ABI till the time I will add ABI tests

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, in intel/llvm this function is defined in headers only because we want to use it in the sycl-ls tool so the tool is always aligned on "known" backend with the SYCL RT.

If this intent still stands, I would add a comment about it, or otherwise this function should not exist here at all, because we don't use it anywhere else in the headers

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is still used in sycl-ls.
added tiny comment
b15b6c0

@tahonermann tahonermann self-requested a review November 17, 2025 18:07
typename backend_traits<Backend>::template return_type<SYCLObjectT>;

namespace detail {
inline std::string_view get_backend_name(const backend &Backend) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, in intel/llvm this function is defined in headers only because we want to use it in the sycl-ls tool so the tool is always aligned on "known" backend with the SYCL RT.

If this intent still stands, I would add a comment about it, or otherwise this function should not exist here at all, because we don't use it anywhere else in the headers

#undef _OFFLOAD_ERRC

default:
return "Unknown error code";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that it would be useful to include the error code here as well so that we can still understand what happened, even if we don't have a nice string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is done by another function:

const char *stringifyErrorCode(int32_t error);

inline std::string formatCodeString(int32_t code) {
  return std::to_string(code) + " (" + std::string(stringifyErrorCode(code)) +
         ")";
}

/// Calls the API, doesn't check result. To be called when specific handling is
/// needed and explicitly done by developer after.
template <typename FunctionType, typename... ArgsT>
ol_result_t call_nocheck(FunctionType &Function, ArgsT &&...Args) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ol_result_t call_nocheck(FunctionType &Function, ArgsT &&...Args) {
ol_result_t call_nocheck(FunctionType &Function, ArgsT &&...Args) noexcept {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's only true if Function itself is noexcept.

aelovikov-intel added a commit to aelovikov-intel/llvm-project that referenced this pull request Nov 18, 2025
Various offload APIs `olGet*Info` are essentially untyped because they
"return" value via `void *PropValue` output parameter. However, for C++
consumers (e.g., [SYCL][1] in llvm#166927) it would be beneficial if we could recover
that type information. Before this PR it was only encoded in the
comments near corresponding information info descriptors, e.g.,

```c++
///////////////////////////////////////////////////////////////////////////////
/// @brief Supported event info.
typedef enum ol_event_info_t {
  /// [ol_queue_handle_t] The handle of the queue associated with the device.
  OL_EVENT_INFO_QUEUE = 0,
  /// [bool] True if and only if the event is complete.
  OL_EVENT_INFO_IS_COMPLETE = 1,
  /// @cond
  OL_EVENT_INFO_LAST = 2,
  OL_EVENT_INFO_FORCE_UINT32 = 0x7fffffff
  /// @endcond

} ol_event_info_t;
```

so was imposible for consumers to recover programmatically.

[1] https://github.com/llvm/llvm-project/blob/b22192afdcbda7441e7a8fe7cbc9a06903e9e6ea/libsycl/src/detail/platform_impl.hpp#L78-L90
aelovikov-intel added a commit to aelovikov-intel/llvm-project that referenced this pull request Nov 18, 2025
Various offload APIs `olGet*Info` are essentially untyped because they
"return" value via `void *PropValue` output parameter. However, for C++
consumers (e.g., [SYCL][1] in llvm#166927) it would be beneficial if we could recover
that type information. Before this PR it was only encoded in the
comments near corresponding information info descriptors, e.g.,

```c++
///////////////////////////////////////////////////////////////////////////////
/// @brief Supported event info.
typedef enum ol_event_info_t {
  /// [ol_queue_handle_t] The handle of the queue associated with the device.
  OL_EVENT_INFO_QUEUE = 0,
  /// [bool] True if and only if the event is complete.
  OL_EVENT_INFO_IS_COMPLETE = 1,
  /// @cond
  OL_EVENT_INFO_LAST = 2,
  OL_EVENT_INFO_FORCE_UINT32 = 0x7fffffff
  /// @endcond

} ol_event_info_t;
```

so was imposible for consumers to recover programmatically.

[1] https://github.com/llvm/llvm-project/blob/b22192afdcbda7441e7a8fe7cbc9a06903e9e6ea/libsycl/src/detail/platform_impl.hpp#L78-L90
aelovikov-intel added a commit to aelovikov-intel/llvm-project that referenced this pull request Nov 18, 2025
Various offload APIs `olGet*Info` are essentially untyped because they
"return" value via `void *PropValue` output parameter. However, for C++
consumers (e.g., SYCL in llvm#166927) it would be beneficial if we could recover
that type information. Before this PR it was only encoded in the
comments near corresponding information info descriptors, e.g.,

```c++
///////////////////////////////////////////////////////////////////////////////
/// @brief Supported event info.
typedef enum ol_event_info_t {
  /// [ol_queue_handle_t] The handle of the queue associated with the device.
  OL_EVENT_INFO_QUEUE = 0,
  /// [bool] True if and only if the event is complete.
  OL_EVENT_INFO_IS_COMPLETE = 1,
  /// @cond
  OL_EVENT_INFO_LAST = 2,
  OL_EVENT_INFO_FORCE_UINT32 = 0x7fffffff
  /// @endcond

} ol_event_info_t;
```

and not accessible programmatically.
Comment on lines +89 to +91
/// Used as a container for a list of asynchronous exceptions
///
class _LIBSYCL_EXPORT exception_list {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need it in this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need exception in this PR and since exception_list can be almost fully implemented at the same time - I just did it.

Signed-off-by: Tikhomirova, Kseniya <[email protected]>
Signed-off-by: Tikhomirova, Kseniya <[email protected]>
Signed-off-by: Tikhomirova, Kseniya <[email protected]>
Signed-off-by: Tikhomirova, Kseniya <[email protected]>
_LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {

const char *stringifyErrorCode(int32_t error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this API should become an liboffload entry point. I'll add that to my TODO list.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is allready API in liboffload. There are << operator for enum ol_errc_t

Check OffloadPrint.hpp (note this is tablegen generated file)

Comment on lines 1 to 8
#ifndef __SYCL_PARAM_TRAITS_SPEC
static_assert(false, "__SYCL_PARAM_TRAITS_SPEC is required but not defined");
#endif

// 4.6.2.4. Information descriptors
__SYCL_PARAM_TRAITS_SPEC(platform, version, std::string, OL_PLATFORM_INFO_VERSION)
__SYCL_PARAM_TRAITS_SPEC(platform, name, std::string, OL_PLATFORM_INFO_NAME)
__SYCL_PARAM_TRAITS_SPEC(platform, vendor, std::string, OL_PLATFORM_INFO_VENDOR_NAME)
Copy link
Contributor

@aelovikov-intel aelovikov-intel Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subjective, but I'm not a fan of this approach for two reasons:

  1. Smart use of preprocessor in distributable header files (i.e., libsycl/include instead of libsycl/source/**).
  2. OL_* are implementation details and don't need to be present in those distributable headers.

I think avoiding it doesn't result in too much code duplication: 823c765, but I understand that this is subjective.

Macro in exports can be avoided too, but that's probably too much magic:

// sycl.hpp
#define _EXPORT __attribute__((visibility("default")))
struct S {
  template <typename> _EXPORT void foo();
};

// libsycl.so
template <typename> [[gnu::used]] _EXPORT void S::foo() {}

template _EXPORT void S::foo<char>(); // current approach

// "Clever" helper, needs `[[gnu::used]]` above.
template <typename... Ts> void instantiate_helper() {
  (((void)(&S::foo<Ts>), ...));
}
static void instantiate() { instantiate_helper<int, float, double>(); }
$ clang++ a.cpp -c -fvisibility=hidden -fvisibility-inlines-hidden -O0 ; nm a.o | c++filt
0000000000000000 W void S::foo<char>()                                                   
0000000000000000 W void S::foo<double>()                                                 
0000000000000000 W void S::foo<float>()                                                  
0000000000000000 W void S::foo<int>()                                                    

$ clang++ a.cpp -c -fvisibility=hidden -fvisibility-inlines-hidden -O3 ; nm a.o | c++filt
0000000000000000 W void foo<char>()                                                      
0000000000000000 W void foo<double>()                                                    
0000000000000000 W void foo<float>()                                                     
0000000000000000 W void foo<int>()                                                       

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to ask other folks for opinion here. The benefit that .def file provides is necessity in update of only one place in the code to add info or property (we use the same approach there) if there is no special handling.

@sergey-semenov, @bader, @vinser52, @tahonermann, @AlexeySachkov do you have any preference?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subjective, but I'm not a fan of this approach for two reasons:

  1. Smart use of preprocessor in distributable header files (i.e., libsycl/include instead of libsycl/source/**).
  2. OL_* are implementation details and don't need to be present in those distributable headers.

I don't understand the problem with using pre-processor in distributed header files, but I agree that mapping from SYCL API to liboffload API should be done in the library source code.

Signed-off-by: Tikhomirova, Kseniya <[email protected]>
Signed-off-by: Tikhomirova, Kseniya <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants