Path: blob/main/contrib/llvm-project/llvm/lib/Frontend/OpenMP/OMPContext.cpp
35271 views
//===- OMPContext.cpp ------ Collection of helpers for OpenMP contexts ----===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7/// \file8///9/// This file implements helper functions and classes to deal with OpenMP10/// contexts as used by `[begin/end] declare variant` and `metadirective`.11///12//===----------------------------------------------------------------------===//1314#include "llvm/Frontend/OpenMP/OMPContext.h"15#include "llvm/ADT/StringRef.h"16#include "llvm/ADT/StringSwitch.h"17#include "llvm/Support/Debug.h"18#include "llvm/Support/raw_ostream.h"19#include "llvm/TargetParser/Triple.h"2021#define DEBUG_TYPE "openmp-ir-builder"2223using namespace llvm;24using namespace omp;2526OMPContext::OMPContext(bool IsDeviceCompilation, Triple TargetTriple) {27// Add the appropriate device kind trait based on the triple and the28// IsDeviceCompilation flag.29ActiveTraits.set(unsigned(IsDeviceCompilation30? TraitProperty::device_kind_nohost31: TraitProperty::device_kind_host));32switch (TargetTriple.getArch()) {33case Triple::arm:34case Triple::armeb:35case Triple::aarch64:36case Triple::aarch64_be:37case Triple::aarch64_32:38case Triple::mips:39case Triple::mipsel:40case Triple::mips64:41case Triple::mips64el:42case Triple::ppc:43case Triple::ppcle:44case Triple::ppc64:45case Triple::ppc64le:46case Triple::systemz:47case Triple::x86:48case Triple::x86_64:49ActiveTraits.set(unsigned(TraitProperty::device_kind_cpu));50break;51case Triple::amdgcn:52case Triple::nvptx:53case Triple::nvptx64:54ActiveTraits.set(unsigned(TraitProperty::device_kind_gpu));55break;56default:57break;58}5960// Add the appropriate device architecture trait based on the triple.61#define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \62if (TraitSelector::TraitSelectorEnum == TraitSelector::device_arch) { \63if (TargetTriple.getArch() == TargetTriple.getArchTypeForLLVMName(Str)) \64ActiveTraits.set(unsigned(TraitProperty::Enum)); \65if (StringRef(Str) == "x86_64" && \66TargetTriple.getArch() == Triple::x86_64) \67ActiveTraits.set(unsigned(TraitProperty::Enum)); \68}69#include "llvm/Frontend/OpenMP/OMPKinds.def"7071// TODO: What exactly do we want to see as device ISA trait?72// The discussion on the list did not seem to have come to an agreed73// upon solution.7475// LLVM is the "OpenMP vendor" but we could also interpret vendor as the76// target vendor.77ActiveTraits.set(unsigned(TraitProperty::implementation_vendor_llvm));7879// The user condition true is accepted but not false.80ActiveTraits.set(unsigned(TraitProperty::user_condition_true));8182// This is for sure some device.83ActiveTraits.set(unsigned(TraitProperty::device_kind_any));8485LLVM_DEBUG({86dbgs() << "[" << DEBUG_TYPE87<< "] New OpenMP context with the following properties:\n";88for (unsigned Bit : ActiveTraits.set_bits()) {89TraitProperty Property = TraitProperty(Bit);90dbgs() << "\t " << getOpenMPContextTraitPropertyFullName(Property)91<< "\n";92}93});94}9596/// Return true if \p C0 is a subset of \p C1. Note that both arrays are97/// expected to be sorted.98template <typename T> static bool isSubset(ArrayRef<T> C0, ArrayRef<T> C1) {99#ifdef EXPENSIVE_CHECKS100assert(llvm::is_sorted(C0) && llvm::is_sorted(C1) &&101"Expected sorted arrays!");102#endif103if (C0.size() > C1.size())104return false;105auto It0 = C0.begin(), End0 = C0.end();106auto It1 = C1.begin(), End1 = C1.end();107while (It0 != End0) {108if (It1 == End1)109return false;110if (*It0 == *It1) {111++It0;112++It1;113continue;114}115++It0;116}117return true;118}119120/// Return true if \p C0 is a strict subset of \p C1. Note that both arrays are121/// expected to be sorted.122template <typename T>123static bool isStrictSubset(ArrayRef<T> C0, ArrayRef<T> C1) {124if (C0.size() >= C1.size())125return false;126return isSubset<T>(C0, C1);127}128129static bool isStrictSubset(const VariantMatchInfo &VMI0,130const VariantMatchInfo &VMI1) {131// If all required traits are a strict subset and the ordered vectors storing132// the construct traits, we say it is a strict subset. Note that the latter133// relation is not required to be strict.134if (VMI0.RequiredTraits.count() >= VMI1.RequiredTraits.count())135return false;136for (unsigned Bit : VMI0.RequiredTraits.set_bits())137if (!VMI1.RequiredTraits.test(Bit))138return false;139if (!isSubset<TraitProperty>(VMI0.ConstructTraits, VMI1.ConstructTraits))140return false;141return true;142}143144static int isVariantApplicableInContextHelper(145const VariantMatchInfo &VMI, const OMPContext &Ctx,146SmallVectorImpl<unsigned> *ConstructMatches, bool DeviceSetOnly) {147148// The match kind determines if we need to match all traits, any of the149// traits, or none of the traits for it to be an applicable context.150enum MatchKind { MK_ALL, MK_ANY, MK_NONE };151152MatchKind MK = MK_ALL;153// Determine the match kind the user wants, "all" is the default and provided154// to the user only for completeness.155if (VMI.RequiredTraits.test(156unsigned(TraitProperty::implementation_extension_match_any)))157MK = MK_ANY;158if (VMI.RequiredTraits.test(159unsigned(TraitProperty::implementation_extension_match_none)))160MK = MK_NONE;161162// Helper to deal with a single property that was (not) found in the OpenMP163// context based on the match kind selected by the user via164// `implementation={extensions(match_[all,any,none])}'165auto HandleTrait = [MK](TraitProperty Property,166bool WasFound) -> std::optional<bool> /* Result */ {167// For kind "any" a single match is enough but we ignore non-matched168// properties.169if (MK == MK_ANY) {170if (WasFound)171return true;172return std::nullopt;173}174175// In "all" or "none" mode we accept a matching or non-matching property176// respectively and move on. We are not done yet!177if ((WasFound && MK == MK_ALL) || (!WasFound && MK == MK_NONE))178return std::nullopt;179180// We missed a property, provide some debug output and indicate failure.181LLVM_DEBUG({182if (MK == MK_ALL)183dbgs() << "[" << DEBUG_TYPE << "] Property "184<< getOpenMPContextTraitPropertyName(Property, "")185<< " was not in the OpenMP context but match kind is all.\n";186if (MK == MK_NONE)187dbgs() << "[" << DEBUG_TYPE << "] Property "188<< getOpenMPContextTraitPropertyName(Property, "")189<< " was in the OpenMP context but match kind is none.\n";190});191return false;192};193194for (unsigned Bit : VMI.RequiredTraits.set_bits()) {195TraitProperty Property = TraitProperty(Bit);196if (DeviceSetOnly &&197getOpenMPContextTraitSetForProperty(Property) != TraitSet::device)198continue;199200// So far all extensions are handled elsewhere, we skip them here as they201// are not part of the OpenMP context.202if (getOpenMPContextTraitSelectorForProperty(Property) ==203TraitSelector::implementation_extension)204continue;205206bool IsActiveTrait = Ctx.ActiveTraits.test(unsigned(Property));207208// We overwrite the isa trait as it is actually up to the OMPContext hook to209// check the raw string(s).210if (Property == TraitProperty::device_isa___ANY)211IsActiveTrait = llvm::all_of(VMI.ISATraits, [&](StringRef RawString) {212return Ctx.matchesISATrait(RawString);213});214215if (std::optional<bool> Result = HandleTrait(Property, IsActiveTrait))216return *Result;217}218219if (!DeviceSetOnly) {220// We could use isSubset here but we also want to record the match221// locations.222unsigned ConstructIdx = 0, NoConstructTraits = Ctx.ConstructTraits.size();223for (TraitProperty Property : VMI.ConstructTraits) {224assert(getOpenMPContextTraitSetForProperty(Property) ==225TraitSet::construct &&226"Variant context is ill-formed!");227228// Verify the nesting.229bool FoundInOrder = false;230while (!FoundInOrder && ConstructIdx != NoConstructTraits)231FoundInOrder = (Ctx.ConstructTraits[ConstructIdx++] == Property);232if (ConstructMatches)233ConstructMatches->push_back(ConstructIdx - 1);234235if (std::optional<bool> Result = HandleTrait(Property, FoundInOrder))236return *Result;237238if (!FoundInOrder) {239LLVM_DEBUG(dbgs() << "[" << DEBUG_TYPE << "] Construct property "240<< getOpenMPContextTraitPropertyName(Property, "")241<< " was not nested properly.\n");242return false;243}244245// TODO: Verify SIMD246}247248assert(isSubset<TraitProperty>(VMI.ConstructTraits, Ctx.ConstructTraits) &&249"Broken invariant!");250}251252if (MK == MK_ANY) {253LLVM_DEBUG(dbgs() << "[" << DEBUG_TYPE254<< "] None of the properties was in the OpenMP context "255"but match kind is any.\n");256return false;257}258259return true;260}261262bool llvm::omp::isVariantApplicableInContext(const VariantMatchInfo &VMI,263const OMPContext &Ctx,264bool DeviceSetOnly) {265return isVariantApplicableInContextHelper(266VMI, Ctx, /* ConstructMatches */ nullptr, DeviceSetOnly);267}268269static APInt getVariantMatchScore(const VariantMatchInfo &VMI,270const OMPContext &Ctx,271SmallVectorImpl<unsigned> &ConstructMatches) {272APInt Score(64, 1);273274unsigned NoConstructTraits = VMI.ConstructTraits.size();275for (unsigned Bit : VMI.RequiredTraits.set_bits()) {276TraitProperty Property = TraitProperty(Bit);277// If there is a user score attached, use it.278if (VMI.ScoreMap.count(Property)) {279const APInt &UserScore = VMI.ScoreMap.lookup(Property);280assert(UserScore.uge(0) && "Expect non-negative user scores!");281Score += UserScore.getZExtValue();282continue;283}284285switch (getOpenMPContextTraitSetForProperty(Property)) {286case TraitSet::construct:287// We handle the construct traits later via the VMI.ConstructTraits288// container.289continue;290case TraitSet::implementation:291// No effect on the score (implementation defined).292continue;293case TraitSet::user:294// No effect on the score.295continue;296case TraitSet::device:297// Handled separately below.298break;299case TraitSet::invalid:300llvm_unreachable("Unknown trait set is not to be used!");301}302303// device={kind(any)} is "as if" no kind selector was specified.304if (Property == TraitProperty::device_kind_any)305continue;306307switch (getOpenMPContextTraitSelectorForProperty(Property)) {308case TraitSelector::device_kind:309Score += (1ULL << (NoConstructTraits + 0));310continue;311case TraitSelector::device_arch:312Score += (1ULL << (NoConstructTraits + 1));313continue;314case TraitSelector::device_isa:315Score += (1ULL << (NoConstructTraits + 2));316continue;317default:318continue;319}320}321322unsigned ConstructIdx = 0;323assert(NoConstructTraits == ConstructMatches.size() &&324"Mismatch in the construct traits!");325for (TraitProperty Property : VMI.ConstructTraits) {326assert(getOpenMPContextTraitSetForProperty(Property) ==327TraitSet::construct &&328"Ill-formed variant match info!");329(void)Property;330// ConstructMatches is the position p - 1 and we need 2^(p-1).331Score += (1ULL << ConstructMatches[ConstructIdx++]);332}333334LLVM_DEBUG(dbgs() << "[" << DEBUG_TYPE << "] Variant has a score of " << Score335<< "\n");336return Score;337}338339int llvm::omp::getBestVariantMatchForContext(340const SmallVectorImpl<VariantMatchInfo> &VMIs, const OMPContext &Ctx) {341342APInt BestScore(64, 0);343int BestVMIIdx = -1;344const VariantMatchInfo *BestVMI = nullptr;345346for (unsigned u = 0, e = VMIs.size(); u < e; ++u) {347const VariantMatchInfo &VMI = VMIs[u];348349SmallVector<unsigned, 8> ConstructMatches;350// If the variant is not applicable its not the best.351if (!isVariantApplicableInContextHelper(VMI, Ctx, &ConstructMatches,352/* DeviceSetOnly */ false))353continue;354// Check if its clearly not the best.355APInt Score = getVariantMatchScore(VMI, Ctx, ConstructMatches);356if (Score.ult(BestScore))357continue;358// Equal score need subset checks.359if (Score.eq(BestScore)) {360// Strict subset are never best.361if (isStrictSubset(VMI, *BestVMI))362continue;363// Same score and the current best is no strict subset so we keep it.364if (!isStrictSubset(*BestVMI, VMI))365continue;366}367// New best found.368BestVMI = &VMI;369BestVMIIdx = u;370BestScore = Score;371}372373return BestVMIIdx;374}375376TraitSet llvm::omp::getOpenMPContextTraitSetKind(StringRef S) {377return StringSwitch<TraitSet>(S)378#define OMP_TRAIT_SET(Enum, Str) .Case(Str, TraitSet::Enum)379#include "llvm/Frontend/OpenMP/OMPKinds.def"380.Default(TraitSet::invalid);381}382383TraitSet384llvm::omp::getOpenMPContextTraitSetForSelector(TraitSelector Selector) {385switch (Selector) {386#define OMP_TRAIT_SELECTOR(Enum, TraitSetEnum, Str, ReqProp) \387case TraitSelector::Enum: \388return TraitSet::TraitSetEnum;389#include "llvm/Frontend/OpenMP/OMPKinds.def"390}391llvm_unreachable("Unknown trait selector!");392}393TraitSet394llvm::omp::getOpenMPContextTraitSetForProperty(TraitProperty Property) {395switch (Property) {396#define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \397case TraitProperty::Enum: \398return TraitSet::TraitSetEnum;399#include "llvm/Frontend/OpenMP/OMPKinds.def"400}401llvm_unreachable("Unknown trait set!");402}403StringRef llvm::omp::getOpenMPContextTraitSetName(TraitSet Kind) {404switch (Kind) {405#define OMP_TRAIT_SET(Enum, Str) \406case TraitSet::Enum: \407return Str;408#include "llvm/Frontend/OpenMP/OMPKinds.def"409}410llvm_unreachable("Unknown trait set!");411}412413TraitSelector llvm::omp::getOpenMPContextTraitSelectorKind(StringRef S) {414return StringSwitch<TraitSelector>(S)415#define OMP_TRAIT_SELECTOR(Enum, TraitSetEnum, Str, ReqProp) \416.Case(Str, TraitSelector::Enum)417#include "llvm/Frontend/OpenMP/OMPKinds.def"418.Default(TraitSelector::invalid);419}420TraitSelector421llvm::omp::getOpenMPContextTraitSelectorForProperty(TraitProperty Property) {422switch (Property) {423#define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \424case TraitProperty::Enum: \425return TraitSelector::TraitSelectorEnum;426#include "llvm/Frontend/OpenMP/OMPKinds.def"427}428llvm_unreachable("Unknown trait set!");429}430StringRef llvm::omp::getOpenMPContextTraitSelectorName(TraitSelector Kind) {431switch (Kind) {432#define OMP_TRAIT_SELECTOR(Enum, TraitSetEnum, Str, ReqProp) \433case TraitSelector::Enum: \434return Str;435#include "llvm/Frontend/OpenMP/OMPKinds.def"436}437llvm_unreachable("Unknown trait selector!");438}439440TraitProperty llvm::omp::getOpenMPContextTraitPropertyKind(441TraitSet Set, TraitSelector Selector, StringRef S) {442// Special handling for `device={isa(...)}` as we accept anything here. It is443// up to the target to decide if the feature is available.444if (Set == TraitSet::device && Selector == TraitSelector::device_isa)445return TraitProperty::device_isa___ANY;446#define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \447if (Set == TraitSet::TraitSetEnum && Str == S) \448return TraitProperty::Enum;449#include "llvm/Frontend/OpenMP/OMPKinds.def"450return TraitProperty::invalid;451}452TraitProperty453llvm::omp::getOpenMPContextTraitPropertyForSelector(TraitSelector Selector) {454return StringSwitch<TraitProperty>(455getOpenMPContextTraitSelectorName(Selector))456#define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \457.Case(Str, Selector == TraitSelector::TraitSelectorEnum \458? TraitProperty::Enum \459: TraitProperty::invalid)460#include "llvm/Frontend/OpenMP/OMPKinds.def"461.Default(TraitProperty::invalid);462}463StringRef llvm::omp::getOpenMPContextTraitPropertyName(TraitProperty Kind,464StringRef RawString) {465if (Kind == TraitProperty::device_isa___ANY)466return RawString;467switch (Kind) {468#define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \469case TraitProperty::Enum: \470return Str;471#include "llvm/Frontend/OpenMP/OMPKinds.def"472}473llvm_unreachable("Unknown trait property!");474}475StringRef llvm::omp::getOpenMPContextTraitPropertyFullName(TraitProperty Kind) {476switch (Kind) {477#define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \478case TraitProperty::Enum: \479return "(" #TraitSetEnum "," #TraitSelectorEnum "," Str ")";480#include "llvm/Frontend/OpenMP/OMPKinds.def"481}482llvm_unreachable("Unknown trait property!");483}484485bool llvm::omp::isValidTraitSelectorForTraitSet(TraitSelector Selector,486TraitSet Set,487bool &AllowsTraitScore,488bool &RequiresProperty) {489AllowsTraitScore = Set != TraitSet::construct && Set != TraitSet::device;490switch (Selector) {491#define OMP_TRAIT_SELECTOR(Enum, TraitSetEnum, Str, ReqProp) \492case TraitSelector::Enum: \493RequiresProperty = ReqProp; \494return Set == TraitSet::TraitSetEnum;495#include "llvm/Frontend/OpenMP/OMPKinds.def"496}497llvm_unreachable("Unknown trait selector!");498}499500bool llvm::omp::isValidTraitPropertyForTraitSetAndSelector(501TraitProperty Property, TraitSelector Selector, TraitSet Set) {502switch (Property) {503#define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \504case TraitProperty::Enum: \505return Set == TraitSet::TraitSetEnum && \506Selector == TraitSelector::TraitSelectorEnum;507#include "llvm/Frontend/OpenMP/OMPKinds.def"508}509llvm_unreachable("Unknown trait property!");510}511512std::string llvm::omp::listOpenMPContextTraitSets() {513std::string S;514#define OMP_TRAIT_SET(Enum, Str) \515if (StringRef(Str) != "invalid") \516S.append("'").append(Str).append("'").append(" ");517#include "llvm/Frontend/OpenMP/OMPKinds.def"518S.pop_back();519return S;520}521522std::string llvm::omp::listOpenMPContextTraitSelectors(TraitSet Set) {523std::string S;524#define OMP_TRAIT_SELECTOR(Enum, TraitSetEnum, Str, ReqProp) \525if (TraitSet::TraitSetEnum == Set && StringRef(Str) != "Invalid") \526S.append("'").append(Str).append("'").append(" ");527#include "llvm/Frontend/OpenMP/OMPKinds.def"528S.pop_back();529return S;530}531532std::string533llvm::omp::listOpenMPContextTraitProperties(TraitSet Set,534TraitSelector Selector) {535std::string S;536#define OMP_TRAIT_PROPERTY(Enum, TraitSetEnum, TraitSelectorEnum, Str) \537if (TraitSet::TraitSetEnum == Set && \538TraitSelector::TraitSelectorEnum == Selector && \539StringRef(Str) != "invalid") \540S.append("'").append(Str).append("'").append(" ");541#include "llvm/Frontend/OpenMP/OMPKinds.def"542if (S.empty())543return "<none>";544S.pop_back();545return S;546}547548549