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