DPC++ Runtime
Runtime libraries for oneAPI DPC++
ur.cpp
Go to the documentation of this file.
1 //==---------- ur.cpp - Unified Runtime integration helpers ----------------==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
14 
15 #include "context_impl.hpp"
16 #include <detail/config.hpp>
18 #include <detail/plugin.hpp>
19 #include <detail/xpti_registry.hpp>
20 #include <sycl/context.hpp>
21 #include <sycl/detail/common.hpp>
24 #include <sycl/detail/ur.hpp>
25 #include <sycl/version.hpp>
26 #include <ur_api.h>
27 
28 #include <bitset>
29 #include <cstdarg>
30 #include <cstring>
31 #include <iostream>
32 #include <map>
33 #include <sstream>
34 #include <stddef.h>
35 #include <string>
36 #include <tuple>
37 
38 #ifdef XPTI_ENABLE_INSTRUMENTATION
39 // Include the headers necessary for emitting
40 // traces using the trace framework
41 #include "xpti/xpti_trace_framework.h"
42 #endif
43 
44 namespace sycl {
45 inline namespace _V1 {
46 namespace detail {
47 namespace pi {
50  void *user_data) {
51  auto impl = getSyclObjImpl(context);
52  const auto &Plugin = impl->getPlugin();
53  Plugin->call<UrApiKind::urContextSetExtendedDeleter>(
54  impl->getHandleRef(),
55  reinterpret_cast<ur_context_extended_deleter_t>(func), user_data);
56 }
57 } // namespace pi
58 
59 #ifdef XPTI_ENABLE_INSTRUMENTATION
60 // Global (to the SYCL runtime) graph handle that all command groups are a
61 // child of
63 xpti_td *GSYCLGraphEvent = nullptr;
64 #endif // XPTI_ENABLE_INSTRUMENTATION
65 
66 template <sycl::backend BE>
67 void *getPluginOpaqueData([[maybe_unused]] void *OpaqueDataParam) {
68  // This was formerly a call to piextPluginGetOpaqueData, a deprecated PI entry
69  // point introduced for the now deleted ESIMD plugin. All calls to this entry
70  // point returned a similar error code to INVALID_OPERATION and would have
71  // resulted in a similar throw to this one
72  throw exception(
74  "This operation is not supported by any existing backends.");
75  return nullptr;
76 }
77 
78 ur_code_location_t codeLocationCallback(void *);
79 
80 namespace ur {
81 bool trace(TraceLevel Level) {
82  auto TraceLevelMask = SYCLConfig<SYCL_UR_TRACE>::get();
83  return (TraceLevelMask & Level) == Level;
84 }
85 
86 static void initializePlugins(std::vector<PluginPtr> &Plugins,
87  ur_loader_config_handle_t LoaderConfig);
88 
89 bool XPTIInitDone = false;
90 
91 // Initializes all available Plugins.
92 std::vector<PluginPtr> &initializeUr(ur_loader_config_handle_t LoaderConfig) {
93  // This uses static variable initialization to work around a gcc bug with
94  // std::call_once and exceptions.
95  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66146
96  auto initializeHelper = [=]() {
97  // TODO: Remove this SYCL_PI_TRACE notification in the first patch release
98  // after the next ABI breaking window.
99  if (std::getenv("SYCL_PI_TRACE")) {
100  std::cerr << "SYCL_PI_TRACE has been removed use SYCL_UR_TRACE instead\n";
101  std::exit(1);
102  }
103 
104  initializePlugins(GlobalHandler::instance().getPlugins(), LoaderConfig);
105  return true;
106  };
107  static bool Initialized = initializeHelper();
108  std::ignore = Initialized;
109 
111 }
112 
113 static void initializePlugins(std::vector<PluginPtr> &Plugins,
114  ur_loader_config_handle_t LoaderConfig) {
115 #define CHECK_UR_SUCCESS(Call) __SYCL_CHECK_UR_CODE_NO_EXC(Call)
116 
117  UrFuncInfo<UrApiKind::urLoaderConfigCreate> loaderConfigCreateInfo;
118  auto loaderConfigCreate =
119  loaderConfigCreateInfo.getFuncPtrFromModule(ur::getURLoaderLibrary());
120  UrFuncInfo<UrApiKind::urLoaderConfigEnableLayer> loaderConfigEnableLayerInfo;
121  auto loaderConfigEnableLayer =
122  loaderConfigEnableLayerInfo.getFuncPtrFromModule(
124  UrFuncInfo<UrApiKind::urLoaderConfigRelease> loaderConfigReleaseInfo;
125  auto loaderConfigRelease =
126  loaderConfigReleaseInfo.getFuncPtrFromModule(ur::getURLoaderLibrary());
128  loaderConfigSetCodeLocationCallbackInfo;
129  auto loaderConfigSetCodeLocationCallback =
130  loaderConfigSetCodeLocationCallbackInfo.getFuncPtrFromModule(
133  auto loaderInit =
134  loaderInitInfo.getFuncPtrFromModule(ur::getURLoaderLibrary());
135  UrFuncInfo<UrApiKind::urAdapterGet> adapterGet_Info;
136  auto adapterGet =
137  adapterGet_Info.getFuncPtrFromModule(ur::getURLoaderLibrary());
138  UrFuncInfo<UrApiKind::urAdapterGetInfo> adapterGetInfoInfo;
139  auto adapterGetInfo =
140  adapterGetInfoInfo.getFuncPtrFromModule(ur::getURLoaderLibrary());
141 
142  bool OwnLoaderConfig = false;
143  // If we weren't provided with a custom config handle create our own.
144  if(!LoaderConfig) {
145  CHECK_UR_SUCCESS(loaderConfigCreate(&LoaderConfig))
146  OwnLoaderConfig = true;
147  }
148 
149  const char *LogOptions = "level:info;output:stdout;flush:info";
151 #ifdef _WIN32
152  _putenv_s("UR_LOG_TRACING", LogOptions);
153 #else
154  setenv("UR_LOG_TRACING", LogOptions, 1);
155 #endif
156  CHECK_UR_SUCCESS(loaderConfigEnableLayer(LoaderConfig, "UR_LAYER_TRACING"));
157  }
158 
160 #ifdef _WIN32
161  _putenv_s("UR_LOG_LOADER", LogOptions);
162 #else
163  setenv("UR_LOG_LOADER", LogOptions, 1);
164 #endif
165  }
166 
167  CHECK_UR_SUCCESS(loaderConfigSetCodeLocationCallback(
168  LoaderConfig, codeLocationCallback, nullptr));
169 
170  if (ProgramManager::getInstance().kernelUsesAsan()) {
171  if (loaderConfigEnableLayer(LoaderConfig, "UR_LAYER_ASAN")) {
172  loaderConfigRelease(LoaderConfig);
173  std::cerr << "Failed to enable ASAN layer\n";
174  return;
175  }
176  }
177 
178  loaderConfigSetCodeLocationCallback(LoaderConfig, codeLocationCallback,
179  nullptr);
180 
181  if (ProgramManager::getInstance().kernelUsesAsan()) {
182  if (loaderConfigEnableLayer(LoaderConfig, "UR_LAYER_ASAN")) {
183  loaderConfigRelease(LoaderConfig);
184  std::cerr << "Failed to enable ASAN layer\n";
185  return;
186  }
187  }
188 
189  ur_device_init_flags_t device_flags = 0;
190  CHECK_UR_SUCCESS(loaderInit(device_flags, LoaderConfig));
191 
192  if (OwnLoaderConfig) {
193  CHECK_UR_SUCCESS(loaderConfigRelease(LoaderConfig));
194  }
195 
196  uint32_t adapterCount = 0;
197  CHECK_UR_SUCCESS(adapterGet(0, nullptr, &adapterCount));
198  std::vector<ur_adapter_handle_t> adapters(adapterCount);
199  CHECK_UR_SUCCESS(adapterGet(adapterCount, adapters.data(), nullptr));
200 
201  auto UrToSyclBackend = [](ur_adapter_backend_t backend) -> sycl::backend {
202  switch (backend) {
203  case UR_ADAPTER_BACKEND_LEVEL_ZERO:
205  case UR_ADAPTER_BACKEND_OPENCL:
206  return backend::opencl;
207  case UR_ADAPTER_BACKEND_CUDA:
209  case UR_ADAPTER_BACKEND_HIP:
211  case UR_ADAPTER_BACKEND_NATIVE_CPU:
213  default:
214  // Throw an exception, this should be unreachable.
215  CHECK_UR_SUCCESS(UR_RESULT_ERROR_INVALID_ENUMERATION)
216  return backend::all;
217  }
218  };
219 
220  for (const auto &adapter : adapters) {
221  ur_adapter_backend_t adapterBackend = UR_ADAPTER_BACKEND_UNKNOWN;
222  CHECK_UR_SUCCESS(adapterGetInfo(adapter, UR_ADAPTER_INFO_BACKEND,
223  sizeof(adapterBackend), &adapterBackend,
224  nullptr));
225  auto syclBackend = UrToSyclBackend(adapterBackend);
226  Plugins.emplace_back(std::make_shared<plugin>(adapter, syclBackend));
227  }
228 
229 #ifdef XPTI_ENABLE_INSTRUMENTATION
231 
232  if (!(xptiTraceEnabled() && !XPTIInitDone))
233  return;
234  // Not sure this is the best place to initialize the framework; SYCL runtime
235  // team needs to advise on the right place, until then we piggy-back on the
236  // initialization of the UR layer.
237 
238  // Initialize the global events just once, in the case ur::initialize() is
239  // called multiple times
240  XPTIInitDone = true;
241  // Registers a new stream for 'sycl' and any plugin that wants to listen to
242  // this stream will register itself using this string or stream ID for this
243  // string.
244  uint8_t StreamID = xptiRegisterStream(SYCL_STREAM_NAME);
245  // Let all tool plugins know that a stream by the name of 'sycl' has been
246  // initialized and will be generating the trace stream.
248  SYCL_STREAM_NAME, GMajVer, GMinVer, GVerStr);
249  // Create a tracepoint to indicate the graph creation
250  xpti::payload_t GraphPayload("application_graph");
251  uint64_t GraphInstanceNo;
252  GSYCLGraphEvent =
253  xptiMakeEvent("application_graph", &GraphPayload, xpti::trace_graph_event,
254  xpti_at::active, &GraphInstanceNo);
255  if (GSYCLGraphEvent) {
256  // The graph event is a global event and will be used as the parent for
257  // all nodes (command groups)
258  xptiNotifySubscribers(StreamID, xpti::trace_graph_create, nullptr,
259  GSYCLGraphEvent, GraphInstanceNo, nullptr);
260  }
261 #endif
262 #undef CHECK_UR_SUCCESS
263 }
264 
265 // Get the plugin serving given backend.
266 template <backend BE> const PluginPtr &getPlugin() {
267  static PluginPtr *Plugin = nullptr;
268  if (Plugin)
269  return *Plugin;
270 
271  std::vector<PluginPtr> &Plugins = ur::initializeUr();
272  for (auto &P : Plugins)
273  if (P->hasBackend(BE)) {
274  Plugin = &P;
275  return *Plugin;
276  }
277 
278  throw exception(errc::runtime, "ur::getPlugin couldn't find plugin");
279 }
280 
281 template const PluginPtr &getPlugin<backend::opencl>();
282 template const PluginPtr &getPlugin<backend::ext_oneapi_level_zero>();
283 template const PluginPtr &getPlugin<backend::ext_oneapi_cuda>();
284 template const PluginPtr &getPlugin<backend::ext_oneapi_hip>();
285 
286 // Reads an integer value from ELF data.
287 template <typename ResT>
288 static ResT readELFValue(const unsigned char *Data, size_t NumBytes,
289  bool IsBigEndian) {
290  assert(NumBytes <= sizeof(ResT));
291  ResT Result = 0;
292  if (IsBigEndian) {
293  for (size_t I = 0; I < NumBytes; ++I) {
294  Result = (Result << 8) | static_cast<ResT>(Data[I]);
295  }
296  } else {
297  std::copy(Data, Data + NumBytes, reinterpret_cast<char *>(&Result));
298  }
299  return Result;
300 }
301 
302 // Checks if an ELF image contains a section with a specified name.
303 static bool checkELFSectionPresent(const std::string &ExpectedSectionName,
304  const unsigned char *ImgData,
305  size_t ImgSize) {
306  // Check for 64bit and big-endian.
307  bool Is64bit = ImgData[4] == 2;
308  bool IsBigEndian = ImgData[5] == 2;
309 
310  // Make offsets based on whether the ELF file is 64bit or not.
311  size_t SectionHeaderOffsetInfoOffset = Is64bit ? 0x28 : 0x20;
312  size_t SectionHeaderSizeInfoOffset = Is64bit ? 0x3A : 0x2E;
313  size_t SectionHeaderNumInfoOffset = Is64bit ? 0x3C : 0x30;
314  size_t SectionStringsHeaderIndexInfoOffset = Is64bit ? 0x3E : 0x32;
315 
316  // if the image doesn't contain enough data for the header values, end early.
317  if (ImgSize < SectionStringsHeaderIndexInfoOffset + 2)
318  return false;
319 
320  // Read the e_shoff, e_shentsize, e_shnum, and e_shstrndx entries in the
321  // header.
322  uint64_t SectionHeaderOffset = readELFValue<uint64_t>(
323  ImgData + SectionHeaderOffsetInfoOffset, Is64bit ? 8 : 4, IsBigEndian);
324  uint16_t SectionHeaderSize = readELFValue<uint16_t>(
325  ImgData + SectionHeaderSizeInfoOffset, 2, IsBigEndian);
326  uint16_t SectionHeaderNum = readELFValue<uint16_t>(
327  ImgData + SectionHeaderNumInfoOffset, 2, IsBigEndian);
328  uint16_t SectionStringsHeaderIndex = readELFValue<uint16_t>(
329  ImgData + SectionStringsHeaderIndexInfoOffset, 2, IsBigEndian);
330 
331  // End early if we do not have the expected number of section headers or
332  // if the read section string header index is out-of-range.
333  if (ImgSize < SectionHeaderOffset + static_cast<uint64_t>(SectionHeaderNum) *
334  SectionHeaderSize ||
335  SectionStringsHeaderIndex >= SectionHeaderNum)
336  return false;
337 
338  // Get the location of the section string data.
339  size_t SectionStringsInfoOffset = Is64bit ? 0x18 : 0x10;
340  const unsigned char *SectionStringsHeaderData =
341  ImgData + SectionHeaderOffset +
342  SectionStringsHeaderIndex * SectionHeaderSize;
343  uint64_t SectionStrings = readELFValue<uint64_t>(
344  SectionStringsHeaderData + SectionStringsInfoOffset, Is64bit ? 8 : 4,
345  IsBigEndian);
346  const unsigned char *SectionStringsData = ImgData + SectionStrings;
347 
348  // For each section, check the name against the expected section and return
349  // true if we find it.
350  for (size_t I = 0; I < SectionHeaderNum; ++I) {
351  // Get the offset into the section string data of this sections name.
352  const unsigned char *HeaderData =
353  ImgData + SectionHeaderOffset + I * SectionHeaderSize;
354  uint32_t SectionNameOffset =
355  readELFValue<uint32_t>(HeaderData, 4, IsBigEndian);
356 
357  // Read the section name and check if it is the same as the name we are
358  // looking for.
359  const char *SectionName =
360  reinterpret_cast<const char *>(SectionStringsData + SectionNameOffset);
361  if (SectionName == ExpectedSectionName)
362  return true;
363  }
364  return false;
365 }
366 
367 // Returns the e_type field from an ELF image.
368 static uint16_t getELFHeaderType(const unsigned char *ImgData, size_t ImgSize) {
369  (void)ImgSize;
370  assert(ImgSize >= 18 && "Not enough bytes to have an ELF header type.");
371 
372  bool IsBigEndian = ImgData[5] == 2;
373  return readELFValue<uint16_t>(ImgData + 16, 2, IsBigEndian);
374 }
375 
376 sycl_device_binary_type getBinaryImageFormat(const unsigned char *ImgData,
377  size_t ImgSize) {
378  // Top-level magic numbers for the recognized binary image formats.
379  auto MatchMagicNumber = [&](auto Number) {
380  return ImgSize >= sizeof(Number) &&
381  std::memcmp(ImgData, &Number, sizeof(Number)) == 0;
382  };
383 
384  if (MatchMagicNumber(uint32_t{0x07230203}))
386 
387  if (MatchMagicNumber(uint32_t{0xDEC04342}))
389 
390  if (MatchMagicNumber(uint32_t{0x43544E49}))
391  // 'I', 'N', 'T', 'C' ; Intel native
393 
394  // Check for ELF format, size requirements include data we'll read in case of
395  // succesful match.
396  if (ImgSize >= 18 && MatchMagicNumber(uint32_t{0x464c457F})) {
397  uint16_t ELFHdrType = getELFHeaderType(ImgData, ImgSize);
398  if (ELFHdrType == 0xFF04)
399  // OpenCL executable.
401 
402  if (ELFHdrType == 0xFF12)
403  // ZEBIN executable.
405 
406  // Newer ZEBIN format does not have a special header type, but can instead
407  // be identified by having a required .ze_info section.
408  if (checkELFSectionPresent(".ze_info", ImgData, ImgSize))
410  }
411 
412  if (MatchMagicNumber(std::array{'!', '<', 'a', 'r', 'c', 'h', '>', '\n'}))
413  // "ar" format is used to pack binaries for multiple devices, e.g. via
414  //
415  // -Xsycl-target-backend=spir64_gen "-device acm-g10,acm-g11"
416  //
417  // option.
419 
421 }
422 
425  ur_program_metadata_t URMetadata{};
426  URMetadata.pName = DeviceBinaryProperty->Name;
427  URMetadata.size = DeviceBinaryProperty->ValSize;
428  switch (DeviceBinaryProperty->Type) {
430  URMetadata.type = UR_PROGRAM_METADATA_TYPE_UINT32;
431  URMetadata.value.data32 = DeviceBinaryProperty->ValSize;
432  break;
434  URMetadata.type = UR_PROGRAM_METADATA_TYPE_BYTE_ARRAY;
435  URMetadata.value.pData = DeviceBinaryProperty->ValAddr;
436  break;
438  URMetadata.type = UR_PROGRAM_METADATA_TYPE_STRING;
439  URMetadata.value.pString =
440  reinterpret_cast<char *>(DeviceBinaryProperty->ValAddr);
441  break;
442  default:
443  break;
444  }
445  return URMetadata;
446 }
447 
448 } // namespace ur
449 } // namespace detail
450 } // namespace _V1
451 } // namespace sycl
The context class represents a SYCL context on which kernel functions may be executed.
Definition: context.hpp:50
std::vector< PluginPtr > & getPlugins()
static GlobalHandler & instance()
static ProgramManager & getInstance()
static const char * get()
Definition: config.hpp:115
void initializeStream(const std::string &StreamName, uint32_t MajVer, uint32_t MinVer, const std::string &VerStr)
Notifies XPTI subscribers about new stream.
std::function< void(interop_handle)> func
Definition: commands.cpp:328
@ SYCL_PROPERTY_TYPE_STRING
Definition: compiler.hpp:89
@ SYCL_PROPERTY_TYPE_BYTE_ARRAY
Definition: compiler.hpp:88
@ SYCL_PROPERTY_TYPE_UINT32
Definition: compiler.hpp:87
sycl_device_binary_type
Types of device binary.
Definition: compiler.hpp:114
@ SYCL_DEVICE_BINARY_TYPE_SPIRV
Definition: compiler.hpp:117
@ SYCL_DEVICE_BINARY_TYPE_LLVMIR_BITCODE
Definition: compiler.hpp:118
@ SYCL_DEVICE_BINARY_TYPE_NONE
Definition: compiler.hpp:115
@ SYCL_DEVICE_BINARY_TYPE_NATIVE
Definition: compiler.hpp:116
__SYCL_EXTERN_STREAM_ATTRS ostream cerr
Linked to standard error (unbuffered)
void contextSetExtendedDeleter(const sycl::context &constext, pi_context_extended_deleter func, void *user_data)
Definition: ur.cpp:48
sycl_device_binary_type getBinaryImageFormat(const unsigned char *ImgData, size_t ImgSize)
Tries to determine the device binary image foramat.
Definition: ur.cpp:376
std::vector< PluginPtr > & initializeUr(ur_loader_config_handle_t LoaderConfig=nullptr)
Definition: ur.cpp:92
bool trace(TraceLevel level)
Definition: ur.cpp:81
static bool checkELFSectionPresent(const std::string &ExpectedSectionName, const unsigned char *ImgData, size_t ImgSize)
Definition: ur.cpp:303
ur_program_metadata_t mapDeviceBinaryPropertyToProgramMetadata(const sycl_device_binary_property &DeviceBinaryProperty)
Definition: ur.cpp:423
void * getURLoaderLibrary()
Definition: posix_ur.cpp:38
static uint16_t getELFHeaderType(const unsigned char *ImgData, size_t ImgSize)
Definition: ur.cpp:368
static void initializePlugins(std::vector< PluginPtr > &Plugins, ur_loader_config_handle_t LoaderConfig)
Definition: ur.cpp:113
static ResT readELFValue(const unsigned char *Data, size_t NumBytes, bool IsBigEndian)
Definition: ur.cpp:288
const PluginPtr & getPlugin()
Definition: ur.cpp:266
decltype(Obj::impl) const & getSyclObjImpl(const Obj &SyclObject)
Definition: impl_utils.hpp:31
constexpr const char * SYCL_STREAM_NAME
void * getPluginOpaqueData(void *opaquedata_arg)
std::shared_ptr< plugin > PluginPtr
Definition: ur.hpp:107
ur_code_location_t codeLocationCallback(void *)
Definition: common.cpp:32
void copy(handler &CGH, const T *Src, T *Dest, size_t Count)
std::error_code make_error_code(sycl::errc E) noexcept
Constructs an error code using e and sycl_category()
Definition: exception.cpp:65
Definition: access.hpp:18
#define CHECK_UR_SUCCESS(Call)
C++ utilities for Unified Runtime integration.
void(* pi_context_extended_deleter)(void *user_data)
Definition: ur.hpp:31