OMPContext.cpp 20 KB

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