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