OMPContext.cpp 20 KB

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