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(PI_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(PI_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(PI_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(PI_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 + " " + detail::codeToString(PI_ERROR_INVALID_VALUE));
201  }
202  // cut off prefix from the value
203  ValueStart += Prefix.length();
204 
205  ValueEnd = ValueStart;
206  const std::string Postfix("}}");
207  for (; ValueEnd < AllowListRaw.length() - Postfix.length() + 1;
208  ++ValueEnd) {
209  if (Postfix == AllowListRaw.substr(ValueEnd, Postfix.length()))
210  break;
211  // if it is the last iteration and next 2 symbols are not a postfix,
212  // throw exception
213  if (ValueEnd == AllowListRaw.length() - Postfix.length())
214  throw sycl::exception(
215  sycl::make_error_code(sycl::errc::runtime),
216  "Key " + Key +
217  " of SYCL_DEVICE_ALLOWLIST should have "
218  "value which ends with " +
219  Postfix + " " +
220  detail::codeToString(PI_ERROR_INVALID_VALUE));
221  }
222  size_t NextExpectedDelimiterPos = ValueEnd + Postfix.length();
223  // if it is not the end of the string, check that symbol next to a
224  // postfix is a delimiter (, or ;)
225  if ((AllowListRaw.length() != NextExpectedDelimiterPos) &&
226  (AllowListRaw[NextExpectedDelimiterPos] !=
227  DelimiterBtwItemsInDeviceDesc) &&
228  (AllowListRaw[NextExpectedDelimiterPos] != DelimiterBtwDeviceDescs))
229  throw sycl::exception(
230  sycl::make_error_code(sycl::errc::runtime),
231  "Unexpected symbol on position " +
232  std::to_string(NextExpectedDelimiterPos) + ": " +
233  AllowListRaw[NextExpectedDelimiterPos] +
234  ". Should be either " + DelimiterBtwItemsInDeviceDesc +
235  " or " + DelimiterBtwDeviceDescs +
236  codeToString(PI_ERROR_INVALID_VALUE));
237 
238  if (AllowListRaw[NextExpectedDelimiterPos] == DelimiterBtwDeviceDescs)
239  ShouldAllocateNewDeviceDescMap = true;
240 
241  Value = AllowListRaw.substr(ValueStart, ValueEnd - ValueStart);
242 
243  ValueEnd += Postfix.length();
244  } else
245  assert(false &&
246  "Key should be either in SupportedKeyNamesHaveFixedValue "
247  "or SupportedKeyNamesRequireRegexValue");
248 
249  // add key and value to the map
250  DeviceDescMap.emplace(Key, Value);
251  } else
252  throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
253  "Re-definition of key " + Key +
254  " is not allowed in "
255  "SYCL_DEVICE_ALLOWLIST " +
256  codeToString(PI_ERROR_INVALID_VALUE));
257 
258  KeyStart = ValueEnd;
259  if (KeyStart != std::string::npos)
260  ++KeyStart;
261  if (ShouldAllocateNewDeviceDescMap) {
262  ++DeviceDescIndex;
263  AllowListParsed.emplace_back();
264  }
265  }
266 
267  if (IsDeprecatedKeyNameDeviceNameWasUsed &&
268  IsDeprecatedKeyNamePlatformNameWasUsed) {
269  std::cout << "\nWARNING: " << DeprecatedKeyNameDeviceName << " and "
270  << DeprecatedKeyNamePlatformName
271  << " in SYCL_DEVICE_ALLOWLIST are deprecated. ";
272  } else if (IsDeprecatedKeyNameDeviceNameWasUsed) {
273  std::cout << "\nWARNING: " << DeprecatedKeyNameDeviceName
274  << " in SYCL_DEVICE_ALLOWLIST is deprecated. ";
275  } else if (IsDeprecatedKeyNamePlatformNameWasUsed) {
276  std::cout << "\nWARNING: " << DeprecatedKeyNamePlatformName
277  << " in SYCL_DEVICE_ALLOWLIST is deprecated. ";
278  }
279  if (IsDeprecatedKeyNameDeviceNameWasUsed ||
280  IsDeprecatedKeyNamePlatformNameWasUsed) {
281  std::cout << "Please use " << BackendNameKeyName << ", "
283  << " instead. For details, please refer to "
284  "https://github.com/intel/llvm/blob/sycl/sycl/doc/"
285  "EnvironmentVariables.md\n\n";
286  }
287 
288  return AllowListParsed;
289 }
290 
291 // Checking if we can allow device with device description DeviceDesc
292 bool deviceIsAllowed(const DeviceDescT &DeviceDesc,
293  const AllowListParsedT &AllowListParsed) {
296  [&DeviceDesc](const auto &SupportedKeyName) {
297  return DeviceDesc.find(SupportedKeyName) !=
298  DeviceDesc.end();
299  }) &&
300  "DeviceDesc map should have all supported keys for "
301  "SYCL_DEVICE_ALLOWLIST.");
302  auto EqualityComp = [&](const std::string &KeyName,
303  const DeviceDescT &AllowListDeviceDesc) {
304  // change to map::contains after switching DPC++ RT to C++20
305  if (AllowListDeviceDesc.find(KeyName) != AllowListDeviceDesc.end())
306  if (AllowListDeviceDesc.at(KeyName) != DeviceDesc.at(KeyName))
307  return false;
308  return true;
309  };
310  auto RegexComp = [&](const std::string &KeyName,
311  const DeviceDescT &AllowListDeviceDesc) {
312  if (AllowListDeviceDesc.find(KeyName) != AllowListDeviceDesc.end())
313  if (!std::regex_match(DeviceDesc.at(KeyName),
314  std::regex(AllowListDeviceDesc.at(KeyName))))
315  return false;
316  return true;
317  };
318 
319  bool ShouldDeviceBeAllowed = false;
320 
321  for (const auto &AllowListDeviceDesc : AllowListParsed) {
322  if (!EqualityComp(BackendNameKeyName, AllowListDeviceDesc))
323  continue;
324  if (!EqualityComp(DeviceTypeKeyName, AllowListDeviceDesc))
325  continue;
326  if (!EqualityComp(DeviceVendorIdKeyName, AllowListDeviceDesc))
327  continue;
328  if (!RegexComp(DriverVersionKeyName, AllowListDeviceDesc))
329  continue;
330  if (!RegexComp(PlatformVersionKeyName, AllowListDeviceDesc))
331  continue;
332  if (!RegexComp(DeviceNameKeyName, AllowListDeviceDesc))
333  continue;
334  if (!RegexComp(PlatformNameKeyName, AllowListDeviceDesc))
335  continue;
336 
337  // no any continue was called on this iteration, so all parameters matched
338  // successfully, so allow this device to use
339  ShouldDeviceBeAllowed = true;
340  break;
341  }
342 
343  return ShouldDeviceBeAllowed;
344 }
345 
346 void applyAllowList(std::vector<sycl::detail::pi::PiDevice> &PiDevices,
348  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(PiPlatform, 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  PiPlatform, Plugin));
370  // get PlatformName value and put it to DeviceDesc
371  DeviceDesc.emplace(PlatformNameKeyName,
372  sycl::detail::get_platform_info<info::platform::name>(
373  PiPlatform, Plugin));
374 
375  int InsertIDx = 0;
376  for (sycl::detail::pi::PiDevice Device : PiDevices) {
377  auto DeviceImpl = PlatformImpl->getOrMakeDeviceImpl(Device, PlatformImpl);
378  // get DeviceType value and put it to DeviceDesc
380  Plugin->call<PiApiKind::piDeviceGetInfo>(
382  &PiDevType, nullptr);
383  sycl::info::device_type DeviceType = pi::cast<info::device_type>(PiDevType);
384  for (const auto &SyclDeviceType :
385  getSyclDeviceTypeMap<true /*Enable 'acc'*/>()) {
386  if (SyclDeviceType.second == DeviceType) {
387  const auto &DeviceTypeValue = SyclDeviceType.first;
388  DeviceDesc[DeviceTypeKeyName] = DeviceTypeValue;
389  break;
390  }
391  }
392  // get DeviceVendorId value and put it to DeviceDesc
393  uint32_t DeviceVendorIdUInt =
394  sycl::detail::get_device_info<info::device::vendor_id>(DeviceImpl);
395  std::stringstream DeviceVendorIdHexStringStream;
396  DeviceVendorIdHexStringStream << "0x" << std::hex << DeviceVendorIdUInt;
397  const auto &DeviceVendorIdValue = DeviceVendorIdHexStringStream.str();
398  DeviceDesc[DeviceVendorIdKeyName] = DeviceVendorIdValue;
399  // get DriverVersion value and put it to DeviceDesc
400  const std::string &DriverVersionValue =
401  sycl::detail::get_device_info<info::device::driver_version>(DeviceImpl);
402  DeviceDesc[DriverVersionKeyName] = DriverVersionValue;
403  // get DeviceName value and put it to DeviceDesc
404  const std::string &DeviceNameValue =
405  sycl::detail::get_device_info<info::device::name>(DeviceImpl);
406  DeviceDesc[DeviceNameKeyName] = DeviceNameValue;
407 
408  // check if we can allow device with such device description DeviceDesc
409  if (deviceIsAllowed(DeviceDesc, AllowListParsed)) {
410  PiDevices[InsertIDx++] = Device;
411  }
412  }
413  PiDevices.resize(InsertIDx);
414 }
415 
416 } // namespace detail
417 } // namespace _V1
418 } // namespace sycl
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.
__SYCL_EXTERN_STREAM_ATTRS ostream cout
Linked to standard output.
::pi_platform PiPlatform
Definition: pi.hpp:129
::pi_device_type PiDeviceType
Definition: pi.hpp:132
const std::array< std::pair< std::string, backend >, 8 > & getSyclBeMap()
Definition: config.cpp:169
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
std::string codeToString(pi_int32 code)
Definition: common.hpp:153
AllowListParsedT parseAllowList(const std::string &AllowListRaw)
Definition: allowlist.cpp:54
bool deviceIsAllowed(const DeviceDescT &DeviceDesc, const AllowListParsedT &AllowListParsed)
Definition: allowlist.cpp:292
constexpr char DeviceVendorIdKeyName[]
Definition: allowlist.cpp:24
std::shared_ptr< plugin > PluginPtr
Definition: pi.hpp:48
constexpr std::array< const char *, 7 > SupportedAllowListKeyNames
Definition: allowlist.cpp:30
constexpr char PlatformVersionKeyName[]
Definition: allowlist.cpp:26
constexpr char DeviceTypeKeyName[]
Definition: allowlist.cpp:23
constexpr char DriverVersionKeyName[]
Definition: allowlist.cpp:25
void applyAllowList(std::vector< sycl::detail::pi::PiDevice > &PiDevices, sycl::detail::pi::PiPlatform PiPlatform, const PluginPtr &Plugin)
Definition: allowlist.cpp:346
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:87
Definition: access.hpp:18
@ PI_DEVICE_INFO_TYPE
Definition: pi.h:305
pi_result piDeviceGetInfo(pi_device device, pi_device_info param_name, size_t param_value_size, void *param_value, size_t *param_value_size_ret)
Returns requested info for provided native device Return PI_DEVICE_INFO_EXTENSION_DEVICELIB_ASSERT fo...
Definition: pi_cuda.cpp:78
_pi_device_type
Definition: pi.h:272
bool all_of(const simd_mask< _Tp, _Abi > &) noexcept