brintos

brintos / llvm-project-archived public Read only

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