1 | //===- OMPContext.cpp ------ Collection of helpers for OpenMP contexts ----===// |
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 | /// \file |
9 | /// |
10 | /// This file implements helper functions and classes to deal with OpenMP |
11 | /// contexts as used by `[begin/end] declare variant` and `metadirective`. |
12 | /// |
13 | //===----------------------------------------------------------------------===// |
14 | |
15 | #include "llvm/Frontend/OpenMP/OMPContext.h" |
16 | #include "llvm/ADT/StringRef.h" |
17 | #include "llvm/ADT/StringSwitch.h" |
18 | #include "llvm/Support/Debug.h" |
19 | #include "llvm/Support/raw_ostream.h" |
20 | #include "llvm/TargetParser/Triple.h" |
21 | |
22 | #define DEBUG_TYPE "openmp-ir-builder" |
23 | |
24 | using namespace llvm; |
25 | using namespace omp; |
26 | |
27 | OMPContext::OMPContext(bool IsDeviceCompilation, Triple TargetTriple) { |
28 | // Add the appropriate device kind trait based on the triple and the |
29 | // IsDeviceCompilation flag. |
30 | ActiveTraits.set(unsigned(IsDeviceCompilation |
31 | ? TraitProperty::device_kind_nohost |
32 | : TraitProperty::device_kind_host)); |
33 | switch (TargetTriple.getArch()) { |
34 | case Triple::arm: |
35 | case Triple::armeb: |
36 | case Triple::aarch64: |
37 | case Triple::aarch64_be: |
38 | case Triple::aarch64_32: |
39 | case Triple::mips: |
40 | case Triple::mipsel: |
41 | case Triple::mips64: |
42 | case Triple::mips64el: |
43 | case Triple::ppc: |
44 | case Triple::ppcle: |
45 | case Triple::ppc64: |
46 | case Triple::ppc64le: |
47 | case Triple::systemz: |
48 | case Triple::x86: |
49 | case Triple::x86_64: |
50 | ActiveTraits.set(unsigned(TraitProperty::device_kind_cpu)); |
51 | break; |
52 | case Triple::amdgcn: |
53 | case Triple::nvptx: |
54 | case Triple::nvptx64: |
55 | ActiveTraits.set(unsigned(TraitProperty::device_kind_gpu)); |
56 | break; |
57 | default: |
58 | break; |
59 | } |
60 | |
61 | // Add the appropriate device architecture trait based on the triple. |
62 | #define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \ |
63 | if (TraitSelector::TraitSelectorEnum == TraitSelector::device_arch) { \ |
64 | if (TargetTriple.getArch() == TargetTriple.getArchTypeForLLVMName(Str)) \ |
65 | ActiveTraits.set(unsigned(TraitProperty::Enum)); \ |
66 | if (StringRef(Str) == "x86_64" && \ |
67 | TargetTriple.getArch() == Triple::x86_64) \ |
68 | ActiveTraits.set(unsigned(TraitProperty::Enum)); \ |
69 | } |
70 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
71 | |
72 | // TODO: What exactly do we want to see as device ISA trait? |
73 | // The discussion on the list did not seem to have come to an agreed |
74 | // upon solution. |
75 | |
76 | // LLVM is the "OpenMP vendor" but we could also interpret vendor as the |
77 | // target vendor. |
78 | ActiveTraits.set(unsigned(TraitProperty::implementation_vendor_llvm)); |
79 | |
80 | // The user condition true is accepted but not false. |
81 | ActiveTraits.set(unsigned(TraitProperty::user_condition_true)); |
82 | |
83 | // This is for sure some device. |
84 | ActiveTraits.set(unsigned(TraitProperty::device_kind_any)); |
85 | |
86 | LLVM_DEBUG({ |
87 | dbgs() << "[" << DEBUG_TYPE |
88 | << "] New OpenMP context with the following properties:\n" ; |
89 | for (unsigned Bit : ActiveTraits.set_bits()) { |
90 | TraitProperty Property = TraitProperty(Bit); |
91 | dbgs() << "\t " << getOpenMPContextTraitPropertyFullName(Property) |
92 | << "\n" ; |
93 | } |
94 | }); |
95 | } |
96 | |
97 | /// Return true if \p C0 is a subset of \p C1. Note that both arrays are |
98 | /// expected to be sorted. |
99 | template <typename T> static bool isSubset(ArrayRef<T> C0, ArrayRef<T> C1) { |
100 | #ifdef EXPENSIVE_CHECKS |
101 | assert(llvm::is_sorted(C0) && llvm::is_sorted(C1) && |
102 | "Expected sorted arrays!" ); |
103 | #endif |
104 | if (C0.size() > C1.size()) |
105 | return false; |
106 | auto It0 = C0.begin(), End0 = C0.end(); |
107 | auto It1 = C1.begin(), End1 = C1.end(); |
108 | while (It0 != End0) { |
109 | if (It1 == End1) |
110 | return false; |
111 | if (*It0 == *It1) { |
112 | ++It0; |
113 | ++It1; |
114 | continue; |
115 | } |
116 | ++It0; |
117 | } |
118 | return true; |
119 | } |
120 | |
121 | /// Return true if \p C0 is a strict subset of \p C1. Note that both arrays are |
122 | /// expected to be sorted. |
123 | template <typename T> |
124 | static bool isStrictSubset(ArrayRef<T> C0, ArrayRef<T> C1) { |
125 | if (C0.size() >= C1.size()) |
126 | return false; |
127 | return isSubset<T>(C0, C1); |
128 | } |
129 | |
130 | static bool isStrictSubset(const VariantMatchInfo &VMI0, |
131 | const VariantMatchInfo &VMI1) { |
132 | // If all required traits are a strict subset and the ordered vectors storing |
133 | // the construct traits, we say it is a strict subset. Note that the latter |
134 | // relation is not required to be strict. |
135 | if (VMI0.RequiredTraits.count() >= VMI1.RequiredTraits.count()) |
136 | return false; |
137 | for (unsigned Bit : VMI0.RequiredTraits.set_bits()) |
138 | if (!VMI1.RequiredTraits.test(Idx: Bit)) |
139 | return false; |
140 | if (!isSubset<TraitProperty>(C0: VMI0.ConstructTraits, C1: VMI1.ConstructTraits)) |
141 | return false; |
142 | return true; |
143 | } |
144 | |
145 | static int isVariantApplicableInContextHelper( |
146 | const VariantMatchInfo &VMI, const OMPContext &Ctx, |
147 | SmallVectorImpl<unsigned> *ConstructMatches, bool DeviceSetOnly) { |
148 | |
149 | // The match kind determines if we need to match all traits, any of the |
150 | // traits, or none of the traits for it to be an applicable context. |
151 | enum MatchKind { MK_ALL, MK_ANY, MK_NONE }; |
152 | |
153 | MatchKind MK = MK_ALL; |
154 | // Determine the match kind the user wants, "all" is the default and provided |
155 | // to the user only for completeness. |
156 | if (VMI.RequiredTraits.test( |
157 | Idx: unsigned(TraitProperty::implementation_extension_match_any))) |
158 | MK = MK_ANY; |
159 | if (VMI.RequiredTraits.test( |
160 | Idx: unsigned(TraitProperty::implementation_extension_match_none))) |
161 | MK = MK_NONE; |
162 | |
163 | // Helper to deal with a single property that was (not) found in the OpenMP |
164 | // context based on the match kind selected by the user via |
165 | // `implementation={extensions(match_[all,any,none])}' |
166 | auto HandleTrait = [MK](TraitProperty Property, |
167 | bool WasFound) -> std::optional<bool> /* Result */ { |
168 | // For kind "any" a single match is enough but we ignore non-matched |
169 | // properties. |
170 | if (MK == MK_ANY) { |
171 | if (WasFound) |
172 | return true; |
173 | return std::nullopt; |
174 | } |
175 | |
176 | // In "all" or "none" mode we accept a matching or non-matching property |
177 | // respectively and move on. We are not done yet! |
178 | if ((WasFound && MK == MK_ALL) || (!WasFound && MK == MK_NONE)) |
179 | return std::nullopt; |
180 | |
181 | // We missed a property, provide some debug output and indicate failure. |
182 | LLVM_DEBUG({ |
183 | if (MK == MK_ALL) |
184 | dbgs() << "[" << DEBUG_TYPE << "] Property " |
185 | << getOpenMPContextTraitPropertyName(Property, "" ) |
186 | << " was not in the OpenMP context but match kind is all.\n" ; |
187 | if (MK == MK_NONE) |
188 | dbgs() << "[" << DEBUG_TYPE << "] Property " |
189 | << getOpenMPContextTraitPropertyName(Property, "" ) |
190 | << " was in the OpenMP context but match kind is none.\n" ; |
191 | }); |
192 | return false; |
193 | }; |
194 | |
195 | for (unsigned Bit : VMI.RequiredTraits.set_bits()) { |
196 | TraitProperty Property = TraitProperty(Bit); |
197 | if (DeviceSetOnly && |
198 | getOpenMPContextTraitSetForProperty(Property) != TraitSet::device) |
199 | continue; |
200 | |
201 | // So far all extensions are handled elsewhere, we skip them here as they |
202 | // are not part of the OpenMP context. |
203 | if (getOpenMPContextTraitSelectorForProperty(Property) == |
204 | TraitSelector::implementation_extension) |
205 | continue; |
206 | |
207 | bool IsActiveTrait = Ctx.ActiveTraits.test(Idx: unsigned(Property)); |
208 | |
209 | // We overwrite the isa trait as it is actually up to the OMPContext hook to |
210 | // check the raw string(s). |
211 | if (Property == TraitProperty::device_isa___ANY) |
212 | IsActiveTrait = llvm::all_of(Range: VMI.ISATraits, P: [&](StringRef RawString) { |
213 | return Ctx.matchesISATrait(RawString); |
214 | }); |
215 | |
216 | if (std::optional<bool> Result = HandleTrait(Property, IsActiveTrait)) |
217 | return *Result; |
218 | } |
219 | |
220 | if (!DeviceSetOnly) { |
221 | // We could use isSubset here but we also want to record the match |
222 | // locations. |
223 | unsigned ConstructIdx = 0, NoConstructTraits = Ctx.ConstructTraits.size(); |
224 | for (TraitProperty Property : VMI.ConstructTraits) { |
225 | assert(getOpenMPContextTraitSetForProperty(Property) == |
226 | TraitSet::construct && |
227 | "Variant context is ill-formed!" ); |
228 | |
229 | // Verify the nesting. |
230 | bool FoundInOrder = false; |
231 | while (!FoundInOrder && ConstructIdx != NoConstructTraits) |
232 | FoundInOrder = (Ctx.ConstructTraits[ConstructIdx++] == Property); |
233 | if (ConstructMatches) |
234 | ConstructMatches->push_back(Elt: ConstructIdx - 1); |
235 | |
236 | if (std::optional<bool> Result = HandleTrait(Property, FoundInOrder)) |
237 | return *Result; |
238 | |
239 | if (!FoundInOrder) { |
240 | LLVM_DEBUG(dbgs() << "[" << DEBUG_TYPE << "] Construct property " |
241 | << getOpenMPContextTraitPropertyName(Property, "" ) |
242 | << " was not nested properly.\n" ); |
243 | return false; |
244 | } |
245 | |
246 | // TODO: Verify SIMD |
247 | } |
248 | |
249 | assert(isSubset<TraitProperty>(VMI.ConstructTraits, Ctx.ConstructTraits) && |
250 | "Broken invariant!" ); |
251 | } |
252 | |
253 | if (MK == MK_ANY) { |
254 | LLVM_DEBUG(dbgs() << "[" << DEBUG_TYPE |
255 | << "] None of the properties was in the OpenMP context " |
256 | "but match kind is any.\n" ); |
257 | return false; |
258 | } |
259 | |
260 | return true; |
261 | } |
262 | |
263 | bool llvm::omp::isVariantApplicableInContext(const VariantMatchInfo &VMI, |
264 | const OMPContext &Ctx, |
265 | bool DeviceSetOnly) { |
266 | return isVariantApplicableInContextHelper( |
267 | VMI, Ctx, /* ConstructMatches */ nullptr, DeviceSetOnly); |
268 | } |
269 | |
270 | static APInt getVariantMatchScore(const VariantMatchInfo &VMI, |
271 | const OMPContext &Ctx, |
272 | SmallVectorImpl<unsigned> &ConstructMatches) { |
273 | APInt Score(64, 1); |
274 | |
275 | unsigned NoConstructTraits = VMI.ConstructTraits.size(); |
276 | for (unsigned Bit : VMI.RequiredTraits.set_bits()) { |
277 | TraitProperty Property = TraitProperty(Bit); |
278 | // If there is a user score attached, use it. |
279 | if (VMI.ScoreMap.count(Val: Property)) { |
280 | const APInt &UserScore = VMI.ScoreMap.lookup(Val: Property); |
281 | assert(UserScore.uge(0) && "Expect non-negative user scores!" ); |
282 | Score += UserScore.getZExtValue(); |
283 | continue; |
284 | } |
285 | |
286 | switch (getOpenMPContextTraitSetForProperty(Property)) { |
287 | case TraitSet::construct: |
288 | // We handle the construct traits later via the VMI.ConstructTraits |
289 | // container. |
290 | continue; |
291 | case TraitSet::implementation: |
292 | // No effect on the score (implementation defined). |
293 | continue; |
294 | case TraitSet::user: |
295 | // No effect on the score. |
296 | continue; |
297 | case TraitSet::device: |
298 | // Handled separately below. |
299 | break; |
300 | case TraitSet::invalid: |
301 | llvm_unreachable("Unknown trait set is not to be used!" ); |
302 | } |
303 | |
304 | // device={kind(any)} is "as if" no kind selector was specified. |
305 | if (Property == TraitProperty::device_kind_any) |
306 | continue; |
307 | |
308 | switch (getOpenMPContextTraitSelectorForProperty(Property)) { |
309 | case TraitSelector::device_kind: |
310 | Score += (1ULL << (NoConstructTraits + 0)); |
311 | continue; |
312 | case TraitSelector::device_arch: |
313 | Score += (1ULL << (NoConstructTraits + 1)); |
314 | continue; |
315 | case TraitSelector::device_isa: |
316 | Score += (1ULL << (NoConstructTraits + 2)); |
317 | continue; |
318 | default: |
319 | continue; |
320 | } |
321 | } |
322 | |
323 | unsigned ConstructIdx = 0; |
324 | assert(NoConstructTraits == ConstructMatches.size() && |
325 | "Mismatch in the construct traits!" ); |
326 | for (TraitProperty Property : VMI.ConstructTraits) { |
327 | assert(getOpenMPContextTraitSetForProperty(Property) == |
328 | TraitSet::construct && |
329 | "Ill-formed variant match info!" ); |
330 | (void)Property; |
331 | // ConstructMatches is the position p - 1 and we need 2^(p-1). |
332 | Score += (1ULL << ConstructMatches[ConstructIdx++]); |
333 | } |
334 | |
335 | LLVM_DEBUG(dbgs() << "[" << DEBUG_TYPE << "] Variant has a score of " << Score |
336 | << "\n" ); |
337 | return Score; |
338 | } |
339 | |
340 | int llvm::omp::getBestVariantMatchForContext( |
341 | const SmallVectorImpl<VariantMatchInfo> &VMIs, const OMPContext &Ctx) { |
342 | |
343 | APInt BestScore(64, 0); |
344 | int BestVMIIdx = -1; |
345 | const VariantMatchInfo *BestVMI = nullptr; |
346 | |
347 | for (unsigned u = 0, e = VMIs.size(); u < e; ++u) { |
348 | const VariantMatchInfo &VMI = VMIs[u]; |
349 | |
350 | SmallVector<unsigned, 8> ConstructMatches; |
351 | // If the variant is not applicable its not the best. |
352 | if (!isVariantApplicableInContextHelper(VMI, Ctx, ConstructMatches: &ConstructMatches, |
353 | /* DeviceSetOnly */ false)) |
354 | continue; |
355 | // Check if its clearly not the best. |
356 | APInt Score = getVariantMatchScore(VMI, Ctx, ConstructMatches); |
357 | if (Score.ult(RHS: BestScore)) |
358 | continue; |
359 | // Equal score need subset checks. |
360 | if (Score.eq(RHS: BestScore)) { |
361 | // Strict subset are never best. |
362 | if (isStrictSubset(VMI0: VMI, VMI1: *BestVMI)) |
363 | continue; |
364 | // Same score and the current best is no strict subset so we keep it. |
365 | if (!isStrictSubset(VMI0: *BestVMI, VMI1: VMI)) |
366 | continue; |
367 | } |
368 | // New best found. |
369 | BestVMI = &VMI; |
370 | BestVMIIdx = u; |
371 | BestScore = Score; |
372 | } |
373 | |
374 | return BestVMIIdx; |
375 | } |
376 | |
377 | TraitSet llvm::omp::getOpenMPContextTraitSetKind(StringRef S) { |
378 | return StringSwitch<TraitSet>(S) |
379 | #define OMP_TRAIT_SET(Enum, Str) .Case(Str, TraitSet::Enum) |
380 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
381 | .Default(Value: TraitSet::invalid); |
382 | } |
383 | |
384 | TraitSet |
385 | llvm::omp::getOpenMPContextTraitSetForSelector(TraitSelector Selector) { |
386 | switch (Selector) { |
387 | #define OMP_TRAIT_SELECTOR(Enum, TraitSetEnum, Str, ReqProp) \ |
388 | case TraitSelector::Enum: \ |
389 | return TraitSet::TraitSetEnum; |
390 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
391 | } |
392 | llvm_unreachable("Unknown trait selector!" ); |
393 | } |
394 | TraitSet |
395 | llvm::omp::getOpenMPContextTraitSetForProperty(TraitProperty Property) { |
396 | switch (Property) { |
397 | #define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \ |
398 | case TraitProperty::Enum: \ |
399 | return TraitSet::TraitSetEnum; |
400 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
401 | } |
402 | llvm_unreachable("Unknown trait set!" ); |
403 | } |
404 | StringRef llvm::omp::getOpenMPContextTraitSetName(TraitSet Kind) { |
405 | switch (Kind) { |
406 | #define OMP_TRAIT_SET(Enum, Str) \ |
407 | case TraitSet::Enum: \ |
408 | return Str; |
409 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
410 | } |
411 | llvm_unreachable("Unknown trait set!" ); |
412 | } |
413 | |
414 | TraitSelector llvm::omp::getOpenMPContextTraitSelectorKind(StringRef S) { |
415 | return StringSwitch<TraitSelector>(S) |
416 | #define OMP_TRAIT_SELECTOR(Enum, TraitSetEnum, Str, ReqProp) \ |
417 | .Case(Str, TraitSelector::Enum) |
418 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
419 | .Default(Value: TraitSelector::invalid); |
420 | } |
421 | TraitSelector |
422 | llvm::omp::getOpenMPContextTraitSelectorForProperty(TraitProperty Property) { |
423 | switch (Property) { |
424 | #define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \ |
425 | case TraitProperty::Enum: \ |
426 | return TraitSelector::TraitSelectorEnum; |
427 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
428 | } |
429 | llvm_unreachable("Unknown trait set!" ); |
430 | } |
431 | StringRef llvm::omp::getOpenMPContextTraitSelectorName(TraitSelector Kind) { |
432 | switch (Kind) { |
433 | #define OMP_TRAIT_SELECTOR(Enum, TraitSetEnum, Str, ReqProp) \ |
434 | case TraitSelector::Enum: \ |
435 | return Str; |
436 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
437 | } |
438 | llvm_unreachable("Unknown trait selector!" ); |
439 | } |
440 | |
441 | TraitProperty llvm::omp::getOpenMPContextTraitPropertyKind( |
442 | TraitSet Set, TraitSelector Selector, StringRef S) { |
443 | // Special handling for `device={isa(...)}` as we accept anything here. It is |
444 | // up to the target to decide if the feature is available. |
445 | if (Set == TraitSet::device && Selector == TraitSelector::device_isa) |
446 | return TraitProperty::device_isa___ANY; |
447 | #define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \ |
448 | if (Set == TraitSet::TraitSetEnum && Str == S) \ |
449 | return TraitProperty::Enum; |
450 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
451 | return TraitProperty::invalid; |
452 | } |
453 | TraitProperty |
454 | llvm::omp::getOpenMPContextTraitPropertyForSelector(TraitSelector Selector) { |
455 | return StringSwitch<TraitProperty>( |
456 | getOpenMPContextTraitSelectorName(Kind: Selector)) |
457 | #define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \ |
458 | .Case(Str, Selector == TraitSelector::TraitSelectorEnum \ |
459 | ? TraitProperty::Enum \ |
460 | : TraitProperty::invalid) |
461 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
462 | .Default(Value: TraitProperty::invalid); |
463 | } |
464 | StringRef llvm::omp::getOpenMPContextTraitPropertyName(TraitProperty Kind, |
465 | StringRef RawString) { |
466 | if (Kind == TraitProperty::device_isa___ANY) |
467 | return RawString; |
468 | switch (Kind) { |
469 | #define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \ |
470 | case TraitProperty::Enum: \ |
471 | return Str; |
472 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
473 | } |
474 | llvm_unreachable("Unknown trait property!" ); |
475 | } |
476 | StringRef llvm::omp::getOpenMPContextTraitPropertyFullName(TraitProperty Kind) { |
477 | switch (Kind) { |
478 | #define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \ |
479 | case TraitProperty::Enum: \ |
480 | return "(" #TraitSetEnum "," #TraitSelectorEnum "," Str ")"; |
481 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
482 | } |
483 | llvm_unreachable("Unknown trait property!" ); |
484 | } |
485 | |
486 | bool llvm::omp::isValidTraitSelectorForTraitSet(TraitSelector Selector, |
487 | TraitSet Set, |
488 | bool &AllowsTraitScore, |
489 | bool &RequiresProperty) { |
490 | AllowsTraitScore = Set != TraitSet::construct && Set != TraitSet::device; |
491 | switch (Selector) { |
492 | #define OMP_TRAIT_SELECTOR(Enum, TraitSetEnum, Str, ReqProp) \ |
493 | case TraitSelector::Enum: \ |
494 | RequiresProperty = ReqProp; \ |
495 | return Set == TraitSet::TraitSetEnum; |
496 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
497 | } |
498 | llvm_unreachable("Unknown trait selector!" ); |
499 | } |
500 | |
501 | bool llvm::omp::isValidTraitPropertyForTraitSetAndSelector( |
502 | TraitProperty Property, TraitSelector Selector, TraitSet Set) { |
503 | switch (Property) { |
504 | #define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \ |
505 | case TraitProperty::Enum: \ |
506 | return Set == TraitSet::TraitSetEnum && \ |
507 | Selector == TraitSelector::TraitSelectorEnum; |
508 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
509 | } |
510 | llvm_unreachable("Unknown trait property!" ); |
511 | } |
512 | |
513 | std::string llvm::omp::listOpenMPContextTraitSets() { |
514 | std::string S; |
515 | #define OMP_TRAIT_SET(Enum, Str) \ |
516 | if (StringRef(Str) != "invalid") \ |
517 | S.append("'").append(Str).append("'").append(" "); |
518 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
519 | S.pop_back(); |
520 | return S; |
521 | } |
522 | |
523 | std::string llvm::omp::listOpenMPContextTraitSelectors(TraitSet Set) { |
524 | std::string S; |
525 | #define OMP_TRAIT_SELECTOR(Enum, TraitSetEnum, Str, ReqProp) \ |
526 | if (TraitSet::TraitSetEnum == Set && StringRef(Str) != "Invalid") \ |
527 | S.append("'").append(Str).append("'").append(" "); |
528 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
529 | S.pop_back(); |
530 | return S; |
531 | } |
532 | |
533 | std::string |
534 | llvm::omp::listOpenMPContextTraitProperties(TraitSet Set, |
535 | TraitSelector Selector) { |
536 | std::string S; |
537 | #define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \ |
538 | if (TraitSet::TraitSetEnum == Set && \ |
539 | TraitSelector::TraitSelectorEnum == Selector && \ |
540 | StringRef(Str) != "invalid") \ |
541 | S.append("'").append(Str).append("'").append(" "); |
542 | #include "llvm/Frontend/OpenMP/OMPKinds.def" |
543 | if (S.empty()) |
544 | return "<none>" ; |
545 | S.pop_back(); |
546 | return S; |
547 | } |
548 | |