DPC++ Runtime
Runtime libraries for oneAPI DPC++
allowlist.cpp
Go to the documentation of this file.
1 //==-------------- allowlist.cpp - SYCL_DEVICE_ALLOWLIST -------------------==//
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 #include <detail/allowlist.hpp>
9 #include <detail/config.hpp>
10 #include <detail/device_impl.hpp>
11 #include <detail/device_info.hpp>
12 #include <detail/platform_info.hpp>
13 #include <sycl/backend.hpp>
14 
15 #include <algorithm>
16 #include <regex>
17 
18 namespace sycl {
19 inline namespace _V1 {
20 namespace detail {
21 
22 constexpr char BackendNameKeyName[] = "BackendName";
23 constexpr char DeviceTypeKeyName[] = "DeviceType";
24 constexpr char DeviceVendorIdKeyName[] = "DeviceVendorId";
25 constexpr char DriverVersionKeyName[] = "DriverVersion";
26 constexpr char PlatformVersionKeyName[] = "PlatformVersion";
27 constexpr char DeviceNameKeyName[] = "DeviceName";
28 constexpr char PlatformNameKeyName[] = "PlatformName";
29 
30 constexpr std::array<const char *, 7> SupportedAllowListKeyNames{
34 
35 // Parsing and validating SYCL_DEVICE_ALLOWLIST variable value.
36 //
37 // The value has the following form:
38 // DeviceDesc1|DeviceDesc2|<...>|DeviceDescN
39 // DeviceDescN is the set of descriptions for the device which should be
40 // allowed. The sets of device descriptions are separated by '|' symbol. The set
41 // of descriptions has the following structure:
42 // DeviceDescN = Key1:Value1,Key2:Value2,...,KeyN:ValueN
43 // Device descriptions are separated by ',' symbol.
44 // Key and value of a device description are separated by ":" symbol.
45 // KeyN is the key of a device description, it could be one of the following
46 // from SupportedAllowListKeyNames vector above.
47 // DeviceName and PlatformName device descriptions are deprecated and will be
48 // removed in one of the future releases.
49 // ValueN is the value of a device description, it could be regex and some fixed
50 // string.
51 // Function should return parsed SYCL_DEVICE_ALLOWLIST variable value as
52 // AllowListParsedT type (vector of maps), e.g.:
53 // {{Key1: Value1, Key2: Value2}, ..., {Key1: Value1, ..., KeyN: ValueN}}
54 AllowListParsedT parseAllowList(const std::string &AllowListRaw) {
55  if (AllowListRaw.empty())
56  return {};
57 
58  AllowListParsedT AllowListParsed;
59  AllowListParsed.emplace_back();
60 
61  constexpr std::array<const char *, 3> SupportedKeyNamesHaveFixedValue{
63  constexpr std::array<const char *, 4> SupportedKeyNamesRequireRegexValue{
66 
67  size_t KeyStart = 0, KeyEnd = 0, ValueStart = 0, ValueEnd = 0,
68  DeviceDescIndex = 0;
69 
70  const char DelimiterBtwKeyAndValue = ':';
71  const char DelimiterBtwItemsInDeviceDesc = ',';
72  const char DelimiterBtwDeviceDescs = '|';
73 
74  if (AllowListRaw.find(DelimiterBtwKeyAndValue, KeyStart) == std::string::npos)
75  throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
76  "SYCL_DEVICE_ALLOWLIST has incorrect format. For "
77  "details, please refer to "
78  "https://github.com/intel/llvm/blob/sycl/sycl/"
79  "doc/EnvironmentVariables.md " +
80  codeToString(UR_RESULT_ERROR_INVALID_VALUE));
81 
82  const std::string &DeprecatedKeyNameDeviceName = DeviceNameKeyName;
83  const std::string &DeprecatedKeyNamePlatformName = PlatformNameKeyName;
84 
85  bool IsDeprecatedKeyNameDeviceNameWasUsed = false;
86  bool IsDeprecatedKeyNamePlatformNameWasUsed = false;
87 
88  while ((KeyEnd = AllowListRaw.find(DelimiterBtwKeyAndValue, KeyStart)) !=
89  std::string::npos) {
90  if ((ValueStart = AllowListRaw.find_first_not_of(
91  DelimiterBtwKeyAndValue, KeyEnd)) == std::string::npos)
92  break;
93  const std::string &Key = AllowListRaw.substr(KeyStart, KeyEnd - KeyStart);
94 
95  // check that provided key is supported
96  if (std::find(SupportedAllowListKeyNames.begin(),
98  Key) == SupportedAllowListKeyNames.end()) {
99  throw sycl::exception(
100  sycl::make_error_code(sycl::errc::runtime),
101  "Unrecognized key in SYCL_DEVICE_ALLOWLIST. For details, please "
102  "refer to "
103  "https://github.com/intel/llvm/blob/sycl/sycl/doc/"
104  "EnvironmentVariables.md " +
105  codeToString(UR_RESULT_ERROR_INVALID_VALUE));
106  }
107 
108  if (Key == DeprecatedKeyNameDeviceName) {
109  IsDeprecatedKeyNameDeviceNameWasUsed = true;
110  }
111  if (Key == DeprecatedKeyNamePlatformName) {
112  IsDeprecatedKeyNamePlatformNameWasUsed = true;
113  }
114 
115  bool ShouldAllocateNewDeviceDescMap = false;
116 
117  std::string Value;
118 
119  auto &DeviceDescMap = AllowListParsed[DeviceDescIndex];
120 
121  // check if Key is not already defined in DeviceDescMap, e.g., caused by the
122  // following invalid syntax: Key1:Value1,Key2:Value2,Key1:Value3
123  if (DeviceDescMap.find(Key) == DeviceDescMap.end()) {
124  // calculate and validate value which has fixed format
125  if (std::find(SupportedKeyNamesHaveFixedValue.begin(),
126  SupportedKeyNamesHaveFixedValue.end(),
127  Key) != SupportedKeyNamesHaveFixedValue.end()) {
128  ValueEnd = AllowListRaw.find(DelimiterBtwItemsInDeviceDesc, ValueStart);
129  // check if it is the last Key:Value pair in the device description, and
130  // correct end position of that value
131  if (size_t ValueEndCand =
132  AllowListRaw.find(DelimiterBtwDeviceDescs, ValueStart);
133  (ValueEndCand != std::string::npos) && (ValueEndCand < ValueEnd)) {
134  ValueEnd = ValueEndCand;
135  ShouldAllocateNewDeviceDescMap = true;
136  }
137  if (ValueEnd == std::string::npos)
138  ValueEnd = AllowListRaw.length();
139 
140  Value = AllowListRaw.substr(ValueStart, ValueEnd - ValueStart);
141 
142  // post-processing checks for some values
143 
144  auto ValidateEnumValues = [&](std::string CheckingKeyName,
145  auto SourceOfSupportedValues) {
146  if (Key == CheckingKeyName) {
147  bool ValueIsValid = false;
148  for (const auto &Item : SourceOfSupportedValues)
149  if (Value == Item.first) {
150  ValueIsValid = true;
151  break;
152  }
153  if (!ValueIsValid)
154  throw sycl::exception(
155  sycl::make_error_code(sycl::errc::runtime),
156  "Value " + Value + " for key " + Key +
157  " is not valid in "
158  "SYCL_DEVICE_ALLOWLIST. For details, please refer to "
159  "https://github.com/intel/llvm/blob/sycl/sycl/doc/"
160  "EnvironmentVariables.md " +
161  codeToString(UR_RESULT_ERROR_INVALID_VALUE));
162  }
163  };
164 
165  // check that values of keys, which should have some fixed format, are
166  // valid. E.g., for BackendName key, the allowed values are only ones
167  // described in SyclBeMap
168  ValidateEnumValues(BackendNameKeyName, getSyclBeMap());
169  ValidateEnumValues(DeviceTypeKeyName,
170  getSyclDeviceTypeMap<true /*Enable 'acc'*/>());
171 
172  if (Key == DeviceVendorIdKeyName) {
173  // DeviceVendorId should have hex format
174  if (!std::regex_match(Value, std::regex("0[xX][0-9a-fA-F]+"))) {
175  throw sycl::exception(
176  sycl::make_error_code(sycl::errc::runtime),
177  "Value " + Value + " for key " + Key +
178  " is not valid in "
179  "SYCL_DEVICE_ALLOWLIST. It should have the hex format. For "
180  "details, please refer to "
181  "https://github.com/intel/llvm/blob/sycl/sycl/doc/"
182  "EnvironmentVariables.md " +
183  codeToString(UR_RESULT_ERROR_INVALID_VALUE));
184  }
185  }
186  }
187  // calculate and validate value which has regex format
188  else if (std::find(SupportedKeyNamesRequireRegexValue.begin(),
189  SupportedKeyNamesRequireRegexValue.end(),
190  Key) != SupportedKeyNamesRequireRegexValue.end()) {
191  const std::string Prefix("{{");
192  // TODO: can be changed to string_view::starts_with after switching
193  // DPC++ RT to C++20
194  if (Prefix != AllowListRaw.substr(ValueStart, Prefix.length())) {
195  throw sycl::exception(
196  sycl::make_error_code(sycl::errc::runtime),
197  "Key " + Key +
198  " of SYCL_DEVICE_ALLOWLIST should have "
199  "value which starts with " +
200  Prefix + " " +
201  detail::codeToString(UR_RESULT_ERROR_INVALID_VALUE));
202  }
203  // cut off prefix from the value
204  ValueStart += Prefix.length();
205 
206  ValueEnd = ValueStart;
207  const std::string Postfix("}}");
208  for (; ValueEnd < AllowListRaw.length() - Postfix.length() + 1;
209  ++ValueEnd) {
210  if (Postfix == AllowListRaw.substr(ValueEnd, Postfix.length()))
211  break;
212  // if it is the last iteration and next 2 symbols are not a postfix,
213  // throw exception
214  if (ValueEnd == AllowListRaw.length() - Postfix.length())
215  throw sycl::exception(
216  sycl::make_error_code(sycl::errc::runtime),
217  "Key " + Key +
218  " of SYCL_DEVICE_ALLOWLIST should have "
219  "value which ends with " +
220  Postfix + " " +
221  detail::codeToString(UR_RESULT_ERROR_INVALID_VALUE));
222  }
223  size_t NextExpectedDelimiterPos = ValueEnd + Postfix.length();
224  // if it is not the end of the string, check that symbol next to a
225  // postfix is a delimiter (, or ;)
226  if ((AllowListRaw.length() != NextExpectedDelimiterPos) &&
227  (AllowListRaw[NextExpectedDelimiterPos] !=
228  DelimiterBtwItemsInDeviceDesc) &&
229  (AllowListRaw[NextExpectedDelimiterPos] != DelimiterBtwDeviceDescs))
230  throw sycl::exception(
231  sycl::make_error_code(sycl::errc::runtime),
232  "Unexpected symbol on position " +
233  std::to_string(NextExpectedDelimiterPos) + ": " +
234  AllowListRaw[NextExpectedDelimiterPos] +
235  ". Should be either " + DelimiterBtwItemsInDeviceDesc +
236  " or " + DelimiterBtwDeviceDescs +
237  codeToString(UR_RESULT_ERROR_INVALID_VALUE));
238 
239  if (AllowListRaw[NextExpectedDelimiterPos] == DelimiterBtwDeviceDescs)
240  ShouldAllocateNewDeviceDescMap = true;
241 
242  Value = AllowListRaw.substr(ValueStart, ValueEnd - ValueStart);
243 
244  ValueEnd += Postfix.length();
245  } else
246  assert(false &&
247  "Key should be either in SupportedKeyNamesHaveFixedValue "
248  "or SupportedKeyNamesRequireRegexValue");
249 
250  // add key and value to the map
251  DeviceDescMap.emplace(Key, Value);
252  } else
253  throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
254  "Re-definition of key " + Key +
255  " is not allowed in "
256  "SYCL_DEVICE_ALLOWLIST " +
257  codeToString(UR_RESULT_ERROR_INVALID_VALUE));
258 
259  KeyStart = ValueEnd;
260  if (KeyStart != std::string::npos)
261  ++KeyStart;
262  if (ShouldAllocateNewDeviceDescMap) {
263  ++DeviceDescIndex;
264  AllowListParsed.emplace_back();
265  }
266  }
267 
268  if (IsDeprecatedKeyNameDeviceNameWasUsed &&
269  IsDeprecatedKeyNamePlatformNameWasUsed) {
270  std::cout << "\nWARNING: " << DeprecatedKeyNameDeviceName << " and "
271  << DeprecatedKeyNamePlatformName
272  << " in SYCL_DEVICE_ALLOWLIST are deprecated. ";
273  } else if (IsDeprecatedKeyNameDeviceNameWasUsed) {
274  std::cout << "\nWARNING: " << DeprecatedKeyNameDeviceName
275  << " in SYCL_DEVICE_ALLOWLIST is deprecated. ";
276  } else if (IsDeprecatedKeyNamePlatformNameWasUsed) {
277  std::cout << "\nWARNING: " << DeprecatedKeyNamePlatformName
278  << " in SYCL_DEVICE_ALLOWLIST is deprecated. ";
279  }
280  if (IsDeprecatedKeyNameDeviceNameWasUsed ||
281  IsDeprecatedKeyNamePlatformNameWasUsed) {
282  std::cout << "Please use " << BackendNameKeyName << ", "
284  << " instead. For details, please refer to "
285  "https://github.com/intel/llvm/blob/sycl/sycl/doc/"
286  "EnvironmentVariables.md\n\n";
287  }
288 
289  return AllowListParsed;
290 }
291 
292 // Checking if we can allow device with device description DeviceDesc
293 bool deviceIsAllowed(const DeviceDescT &DeviceDesc,
294  const AllowListParsedT &AllowListParsed) {
297  [&DeviceDesc](const auto &SupportedKeyName) {
298  return DeviceDesc.find(SupportedKeyName) !=
299  DeviceDesc.end();
300  }) &&
301  "DeviceDesc map should have all supported keys for "
302  "SYCL_DEVICE_ALLOWLIST.");
303  auto EqualityComp = [&](const std::string &KeyName,
304  const DeviceDescT &AllowListDeviceDesc) {
305  // change to map::contains after switching DPC++ RT to C++20
306  if (AllowListDeviceDesc.find(KeyName) != AllowListDeviceDesc.end())
307  if (AllowListDeviceDesc.at(KeyName) != DeviceDesc.at(KeyName))
308  return false;
309  return true;
310  };
311  auto RegexComp = [&](const std::string &KeyName,
312  const DeviceDescT &AllowListDeviceDesc) {
313  if (AllowListDeviceDesc.find(KeyName) != AllowListDeviceDesc.end())
314  if (!std::regex_match(DeviceDesc.at(KeyName),
315  std::regex(AllowListDeviceDesc.at(KeyName))))
316  return false;
317  return true;
318  };
319 
320  bool ShouldDeviceBeAllowed = false;
321 
322  for (const auto &AllowListDeviceDesc : AllowListParsed) {
323  if (!EqualityComp(BackendNameKeyName, AllowListDeviceDesc))
324  continue;
325  if (!EqualityComp(DeviceTypeKeyName, AllowListDeviceDesc))
326  continue;
327  if (!EqualityComp(DeviceVendorIdKeyName, AllowListDeviceDesc))
328  continue;
329  if (!RegexComp(DriverVersionKeyName, AllowListDeviceDesc))
330  continue;
331  if (!RegexComp(PlatformVersionKeyName, AllowListDeviceDesc))
332  continue;
333  if (!RegexComp(DeviceNameKeyName, AllowListDeviceDesc))
334  continue;
335  if (!RegexComp(PlatformNameKeyName, AllowListDeviceDesc))
336  continue;
337 
338  // no any continue was called on this iteration, so all parameters matched
339  // successfully, so allow this device to use
340  ShouldDeviceBeAllowed = true;
341  break;
342  }
343 
344  return ShouldDeviceBeAllowed;
345 }
346 
347 void applyAllowList(std::vector<ur_device_handle_t> &UrDevices,
348  ur_platform_handle_t UrPlatform, const PluginPtr &Plugin) {
349 
350  AllowListParsedT AllowListParsed =
352  if (AllowListParsed.empty())
353  return;
354 
355  // Get platform's backend and put it to DeviceDesc
356  DeviceDescT DeviceDesc;
357  auto PlatformImpl = platform_impl::getOrMakePlatformImpl(UrPlatform, Plugin);
358  backend Backend = PlatformImpl->getBackend();
359 
360  for (const auto &SyclBe : getSyclBeMap()) {
361  if (SyclBe.second == Backend) {
362  DeviceDesc.emplace(BackendNameKeyName, SyclBe.first);
363  break;
364  }
365  }
366  // get PlatformVersion value and put it to DeviceDesc
367  DeviceDesc.emplace(PlatformVersionKeyName,
368  sycl::detail::get_platform_info<info::platform::version>(
369  UrPlatform, Plugin));
370  // get PlatformName value and put it to DeviceDesc
371  DeviceDesc.emplace(PlatformNameKeyName,
372  sycl::detail::get_platform_info<info::platform::name>(
373  UrPlatform, Plugin));
374 
375  int InsertIDx = 0;
376  for (ur_device_handle_t Device : UrDevices) {
377  auto DeviceImpl = PlatformImpl->getOrMakeDeviceImpl(Device, PlatformImpl);
378  // get DeviceType value and put it to DeviceDesc
379  ur_device_type_t UrDevType = UR_DEVICE_TYPE_ALL;
380  Plugin->call<UrApiKind::urDeviceGetInfo>(
381  Device, UR_DEVICE_INFO_TYPE, sizeof(UrDevType), &UrDevType, nullptr);
382  // TODO need mechanism to do these casts, there's a bunch of this sort of
383  // thing
385  switch (UrDevType) {
386  default:
387  case UR_DEVICE_TYPE_ALL:
388  DeviceType = info::device_type::all;
389  break;
390  case UR_DEVICE_TYPE_GPU:
391  DeviceType = info::device_type::gpu;
392  break;
393  case UR_DEVICE_TYPE_CPU:
394  DeviceType = info::device_type::cpu;
395  break;
396  case UR_DEVICE_TYPE_FPGA:
397  DeviceType = info::device_type::accelerator;
398  break;
399  }
400  for (const auto &SyclDeviceType :
401  getSyclDeviceTypeMap<true /*Enable 'acc'*/>()) {
402  if (SyclDeviceType.second == DeviceType) {
403  const auto &DeviceTypeValue = SyclDeviceType.first;
404  DeviceDesc[DeviceTypeKeyName] = DeviceTypeValue;
405  break;
406  }
407  }
408  // get DeviceVendorId value and put it to DeviceDesc
409  uint32_t DeviceVendorIdUInt =
410  sycl::detail::get_device_info<info::device::vendor_id>(DeviceImpl);
411  std::stringstream DeviceVendorIdHexStringStream;
412  DeviceVendorIdHexStringStream << "0x" << std::hex << DeviceVendorIdUInt;
413  const auto &DeviceVendorIdValue = DeviceVendorIdHexStringStream.str();
414  DeviceDesc[DeviceVendorIdKeyName] = DeviceVendorIdValue;
415  // get DriverVersion value and put it to DeviceDesc
416  const std::string &DriverVersionValue =
417  sycl::detail::get_device_info<info::device::driver_version>(DeviceImpl);
418  DeviceDesc[DriverVersionKeyName] = DriverVersionValue;
419  // get DeviceName value and put it to DeviceDesc
420  const std::string &DeviceNameValue =
421  sycl::detail::get_device_info<info::device::name>(DeviceImpl);
422  DeviceDesc[DeviceNameKeyName] = DeviceNameValue;
423 
424  // check if we can allow device with such device description DeviceDesc
425  if (deviceIsAllowed(DeviceDesc, AllowListParsed)) {
426  UrDevices[InsertIDx++] = Device;
427  }
428  }
429  UrDevices.resize(InsertIDx);
430 }
431 
432 } // namespace detail
433 } // namespace _V1
434 } // namespace sycl
static std::shared_ptr< platform_impl > getOrMakePlatformImpl(ur_platform_handle_t UrPlatform, const PluginPtr &Plugin)
Queries the cache to see if the specified UR platform has been seen before.
__SYCL_EXTERN_STREAM_ATTRS ostream cout
Linked to standard output.
void applyAllowList(std::vector< ur_device_handle_t > &UrDevices, ur_platform_handle_t UrPlatform, const PluginPtr &Plugin)
Definition: allowlist.cpp:347
std::string codeToString(int32_t code)
Definition: exception.hpp:57
constexpr char DeviceNameKeyName[]
Definition: allowlist.cpp:27
constexpr char BackendNameKeyName[]
Definition: allowlist.cpp:22
std::map< std::string, std::string > DeviceDescT
Definition: allowlist.hpp:22
AllowListParsedT parseAllowList(const std::string &AllowListRaw)
Definition: allowlist.cpp:54
bool deviceIsAllowed(const DeviceDescT &DeviceDesc, const AllowListParsedT &AllowListParsed)
Definition: allowlist.cpp:293
constexpr char DeviceVendorIdKeyName[]
Definition: allowlist.cpp:24
std::shared_ptr< plugin > PluginPtr
Definition: ur.hpp:107
constexpr std::array< const char *, 7 > SupportedAllowListKeyNames
Definition: allowlist.cpp:30
constexpr char PlatformVersionKeyName[]
Definition: allowlist.cpp:26
const std::array< std::pair< std::string, backend >, 7 > & getSyclBeMap()
Definition: config.cpp:168
constexpr char DeviceTypeKeyName[]
Definition: allowlist.cpp:23
constexpr char DriverVersionKeyName[]
Definition: allowlist.cpp:25
std::vector< DeviceDescT > AllowListParsedT
Definition: allowlist.hpp:23
constexpr char PlatformNameKeyName[]
Definition: allowlist.cpp:28
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