LLVM OpenMP
kmp_traits.h
Go to the documentation of this file.
1//===----------- Traits.h - OpenMP context traits -------------------------===//
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// Implementation of OpenMP context traits.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef OPENMP_TRAITS_H
14#define OPENMP_TRAITS_H
15
16#include "kmp.h"
17#include "kmp_adt.h"
18
19// Lexer for the OpenMP trait grammar (the grammar is documented in
20// kmp_traits.cpp).
21namespace lexer {
22
23enum class token_kind {
24 END, // end of the input
25 COMMA, // ,
26 STAR, // *
27 NOT, // !
28 L_PAREN, // (
29 R_PAREN, // )
32 COLON, // :
33 AND, // &&
34 OR, // ||
35 WORD, // word characters
36 UNKNOWN, // an unrecognized character (e.g. a lone '&' or '|')
37};
38
39// A single token produced by the lexer. `text` is a non-owning reference into
40// the source string the token was lexed from.
45
46class kmp_lexer final {
47 kmp_str_ref scan;
48 token lookahead{token_kind::END, kmp_str_ref("")};
49 bool has_lookahead = false;
50
51 // Lex the next token directly from the input, advancing past it.
52 token lex();
53
54public:
55 explicit kmp_lexer(kmp_str_ref source) : scan(source) {}
56
57 // Return the next token and advance past it.
59 if (has_lookahead) {
60 has_lookahead = false;
61 return lookahead;
62 }
63 return lex();
64 }
65
66 // Return the next token without advancing.
68 if (!has_lookahead) {
69 lookahead = lex();
70 has_lookahead = true;
71 }
72 return lookahead;
73 }
74
75 // Return the yet-unconsumed portion of the source, with any whitespace before
76 // the next token skipped.
78 token t = peek();
79 return kmp_str_ref(t.text.begin(),
80 static_cast<size_t>(scan.end() - t.text.begin()));
81 }
82};
83
84} // namespace lexer
85
86namespace kmp_traits {
87
88extern "C" int omp_get_num_devices();
89extern "C" const char *omp_get_uid_from_device(int device_num);
90
91class kmp_trait {
92protected:
95
97
98public:
99 virtual ~kmp_trait() = default;
100
101 kmp_trait(const kmp_trait &) = delete;
102 kmp_trait(kmp_trait &&) = delete;
103 kmp_trait &operator=(const kmp_trait &) = delete;
105
106 // Not pure virtual to avoid a dependency on __cxa_pure_virtual, which lives
107 // in libsupc++ and is not linked into libomp. Derived classes must override.
108 virtual bool match([[maybe_unused]] int device) const {
109 KMP_ASSERT2(0, "kmp_trait::match() must be overridden");
110 return false;
111 }
112
113 // Use KMP_INTERNAL_MALLOC/KMP_INTERNAL_FREE for memory management.
114 void *operator new(size_t size) { return KMP_INTERNAL_MALLOC(size); }
115 void operator delete(void *ptr) { KMP_INTERNAL_FREE(ptr); }
116
117 virtual bool operator==(const kmp_trait &other) const {
118 return _type == other._type;
119 }
120};
121
122/// Represents a wildcard trait that matches any device.
123class kmp_wildcard_trait final : public kmp_trait {
124public:
126
127 bool match([[maybe_unused]] int device) const override { return true; }
128
129 bool operator==(const kmp_trait &other) const override {
130 return kmp_trait::operator==(other);
131 }
132};
133
134/// Represents a specific device number.
135class kmp_literal_trait final : public kmp_trait {
136 int device_num;
137
138public:
139 kmp_literal_trait(int device_num)
140 : kmp_trait(LITERAL_T), device_num(device_num) {
141 assert(device_num >= 0 && "Device number must be non-negative");
142 }
143
144 bool match(int device) const override { return device_num == device; }
145
146 bool operator==(const kmp_trait &other) const override {
147 return kmp_trait::operator==(other) &&
148 device_num ==
149 static_cast<const kmp_literal_trait &>(other).device_num;
150 }
151};
152
153/// Represents a specific UID.
154/// UID is deliberately not resolved at construction time since libomptarget
155/// might not be initialized yet. This is why we delay calls to
156/// omp_get_uid_from_device / omp_get_device_from_uid until the trait is
157/// evaluated.
158class kmp_uid_trait final : public kmp_trait {
159 char *uid;
160 // Can be used by unit tests to mock omp_get_uid_from_device.
161 const char *(*get_uid_from_device)(int device) = omp_get_uid_from_device;
162
163public:
164 kmp_uid_trait(kmp_str_ref uid) : kmp_trait(UID_T), uid(uid.copy()) {}
165
166 ~kmp_uid_trait() override {
167 if (uid)
169 }
170
171 bool match(int device) const override {
172 const char *device_uid = get_uid_from_device(device);
173 if (!device_uid || !uid)
174 return false;
175 return strcmp(device_uid, uid) == 0;
176 }
177
178 // For testing purposes only: set the function that returns the UID from a
179 // device.
180 void set_uid_from_device(const char *(*uid_from_device)(int)) {
181 get_uid_from_device = uid_from_device;
182 }
183
184 bool operator==(const kmp_trait &other) const override {
185 if (!kmp_trait::operator==(other))
186 return false;
187 const char *other_uid = static_cast<const kmp_uid_trait &>(other).uid;
188 return uid && other_uid ? strcmp(uid, other_uid) == 0 : uid == other_uid;
189 }
190};
191
192/// Abstract class representing either a single trait expression or a collection
193/// of trait expressions that are ANDed or ORed together.
195protected:
198 // Determines if the expression is negated (true) or not (false).
199 bool negated = false;
200 // Can be used by unit tests to mock omp_get_num_devices.
202
206
207 // Not pure virtual to avoid a dependency on __cxa_pure_virtual, which lives
208 // in libsupc++ and is not linked into libomp. Derived classes must override.
209 virtual bool match_impl([[maybe_unused]] int device,
210 [[maybe_unused]] int num_devices) const {
211 KMP_ASSERT2(0, "kmp_trait_expr::match_impl() must be overridden");
212 return false;
213 }
214
215public:
216 virtual ~kmp_trait_expr() = default;
217
222
223 bool is_negated() const { return negated; }
224
225 // Check if the device matches the expression.
226 bool match(int device, int num_devices = -1) const {
227 if (num_devices == -1)
228 num_devices = get_num_devices();
229 if (device < 0 || device >= num_devices)
230 return false;
231 return match_impl(device, num_devices);
232 }
233
234 void set_negated(bool neg = true) { negated = neg; }
235
236 // For testing purposes only: set the function that returns the number of
237 // devices.
238 void set_num_devices(int (*num_devices)()) { get_num_devices = num_devices; }
239
240 // Use KMP_INTERNAL_MALLOC/KMP_INTERNAL_FREE for memory management.
241 void *operator new(size_t size) { return KMP_INTERNAL_MALLOC(size); }
242 void operator delete(void *ptr) { KMP_INTERNAL_FREE(ptr); }
243
244 virtual bool operator==(const kmp_trait_expr &other) const {
245 return _type == other._type && negated == other.negated;
246 }
247};
248
249/// Represents a single (possibly negated) trait.
251 kmp_trait *trait = nullptr;
252
253protected:
254 bool match_impl(int device, [[maybe_unused]] int num_devices) const override {
255 assert(trait);
256 bool result = trait->match(device);
257 return negated ? !result : result;
258 }
259
260public:
264 : kmp_trait_expr(SINGLE_T), trait(trait) {
265 assert(trait && "kmp_trait_expr_single requires a non-null trait");
266 }
267 ~kmp_trait_expr_single() override { delete trait; }
268
269 void set_trait(kmp_trait *new_trait) {
270 assert(new_trait);
271 if (trait)
272 delete trait;
273 trait = new_trait;
274 }
275
276 bool operator==(const kmp_trait_expr &other) const override {
277 if (!kmp_trait_expr::operator==(other))
278 return false;
279 const kmp_trait_expr_single &other_single =
280 static_cast<const kmp_trait_expr_single &>(other);
281 return trait && other_single.trait ? *trait == *other_single.trait
282 : trait == other_single.trait;
283 }
284};
285
286/// Represents a (possibly negated) collection of traits that are either ANDed
287/// or ORed together.
289public:
290 enum group_type { AND, OR };
291
292private:
294 // Determines if all traits have to match (true) or any of them (false).
295 group_type type = OR;
296
297protected:
298 bool match_impl(int device, int num_devices) const override {
299 size_t matched = 0;
300 for (const kmp_trait_expr *expr : exprs) {
301 if (expr->match(device, num_devices))
302 matched++;
303 }
304 // Note: AND evaluates to true for an empty group.
305 bool result = type == AND ? matched == exprs.size() : matched > 0;
306 return negated ? !result : result;
307 }
308
309public:
313 for (kmp_trait_expr *expr : exprs)
314 delete expr;
315 }
316
317 void add_expr(kmp_trait *trait) {
318 assert(trait);
320 }
322 assert(expr);
323 exprs.push_back(expr);
324 // Propagate get_num_devices to the expression.
326 }
327
328 group_type get_group_type() const { return type; }
329
330 void set_group_type(group_type new_type) { type = new_type; }
331
332 void set_num_devices(int (*num_devices)()) {
334 for (kmp_trait_expr *expr : exprs)
335 expr->set_num_devices(num_devices);
336 }
337
338 bool operator==(const kmp_trait_expr &other) const override {
339 if (!kmp_trait_expr::operator==(other))
340 return false;
341 const kmp_trait_expr_group &other_group =
342 static_cast<const kmp_trait_expr_group &>(other);
343 return type == other_group.type &&
344 exprs.is_set_equal(other_group.exprs,
345 [](const kmp_trait_expr *a,
346 const kmp_trait_expr *b) { return *a == *b; });
347 }
348};
349
350class kmp_trait_clause final {
351 kmp_trait_expr *expr = nullptr;
352
353public:
354 kmp_trait_clause() = default;
355 ~kmp_trait_clause() { delete expr; }
356
361
362 kmp_trait_expr *get_expr() { return expr; }
363
364 bool match(int device, int num_devices = -1) const {
365 assert(expr);
366 return expr->match(device, num_devices);
367 }
368
369 void set_expr(kmp_trait *trait) {
370 assert(trait);
371 if (expr)
372 delete expr;
373 expr = new kmp_trait_expr_single(trait);
374 }
375 void set_expr(kmp_trait_expr *new_expr) {
376 assert(new_expr);
377 if (expr)
378 delete expr;
379 expr = new_expr;
380 }
381
382 // Use KMP_INTERNAL_MALLOC/KMP_INTERNAL_FREE for memory management.
383 void *operator new(size_t size) { return KMP_INTERNAL_MALLOC(size); }
384 void operator delete(void *ptr) { KMP_INTERNAL_FREE(ptr); }
385
386 bool operator==(const kmp_trait_clause &other) const {
387 return expr && other.expr ? *expr == *other.expr : expr == other.expr;
388 }
389};
390
391} // namespace kmp_traits
392
393class kmp_trait_context final {
394 using kmp_trait_clause = kmp_traits::kmp_trait_clause;
395 using kmp_trait_expr = kmp_traits::kmp_trait_expr;
396
398 // List of devices that have been evaluated.
399 kmp_vector<int> devices;
400 bool evaluated = false;
401 // Can be used by unit tests to mock omp_get_num_devices.
402 int (*get_num_devices)() = kmp_traits::omp_get_num_devices;
403
404 void _evaluate() {
405 devices.clear();
406 for (int d = 0; d < get_num_devices(); ++d) {
407 if (_match(d))
408 devices.push_back(d);
409 }
410 evaluated = true;
411 }
412
413 bool _match(int device) const {
414 if (device < 0 || device >= get_num_devices())
415 return false;
416 for (kmp_trait_clause *clause : clauses) {
417 if (clause->match(device))
418 return true;
419 }
420 return false;
421 }
422
423public:
424 kmp_trait_context() = default;
426 for (kmp_trait_clause *clause : clauses)
427 delete clause;
428 }
429
434
435 // Parse a trait specification from a string.
436 // If dbg_name is provided, it will be used in error messages to identify the
437 // source of the trait specification.
439 const char *dbg_name = nullptr);
440
441 void add_clause(kmp_trait_clause *clause) {
442 assert(clause);
443 clauses.push_back(clause);
444 // Propagate get_num_devices to the clause.
445 if (kmp_trait_expr *expr = clause->get_expr())
446 expr->set_num_devices(get_num_devices);
447 }
448
449 // Returns the list of devices that match the trait specification represented
450 // by the context. The list contains devices numbers forming a set and sorted
451 // in ascending order.
452 // Note to future developers: if we want to add an option to force
453 // re-evaluation, we need to consider that the devices vector and thus the
454 // context iterators are invalidated.
457 return devices;
458 }
459
460 const kmp_vector<int> &evaluate() const {
461 assert(evaluated && "kmp_trait_context not evaluated");
462 return devices;
463 }
464
465 // Check if the device matches the trait specification represented by the
466 // context.
467 bool match(int device) { return evaluate().contains(device); }
468
469 bool match(int device) const {
470 assert(evaluated && "kmp_trait_context not evaluated");
471 return devices.contains(device);
472 }
473
474 // For testing purposes only: set the function that returns the number of
475 // devices.
476 void set_num_devices(int (*num_devices)()) {
477 get_num_devices = num_devices;
478 for (kmp_trait_clause *clause : clauses) {
479 if (kmp_trait_expr *expr = clause->get_expr())
480 expr->set_num_devices(num_devices);
481 }
482 }
483
484 // Triggers lazy evaluation if not already evaluated.
486 if (!evaluated)
487 _evaluate();
488 }
489
490 // Use KMP_INTERNAL_MALLOC/KMP_INTERNAL_FREE for memory management.
491 void *operator new(size_t size) { return KMP_INTERNAL_MALLOC(size); }
492 void operator delete(void *ptr) { KMP_INTERNAL_FREE(ptr); }
493
494 bool operator==(const kmp_trait_context &other) const {
495 auto clause_comp = [](kmp_trait_clause *const &a,
496 kmp_trait_clause *const &b) { return *a == *b; };
497 return clauses.is_set_equal(other.clauses, clause_comp);
498 }
499
500 // Iterator support (returns the iterators of the devices vector; triggers
501 // lazy evaluation if not already evaluated and if the context is not const).
502 const int *begin() { return evaluate().begin(); }
503 const int *end() { return evaluate().end(); }
504 const int *begin() const { return evaluate().begin(); }
505 const int *end() const { return evaluate().end(); }
506};
507
508#endif // OPENMP_TRAITS_H
int result[2]
kmp_str_ref is a non-owning string class (similar to llvm::StringRef).
Definition kmp_adt.h:34
const char * begin() const
Iterator support (raw pointers work as iterators for contiguous storage)
Definition kmp_adt.h:141
const int * end()
Definition kmp_traits.h:503
void set_num_devices(int(*num_devices)())
Definition kmp_traits.h:476
bool match(int device)
Definition kmp_traits.h:467
bool match(int device) const
Definition kmp_traits.h:469
const kmp_vector< int > & evaluate()
Definition kmp_traits.h:455
kmp_trait_context(const kmp_trait_context &)=delete
const int * end() const
Definition kmp_traits.h:505
kmp_trait_context(kmp_trait_context &&)=delete
kmp_trait_context & operator=(const kmp_trait_context &)=delete
const int * begin() const
Definition kmp_traits.h:504
const kmp_vector< int > & evaluate() const
Definition kmp_traits.h:460
bool operator==(const kmp_trait_context &other) const
Definition kmp_traits.h:494
kmp_trait_context()=default
kmp_trait_context & operator=(kmp_trait_context &&)=delete
void add_clause(kmp_trait_clause *clause)
Definition kmp_traits.h:441
void trigger_evaluation()
Definition kmp_traits.h:485
static kmp_trait_context * parse_from_spec(kmp_str_ref spec, const char *dbg_name=nullptr)
const int * begin()
Definition kmp_traits.h:502
kmp_literal_trait(int device_num)
Definition kmp_traits.h:139
bool match(int device) const override
Definition kmp_traits.h:144
bool operator==(const kmp_trait &other) const override
Definition kmp_traits.h:146
void set_expr(kmp_trait *trait)
Definition kmp_traits.h:369
kmp_trait_clause & operator=(kmp_trait_clause &&)=delete
kmp_trait_clause(const kmp_trait_clause &)=delete
kmp_trait_clause & operator=(const kmp_trait_clause &)=delete
bool operator==(const kmp_trait_clause &other) const
Definition kmp_traits.h:386
kmp_trait_clause(kmp_trait_clause &&)=delete
bool match(int device, int num_devices=-1) const
Definition kmp_traits.h:364
kmp_trait_expr * get_expr()
Definition kmp_traits.h:362
void set_expr(kmp_trait_expr *new_expr)
Definition kmp_traits.h:375
void add_expr(kmp_trait_expr *expr)
Definition kmp_traits.h:321
void set_num_devices(int(*num_devices)())
Definition kmp_traits.h:332
bool match_impl(int device, int num_devices) const override
Definition kmp_traits.h:298
bool operator==(const kmp_trait_expr &other) const override
Definition kmp_traits.h:338
void set_group_type(group_type new_type)
Definition kmp_traits.h:330
void add_expr(kmp_trait *trait)
Definition kmp_traits.h:317
group_type get_group_type() const
Definition kmp_traits.h:328
Represents a single (possibly negated) trait.
Definition kmp_traits.h:250
void set_trait(kmp_trait *new_trait)
Definition kmp_traits.h:269
bool match_impl(int device, int num_devices) const override
Definition kmp_traits.h:254
bool operator==(const kmp_trait_expr &other) const override
Definition kmp_traits.h:276
kmp_trait_expr_single(kmp_trait *trait)
Definition kmp_traits.h:263
Abstract class representing either a single trait expression or a collection of trait expressions tha...
Definition kmp_traits.h:194
kmp_trait_expr(const kmp_trait_expr &)=delete
kmp_trait_expr(expr_type type)
Definition kmp_traits.h:203
kmp_trait_expr(kmp_trait_expr &&)=delete
kmp_trait_expr(expr_type type, bool negated)
Definition kmp_traits.h:204
virtual ~kmp_trait_expr()=default
virtual bool operator==(const kmp_trait_expr &other) const
Definition kmp_traits.h:244
bool match(int device, int num_devices=-1) const
Definition kmp_traits.h:226
void set_num_devices(int(*num_devices)())
Definition kmp_traits.h:238
kmp_trait_expr & operator=(kmp_trait_expr &&)=delete
kmp_trait_expr & operator=(const kmp_trait_expr &)=delete
void set_negated(bool neg=true)
Definition kmp_traits.h:234
virtual bool match_impl(int device, int num_devices) const
Definition kmp_traits.h:209
virtual ~kmp_trait()=default
kmp_trait & operator=(const kmp_trait &)=delete
virtual bool match(int device) const
Definition kmp_traits.h:108
kmp_trait & operator=(kmp_trait &&)=delete
virtual bool operator==(const kmp_trait &other) const
Definition kmp_traits.h:117
kmp_trait(const kmp_trait &)=delete
kmp_trait(trait_type type)
Definition kmp_traits.h:96
kmp_trait(kmp_trait &&)=delete
bool operator==(const kmp_trait &other) const override
Definition kmp_traits.h:184
bool match(int device) const override
Definition kmp_traits.h:171
void set_uid_from_device(const char *(*uid_from_device)(int))
Definition kmp_traits.h:180
kmp_uid_trait(kmp_str_ref uid)
Definition kmp_traits.h:164
bool operator==(const kmp_trait &other) const override
Definition kmp_traits.h:129
bool match(int device) const override
Definition kmp_traits.h:127
kmp_vector is a vector class for managing small vectors.
Definition kmp_adt.h:148
T * end()
Definition kmp_adt.h:331
T * begin()
Iterator support (raw pointers work as iterators for contiguous storage)
Definition kmp_adt.h:330
bool contains(const T &value, const Fn &comp=Fn{}) const
Check if the vector contains the given value.
Definition kmp_adt.h:271
kmp_lexer(kmp_str_ref source)
Definition kmp_traits.h:55
kmp_str_ref remaining()
Definition kmp_traits.h:77
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t size
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain ITT_FORMAT p const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_id __itt_string_handle __itt_metadata_type size_t void ITT_FORMAT p const __itt_domain __itt_id __itt_string_handle const wchar_t size_t ITT_FORMAT lu const __itt_domain __itt_id __itt_relation __itt_id ITT_FORMAT p const wchar_t int ITT_FORMAT __itt_group_mark d int
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain ITT_FORMAT p const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_id __itt_string_handle __itt_metadata_type type
#define KMP_INTERNAL_MALLOC(sz)
Definition kmp.h:103
#define KMP_INTERNAL_FREE(p)
Definition kmp.h:104
#define KMP_ASSERT2(cond, msg)
Definition kmp_debug.h:60
int a
int omp_get_num_devices()
const char * omp_get_uid_from_device(int device_num)
kmp_str_ref text
Definition kmp_traits.h:43
token_kind kind
Definition kmp_traits.h:42