LLVM OpenMP* Runtime Library
Loading...
Searching...
No Matches
kmp_traits.h
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, // )
30 L_BRACKET, // [
31 R_BRACKET, // ]
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.
41struct token {
42 token_kind kind = token_kind::UNKNOWN;
43 kmp_str_ref text = kmp_str_ref("");
44};
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.
58 token next() {
59 if (has_lookahead) {
60 has_lookahead = false;
61 return lookahead;
62 }
63 return lex();
64 }
65
66 // Return the next token without advancing.
67 token peek() {
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.
77 kmp_str_ref remaining() {
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:
93 enum trait_type { WILDCARD_T, LITERAL_T, UID_T };
94 trait_type _type;
95
96 kmp_trait(trait_type type) : _type(type) {}
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;
104 kmp_trait &operator=(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
123class kmp_wildcard_trait final : public kmp_trait {
124public:
125 kmp_wildcard_trait() : kmp_trait(WILDCARD_T) {}
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
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
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)
168 KMP_INTERNAL_FREE(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
195protected:
196 enum expr_type { SINGLE_T, GROUP_T };
197 expr_type _type;
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.
201 int (*get_num_devices)() = omp_get_num_devices;
202
203 kmp_trait_expr(expr_type type) : _type(type) {}
204 kmp_trait_expr(expr_type type, bool negated)
205 : _type(type), negated(negated) {}
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
218 kmp_trait_expr(const kmp_trait_expr &) = delete;
219 kmp_trait_expr(kmp_trait_expr &&) = delete;
220 kmp_trait_expr &operator=(const kmp_trait_expr &) = delete;
221 kmp_trait_expr &operator=(kmp_trait_expr &&) = delete;
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
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:
262 kmp_trait_expr_single(bool negated) : kmp_trait_expr(SINGLE_T, negated) {}
263 kmp_trait_expr_single(kmp_trait *trait)
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
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:
311 kmp_trait_expr_group(bool negated) : kmp_trait_expr(GROUP_T, negated) {}
312 ~kmp_trait_expr_group() override {
313 for (kmp_trait_expr *expr : exprs)
314 delete expr;
315 }
316
317 void add_expr(kmp_trait *trait) {
318 assert(trait);
319 add_expr(new kmp_trait_expr_single(trait));
320 }
321 void add_expr(kmp_trait_expr *expr) {
322 assert(expr);
323 exprs.push_back(expr);
324 // Propagate get_num_devices to the expression.
325 expr->set_num_devices(get_num_devices);
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)()) {
333 kmp_trait_expr::set_num_devices(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
357 kmp_trait_clause(const kmp_trait_clause &) = delete;
358 kmp_trait_clause(kmp_trait_clause &&) = delete;
359 kmp_trait_clause &operator=(const kmp_trait_clause &) = delete;
360 kmp_trait_clause &operator=(kmp_trait_clause &&) = delete;
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;
425 ~kmp_trait_context() {
426 for (kmp_trait_clause *clause : clauses)
427 delete clause;
428 }
429
430 kmp_trait_context(const kmp_trait_context &) = delete;
431 kmp_trait_context(kmp_trait_context &&) = delete;
432 kmp_trait_context &operator=(const kmp_trait_context &) = delete;
433 kmp_trait_context &operator=(kmp_trait_context &&) = delete;
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.
438 static kmp_trait_context *parse_from_spec(kmp_str_ref spec,
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.
455 const kmp_vector<int> &evaluate() {
456 trigger_evaluation();
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.
485 void trigger_evaluation() {
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
kmp_str_ref is a non-owning string class (similar to llvm::StringRef).
Definition kmp_adt.h:34
Represents a specific device number.
Definition kmp_traits.h:135
Represents a single (possibly negated) trait.
Definition kmp_traits.h:250
Represents a wildcard trait that matches any device.
Definition kmp_traits.h:123
void clear()
Destroy all elements in the vector. Doesn't free the memory.
Definition kmp_adt.h:259
void push_back(const T &value)
Add a new element to the end of the vector.
Definition kmp_adt.h:302
bool is_set_equal(const kmp_vector &other, const Fn &comp=Fn{}) const
Definition kmp_adt.h:287
bool contains(const T &value, const Fn &comp=Fn{}) const
Definition kmp_adt.h:271