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 {
30 namespace detail {
31 
32 static const plugin &getPlugin(backend Backend) {
33  switch (Backend) {
34  case backend::opencl:
35  return pi::getPlugin<backend::opencl>();
36  case backend::ext_oneapi_level_zero:
37  return pi::getPlugin<backend::ext_oneapi_level_zero>();
38  case backend::ext_oneapi_cuda:
39  return pi::getPlugin<backend::ext_oneapi_cuda>();
40  default:
41  throw sycl::runtime_error{"Unsupported backend",
42  PI_ERROR_INVALID_OPERATION};
43  }
44 }
45 
47  const auto &Plugin = getPlugin(Backend);
48 
49  // Create PI platform first.
50  pi::PiPlatform PiPlatform = nullptr;
51  Plugin.call<PiApiKind::piextPlatformCreateWithNativeHandle>(NativeHandle,
52  &PiPlatform);
53 
54  return detail::createSyclObjFromImpl<platform>(
55  platform_impl::getOrMakePlatformImpl(PiPlatform, Plugin));
56 }
57 
58 __SYCL_EXPORT device make_device(pi_native_handle NativeHandle,
59  backend Backend) {
60  const auto &Plugin = getPlugin(Backend);
61 
62  pi::PiDevice PiDevice = nullptr;
63  Plugin.call<PiApiKind::piextDeviceCreateWithNativeHandle>(NativeHandle,
64  nullptr, &PiDevice);
65  // Construct the SYCL device from PI device.
66  return detail::createSyclObjFromImpl<device>(
67  std::make_shared<device_impl>(PiDevice, Plugin));
68 }
69 
70 __SYCL_EXPORT context make_context(pi_native_handle NativeHandle,
71  const async_handler &Handler,
72  backend Backend) {
73  const auto &Plugin = getPlugin(Backend);
74 
75  pi::PiContext PiContext = nullptr;
77  NativeHandle, 0, nullptr, false, &PiContext);
78  // Construct the SYCL context from PI context.
79  return detail::createSyclObjFromImpl<context>(
80  std::make_shared<context_impl>(PiContext, Handler, Plugin));
81 }
82 
83 queue make_queue_impl(pi_native_handle NativeHandle, const context &Context,
84  RT::PiDevice Device, bool KeepOwnership,
85  const async_handler &Handler, backend Backend) {
86  const auto &Plugin = getPlugin(Backend);
87  const auto &ContextImpl = getSyclObjImpl(Context);
88  // Create PI queue first.
89  pi::PiQueue PiQueue = nullptr;
91  NativeHandle, ContextImpl->getHandleRef(), Device, !KeepOwnership,
92  &PiQueue);
93  // Construct the SYCL queue from PI queue.
94  return detail::createSyclObjFromImpl<queue>(
95  std::make_shared<queue_impl>(PiQueue, ContextImpl, Handler));
96 }
97 
98 __SYCL_EXPORT queue make_queue(pi_native_handle NativeHandle,
99  const context &Context, const device *Device,
100  bool KeepOwnership, const async_handler &Handler,
101  backend Backend) {
102  if (Device) {
103  const auto &DeviceImpl = getSyclObjImpl(*Device);
104  return make_queue_impl(NativeHandle, Context, DeviceImpl->getHandleRef(),
105  KeepOwnership, Handler, Backend);
106  } else {
107  return make_queue_impl(NativeHandle, Context, nullptr, KeepOwnership,
108  Handler, Backend);
109  }
110 }
111 
112 __SYCL_EXPORT event make_event(pi_native_handle NativeHandle,
113  const context &Context, backend Backend) {
114  return make_event(NativeHandle, Context, false, Backend);
115 }
116 
117 __SYCL_EXPORT event make_event(pi_native_handle NativeHandle,
118  const context &Context, bool KeepOwnership,
119  backend Backend) {
120  const auto &Plugin = getPlugin(Backend);
121  const auto &ContextImpl = getSyclObjImpl(Context);
122 
123  pi::PiEvent PiEvent = nullptr;
125  NativeHandle, ContextImpl->getHandleRef(), !KeepOwnership, &PiEvent);
126 
127  event Event = detail::createSyclObjFromImpl<event>(
128  std::make_shared<event_impl>(PiEvent, Context));
129 
130  if (Backend == backend::opencl)
131  Plugin.call<PiApiKind::piEventRetain>(PiEvent);
132  return Event;
133 }
134 
135 std::shared_ptr<detail::kernel_bundle_impl>
136 make_kernel_bundle(pi_native_handle NativeHandle, const context &TargetContext,
137  bool KeepOwnership, bundle_state State, backend Backend) {
138  const auto &Plugin = getPlugin(Backend);
139  const auto &ContextImpl = getSyclObjImpl(TargetContext);
140 
141  pi::PiProgram PiProgram = nullptr;
143  NativeHandle, ContextImpl->getHandleRef(), !KeepOwnership, &PiProgram);
144 
145  std::vector<pi::PiDevice> ProgramDevices;
146  size_t NumDevices = 0;
147 
148  Plugin.call<PiApiKind::piProgramGetInfo>(
149  PiProgram, PI_PROGRAM_INFO_NUM_DEVICES, sizeof(size_t), &NumDevices,
150  nullptr);
151  ProgramDevices.resize(NumDevices);
153  sizeof(pi::PiDevice) * NumDevices,
154  ProgramDevices.data(), nullptr);
155 
156  for (const auto &Dev : ProgramDevices) {
157  size_t BinaryType = 0;
159  PiProgram, Dev, PI_PROGRAM_BUILD_INFO_BINARY_TYPE, sizeof(size_t),
160  &BinaryType, nullptr);
161  switch (BinaryType) {
163  if (State == bundle_state::object)
165  PiProgram, 1, &Dev, nullptr, 0, nullptr, nullptr, nullptr, nullptr);
166  else if (State == bundle_state::executable)
168  PiProgram, 1, &Dev, nullptr, nullptr, nullptr);
169  break;
172  if (State == bundle_state::input)
173  // TODO SYCL2020 exception
174  throw sycl::runtime_error(errc::invalid,
175  "Program and kernel_bundle state mismatch",
176  PI_ERROR_INVALID_VALUE);
177  if (State == bundle_state::executable)
179  ContextImpl->getHandleRef(), 1, &Dev, nullptr, 1, &PiProgram,
180  nullptr, nullptr, &PiProgram);
181  break;
183  if (State == bundle_state::input || State == bundle_state::object)
184  // TODO SYCL2020 exception
185  throw sycl::runtime_error(errc::invalid,
186  "Program and kernel_bundle state mismatch",
187  PI_ERROR_INVALID_VALUE);
188  break;
189  }
190  }
191 
192  std::vector<device> Devices;
193  Devices.reserve(ProgramDevices.size());
194  std::transform(
195  ProgramDevices.begin(), ProgramDevices.end(), std::back_inserter(Devices),
196  [&Plugin](const auto &Dev) {
197  auto Platform =
198  detail::platform_impl::getPlatformFromPiDevice(Dev, Plugin);
199  auto DeviceImpl = Platform->getOrMakeDeviceImpl(Dev, Platform);
200  return createSyclObjFromImpl<device>(DeviceImpl);
201  });
202 
203  // Unlike SYCL, other backends, like OpenCL or Level Zero, may not support
204  // getting kernel IDs before executable is built. The SYCL Runtime workarounds
205  // this by pre-building the device image and extracting kernel info. We can't
206  // do the same to user images, since they may contain references to undefined
207  // symbols (e.g. when kernel_bundle is supposed to be joined with another).
208  auto KernelIDs = std::make_shared<std::vector<kernel_id>>();
209  auto DevImgImpl = std::make_shared<device_image_impl>(
210  nullptr, TargetContext, Devices, State, KernelIDs, PiProgram);
211  device_image_plain DevImg{DevImgImpl};
212 
213  return std::make_shared<kernel_bundle_impl>(TargetContext, Devices, DevImg);
214 }
215 
216 // TODO: Unused. Remove when allowed.
217 std::shared_ptr<detail::kernel_bundle_impl>
218 make_kernel_bundle(pi_native_handle NativeHandle, const context &TargetContext,
219  bundle_state State, backend Backend) {
220  return make_kernel_bundle(NativeHandle, TargetContext, false, State, Backend);
221 }
222 
223 kernel make_kernel(const context &TargetContext,
224  const kernel_bundle<bundle_state::executable> &KernelBundle,
225  pi_native_handle NativeHandle, bool KeepOwnership,
226  backend Backend) {
227  const auto &Plugin = getPlugin(Backend);
228  const auto &ContextImpl = getSyclObjImpl(TargetContext);
229  const auto KernelBundleImpl = getSyclObjImpl(KernelBundle);
230 
231  // For Level-Zero expect exactly one device image in the bundle. This is
232  // natural for interop kernel to get created out of a single native
233  // program/module. This way we don't need to search the exact device image for
234  // the kernel, which may not be trivial.
235  //
236  // Other backends don't need PI program.
237  //
238  pi::PiProgram PiProgram = nullptr;
239  if (Backend == backend::ext_oneapi_level_zero) {
240  if (KernelBundleImpl->size() != 1)
241  throw sycl::runtime_error{
242  "make_kernel: kernel_bundle must have single program image",
243  PI_ERROR_INVALID_PROGRAM};
244 
245  const device_image<bundle_state::executable> &DeviceImage =
246  *KernelBundle.begin();
247  const auto &DeviceImageImpl = getSyclObjImpl(DeviceImage);
248  PiProgram = DeviceImageImpl->get_program_ref();
249  }
250 
251  // Create PI kernel first.
252  pi::PiKernel PiKernel = nullptr;
254  NativeHandle, ContextImpl->getHandleRef(), PiProgram, !KeepOwnership,
255  &PiKernel);
256 
257  if (Backend == backend::opencl)
258  Plugin.call<PiApiKind::piKernelRetain>(PiKernel);
259 
260  // Construct the SYCL queue from PI queue.
261  return detail::createSyclObjFromImpl<kernel>(
262  std::make_shared<kernel_impl>(PiKernel, ContextImpl, KernelBundleImpl));
263 }
264 
265 kernel make_kernel(pi_native_handle NativeHandle, const context &TargetContext,
266  backend Backend) {
267  return make_kernel(
268  TargetContext,
269  get_empty_interop_kernel_bundle<bundle_state::executable>(TargetContext),
270  NativeHandle, false, Backend);
271 }
272 
273 } // namespace detail
274 } // __SYCL_INLINE_VER_NAMESPACE(_V1)
275 } // namespace sycl
The context class represents a SYCL context on which kernel functions may be executed.
Definition: context.hpp:39
The plugin class provides a unified interface to the underlying low-level runtimes for the device-agn...
Definition: plugin.hpp:90
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:47
device_image_iterator begin() const
Provides an abstraction of a SYCL kernel.
Definition: kernel.hpp:69
Encapsulates a SYCL platform on which kernels may be executed.
Definition: platform.hpp:45
Encapsulates a single SYCL queue which schedules kernels on a SYCL device.
Definition: queue.hpp:86
#define __SYCL_INLINE_VER_NAMESPACE(X)
::pi_device PiDevice
Definition: pi.hpp:110
::pi_platform PiPlatform
Definition: pi.hpp:109
::pi_event PiEvent
Definition: pi.hpp:121
::pi_queue PiQueue
Definition: pi.hpp:117
::pi_kernel PiKernel
Definition: pi.hpp:116
::pi_context PiContext
Definition: pi.hpp:114
::pi_program PiProgram
Definition: pi.hpp:115
static const plugin & getPlugin(backend Backend)
Definition: backend.cpp:32
kernel make_kernel(pi_native_handle NativeHandle, const context &TargetContext, backend Backend)
Definition: backend.cpp:265
queue make_queue_impl(pi_native_handle NativeHandle, const context &Context, RT::PiDevice Device, bool KeepOwnership, const async_handler &Handler, backend Backend)
Definition: backend.cpp:83
decltype(Obj::impl) getSyclObjImpl(const Obj &SyclObject)
Definition: common.hpp:240
event make_event(pi_native_handle NativeHandle, const context &TargetContext, backend Backend)
Definition: backend.cpp:112
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:218
device make_device(pi_native_handle NativeHandle)
Definition: opencl.cpp:32
platform make_platform(pi_native_handle NativeHandle)
Definition: opencl.cpp:26
context make_context(pi_native_handle NativeHandle)
Definition: opencl.cpp:38
queue make_queue(const context &Context, pi_native_handle InteropHandle)
Definition: opencl.cpp:45
kernel_bundle< bundle_state::executable > build(const kernel_bundle< bundle_state::input > &InputBundle, const std::vector< device > &Devs, const property_list &PropList={})
std::function< void(sycl::exception_list)> async_handler
---— Error handling, matching OpenCL plugin semantics.
Definition: access.hpp:14
uintptr_t pi_native_handle
Definition: pi.h:107
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)
pi_result piextQueueCreateWithNativeHandle(pi_native_handle nativeHandle, pi_context context, pi_device device, bool pluginOwnsNativeHandle, pi_queue *queue)
Creates PI queue object from a native handle.
pi_result piextProgramCreateWithNativeHandle(pi_native_handle nativeHandle, pi_context context, bool pluginOwnsNativeHandle, pi_program *program)
Creates PI program object from a native handle.
pi_result piKernelRetain(pi_kernel kernel)
pi_result piextPlatformCreateWithNativeHandle(pi_native_handle nativeHandle, pi_platform *platform)
Creates PI platform object from a native handle.
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.
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)
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.
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)
Definition: pi_opencl.cpp:882
pi_result piextDeviceCreateWithNativeHandle(pi_native_handle nativeHandle, pi_platform platform, pi_device *device)
Creates PI device object from a native handle.
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.
@ PI_PROGRAM_INFO_NUM_DEVICES
Definition: pi.h:306
@ PI_PROGRAM_INFO_DEVICES
Definition: pi.h:307
@ PI_PROGRAM_BINARY_TYPE_LIBRARY
Definition: pi.h:154
@ PI_PROGRAM_BINARY_TYPE_EXECUTABLE
Definition: pi.h:155
@ PI_PROGRAM_BINARY_TYPE_COMPILED_OBJECT
Definition: pi.h:153
@ PI_PROGRAM_BINARY_TYPE_NONE
Definition: pi.h:152
pi_result piEventRetain(pi_event event)
@ PI_PROGRAM_BUILD_INFO_BINARY_TYPE
Definition: pi.h:141
C++ wrapper of extern "C" PI interfaces.
@ Device
PI context mapping to a CUDA context object.
Definition: pi_cuda.hpp:160
PI device mapping to a CUdevice.
Definition: pi_cuda.hpp:83
PI Event mapping to CUevent.
Definition: pi_cuda.hpp:608
Implementation of a PI Kernel for CUDA.
Definition: pi_cuda.hpp:791
A PI platform stores all known PI devices, in the CUDA plugin this is just a vector of available devi...
Definition: pi_cuda.hpp:73
Implementation of PI Program on CUDA Module object.
Definition: pi_cuda.hpp:736
PI queue mapping on to CUstream objects.
Definition: pi_cuda.hpp:393