DPC++ Runtime
Runtime libraries for oneAPI DPC++
kernel_bundle.cpp
Go to the documentation of this file.
1 //==------- kernel_bundle.cpp - SYCL kernel_bundle and free functions ------==//
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 
9 #include <detail/compiler.hpp>
16 
17 #include <cstddef>
18 #include <set>
19 #include <vector>
20 
21 namespace sycl {
22 inline namespace _V1 {
23 
24 kernel_id::kernel_id(const char *Name)
25  : impl(std::make_shared<detail::kernel_id_impl>(Name)) {}
26 
27 const char *kernel_id::get_name() const noexcept { return impl->get_name(); }
28 
29 namespace detail {
30 
34 
35 bool device_image_plain::has_kernel(const kernel_id &KernelID) const noexcept {
36  return impl->has_kernel(KernelID);
37 }
38 
39 bool device_image_plain::has_kernel(const kernel_id &KernelID,
40  const device &Dev) const noexcept {
41  return impl->has_kernel(KernelID, Dev);
42 }
43 
44 ur_native_handle_t device_image_plain::getNative() const {
45  return impl->getNative();
46 }
47 
51 
52 bool kernel_bundle_plain::empty() const noexcept { return impl->empty(); }
53 
54 backend kernel_bundle_plain::get_backend() const noexcept {
55  return impl->get_backend();
56 }
57 
58 context kernel_bundle_plain::get_context() const noexcept {
59  return impl->get_context();
60 }
61 
62 std::vector<device> kernel_bundle_plain::get_devices() const noexcept {
63  return impl->get_devices();
64 }
65 
66 std::vector<kernel_id> kernel_bundle_plain::get_kernel_ids() const {
67  return impl->get_kernel_ids();
68 }
69 
70 bool kernel_bundle_plain::contains_specialization_constants() const noexcept {
71  return impl->contains_specialization_constants();
72 }
73 
74 bool kernel_bundle_plain::native_specialization_constant() const noexcept {
75  return impl->native_specialization_constant();
76 }
77 
78 kernel kernel_bundle_plain::get_kernel(const kernel_id &KernelID) const {
79  return impl->get_kernel(KernelID, impl);
80 }
81 
82 const device_image_plain *kernel_bundle_plain::begin() const {
83  return impl->begin();
84 }
85 
86 const device_image_plain *kernel_bundle_plain::end() const {
87  return impl->end();
88 }
89 
90 bool kernel_bundle_plain::has_kernel(const kernel_id &KernelID) const noexcept {
91  return impl->has_kernel(KernelID);
92 }
93 
94 bool kernel_bundle_plain::has_kernel(const kernel_id &KernelID,
95  const device &Dev) const noexcept {
96  return impl->has_kernel(KernelID, Dev);
97 }
98 
99 bool kernel_bundle_plain::has_specialization_constant_impl(
100  const char *SpecName) const noexcept {
101  return impl->has_specialization_constant(SpecName);
102 }
103 
104 void kernel_bundle_plain::set_specialization_constant_impl(
105  const char *SpecName, void *Value, size_t Size) noexcept {
106  impl->set_specialization_constant_raw_value(SpecName, Value, Size);
107 }
108 
109 void kernel_bundle_plain::get_specialization_constant_impl(
110  const char *SpecName, void *Value) const noexcept {
111  impl->get_specialization_constant_raw_value(SpecName, Value);
112 }
113 
114 bool kernel_bundle_plain::is_specialization_constant_set(
115  const char *SpecName) const noexcept {
116  return impl->is_specialization_constant_set(SpecName);
117 }
118 
119 bool kernel_bundle_plain::ext_oneapi_has_kernel(detail::string_view name) {
120  return impl->ext_oneapi_has_kernel(name.data());
121 }
122 
123 kernel kernel_bundle_plain::ext_oneapi_get_kernel(detail::string_view name) {
124  return impl->ext_oneapi_get_kernel(name.data(), impl);
125 }
126 
130 
131 const std::vector<device>
132 removeDuplicateDevices(const std::vector<device> &Devs) {
133  std::vector<device> UniqueDevices;
134 
135  // Building a new vector with unique elements and keep original order
136  std::unordered_set<device> UniqueDeviceSet;
137  for (const device &Dev : Devs)
138  if (UniqueDeviceSet.insert(Dev).second)
139  UniqueDevices.push_back(Dev);
140 
141  return UniqueDevices;
142 }
143 
145  return detail::ProgramManager::getInstance().getSYCLKernelID(
146  KernelName.data());
147 }
148 
150 get_kernel_bundle_impl(const context &Ctx, const std::vector<device> &Devs,
151  bundle_state State) {
152  return std::make_shared<detail::kernel_bundle_impl>(Ctx, Devs, State);
153 }
154 
156 get_kernel_bundle_impl(const context &Ctx, const std::vector<device> &Devs,
157  const std::vector<kernel_id> &KernelIDs,
158  bundle_state State) {
159  return std::make_shared<detail::kernel_bundle_impl>(Ctx, Devs, KernelIDs,
160  State);
161 }
162 
164 get_kernel_bundle_impl(const context &Ctx, const std::vector<device> &Devs,
165  bundle_state State, const DevImgSelectorImpl &Selector) {
166  return std::make_shared<detail::kernel_bundle_impl>(Ctx, Devs, Selector,
167  State);
168 }
169 
172  const std::vector<device> &Devs) {
173  return std::make_shared<detail::kernel_bundle_impl>(Ctx, Devs);
174 }
175 
176 std::shared_ptr<detail::kernel_bundle_impl>
177 join_impl(const std::vector<detail::KernelBundleImplPtr> &Bundles,
178  bundle_state State) {
179  return std::make_shared<detail::kernel_bundle_impl>(Bundles, State);
180 }
181 
182 bool has_kernel_bundle_impl(const context &Ctx, const std::vector<device> &Devs,
183  bundle_state State) {
184  // Check that all requested devices are associated with the context
185  const bool AllDevicesInTheContext = checkAllDevicesAreInContext(Devs, Ctx);
186  if (Devs.empty() || !AllDevicesInTheContext)
187  throw sycl::exception(make_error_code(errc::invalid),
188  "Not all devices are associated with the context or "
189  "vector of devices is empty");
190 
191  if (bundle_state::input == State &&
192  !checkAllDevicesHaveAspect(Devs, aspect::online_compiler))
193  return false;
194  if (bundle_state::object == State &&
195  !checkAllDevicesHaveAspect(Devs, aspect::online_linker))
196  return false;
197 
198  const std::vector<device_image_plain> DeviceImages =
199  detail::ProgramManager::getInstance()
200  .getSYCLDeviceImagesWithCompatibleState(Ctx, Devs, State);
201 
202  return (bool)DeviceImages.size();
203 }
204 
205 bool has_kernel_bundle_impl(const context &Ctx, const std::vector<device> &Devs,
206  const std::vector<kernel_id> &KernelIds,
207  bundle_state State) {
208  // Check that all requested devices are associated with the context
209  const bool AllDevicesInTheContext = checkAllDevicesAreInContext(Devs, Ctx);
210 
211  if (Devs.empty() || !AllDevicesInTheContext)
212  throw sycl::exception(make_error_code(errc::invalid),
213  "Not all devices are associated with the context or "
214  "vector of devices is empty");
215 
216  bool DeviceHasRequireAspectForState = true;
217  if (bundle_state::input == State) {
218  DeviceHasRequireAspectForState =
219  std::all_of(Devs.begin(), Devs.end(), [](const device &Dev) {
220  return Dev.has(aspect::online_compiler);
221  });
222  } else if (bundle_state::object == State) {
223  DeviceHasRequireAspectForState =
224  std::all_of(Devs.begin(), Devs.end(), [](const device &Dev) {
225  return Dev.has(aspect::online_linker);
226  });
227  }
228 
229  if (!DeviceHasRequireAspectForState)
230  return false;
231 
232  const std::vector<device_image_plain> DeviceImages =
233  detail::ProgramManager::getInstance()
234  .getSYCLDeviceImagesWithCompatibleState(Ctx, Devs, State);
235 
236  std::set<kernel_id, LessByNameComp> CombinedKernelIDs;
237  for (const device_image_plain &DeviceImage : DeviceImages) {
238  const std::shared_ptr<device_image_impl> &DeviceImageImpl =
239  getSyclObjImpl(DeviceImage);
240 
241  CombinedKernelIDs.insert(DeviceImageImpl->get_kernel_ids_ptr()->begin(),
242  DeviceImageImpl->get_kernel_ids_ptr()->end());
243  }
244 
245  const bool AllKernelIDsRepresented =
246  std::all_of(KernelIds.begin(), KernelIds.end(),
247  [&CombinedKernelIDs](const kernel_id &KernelID) {
248  return CombinedKernelIDs.count(KernelID);
249  });
250 
251  return AllKernelIDsRepresented;
252 }
253 
254 std::shared_ptr<detail::kernel_bundle_impl>
256  const std::vector<device> &Devs, const property_list &PropList) {
257  return std::make_shared<detail::kernel_bundle_impl>(
258  InputBundle, Devs, PropList, bundle_state::object);
259 }
260 
261 std::shared_ptr<detail::kernel_bundle_impl>
262 link_impl(const std::vector<kernel_bundle<bundle_state::object>> &ObjectBundles,
263  const std::vector<device> &Devs, const property_list &PropList) {
264  return std::make_shared<detail::kernel_bundle_impl>(ObjectBundles, Devs,
265  PropList);
266 }
267 
268 std::shared_ptr<detail::kernel_bundle_impl>
270  const std::vector<device> &Devs, const property_list &PropList) {
271  return std::make_shared<detail::kernel_bundle_impl>(
272  InputBundle, Devs, PropList, bundle_state::executable);
273 }
274 
275 // This function finds intersection of associated devices in common for all
276 // bundles
277 std::vector<sycl::device> find_device_intersection(
278  const std::vector<kernel_bundle<bundle_state::object>> &ObjectBundles) {
279  std::vector<sycl::device> IntersectDevices;
280  std::vector<unsigned int> DevsCounters;
281  std::map<device, unsigned int, LessByHash<device>> DevCounters;
282  for (const sycl::kernel_bundle<bundle_state::object> &ObjectBundle :
283  ObjectBundles)
284  // Increment counter in "DevCounters" each time a device is seen
285  for (const sycl::device &Device : ObjectBundle.get_devices())
286  DevCounters[Device]++;
287 
288  // If some device counter is less than ObjectBundles.size() then some bundle
289  // doesn't have it - do not add such a device to the final result
290  for (const std::pair<const device, unsigned int> &It : DevCounters)
291  if (ObjectBundles.size() == It.second)
292  IntersectDevices.push_back(It.first);
293 
294  return IntersectDevices;
295 }
296 
297 } // namespace detail
298 
302 
303 std::vector<kernel_id> get_kernel_ids() {
304  return detail::ProgramManager::getInstance().getAllSYCLKernelIDs();
305 }
306 
307 bool is_compatible(const std::vector<kernel_id> &KernelIDs, const device &Dev) {
308  if (KernelIDs.empty())
309  return true;
310  // TODO: also need to check that the architecture specified by the
311  // "-fsycl-targets" flag matches the device when we are able to get the
312  // device's arch.
313  auto doesImageTargetMatchDevice = [](const device &Dev,
314  const detail::RTDeviceBinaryImage &Img) {
315  const char *Target = Img.getRawData().DeviceTargetSpec;
316  auto BE = Dev.get_backend();
317  if (strcmp(Target, __SYCL_DEVICE_BINARY_TARGET_SPIRV64) == 0) {
318  return (BE == sycl::backend::opencl ||
319  BE == sycl::backend::ext_oneapi_level_zero);
320  } else if (strcmp(Target, __SYCL_DEVICE_BINARY_TARGET_SPIRV64_X86_64) ==
321  0) {
322  return Dev.is_cpu();
323  } else if (strcmp(Target, __SYCL_DEVICE_BINARY_TARGET_SPIRV64_GEN) == 0) {
324  return Dev.is_gpu() && (BE == sycl::backend::opencl ||
325  BE == sycl::backend::ext_oneapi_level_zero);
326  } else if (strcmp(Target, __SYCL_DEVICE_BINARY_TARGET_SPIRV64_FPGA) == 0) {
327  return Dev.is_accelerator();
328  } else if (strcmp(Target, __SYCL_DEVICE_BINARY_TARGET_NVPTX64) == 0) {
329  return BE == sycl::backend::ext_oneapi_cuda;
330  } else if (strcmp(Target, __SYCL_DEVICE_BINARY_TARGET_AMDGCN) == 0) {
331  return BE == sycl::backend::ext_oneapi_hip;
332  }
333 
334  return false;
335  };
336 
337  // One kernel may be contained in several binary images depending on the
338  // number of targets. This kernel is compatible with the device if there is
339  // at least one image (containing this kernel) whose aspects are supported by
340  // the device and whose target matches the device.
341  for (const auto &KernelID : KernelIDs) {
342  std::set<detail::RTDeviceBinaryImage *> BinImages =
343  detail::ProgramManager::getInstance().getRawDeviceImages({KernelID});
344 
345  if (std::none_of(BinImages.begin(), BinImages.end(),
346  [&](const detail::RTDeviceBinaryImage *Img) {
347  return doesDevSupportDeviceRequirements(Dev, *Img) &&
348  doesImageTargetMatchDevice(Dev, *Img);
349  }))
350  return false;
351  }
352 
353  return true;
354 }
355 
357 // * kernel_compiler extension *
359 namespace ext::oneapi::experimental {
360 
364 
365 namespace detail {
366 
368 // syclex::detail::is_source_kernel_bundle_supported
371  // Support is limited to the opencl and level_zero backends.
372  bool BE_Acceptable = (BE == sycl::backend::ext_oneapi_level_zero) ||
373  (BE == sycl::backend::opencl);
374  if (BE_Acceptable) {
375  if (Language == source_language::opencl) {
377  } else if (Language == source_language::spirv) {
378  return true;
379  } else if (Language == source_language::sycl) {
381  }
382  }
383 
384  // otherwise
385  return false;
386 }
387 
389 // syclex::detail::create_kernel_bundle_from_source
391 
392 using include_pairs_t = std::vector<std::pair<std::string, std::string>>;
393 using include_pairs_view_t = std::vector<
394  std::pair<sycl::detail::string_view, sycl::detail::string_view>>;
395 
396 source_kb
398  source_language Language,
399  sycl::detail::string_view SourceView,
400  include_pairs_view_t IncludePairViews) {
401  // TODO: if we later support a "reason" why support isn't present
402  // (like a missing shared library etc.) it'd be nice to include it in
403  // the exception message here.
404  std::string Source{SourceView.data()};
405  include_pairs_t IncludePairs;
406  size_t n = IncludePairViews.size();
407  IncludePairs.reserve(n);
408  for (auto &p : IncludePairViews)
409  IncludePairs.push_back({p.first.data(), p.second.data()});
410 
411  backend BE = SyclContext.get_backend();
412  if (!is_source_kernel_bundle_supported(BE, Language))
413  throw sycl::exception(make_error_code(errc::invalid),
414  "kernel_bundle creation from source not supported");
415 
416  // throw if include not supported? awaiting guidance
417  // if(!IncludePairs.empty() && is_include_supported(Languuage)){ throw invalid
418  // }
419 
420  std::shared_ptr<kernel_bundle_impl> KBImpl =
421  std::make_shared<kernel_bundle_impl>(SyclContext, Language, Source,
422  IncludePairs);
423  return sycl::detail::createSyclObjFromImpl<source_kb>(KBImpl);
424 }
425 
427  source_language Language,
428  const std::vector<std::byte> &Bytes,
429  include_pairs_view_t IncludePairs) {
430  (void)IncludePairs;
431  backend BE = SyclContext.get_backend();
432  if (!is_source_kernel_bundle_supported(BE, Language))
433  throw sycl::exception(make_error_code(errc::invalid),
434  "kernel_bundle creation from source not supported");
435 
436  std::shared_ptr<kernel_bundle_impl> KBImpl =
437  std::make_shared<kernel_bundle_impl>(SyclContext, Language, Bytes);
438  return sycl::detail::createSyclObjFromImpl<source_kb>(KBImpl);
439 }
440 
442 // syclex::detail::build_from_source(source_kb) => exe_kb
444 
446  source_kb &SourceKB, const std::vector<device> &Devices,
447  const std::vector<sycl::detail::string_view> &BuildOptions,
448  sycl::detail::string *LogView,
449  const std::vector<sycl::detail::string_view> &RegisteredKernelNames) {
450  std::vector<std::string> Options;
451  for (const sycl::detail::string_view option : BuildOptions)
452  Options.push_back(option.data());
453 
454  std::vector<std::string> KernelNames;
455  for (const sycl::detail::string_view name : RegisteredKernelNames)
456  KernelNames.push_back(name.data());
457 
458  std::string Log;
459  std::string *LogPtr = nullptr;
460  if (LogView)
461  LogPtr = &Log;
462  std::vector<device> UniqueDevices =
464  std::shared_ptr<kernel_bundle_impl> sourceImpl = getSyclObjImpl(SourceKB);
465  std::shared_ptr<kernel_bundle_impl> KBImpl = sourceImpl->build_from_source(
466  UniqueDevices, Options, LogPtr, KernelNames);
467  auto result = sycl::detail::createSyclObjFromImpl<exe_kb>(KBImpl);
468  if (LogView)
469  *LogView = Log;
470  return result;
471 }
472 
473 } // namespace detail
474 } // namespace ext::oneapi::experimental
475 
476 } // namespace _V1
477 } // namespace sycl
The context class represents a SYCL context on which kernel functions may be executed.
Definition: context.hpp:50
backend get_backend() const noexcept
Gets OpenCL interoperability context.
Definition: context.cpp:118
const char * data() const noexcept
Definition: string_view.hpp:38
The SYCL device class encapsulates a single SYCL device on which kernels may be executed.
Definition: device.hpp:64
bool is_accelerator() const
Check if device is an accelerator device.
Definition: device.cpp:79
bool is_gpu() const
Check if device is a GPU device.
Definition: device.cpp:77
static std::vector< device > get_devices(info::device_type deviceType=info::device_type::all)
Query available SYCL devices.
Definition: device.cpp:53
backend get_backend() const noexcept
Returns the backend associated with this device.
Definition: device.cpp:203
bool is_cpu() const
Get instance of device.
Definition: device.cpp:75
The kernel_bundle class represents collection of device images in a particular state.
Objects of the class identify kernel is some kernel_bundle related APIs.
Provides an abstraction of a SYCL kernel.
Definition: kernel.hpp:71
Objects of the property_list class are containers for the SYCL properties.
#define __SYCL_DEVICE_BINARY_TARGET_SPIRV64_FPGA
Definition: compiler.hpp:29
#define __SYCL_DEVICE_BINARY_TARGET_AMDGCN
Definition: compiler.hpp:32
#define __SYCL_DEVICE_BINARY_TARGET_NVPTX64
PTX 64-bit image <-> "nvptx64", 64-bit NVIDIA PTX device.
Definition: compiler.hpp:31
#define __SYCL_DEVICE_BINARY_TARGET_SPIRV64
SPIR-V 64-bit image <-> "spir64", 64-bit OpenCL device.
Definition: compiler.hpp:24
#define __SYCL_DEVICE_BINARY_TARGET_SPIRV64_X86_64
Device-specific binary images produced from SPIR-V 64-bit <-> various "spir64_*" triples for specific...
Definition: compiler.hpp:27
#define __SYCL_DEVICE_BINARY_TARGET_SPIRV64_GEN
Definition: compiler.hpp:28
std::vector< sycl::device > find_device_intersection(const std::vector< kernel_bundle< bundle_state::object >> &ObjectBundles)
std::shared_ptr< detail::kernel_bundle_impl > build_impl(const kernel_bundle< bundle_state::input > &InputBundle, const std::vector< device > &Devs, const property_list &PropList)
decltype(Obj::impl) const & getSyclObjImpl(const Obj &SyclObject)
Definition: impl_utils.hpp:31
static bool checkAllDevicesAreInContext(const std::vector< device > &Devices, const context &Context)
std::function< bool(const detail::DeviceImageImplPtr &DevImgImpl)> DevImgSelectorImpl
static bool checkAllDevicesHaveAspect(const std::vector< device > &Devices, aspect Aspect)
const std::vector< device > removeDuplicateDevices(const std::vector< device > &Devs)
detail::KernelBundleImplPtr get_kernel_bundle_impl(const context &Ctx, const std::vector< device > &Devs, bundle_state State)
std::shared_ptr< detail::kernel_bundle_impl > compile_impl(const kernel_bundle< bundle_state::input > &InputBundle, const std::vector< device > &Devs, const property_list &PropList)
std::shared_ptr< detail::kernel_bundle_impl > join_impl(const std::vector< detail::KernelBundleImplPtr > &Bundles, bundle_state State)
kernel_id get_kernel_id_impl(string_view KernelName)
std::shared_ptr< detail::kernel_bundle_impl > KernelBundleImplPtr
bool has_kernel_bundle_impl(const context &Ctx, const std::vector< device > &Devs, bundle_state State)
detail::KernelBundleImplPtr get_empty_interop_kernel_bundle_impl(const context &Ctx, const std::vector< device > &Devs)
std::shared_ptr< detail::kernel_bundle_impl > link_impl(const std::vector< kernel_bundle< bundle_state::object >> &ObjectBundles, const std::vector< device > &Devs, const property_list &PropList)
source_kb make_kernel_bundle_from_source(const context &SyclContext, source_language Language, const std::vector< std::byte > &Bytes, include_pairs_view_t IncludePairs)
std::vector< std::pair< std::string, std::string > > include_pairs_t
std::vector< std::pair< sycl::detail::string_view, sycl::detail::string_view > > include_pairs_view_t
exe_kb build_from_source(source_kb &SourceKB, const std::vector< device > &Devices, const std::vector< sycl::detail::string_view > &BuildOptions, sycl::detail::string *LogView, const std::vector< sycl::detail::string_view > &RegisteredKernelNames)
bool is_source_kernel_bundle_supported(backend BE, source_language Language)
sycl::detail::kernel_bundle_impl kernel_bundle_impl
std::vector< kernel_id > get_kernel_ids()
bool is_compatible(const std::vector< kernel_id > &KernelIDs, const device &Dev)
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
bool all_of(const simd_mask< _Tp, _Abi > &) noexcept
_Abi const simd< _Tp, _Abi > & noexcept
Definition: simd.hpp:1324
bool none_of(const simd_mask< _Tp, _Abi > &) noexcept