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;
63  }
65  "convertBackend: Unsupported backend");
66 }
67 
69  const auto &Plugin = getPlugin(Backend);
70 
71  // Create PI platform first.
72  pi::PiPlatform PiPlatform = nullptr;
73  Plugin->call<PiApiKind::piextPlatformCreateWithNativeHandle>(NativeHandle,
74  &PiPlatform);
75 
76  return detail::createSyclObjFromImpl<platform>(
78 }
79 
80 __SYCL_EXPORT device make_device(pi_native_handle NativeHandle,
81  backend Backend) {
82  const auto &Plugin = getPlugin(Backend);
83 
84  pi::PiDevice PiDevice = nullptr;
86  NativeHandle, nullptr, &PiDevice);
87  // Construct the SYCL device from PI device.
88  return detail::createSyclObjFromImpl<device>(
89  std::make_shared<device_impl>(PiDevice, Plugin));
90 }
91 
92 __SYCL_EXPORT context make_context(pi_native_handle NativeHandle,
93  const async_handler &Handler,
94  backend Backend, bool KeepOwnership,
95  const std::vector<device> &DeviceList) {
96  const auto &Plugin = getPlugin(Backend);
97 
98  pi::PiContext PiContext = nullptr;
99  std::vector<pi_device> DeviceHandles;
100  for (auto Dev : DeviceList) {
101  DeviceHandles.push_back(detail::getSyclObjImpl(Dev)->getHandleRef());
102  }
104  NativeHandle, DeviceHandles.size(), DeviceHandles.data(), false,
105  &PiContext);
106  // Construct the SYCL context from PI context.
107  return detail::createSyclObjFromImpl<context>(std::make_shared<context_impl>(
108  PiContext, Handler, Plugin, DeviceList, !KeepOwnership));
109 }
110 
111 __SYCL_EXPORT queue make_queue(pi_native_handle NativeHandle,
112  int32_t NativeHandleDesc, const context &Context,
113  const device *Device, bool KeepOwnership,
114  const property_list &PropList,
115  const async_handler &Handler, backend Backend) {
117  Device ? getSyclObjImpl(*Device)->getHandleRef() : nullptr;
118  const auto &Plugin = getPlugin(Backend);
119  const auto &ContextImpl = getSyclObjImpl(Context);
120 
121  // Create PI properties from SYCL properties.
122  sycl::detail::pi::PiQueueProperties Properties[] = {
125  PropList, PropList.has_property<property::queue::in_order>()
127  : QueueOrder::OOO),
128  0, 0, 0};
130  throw sycl::exception(
132  "Queue create using make_queue cannot have compute_index property.");
133  }
134 
135  // Create PI queue first.
136  pi::PiQueue PiQueue = nullptr;
138  NativeHandle, NativeHandleDesc, ContextImpl->getHandleRef(), PiDevice,
139  !KeepOwnership, Properties, &PiQueue);
140  // Construct the SYCL queue from PI queue.
141  return detail::createSyclObjFromImpl<queue>(
142  std::make_shared<queue_impl>(PiQueue, ContextImpl, Handler, PropList));
143 }
144 
145 __SYCL_EXPORT event make_event(pi_native_handle NativeHandle,
146  const context &Context, backend Backend) {
147  return make_event(NativeHandle, Context, false, Backend);
148 }
149 
150 __SYCL_EXPORT event make_event(pi_native_handle NativeHandle,
151  const context &Context, bool KeepOwnership,
152  backend Backend) {
153  const auto &Plugin = getPlugin(Backend);
154  const auto &ContextImpl = getSyclObjImpl(Context);
155 
156  pi::PiEvent PiEvent = nullptr;
158  NativeHandle, ContextImpl->getHandleRef(), !KeepOwnership, &PiEvent);
159 
160  event Event = detail::createSyclObjFromImpl<event>(
161  std::make_shared<event_impl>(PiEvent, Context));
162 
163  if (Backend == backend::opencl)
164  Plugin->call<PiApiKind::piEventRetain>(PiEvent);
165  return Event;
166 }
167 
168 std::shared_ptr<detail::kernel_bundle_impl>
169 make_kernel_bundle(pi_native_handle NativeHandle, const context &TargetContext,
170  bool KeepOwnership, bundle_state State, backend Backend) {
171  const auto &Plugin = getPlugin(Backend);
172  const auto &ContextImpl = getSyclObjImpl(TargetContext);
173 
174  pi::PiProgram PiProgram = nullptr;
176  NativeHandle, ContextImpl->getHandleRef(), !KeepOwnership, &PiProgram);
177  if (ContextImpl->getBackend() == backend::opencl)
178  Plugin->call<PiApiKind::piProgramRetain>(PiProgram);
179 
180  std::vector<pi::PiDevice> ProgramDevices;
181  uint32_t NumDevices = 0;
182 
183  Plugin->call<PiApiKind::piProgramGetInfo>(
184  PiProgram, PI_PROGRAM_INFO_NUM_DEVICES, sizeof(NumDevices), &NumDevices,
185  nullptr);
186  ProgramDevices.resize(NumDevices);
188  sizeof(pi::PiDevice) * NumDevices,
189  ProgramDevices.data(), nullptr);
190 
191  for (const auto &Dev : ProgramDevices) {
192  size_t BinaryType = 0;
193  Plugin->call<PiApiKind::piProgramGetBuildInfo>(
194  PiProgram, Dev, PI_PROGRAM_BUILD_INFO_BINARY_TYPE, sizeof(size_t),
195  &BinaryType, nullptr);
196  switch (BinaryType) {
198  if (State == bundle_state::object)
200  PiProgram, 1, &Dev, nullptr, 0, nullptr, nullptr, nullptr, nullptr);
201  else if (State == bundle_state::executable)
202  Plugin->call<errc::build, PiApiKind::piProgramBuild>(
203  PiProgram, 1, &Dev, nullptr, nullptr, nullptr);
204  break;
207  if (State == bundle_state::input)
208  throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
209  "Program and kernel_bundle state mismatch " +
210  detail::codeToString(PI_ERROR_INVALID_VALUE));
211  if (State == bundle_state::executable)
212  Plugin->call<errc::build, PiApiKind::piProgramLink>(
213  ContextImpl->getHandleRef(), 1, &Dev, nullptr, 1, &PiProgram,
214  nullptr, nullptr, &PiProgram);
215  break;
217  if (State == bundle_state::input || State == bundle_state::object)
218  throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
219  "Program and kernel_bundle state mismatch " +
220  detail::codeToString(PI_ERROR_INVALID_VALUE));
221  break;
222  }
223  }
224 
225  std::vector<device> Devices;
226  Devices.reserve(ProgramDevices.size());
227  std::transform(
228  ProgramDevices.begin(), ProgramDevices.end(), std::back_inserter(Devices),
229  [&Plugin](const auto &Dev) {
230  auto Platform =
231  detail::platform_impl::getPlatformFromPiDevice(Dev, Plugin);
232  auto DeviceImpl = Platform->getOrMakeDeviceImpl(Dev, Platform);
233  return createSyclObjFromImpl<device>(DeviceImpl);
234  });
235 
236  // Unlike SYCL, other backends, like OpenCL or Level Zero, may not support
237  // getting kernel IDs before executable is built. The SYCL Runtime workarounds
238  // this by pre-building the device image and extracting kernel info. We can't
239  // do the same to user images, since they may contain references to undefined
240  // symbols (e.g. when kernel_bundle is supposed to be joined with another).
241  auto KernelIDs = std::make_shared<std::vector<kernel_id>>();
242  auto DevImgImpl = std::make_shared<device_image_impl>(
243  nullptr, TargetContext, Devices, State, KernelIDs, PiProgram);
244  device_image_plain DevImg{DevImgImpl};
245 
246  return std::make_shared<kernel_bundle_impl>(TargetContext, Devices, DevImg);
247 }
248 
249 // TODO: Unused. Remove when allowed.
250 std::shared_ptr<detail::kernel_bundle_impl>
251 make_kernel_bundle(pi_native_handle NativeHandle, const context &TargetContext,
252  bundle_state State, backend Backend) {
253  return make_kernel_bundle(NativeHandle, TargetContext, false, State, Backend);
254 }
255 
256 kernel make_kernel(const context &TargetContext,
257  const kernel_bundle<bundle_state::executable> &KernelBundle,
258  pi_native_handle NativeHandle, bool KeepOwnership,
259  backend Backend) {
260  const auto &Plugin = getPlugin(Backend);
261  const auto &ContextImpl = getSyclObjImpl(TargetContext);
262  const auto KernelBundleImpl = getSyclObjImpl(KernelBundle);
263 
264  // For Level-Zero expect exactly one device image in the bundle. This is
265  // natural for interop kernel to get created out of a single native
266  // program/module. This way we don't need to search the exact device image for
267  // the kernel, which may not be trivial.
268  //
269  // Other backends don't need PI program.
270  //
271  pi::PiProgram PiProgram = nullptr;
272  if (Backend == backend::ext_oneapi_level_zero) {
273  if (KernelBundleImpl->size() != 1)
274  throw sycl::exception(
275  sycl::make_error_code(sycl::errc::runtime),
276  "make_kernel: kernel_bundle must have single program image " +
277  detail::codeToString(PI_ERROR_INVALID_PROGRAM));
278 
279  const device_image<bundle_state::executable> &DeviceImage =
280  *KernelBundle.begin();
281  const auto &DeviceImageImpl = getSyclObjImpl(DeviceImage);
282  PiProgram = DeviceImageImpl->get_program_ref();
283  }
284 
285  // Create PI kernel first.
286  pi::PiKernel PiKernel = nullptr;
288  NativeHandle, ContextImpl->getHandleRef(), PiProgram, !KeepOwnership,
289  &PiKernel);
290 
291  if (Backend == backend::opencl)
292  Plugin->call<PiApiKind::piKernelRetain>(PiKernel);
293 
294  // Construct the SYCL queue from PI queue.
295  return detail::createSyclObjFromImpl<kernel>(
296  std::make_shared<kernel_impl>(PiKernel, ContextImpl, KernelBundleImpl));
297 }
298 
299 kernel make_kernel(pi_native_handle NativeHandle, const context &TargetContext,
300  backend Backend) {
301  return make_kernel(
302  TargetContext,
303  get_empty_interop_kernel_bundle<bundle_state::executable>(TargetContext),
304  NativeHandle, false, Backend);
305 }
306 
307 } // namespace detail
308 } // namespace _V1
309 } // namespace sycl
The context class represents a SYCL context on which kernel functions may be executed.
Definition: context.hpp:50
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:488
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:71
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:110
::pi_device PiDevice
Definition: pi.hpp:105
::pi_platform PiPlatform
Definition: pi.hpp:103
::pi_event PiEvent
Definition: pi.hpp:117
::pi_queue PiQueue
Definition: pi.hpp:113
::pi_kernel PiKernel
Definition: pi.hpp:112
::pi_context PiContext
Definition: pi.hpp:109
::pi_queue_properties PiQueueProperties
Definition: pi.hpp:114
::pi_program PiProgram
Definition: pi.hpp:111
backend convertBackend(pi_platform_backend PiBackend)
Definition: backend.cpp:49
decltype(Obj::impl) const & getSyclObjImpl(const Obj &SyclObject)
Definition: impl_utils.hpp:31
kernel make_kernel(pi_native_handle NativeHandle, const context &TargetContext, backend Backend)
Definition: backend.cpp:299
context make_context(pi_native_handle NativeHandle, const async_handler &Handler, backend Backend, bool KeepOwnership, const std::vector< device > &DeviceList={})
Definition: backend.cpp:92
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:111
std::string codeToString(pi_int32 code)
Definition: exception.hpp:58
platform make_platform(pi_native_handle NativeHandle, backend Backend)
Definition: backend.cpp:68
static const PluginPtr & getPlugin(backend Backend)
Definition: backend.cpp:32
device make_device(pi_native_handle NativeHandle, backend Backend)
Definition: backend.cpp:80
event make_event(pi_native_handle NativeHandle, const context &TargetContext, backend Backend)
Definition: backend.cpp:145
std::shared_ptr< plugin > PluginPtr
Definition: pi.hpp:47
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:251
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:64
Definition: access.hpp:18
uintptr_t pi_native_handle
Definition: pi.h:267
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:529
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:643
constexpr pi_queue_properties PI_QUEUE_FLAGS
Definition: pi.h:882
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:573
_pi_platform_backend
Definition: pi.h:333
@ PI_EXT_PLATFORM_BACKEND_OPENCL
The backend is OpenCL.
Definition: pi.h:336
@ PI_EXT_PLATFORM_BACKEND_NATIVE_CPU
The backend is NATIVE_CPU.
Definition: pi.h:341
@ PI_EXT_PLATFORM_BACKEND_UNKNOWN
The backend is not a recognized one.
Definition: pi.h:334
@ PI_EXT_PLATFORM_BACKEND_LEVEL_ZERO
The backend is Level Zero.
Definition: pi.h:335
@ PI_EXT_PLATFORM_BACKEND_HIP
The backend is HIP.
Definition: pi.h:338
@ PI_EXT_PLATFORM_BACKEND_CUDA
The backend is CUDA.
Definition: pi.h:337
@ PI_PROGRAM_INFO_NUM_DEVICES
Definition: pi.h:549
@ PI_PROGRAM_INFO_DEVICES
Definition: pi.h:550
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:315
@ PI_PROGRAM_BINARY_TYPE_EXECUTABLE
Definition: pi.h:316
@ PI_PROGRAM_BINARY_TYPE_COMPILED_OBJECT
Definition: pi.h:314
@ PI_PROGRAM_BINARY_TYPE_NONE
Definition: pi.h:313
pi_result piEventRetain(pi_event event)
Definition: pi_cuda.cpp:631
@ PI_PROGRAM_BUILD_INFO_BINARY_TYPE
Definition: pi.h:302
C++ wrapper of extern "C" PI interfaces.