DPC++ Runtime
Runtime libraries for oneAPI DPC++
backend.cpp
Go to the documentation of this file.
1 //==------------------- backend.cpp ----------------------------------------==//
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 
10 #include "detail/event_impl.hpp"
13 #include "detail/platform_impl.hpp"
14 #include "detail/plugin.hpp"
15 #include "detail/queue_impl.hpp"
16 #include <sycl/backend.hpp>
17 #include <sycl/detail/common.hpp>
18 #include <sycl/detail/export.hpp>
19 #include <sycl/detail/pi.h>
20 #include <sycl/detail/pi.hpp>
21 #include <sycl/exception.hpp>
22 #include <sycl/exception_list.hpp>
23 #include <sycl/kernel_bundle.hpp>
24 
25 #include <algorithm>
26 #include <memory>
27 
28 namespace sycl {
29 inline namespace _V1 {
30 namespace detail {
31 
32 static const PluginPtr &getPlugin(backend Backend) {
33  switch (Backend) {
34  case backend::opencl:
35  return pi::getPlugin<backend::opencl>();
37  return pi::getPlugin<backend::ext_oneapi_level_zero>();
39  return pi::getPlugin<backend::ext_oneapi_cuda>();
41  return pi::getPlugin<backend::ext_oneapi_hip>();
42  default:
43  throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
44  "getPlugin: Unsupported backend " +
45  detail::codeToString(PI_ERROR_INVALID_OPERATION));
46  }
47 }
48 
50  switch (PiBackend) {
52  return backend::all; // No specific backend
56  return backend::opencl;
62  return backend::ext_intel_esimd_emulator;
65  }
66  throw sycl::runtime_error{"convertBackend: Unsupported backend",
67  PI_ERROR_INVALID_OPERATION};
68 }
69 
71  const auto &Plugin = getPlugin(Backend);
72 
73  // Create PI platform first.
74  pi::PiPlatform PiPlatform = nullptr;
75  Plugin->call<PiApiKind::piextPlatformCreateWithNativeHandle>(NativeHandle,
76  &PiPlatform);
77 
78  return detail::createSyclObjFromImpl<platform>(
80 }
81 
82 __SYCL_EXPORT device make_device(pi_native_handle NativeHandle,
83  backend Backend) {
84  const auto &Plugin = getPlugin(Backend);
85 
86  pi::PiDevice PiDevice = nullptr;
88  NativeHandle, nullptr, &PiDevice);
89  // Construct the SYCL device from PI device.
90  return detail::createSyclObjFromImpl<device>(
91  std::make_shared<device_impl>(PiDevice, Plugin));
92 }
93 
94 __SYCL_EXPORT context make_context(pi_native_handle NativeHandle,
95  const async_handler &Handler,
96  backend Backend) {
97  const auto &Plugin = getPlugin(Backend);
98 
99  pi::PiContext PiContext = nullptr;
101  NativeHandle, 0, nullptr, false, &PiContext);
102  // Construct the SYCL context from PI context.
103  return detail::createSyclObjFromImpl<context>(
104  std::make_shared<context_impl>(PiContext, Handler, Plugin));
105 }
106 
107 __SYCL_EXPORT queue make_queue(pi_native_handle NativeHandle,
108  int32_t NativeHandleDesc, const context &Context,
109  const device *Device, bool KeepOwnership,
110  const property_list &PropList,
111  const async_handler &Handler, backend Backend) {
113  Device ? getSyclObjImpl(*Device)->getHandleRef() : nullptr;
114  const auto &Plugin = getPlugin(Backend);
115  const auto &ContextImpl = getSyclObjImpl(Context);
116 
117  // Create PI properties from SYCL properties.
118  sycl::detail::pi::PiQueueProperties Properties[] = {
121  PropList, PropList.has_property<property::queue::in_order>()
123  : QueueOrder::OOO),
124  0, 0, 0};
126  throw sycl::exception(
128  "Queue create using make_queue cannot have compute_index property.");
129  }
130 
131  // Create PI queue first.
132  pi::PiQueue PiQueue = nullptr;
134  NativeHandle, NativeHandleDesc, ContextImpl->getHandleRef(), PiDevice,
135  !KeepOwnership, Properties, &PiQueue);
136  // Construct the SYCL queue from PI queue.
137  return detail::createSyclObjFromImpl<queue>(
138  std::make_shared<queue_impl>(PiQueue, ContextImpl, Handler, PropList));
139 }
140 
141 __SYCL_EXPORT event make_event(pi_native_handle NativeHandle,
142  const context &Context, backend Backend) {
143  return make_event(NativeHandle, Context, false, Backend);
144 }
145 
146 __SYCL_EXPORT event make_event(pi_native_handle NativeHandle,
147  const context &Context, bool KeepOwnership,
148  backend Backend) {
149  const auto &Plugin = getPlugin(Backend);
150  const auto &ContextImpl = getSyclObjImpl(Context);
151 
152  pi::PiEvent PiEvent = nullptr;
154  NativeHandle, ContextImpl->getHandleRef(), !KeepOwnership, &PiEvent);
155 
156  event Event = detail::createSyclObjFromImpl<event>(
157  std::make_shared<event_impl>(PiEvent, Context));
158 
159  if (Backend == backend::opencl)
160  Plugin->call<PiApiKind::piEventRetain>(PiEvent);
161  return Event;
162 }
163 
164 std::shared_ptr<detail::kernel_bundle_impl>
165 make_kernel_bundle(pi_native_handle NativeHandle, const context &TargetContext,
166  bool KeepOwnership, bundle_state State, backend Backend) {
167  const auto &Plugin = getPlugin(Backend);
168  const auto &ContextImpl = getSyclObjImpl(TargetContext);
169 
170  pi::PiProgram PiProgram = nullptr;
172  NativeHandle, ContextImpl->getHandleRef(), !KeepOwnership, &PiProgram);
173  if (ContextImpl->getBackend() == backend::opencl)
174  Plugin->call<PiApiKind::piProgramRetain>(PiProgram);
175 
176  std::vector<pi::PiDevice> ProgramDevices;
177  uint32_t NumDevices = 0;
178 
179  Plugin->call<PiApiKind::piProgramGetInfo>(
180  PiProgram, PI_PROGRAM_INFO_NUM_DEVICES, sizeof(NumDevices), &NumDevices,
181  nullptr);
182  ProgramDevices.resize(NumDevices);
184  sizeof(pi::PiDevice) * NumDevices,
185  ProgramDevices.data(), nullptr);
186 
187  for (const auto &Dev : ProgramDevices) {
188  size_t BinaryType = 0;
189  Plugin->call<PiApiKind::piProgramGetBuildInfo>(
190  PiProgram, Dev, PI_PROGRAM_BUILD_INFO_BINARY_TYPE, sizeof(size_t),
191  &BinaryType, nullptr);
192  switch (BinaryType) {
194  if (State == bundle_state::object)
196  PiProgram, 1, &Dev, nullptr, 0, nullptr, nullptr, nullptr, nullptr);
197  else if (State == bundle_state::executable)
198  Plugin->call<errc::build, PiApiKind::piProgramBuild>(
199  PiProgram, 1, &Dev, nullptr, nullptr, nullptr);
200  break;
203  if (State == bundle_state::input)
204  throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
205  "Program and kernel_bundle state mismatch " +
206  detail::codeToString(PI_ERROR_INVALID_VALUE));
207  if (State == bundle_state::executable)
208  Plugin->call<errc::build, PiApiKind::piProgramLink>(
209  ContextImpl->getHandleRef(), 1, &Dev, nullptr, 1, &PiProgram,
210  nullptr, nullptr, &PiProgram);
211  break;
213  if (State == bundle_state::input || State == bundle_state::object)
214  throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
215  "Program and kernel_bundle state mismatch " +
216  detail::codeToString(PI_ERROR_INVALID_VALUE));
217  break;
218  }
219  }
220 
221  std::vector<device> Devices;
222  Devices.reserve(ProgramDevices.size());
223  std::transform(
224  ProgramDevices.begin(), ProgramDevices.end(), std::back_inserter(Devices),
225  [&Plugin](const auto &Dev) {
226  auto Platform =
227  detail::platform_impl::getPlatformFromPiDevice(Dev, Plugin);
228  auto DeviceImpl = Platform->getOrMakeDeviceImpl(Dev, Platform);
229  return createSyclObjFromImpl<device>(DeviceImpl);
230  });
231 
232  // Unlike SYCL, other backends, like OpenCL or Level Zero, may not support
233  // getting kernel IDs before executable is built. The SYCL Runtime workarounds
234  // this by pre-building the device image and extracting kernel info. We can't
235  // do the same to user images, since they may contain references to undefined
236  // symbols (e.g. when kernel_bundle is supposed to be joined with another).
237  auto KernelIDs = std::make_shared<std::vector<kernel_id>>();
238  auto DevImgImpl = std::make_shared<device_image_impl>(
239  nullptr, TargetContext, Devices, State, KernelIDs, PiProgram);
240  device_image_plain DevImg{DevImgImpl};
241 
242  return std::make_shared<kernel_bundle_impl>(TargetContext, Devices, DevImg);
243 }
244 
245 // TODO: Unused. Remove when allowed.
246 std::shared_ptr<detail::kernel_bundle_impl>
247 make_kernel_bundle(pi_native_handle NativeHandle, const context &TargetContext,
248  bundle_state State, backend Backend) {
249  return make_kernel_bundle(NativeHandle, TargetContext, false, State, Backend);
250 }
251 
252 kernel make_kernel(const context &TargetContext,
253  const kernel_bundle<bundle_state::executable> &KernelBundle,
254  pi_native_handle NativeHandle, bool KeepOwnership,
255  backend Backend) {
256  const auto &Plugin = getPlugin(Backend);
257  const auto &ContextImpl = getSyclObjImpl(TargetContext);
258  const auto KernelBundleImpl = getSyclObjImpl(KernelBundle);
259 
260  // For Level-Zero expect exactly one device image in the bundle. This is
261  // natural for interop kernel to get created out of a single native
262  // program/module. This way we don't need to search the exact device image for
263  // the kernel, which may not be trivial.
264  //
265  // Other backends don't need PI program.
266  //
267  pi::PiProgram PiProgram = nullptr;
268  if (Backend == backend::ext_oneapi_level_zero) {
269  if (KernelBundleImpl->size() != 1)
270  throw sycl::exception(
271  sycl::make_error_code(sycl::errc::runtime),
272  "make_kernel: kernel_bundle must have single program image " +
273  detail::codeToString(PI_ERROR_INVALID_PROGRAM));
274 
275  const device_image<bundle_state::executable> &DeviceImage =
276  *KernelBundle.begin();
277  const auto &DeviceImageImpl = getSyclObjImpl(DeviceImage);
278  PiProgram = DeviceImageImpl->get_program_ref();
279  }
280 
281  // Create PI kernel first.
282  pi::PiKernel PiKernel = nullptr;
284  NativeHandle, ContextImpl->getHandleRef(), PiProgram, !KeepOwnership,
285  &PiKernel);
286 
287  if (Backend == backend::opencl)
288  Plugin->call<PiApiKind::piKernelRetain>(PiKernel);
289 
290  // Construct the SYCL queue from PI queue.
291  return detail::createSyclObjFromImpl<kernel>(
292  std::make_shared<kernel_impl>(PiKernel, ContextImpl, KernelBundleImpl));
293 }
294 
295 kernel make_kernel(pi_native_handle NativeHandle, const context &TargetContext,
296  backend Backend) {
297  return make_kernel(
298  TargetContext,
299  get_empty_interop_kernel_bundle<bundle_state::executable>(TargetContext),
300  NativeHandle, false, Backend);
301 }
302 
303 } // namespace detail
304 } // namespace _V1
305 } // namespace sycl
The context class represents a SYCL context on which kernel functions may be executed.
Definition: context.hpp:51
static std::shared_ptr< platform_impl > getOrMakePlatformImpl(sycl::detail::pi::PiPlatform PiPlatform, const PluginPtr &Plugin)
Queries the cache to see if the specified PiPlatform has been seen before.
static sycl::detail::pi::PiQueueProperties createPiQueueProperties(const property_list &PropList, QueueOrder Order)
Creates PI properties array.
Definition: queue_impl.hpp:482
Objects of the class represents an instance of an image in a specific state.
The SYCL device class encapsulates a single SYCL device on which kernels may be executed.
Definition: device.hpp:64
device_image_iterator begin() const
Provides an abstraction of a SYCL kernel.
Definition: kernel.hpp:77
Encapsulates a SYCL platform on which kernels may be executed.
Definition: platform.hpp:99
Objects of the property_list class are containers for the SYCL properties.
bool has_property() const noexcept
Encapsulates a single SYCL queue which schedules kernels on a SYCL device.
Definition: queue.hpp:111
::pi_device PiDevice
Definition: pi.hpp:131
::pi_platform PiPlatform
Definition: pi.hpp:129
::pi_event PiEvent
Definition: pi.hpp:143
::pi_queue PiQueue
Definition: pi.hpp:139
::pi_kernel PiKernel
Definition: pi.hpp:138
::pi_context PiContext
Definition: pi.hpp:135
::pi_queue_properties PiQueueProperties
Definition: pi.hpp:140
::pi_program PiProgram
Definition: pi.hpp:137
backend convertBackend(pi_platform_backend PiBackend)
Definition: backend.cpp:49
context make_context(pi_native_handle NativeHandle, const async_handler &Handler, backend Backend)
Definition: backend.cpp:94
kernel make_kernel(pi_native_handle NativeHandle, const context &TargetContext, backend Backend)
Definition: backend.cpp:295
queue make_queue(pi_native_handle NativeHandle, int32_t nativeHandleDesc, const context &TargetContext, const device *TargetDevice, bool KeepOwnership, const property_list &PropList, const async_handler &Handler, backend Backend)
Definition: backend.cpp:107
std::string codeToString(pi_int32 code)
Definition: common.hpp:153
platform make_platform(pi_native_handle NativeHandle, backend Backend)
Definition: backend.cpp:70
static const PluginPtr & getPlugin(backend Backend)
Definition: backend.cpp:32
decltype(Obj::impl) getSyclObjImpl(const Obj &SyclObject)
Definition: impl_utils.hpp:30
device make_device(pi_native_handle NativeHandle, backend Backend)
Definition: backend.cpp:82
event make_event(pi_native_handle NativeHandle, const context &TargetContext, backend Backend)
Definition: backend.cpp:141
std::shared_ptr< plugin > PluginPtr
Definition: pi.hpp:48
std::shared_ptr< detail::kernel_bundle_impl > make_kernel_bundle(pi_native_handle NativeHandle, const context &TargetContext, bundle_state State, backend Backend)
Definition: backend.cpp:247
std::function< void(sycl::exception_list)> async_handler
std::error_code make_error_code(sycl::errc E) noexcept
Constructs an error code using e and sycl_category()
Definition: exception.cpp:87
Definition: access.hpp:18
uintptr_t pi_native_handle
Definition: pi.h:217
pi_result piProgramGetBuildInfo(pi_program program, pi_device device, _pi_program_build_info param_name, size_t param_value_size, void *param_value, size_t *param_value_size_ret)
Definition: pi_cuda.cpp:310
pi_result piextProgramCreateWithNativeHandle(pi_native_handle nativeHandle, pi_context context, bool pluginOwnsNativeHandle, pi_program *program)
Creates PI program object from a native handle.
Definition: pi_cuda.cpp:333
pi_result piProgramRetain(pi_program program)
Definition: pi_cuda.cpp:320
pi_result piKernelRetain(pi_kernel kernel)
Definition: pi_cuda.cpp:526
pi_result piextPlatformCreateWithNativeHandle(pi_native_handle nativeHandle, pi_platform *platform)
Creates PI platform object from a native handle.
Definition: pi_cuda.cpp:47
pi_result piextContextCreateWithNativeHandle(pi_native_handle nativeHandle, pi_uint32 numDevices, const pi_device *devices, bool pluginOwnsNativeHandle, pi_context *context)
Creates PI context object from a native handle.
Definition: pi_cuda.cpp:143
pi_result piProgramGetInfo(pi_program program, pi_program_info param_name, size_t param_value_size, void *param_value, size_t *param_value_size_ret)
Definition: pi_cuda.cpp:272
pi_result piProgramBuild(pi_program program, pi_uint32 num_devices, const pi_device *device_list, const char *options, void(*pfn_notify)(pi_program program, void *user_data), void *user_data)
pi_result piextEventCreateWithNativeHandle(pi_native_handle nativeHandle, pi_context context, bool ownNativeHandle, pi_event *event)
Creates PI event object from a native handle.
Definition: pi_cuda.cpp:627
constexpr pi_queue_properties PI_QUEUE_FLAGS
Definition: pi.h:794
pi_result piProgramLink(pi_context context, pi_uint32 num_devices, const pi_device *device_list, const char *options, pi_uint32 num_input_programs, const pi_program *input_programs, void(*pfn_notify)(pi_program program, void *user_data), void *user_data, pi_program *ret_program)
pi_result piextDeviceCreateWithNativeHandle(pi_native_handle nativeHandle, pi_platform platform, pi_device *device)
Creates PI device object from a native handle.
Definition: pi_cuda.cpp:106
pi_result piProgramCompile(pi_program program, pi_uint32 num_devices, const pi_device *device_list, const char *options, pi_uint32 num_input_headers, const pi_program *input_headers, const char **header_include_names, void(*pfn_notify)(pi_program program, void *user_data), void *user_data)
pi_result piextKernelCreateWithNativeHandle(pi_native_handle nativeHandle, pi_context context, pi_program program, bool pluginOwnsNativeHandle, pi_kernel *kernel)
Creates PI kernel object from a native handle.
Definition: pi_cuda.cpp:557
_pi_platform_backend
Definition: pi.h:283
@ PI_EXT_PLATFORM_BACKEND_OPENCL
The backend is OpenCL.
Definition: pi.h:286
@ PI_EXT_PLATFORM_BACKEND_NATIVE_CPU
The backend is NATIVE_CPU.
Definition: pi.h:290
@ PI_EXT_PLATFORM_BACKEND_UNKNOWN
The backend is not a recognized one.
Definition: pi.h:284
@ PI_EXT_PLATFORM_BACKEND_LEVEL_ZERO
The backend is Level Zero.
Definition: pi.h:285
@ PI_EXT_PLATFORM_BACKEND_ESIMD
The backend is ESIMD.
Definition: pi.h:289
@ PI_EXT_PLATFORM_BACKEND_HIP
The backend is HIP.
Definition: pi.h:288
@ PI_EXT_PLATFORM_BACKEND_CUDA
The backend is CUDA.
Definition: pi.h:287
@ PI_PROGRAM_INFO_NUM_DEVICES
Definition: pi.h:470
@ PI_PROGRAM_INFO_DEVICES
Definition: pi.h:471
pi_result piextQueueCreateWithNativeHandle(pi_native_handle nativeHandle, int32_t nativeHandleDesc, pi_context context, pi_device device, bool pluginOwnsNativeHandle, pi_queue_properties *Properties, pi_queue *queue)
Creates PI queue object from a native handle.
Definition: pi_cuda.cpp:198
@ PI_PROGRAM_BINARY_TYPE_LIBRARY
Definition: pi.h:265
@ PI_PROGRAM_BINARY_TYPE_EXECUTABLE
Definition: pi.h:266
@ PI_PROGRAM_BINARY_TYPE_COMPILED_OBJECT
Definition: pi.h:264
@ PI_PROGRAM_BINARY_TYPE_NONE
Definition: pi.h:263
pi_result piEventRetain(pi_event event)
Definition: pi_cuda.cpp:615
@ PI_PROGRAM_BUILD_INFO_BINARY_TYPE
Definition: pi.h:252
C++ wrapper of extern "C" PI interfaces.