[OpenMP][Part 2] Use reusable OpenMP context/traits handling
This patch implements an almost complete handling of OpenMP
contexts/traits such that we can reuse most of the logic in Flang
through the OMPContext.{h,cpp} in llvm/Frontend/OpenMP.
All but construct SIMD specifiers, e.g., inbranch, and the device ISA
selector are define in `llvm/lib/Frontend/OpenMP/OMPKinds.def`. From
these definitions we generate the enum classes `TraitSet`,
`TraitSelector`, and `TraitProperty` as well as conversion and helper
functions in `llvm/lib/Frontend/OpenMP/OMPContext.{h,cpp}`.
The above enum classes are used in the parser, sema, and the AST
attribute. The latter is not a collection of multiple primitive variant
arguments that contain encodings via numbers and strings but instead a
tree that mirrors the `match` clause (see `struct OpenMPTraitInfo`).
The changes to the parser make it more forgiving when wrong syntax is
read and they also resulted in more specialized diagnostics. The tests
are updated and the core issues are detected as before. Here and
elsewhere this patch tries to be generic, thus we do not distinguish
what selector set, selector, or property is parsed except if they do
behave exceptionally, as for example `user={condition(EXPR)}` does.
The sema logic changed in two ways: First, the OMPDeclareVariantAttr
representation changed, as mentioned above, and the sema was adjusted to
work with the new `OpenMPTraitInfo`. Second, the matching and scoring
logic moved into `OMPContext.{h,cpp}`. It is implemented on a flat
representation of the `match` clause that is not tied to clang.
`OpenMPTraitInfo` provides a method to generate this flat structure (see
`struct VariantMatchInfo`) by computing integer score values and boolean
user conditions from the `clang::Expr` we keep for them.
The OpenMP context is now an explicit object (see `struct OMPContext`).
This is in anticipation of construct traits that need to be tracked. The
OpenMP context, as well as the `VariantMatchInfo`, are basically made up
of a set of active or respectively required traits, e.g., 'host', and an
ordered container of constructs which allows duplication. Matching and
scoring is kept as generic as possible to allow easy extension in the
future.
---
Test changes:
The messages checked in `OpenMP/declare_variant_messages.{c,cpp}` have
been auto generated to match the new warnings and notes of the parser.
The "subset" checks were reversed causing the wrong version to be
picked. The tests have been adjusted to correct this.
We do not print scores if the user did not provide one.
We print spaces to make lists in the `match` clause more legible.
Reviewers: kiranchandramohan, ABataev, RaviNarayanaswamy, gtbercea, grokos, sdmitriev, JonChesterfield, hfinkel, fghanim
Subscribers: merge_guards_bot, rampitec, mgorny, hiraditya, aheejin, fedor.sergeev, simoncook, bollu, guansong, dexonsmith, jfb, s.egerton, llvm-commits, cfe-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D71830
diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp
index 6eac982..1cd1c82 100644
--- a/clang/lib/AST/OpenMPClause.cpp
+++ b/clang/lib/AST/OpenMPClause.cpp
@@ -1722,3 +1722,107 @@
OS << "order(" << getOpenMPSimpleClauseTypeName(OMPC_order, Node->getKind())
<< ")";
}
+
+void OMPTraitInfo::getAsVariantMatchInfo(
+ ASTContext &ASTCtx, llvm::omp::VariantMatchInfo &VMI) const {
+ for (const OMPTraitSet &Set : Sets) {
+ for (const OMPTraitSelector &Selector : Set.Selectors) {
+
+ // User conditions are special as we evaluate the condition here.
+ if (Selector.Kind == llvm::omp::TraitSelector::user_condition) {
+ assert(Selector.ScoreOrCondition &&
+ "Ill-formed user condition, expected condition expression!");
+ assert(Selector.Properties.size() == 1 &&
+ Selector.Properties.front().Kind ==
+ llvm::omp::TraitProperty::user_condition_unknown &&
+ "Ill-formed user condition, expected unknown trait property!");
+
+ llvm::APInt CondVal =
+ Selector.ScoreOrCondition->EvaluateKnownConstInt(ASTCtx);
+ VMI.addTrait(CondVal.isNullValue()
+ ? llvm::omp::TraitProperty::user_condition_false
+ : llvm::omp::TraitProperty::user_condition_true);
+ continue;
+ }
+
+ llvm::APInt Score;
+ llvm::APInt *ScorePtr = nullptr;
+ if (Selector.ScoreOrCondition) {
+ Score = Selector.ScoreOrCondition->EvaluateKnownConstInt(ASTCtx);
+ ScorePtr = &Score;
+ }
+ for (const OMPTraitProperty &Property : Selector.Properties)
+ VMI.addTrait(Set.Kind, Property.Kind, ScorePtr);
+
+ if (Set.Kind != llvm::omp::TraitSet::construct)
+ continue;
+
+ // TODO: This might not hold once we implement SIMD properly.
+ assert(Selector.Properties.size() == 1 &&
+ Selector.Properties.front().Kind ==
+ llvm::omp::getOpenMPContextTraitPropertyForSelector(
+ Selector.Kind) &&
+ "Ill-formed construct selector!");
+
+ VMI.ConstructTraits.push_back(Selector.Properties.front().Kind);
+ }
+ }
+}
+
+void OMPTraitInfo::print(llvm::raw_ostream &OS,
+ const PrintingPolicy &Policy) const {
+ bool FirstSet = true;
+ for (const OMPTraitInfo::OMPTraitSet &Set : Sets) {
+ if (!FirstSet)
+ OS << ", ";
+ FirstSet = false;
+ OS << llvm::omp::getOpenMPContextTraitSetName(Set.Kind) << "={";
+
+ bool FirstSelector = true;
+ for (const OMPTraitInfo::OMPTraitSelector &Selector : Set.Selectors) {
+ if (!FirstSelector)
+ OS << ", ";
+ FirstSelector = false;
+ OS << llvm::omp::getOpenMPContextTraitSelectorName(Selector.Kind);
+
+ bool AllowsTraitScore = false;
+ bool RequiresProperty = false;
+ llvm::omp::isValidTraitSelectorForTraitSet(
+ Selector.Kind, Set.Kind, AllowsTraitScore, RequiresProperty);
+
+ if (!RequiresProperty)
+ continue;
+
+ OS << "(";
+ if (Selector.Kind == llvm::omp::TraitSelector::user_condition) {
+ Selector.ScoreOrCondition->printPretty(OS, nullptr, Policy);
+ } else {
+
+ if (Selector.ScoreOrCondition) {
+ OS << "score(";
+ Selector.ScoreOrCondition->printPretty(OS, nullptr, Policy);
+ OS << "): ";
+ }
+
+ bool FirstProperty = true;
+ for (const OMPTraitInfo::OMPTraitProperty &Property :
+ Selector.Properties) {
+ if (!FirstProperty)
+ OS << ", ";
+ FirstProperty = false;
+ OS << llvm::omp::getOpenMPContextTraitPropertyName(Property.Kind);
+ }
+ }
+ OS << ")";
+ }
+ OS << "}";
+ }
+}
+
+llvm::raw_ostream &clang::operator<<(llvm::raw_ostream &OS,
+ const OMPTraitInfo &TI) {
+ LangOptions LO;
+ PrintingPolicy Policy(LO);
+ TI.print(OS, Policy);
+ return OS;
+}
diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp
index 70817f8..ff0f287 100644
--- a/clang/lib/Basic/OpenMPKinds.cpp
+++ b/clang/lib/Basic/OpenMPKinds.cpp
@@ -20,49 +20,6 @@
using namespace clang;
using namespace llvm::omp;
-OpenMPContextSelectorSetKind
-clang::getOpenMPContextSelectorSet(llvm::StringRef Str) {
- return llvm::StringSwitch<OpenMPContextSelectorSetKind>(Str)
-#define OPENMP_CONTEXT_SELECTOR_SET(Name) .Case(#Name, OMP_CTX_SET_##Name)
-#include "clang/Basic/OpenMPKinds.def"
- .Default(OMP_CTX_SET_unknown);
-}
-
-llvm::StringRef
-clang::getOpenMPContextSelectorSetName(OpenMPContextSelectorSetKind Kind) {
- switch (Kind) {
- case OMP_CTX_SET_unknown:
- return "unknown";
-#define OPENMP_CONTEXT_SELECTOR_SET(Name) \
- case OMP_CTX_SET_##Name: \
- return #Name;
-#include "clang/Basic/OpenMPKinds.def"
- break;
- }
- llvm_unreachable("Invalid OpenMP context selector set kind");
-}
-
-OpenMPContextSelectorKind clang::getOpenMPContextSelector(llvm::StringRef Str) {
- return llvm::StringSwitch<OpenMPContextSelectorKind>(Str)
-#define OPENMP_CONTEXT_SELECTOR(Name) .Case(#Name, OMP_CTX_##Name)
-#include "clang/Basic/OpenMPKinds.def"
- .Default(OMP_CTX_unknown);
-}
-
-llvm::StringRef
-clang::getOpenMPContextSelectorName(OpenMPContextSelectorKind Kind) {
- switch (Kind) {
- case OMP_CTX_unknown:
- return "unknown";
-#define OPENMP_CONTEXT_SELECTOR(Name) \
- case OMP_CTX_##Name: \
- return #Name;
-#include "clang/Basic/OpenMPKinds.def"
- break;
- }
- llvm_unreachable("Invalid OpenMP context selector kind");
-}
-
OpenMPClauseKind clang::getOpenMPClauseKind(StringRef Str) {
// 'flush' clause cannot be specified explicitly, because this is an implicit
// clause for 'flush' directive. If the 'flush' clause is explicitly specified
diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
index 7440434..60b8149 100644
--- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp
+++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
@@ -11065,257 +11065,34 @@
return Address(Addr, Align);
}
-namespace {
-using OMPContextSelectorData =
- OpenMPCtxSelectorData<ArrayRef<StringRef>, llvm::APSInt>;
-using CompleteOMPContextSelectorData = SmallVector<OMPContextSelectorData, 4>;
-} // anonymous namespace
-
-/// Checks current context and returns true if it matches the context selector.
-template <OpenMPContextSelectorSetKind CtxSet, OpenMPContextSelectorKind Ctx,
- typename... Arguments>
-static bool checkContext(const OMPContextSelectorData &Data,
- Arguments... Params) {
- assert(Data.CtxSet != OMP_CTX_SET_unknown && Data.Ctx != OMP_CTX_unknown &&
- "Unknown context selector or context selector set.");
- return false;
-}
-
-/// Checks for implementation={vendor(<vendor>)} context selector.
-/// \returns true iff <vendor>="llvm", false otherwise.
-template <>
-bool checkContext<OMP_CTX_SET_implementation, OMP_CTX_vendor>(
- const OMPContextSelectorData &Data) {
- return llvm::all_of(Data.Names,
- [](StringRef S) { return !S.compare_lower("llvm"); });
-}
-
-/// Checks for device={kind(<kind>)} context selector.
-/// \returns true if <kind>="host" and compilation is for host.
-/// true if <kind>="nohost" and compilation is for device.
-/// true if <kind>="cpu" and compilation is for Arm, X86 or PPC CPU.
-/// true if <kind>="gpu" and compilation is for NVPTX or AMDGCN.
-/// false otherwise.
-template <>
-bool checkContext<OMP_CTX_SET_device, OMP_CTX_kind, CodeGenModule &>(
- const OMPContextSelectorData &Data, CodeGenModule &CGM) {
- for (StringRef Name : Data.Names) {
- if (!Name.compare_lower("host")) {
- if (CGM.getLangOpts().OpenMPIsDevice)
- return false;
- continue;
- }
- if (!Name.compare_lower("nohost")) {
- if (!CGM.getLangOpts().OpenMPIsDevice)
- return false;
- continue;
- }
- switch (CGM.getTriple().getArch()) {
- case llvm::Triple::arm:
- case llvm::Triple::armeb:
- case llvm::Triple::aarch64:
- case llvm::Triple::aarch64_be:
- case llvm::Triple::aarch64_32:
- case llvm::Triple::ppc:
- case llvm::Triple::ppc64:
- case llvm::Triple::ppc64le:
- case llvm::Triple::x86:
- case llvm::Triple::x86_64:
- if (Name.compare_lower("cpu"))
- return false;
- break;
- case llvm::Triple::amdgcn:
- case llvm::Triple::nvptx:
- case llvm::Triple::nvptx64:
- if (Name.compare_lower("gpu"))
- return false;
- break;
- case llvm::Triple::UnknownArch:
- case llvm::Triple::arc:
- case llvm::Triple::avr:
- case llvm::Triple::bpfel:
- case llvm::Triple::bpfeb:
- case llvm::Triple::hexagon:
- case llvm::Triple::mips:
- case llvm::Triple::mipsel:
- case llvm::Triple::mips64:
- case llvm::Triple::mips64el:
- case llvm::Triple::msp430:
- case llvm::Triple::r600:
- case llvm::Triple::riscv32:
- case llvm::Triple::riscv64:
- case llvm::Triple::sparc:
- case llvm::Triple::sparcv9:
- case llvm::Triple::sparcel:
- case llvm::Triple::systemz:
- case llvm::Triple::tce:
- case llvm::Triple::tcele:
- case llvm::Triple::thumb:
- case llvm::Triple::thumbeb:
- case llvm::Triple::xcore:
- case llvm::Triple::le32:
- case llvm::Triple::le64:
- case llvm::Triple::amdil:
- case llvm::Triple::amdil64:
- case llvm::Triple::hsail:
- case llvm::Triple::hsail64:
- case llvm::Triple::spir:
- case llvm::Triple::spir64:
- case llvm::Triple::kalimba:
- case llvm::Triple::shave:
- case llvm::Triple::lanai:
- case llvm::Triple::wasm32:
- case llvm::Triple::wasm64:
- case llvm::Triple::renderscript32:
- case llvm::Triple::renderscript64:
- case llvm::Triple::ve:
- return false;
- }
- }
- return true;
-}
-
-static bool matchesContext(CodeGenModule &CGM,
- const CompleteOMPContextSelectorData &ContextData) {
- for (const OMPContextSelectorData &Data : ContextData) {
- switch (Data.Ctx) {
- case OMP_CTX_vendor:
- assert(Data.CtxSet == OMP_CTX_SET_implementation &&
- "Expected implementation context selector set.");
- if (!checkContext<OMP_CTX_SET_implementation, OMP_CTX_vendor>(Data))
- return false;
- break;
- case OMP_CTX_kind:
- assert(Data.CtxSet == OMP_CTX_SET_device &&
- "Expected device context selector set.");
- if (!checkContext<OMP_CTX_SET_device, OMP_CTX_kind, CodeGenModule &>(Data,
- CGM))
- return false;
- break;
- case OMP_CTX_unknown:
- llvm_unreachable("Unknown context selector kind.");
- }
- }
- return true;
-}
-
-static CompleteOMPContextSelectorData
-translateAttrToContextSelectorData(ASTContext &C,
- const OMPDeclareVariantAttr *A) {
- CompleteOMPContextSelectorData Data;
- for (unsigned I = 0, E = A->scores_size(); I < E; ++I) {
- Data.emplace_back();
- auto CtxSet = static_cast<OpenMPContextSelectorSetKind>(
- *std::next(A->ctxSelectorSets_begin(), I));
- auto Ctx = static_cast<OpenMPContextSelectorKind>(
- *std::next(A->ctxSelectors_begin(), I));
- Data.back().CtxSet = CtxSet;
- Data.back().Ctx = Ctx;
- const Expr *Score = *std::next(A->scores_begin(), I);
- Data.back().Score = Score->EvaluateKnownConstInt(C);
- switch (Ctx) {
- case OMP_CTX_vendor:
- assert(CtxSet == OMP_CTX_SET_implementation &&
- "Expected implementation context selector set.");
- Data.back().Names =
- llvm::makeArrayRef(A->implVendors_begin(), A->implVendors_end());
- break;
- case OMP_CTX_kind:
- assert(CtxSet == OMP_CTX_SET_device &&
- "Expected device context selector set.");
- Data.back().Names =
- llvm::makeArrayRef(A->deviceKinds_begin(), A->deviceKinds_end());
- break;
- case OMP_CTX_unknown:
- llvm_unreachable("Unknown context selector kind.");
- }
- }
- return Data;
-}
-
-static bool isStrictSubset(const CompleteOMPContextSelectorData &LHS,
- const CompleteOMPContextSelectorData &RHS) {
- llvm::SmallDenseMap<std::pair<int, int>, llvm::StringSet<>, 4> RHSData;
- for (const OMPContextSelectorData &D : RHS) {
- auto &Pair = RHSData.FindAndConstruct(std::make_pair(D.CtxSet, D.Ctx));
- Pair.getSecond().insert(D.Names.begin(), D.Names.end());
- }
- bool AllSetsAreEqual = true;
- for (const OMPContextSelectorData &D : LHS) {
- auto It = RHSData.find(std::make_pair(D.CtxSet, D.Ctx));
- if (It == RHSData.end())
- return false;
- if (D.Names.size() > It->getSecond().size())
- return false;
- if (llvm::set_union(It->getSecond(), D.Names))
- return false;
- AllSetsAreEqual =
- AllSetsAreEqual && (D.Names.size() == It->getSecond().size());
- }
-
- return LHS.size() != RHS.size() || !AllSetsAreEqual;
-}
-
-static bool greaterCtxScore(const CompleteOMPContextSelectorData &LHS,
- const CompleteOMPContextSelectorData &RHS) {
- // Score is calculated as sum of all scores + 1.
- llvm::APSInt LHSScore(llvm::APInt(64, 1), /*isUnsigned=*/false);
- bool RHSIsSubsetOfLHS = isStrictSubset(RHS, LHS);
- if (RHSIsSubsetOfLHS) {
- LHSScore = llvm::APSInt::get(0);
- } else {
- for (const OMPContextSelectorData &Data : LHS) {
- if (Data.Score.getBitWidth() > LHSScore.getBitWidth()) {
- LHSScore = LHSScore.extend(Data.Score.getBitWidth()) + Data.Score;
- } else if (Data.Score.getBitWidth() < LHSScore.getBitWidth()) {
- LHSScore += Data.Score.extend(LHSScore.getBitWidth());
- } else {
- LHSScore += Data.Score;
- }
- }
- }
- llvm::APSInt RHSScore(llvm::APInt(64, 1), /*isUnsigned=*/false);
- if (!RHSIsSubsetOfLHS && isStrictSubset(LHS, RHS)) {
- RHSScore = llvm::APSInt::get(0);
- } else {
- for (const OMPContextSelectorData &Data : RHS) {
- if (Data.Score.getBitWidth() > RHSScore.getBitWidth()) {
- RHSScore = RHSScore.extend(Data.Score.getBitWidth()) + Data.Score;
- } else if (Data.Score.getBitWidth() < RHSScore.getBitWidth()) {
- RHSScore += Data.Score.extend(RHSScore.getBitWidth());
- } else {
- RHSScore += Data.Score;
- }
- }
- }
- return llvm::APSInt::compareValues(LHSScore, RHSScore) >= 0;
-}
-
/// Finds the variant function that matches current context with its context
/// selector.
static const FunctionDecl *getDeclareVariantFunction(CodeGenModule &CGM,
const FunctionDecl *FD) {
if (!FD->hasAttrs() || !FD->hasAttr<OMPDeclareVariantAttr>())
return FD;
- // Iterate through all DeclareVariant attributes and check context selectors.
- const OMPDeclareVariantAttr *TopMostAttr = nullptr;
- CompleteOMPContextSelectorData TopMostData;
+
+ SmallVector<Expr *, 8> VariantExprs;
+ SmallVector<VariantMatchInfo, 8> VMIs;
for (const auto *A : FD->specific_attrs<OMPDeclareVariantAttr>()) {
- CompleteOMPContextSelectorData Data =
- translateAttrToContextSelectorData(CGM.getContext(), A);
- if (!matchesContext(CGM, Data))
+ const OMPTraitInfo *TI = A->getTraitInfos();
+ if (!TI)
continue;
- // If the attribute matches the context, find the attribute with the highest
- // score.
- if (!TopMostAttr || !greaterCtxScore(TopMostData, Data)) {
- TopMostAttr = A;
- TopMostData.swap(Data);
- }
+ VMIs.push_back(VariantMatchInfo());
+ TI->getAsVariantMatchInfo(CGM.getContext(), VMIs.back());
+ VariantExprs.push_back(A->getVariantFuncRef());
}
- if (!TopMostAttr)
+
+ OMPContext Ctx(CGM.getLangOpts().OpenMPIsDevice, CGM.getTriple());
+ // FIXME: Keep the context in the OMPIRBuilder so we can add constructs as we
+ // build them.
+
+ int BestMatchIdx = getBestVariantMatchForContext(VMIs, Ctx);
+ if (BestMatchIdx < 0)
return FD;
+
return cast<FunctionDecl>(
- cast<DeclRefExpr>(TopMostAttr->getVariantFuncRef()->IgnoreParenImpCasts())
+ cast<DeclRefExpr>(VariantExprs[BestMatchIdx]->IgnoreParenImpCasts())
->getDecl());
}
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index fbabe92..e1bcbdb 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -19,6 +19,7 @@
#include "clang/Sema/Scope.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/UniqueVector.h"
+#include "llvm/Frontend/OpenMP/OMPContext.h"
using namespace clang;
using namespace llvm::omp;
@@ -810,10 +811,225 @@
LinModifiers, Steps, SourceRange(Loc, EndLoc));
}
+namespace {
+/// Constant used in the diagnostics to distinguish the levels in an OpenMP
+/// contexts: selector-set={selector(trait, ...), ...}, ....
+enum OMPContextLvl {
+ CONTEXT_SELECTOR_SET_LVL = 0,
+ CONTEXT_SELECTOR_LVL = 1,
+ CONTEXT_TRAIT_LVL = 2,
+};
+
+static StringRef stringLiteralParser(Parser &P) {
+ ExprResult Res = P.ParseStringLiteralExpression(true);
+ return Res.isUsable() ? Res.getAs<StringLiteral>()->getString() : "";
+}
+
+static StringRef getNameFromIdOrString(Parser &P, Token &Tok,
+ OMPContextLvl Lvl) {
+ if (Tok.is(tok::identifier)) {
+ llvm::SmallString<16> Buffer;
+ StringRef Name = P.getPreprocessor().getSpelling(Tok, Buffer);
+ (void)P.ConsumeToken();
+ return Name;
+ }
+
+ if (tok::isStringLiteral(Tok.getKind()))
+ return stringLiteralParser(P);
+
+ P.Diag(Tok.getLocation(),
+ diag::warn_omp_declare_variant_string_literal_or_identifier)
+ << Lvl;
+ return "";
+}
+
+static bool checkForDuplicates(Parser &P, StringRef Name,
+ SourceLocation NameLoc,
+ llvm::StringMap<SourceLocation> &Seen,
+ OMPContextLvl Lvl) {
+ auto Res = Seen.try_emplace(Name, NameLoc);
+ if (Res.second)
+ return false;
+
+ // Each trait-set-selector-name, trait-selector-name and trait-name can
+ // only be specified once.
+ P.Diag(NameLoc, diag::warn_omp_declare_variant_ctx_mutiple_use)
+ << Lvl << Name;
+ P.Diag(Res.first->getValue(), diag::note_omp_declare_variant_ctx_used_here)
+ << Lvl << Name;
+ return true;
+}
+} // namespace
+
+void Parser::parseOMPTraitPropertyKind(
+ OMPTraitInfo::OMPTraitProperty &TIProperty, llvm::omp::TraitSet Set,
+ llvm::omp::TraitSelector Selector, llvm::StringMap<SourceLocation> &Seen) {
+ TIProperty.Kind = TraitProperty::invalid;
+
+ SourceLocation NameLoc = Tok.getLocation();
+ StringRef Name =
+ getNameFromIdOrString(*this, Tok, CONTEXT_TRAIT_LVL);
+ if (Name.empty()) {
+ Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_options)
+ << CONTEXT_TRAIT_LVL << listOpenMPContextTraitProperties(Set, Selector);
+ return;
+ }
+
+ TIProperty.Kind = getOpenMPContextTraitPropertyKind(Set, Name);
+ if (TIProperty.Kind != TraitProperty::invalid) {
+ if (checkForDuplicates(*this, Name, NameLoc, Seen, CONTEXT_TRAIT_LVL))
+ TIProperty.Kind = TraitProperty::invalid;
+ return;
+ }
+
+ // It follows diagnosis and helping notes.
+ // FIXME: We should move the diagnosis string generation into libFrontend.
+ Diag(NameLoc, diag::warn_omp_declare_variant_ctx_not_a_property)
+ << Name << getOpenMPContextTraitSelectorName(Selector)
+ << getOpenMPContextTraitSetName(Set);
+
+ TraitSet SetForName = getOpenMPContextTraitSetKind(Name);
+ if (SetForName != TraitSet::invalid) {
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a)
+ << Name << CONTEXT_SELECTOR_SET_LVL << CONTEXT_TRAIT_LVL;
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_try)
+ << Name << "<selector-name>"
+ << "(<property-name>)";
+ return;
+ }
+ TraitSelector SelectorForName = getOpenMPContextTraitSelectorKind(Name);
+ if (SelectorForName != TraitSelector::invalid) {
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a)
+ << Name << CONTEXT_SELECTOR_LVL << CONTEXT_TRAIT_LVL;
+ bool AllowsTraitScore = false;
+ bool RequiresProperty = false;
+ isValidTraitSelectorForTraitSet(
+ SelectorForName, getOpenMPContextTraitSetForSelector(SelectorForName),
+ AllowsTraitScore, RequiresProperty);
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_try)
+ << getOpenMPContextTraitSetName(
+ getOpenMPContextTraitSetForSelector(SelectorForName))
+ << Name << (RequiresProperty ? "(<property-name>)" : "");
+ return;
+ }
+ for (const auto &PotentialSet :
+ {TraitSet::construct, TraitSet::user, TraitSet::implementation,
+ TraitSet::device}) {
+ TraitProperty PropertyForName =
+ getOpenMPContextTraitPropertyKind(PotentialSet, Name);
+ if (PropertyForName == TraitProperty::invalid)
+ continue;
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_try)
+ << getOpenMPContextTraitSetName(
+ getOpenMPContextTraitSetForProperty(PropertyForName))
+ << getOpenMPContextTraitSelectorName(
+ getOpenMPContextTraitSelectorForProperty(PropertyForName))
+ << ("(" + Name + ")").str();
+ return;
+ }
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_options)
+ << CONTEXT_TRAIT_LVL << listOpenMPContextTraitProperties(Set, Selector);
+}
+
+void Parser::parseOMPContextProperty(OMPTraitInfo::OMPTraitSelector &TISelector,
+ llvm::omp::TraitSet Set,
+ llvm::StringMap<SourceLocation> &Seen) {
+ assert(TISelector.Kind != TraitSelector::user_condition &&
+ "User conditions are special properties not handled here!");
+
+ SourceLocation PropertyLoc = Tok.getLocation();
+ OMPTraitInfo::OMPTraitProperty TIProperty;
+ parseOMPTraitPropertyKind(TIProperty, Set, TISelector.Kind, Seen);
+
+ // If we have an invalid property here we already issued a warning.
+ if (TIProperty.Kind == TraitProperty::invalid) {
+ if (PropertyLoc != Tok.getLocation())
+ Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_continue_here)
+ << CONTEXT_TRAIT_LVL;
+ return;
+ }
+
+ if (isValidTraitPropertyForTraitSetAndSelector(TIProperty.Kind,
+ TISelector.Kind, Set)) {
+ // If we make it here the property, selector, set, score, condition, ... are
+ // all valid (or have been corrected). Thus we can record the property.
+ TISelector.Properties.push_back(TIProperty);
+ return;
+ }
+
+ Diag(PropertyLoc, diag::warn_omp_ctx_incompatible_property_for_selector)
+ << getOpenMPContextTraitPropertyName(TIProperty.Kind)
+ << getOpenMPContextTraitSelectorName(TISelector.Kind)
+ << getOpenMPContextTraitSetName(Set);
+ Diag(PropertyLoc, diag::note_omp_ctx_compatible_set_and_selector_for_property)
+ << getOpenMPContextTraitPropertyName(TIProperty.Kind)
+ << getOpenMPContextTraitSelectorName(
+ getOpenMPContextTraitSelectorForProperty(TIProperty.Kind))
+ << getOpenMPContextTraitSetName(
+ getOpenMPContextTraitSetForProperty(TIProperty.Kind));
+ Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_continue_here)
+ << CONTEXT_TRAIT_LVL;
+}
+
+void Parser::parseOMPTraitSelectorKind(
+ OMPTraitInfo::OMPTraitSelector &TISelector, llvm::omp::TraitSet Set,
+ llvm::StringMap<SourceLocation> &Seen) {
+ TISelector.Kind = TraitSelector::invalid;
+
+ SourceLocation NameLoc = Tok.getLocation();
+ StringRef Name = getNameFromIdOrString(*this, Tok, CONTEXT_SELECTOR_LVL
+ );
+ if (Name.empty()) {
+ Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_options)
+ << CONTEXT_SELECTOR_LVL << listOpenMPContextTraitSelectors(Set);
+ return;
+ }
+
+ TISelector.Kind = getOpenMPContextTraitSelectorKind(Name);
+ if (TISelector.Kind != TraitSelector::invalid) {
+ if (checkForDuplicates(*this, Name, NameLoc, Seen, CONTEXT_SELECTOR_LVL))
+ TISelector.Kind = TraitSelector::invalid;
+ return;
+ }
+
+ // It follows diagnosis and helping notes.
+ Diag(NameLoc, diag::warn_omp_declare_variant_ctx_not_a_selector)
+ << Name << getOpenMPContextTraitSetName(Set);
+
+ TraitSet SetForName = getOpenMPContextTraitSetKind(Name);
+ if (SetForName != TraitSet::invalid) {
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a)
+ << Name << CONTEXT_SELECTOR_SET_LVL << CONTEXT_SELECTOR_LVL;
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_try)
+ << Name << "<selector-name>"
+ << "<property-name>";
+ return;
+ }
+ for (const auto &PotentialSet :
+ {TraitSet::construct, TraitSet::user, TraitSet::implementation,
+ TraitSet::device}) {
+ TraitProperty PropertyForName =
+ getOpenMPContextTraitPropertyKind(PotentialSet, Name);
+ if (PropertyForName == TraitProperty::invalid)
+ continue;
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a)
+ << Name << CONTEXT_TRAIT_LVL << CONTEXT_SELECTOR_LVL;
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_try)
+ << getOpenMPContextTraitSetName(
+ getOpenMPContextTraitSetForProperty(PropertyForName))
+ << getOpenMPContextTraitSelectorName(
+ getOpenMPContextTraitSelectorForProperty(PropertyForName))
+ << ("(" + Name + ")").str();
+ return;
+ }
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_options)
+ << CONTEXT_SELECTOR_LVL << listOpenMPContextTraitSelectors(Set);
+}
+
/// Parse optional 'score' '(' <expr> ')' ':'.
static ExprResult parseContextScore(Parser &P) {
ExprResult ScoreExpr;
- Sema::OMPCtxStringType Buffer;
+ llvm::SmallString<16> Buffer;
StringRef SelectorName =
P.getPreprocessor().getSpelling(P.getCurToken(), Buffer);
if (!SelectorName.equals("score"))
@@ -825,246 +1041,266 @@
if (P.getCurToken().is(tok::colon))
(void)P.ConsumeAnyToken();
else
- P.Diag(P.getCurToken(), diag::warn_pragma_expected_colon)
- << "context selector score clause";
+ P.Diag(P.getCurToken(), diag::warn_omp_declare_variant_expected)
+ << "':'"
+ << "score expression";
return ScoreExpr;
}
-/// Parse context selector for 'implementation' selector set:
-/// 'vendor' '(' [ 'score' '(' <score _expr> ')' ':' ] <vendor> { ',' <vendor> }
-/// ')'
-static void
-parseImplementationSelector(Parser &P, SourceLocation Loc,
- llvm::StringMap<SourceLocation> &UsedCtx,
- SmallVectorImpl<Sema::OMPCtxSelectorData> &Data) {
- const Token &Tok = P.getCurToken();
- // Parse inner context selector set name, if any.
- if (!Tok.is(tok::identifier)) {
- P.Diag(Tok.getLocation(), diag::warn_omp_declare_variant_cs_name_expected)
- << "implementation";
- // Skip until either '}', ')', or end of directive.
- while (!P.SkipUntil(tok::r_brace, tok::r_paren,
- tok::annot_pragma_openmp_end, Parser::StopBeforeMatch))
- ;
- return;
- }
- Sema::OMPCtxStringType Buffer;
- StringRef CtxSelectorName = P.getPreprocessor().getSpelling(Tok, Buffer);
- auto Res = UsedCtx.try_emplace(CtxSelectorName, Tok.getLocation());
- if (!Res.second) {
- // OpenMP 5.0, 2.3.2 Context Selectors, Restrictions.
- // Each trait-selector-name can only be specified once.
- P.Diag(Tok.getLocation(), diag::err_omp_declare_variant_ctx_mutiple_use)
- << CtxSelectorName << "implementation";
- P.Diag(Res.first->getValue(), diag::note_omp_declare_variant_ctx_used_here)
- << CtxSelectorName;
- }
- OpenMPContextSelectorKind CSKind = getOpenMPContextSelector(CtxSelectorName);
- (void)P.ConsumeToken();
- switch (CSKind) {
- case OMP_CTX_vendor: {
- // Parse '('.
- BalancedDelimiterTracker T(P, tok::l_paren, tok::annot_pragma_openmp_end);
- (void)T.expectAndConsume(diag::err_expected_lparen_after,
- CtxSelectorName.data());
- ExprResult Score = parseContextScore(P);
- llvm::UniqueVector<Sema::OMPCtxStringType> Vendors;
- do {
- // Parse <vendor>.
- StringRef VendorName;
- if (Tok.is(tok::identifier)) {
- Buffer.clear();
- VendorName = P.getPreprocessor().getSpelling(P.getCurToken(), Buffer);
- (void)P.ConsumeToken();
- if (!VendorName.empty())
- Vendors.insert(VendorName);
- } else {
- P.Diag(Tok.getLocation(), diag::err_omp_declare_variant_item_expected)
- << "vendor identifier"
- << "vendor"
- << "implementation";
- }
- if (!P.TryConsumeToken(tok::comma) && Tok.isNot(tok::r_paren)) {
- P.Diag(Tok, diag::err_expected_punc)
- << (VendorName.empty() ? "vendor name" : VendorName);
- }
- } while (Tok.is(tok::identifier));
- // Parse ')'.
- (void)T.consumeClose();
- if (!Vendors.empty())
- Data.emplace_back(OMP_CTX_SET_implementation, CSKind, Score, Vendors);
- break;
- }
- case OMP_CTX_kind:
- case OMP_CTX_unknown:
- P.Diag(Tok.getLocation(), diag::warn_omp_declare_variant_cs_name_expected)
- << "implementation";
- // Skip until either '}', ')', or end of directive.
- while (!P.SkipUntil(tok::r_brace, tok::r_paren,
- tok::annot_pragma_openmp_end, Parser::StopBeforeMatch))
- ;
- return;
- }
-}
+/// Parses an OpenMP context selector.
+///
+/// <trait-selector-name> ['('[<trait-score>] <trait-property> [, <t-p>]* ')']
+void Parser::parseOMPContextSelector(
+ OMPTraitInfo::OMPTraitSelector &TISelector, llvm::omp::TraitSet Set,
+ llvm::StringMap<SourceLocation> &SeenSelectors) {
+ unsigned short OuterPC = ParenCount;
-/// Parse context selector for 'device' selector set:
-/// 'kind' '(' <kind> { ',' <kind> } ')'
-static void
-parseDeviceSelector(Parser &P, SourceLocation Loc,
- llvm::StringMap<SourceLocation> &UsedCtx,
- SmallVectorImpl<Sema::OMPCtxSelectorData> &Data) {
- const Token &Tok = P.getCurToken();
- // Parse inner context selector set name, if any.
- if (!Tok.is(tok::identifier)) {
- P.Diag(Tok.getLocation(), diag::warn_omp_declare_variant_cs_name_expected)
- << "device";
- // Skip until either '}', ')', or end of directive.
- while (!P.SkipUntil(tok::r_brace, tok::r_paren,
- tok::annot_pragma_openmp_end, Parser::StopBeforeMatch))
- ;
- return;
- }
- Sema::OMPCtxStringType Buffer;
- StringRef CtxSelectorName = P.getPreprocessor().getSpelling(Tok, Buffer);
- auto Res = UsedCtx.try_emplace(CtxSelectorName, Tok.getLocation());
- if (!Res.second) {
- // OpenMP 5.0, 2.3.2 Context Selectors, Restrictions.
- // Each trait-selector-name can only be specified once.
- P.Diag(Tok.getLocation(), diag::err_omp_declare_variant_ctx_mutiple_use)
- << CtxSelectorName << "device";
- P.Diag(Res.first->getValue(), diag::note_omp_declare_variant_ctx_used_here)
- << CtxSelectorName;
- }
- OpenMPContextSelectorKind CSKind = getOpenMPContextSelector(CtxSelectorName);
- (void)P.ConsumeToken();
- switch (CSKind) {
- case OMP_CTX_kind: {
- // Parse '('.
- BalancedDelimiterTracker T(P, tok::l_paren, tok::annot_pragma_openmp_end);
- (void)T.expectAndConsume(diag::err_expected_lparen_after,
- CtxSelectorName.data());
- llvm::UniqueVector<Sema::OMPCtxStringType> Kinds;
- do {
- // Parse <kind>.
- StringRef KindName;
- if (Tok.is(tok::identifier)) {
- Buffer.clear();
- KindName = P.getPreprocessor().getSpelling(P.getCurToken(), Buffer);
- SourceLocation SLoc = P.getCurToken().getLocation();
- (void)P.ConsumeToken();
- if (llvm::StringSwitch<bool>(KindName)
- .Case("host", false)
- .Case("nohost", false)
- .Case("cpu", false)
- .Case("gpu", false)
- .Case("fpga", false)
- .Default(true)) {
- P.Diag(SLoc, diag::err_omp_wrong_device_kind_trait) << KindName;
- } else {
- Kinds.insert(KindName);
- }
- } else {
- P.Diag(Tok.getLocation(), diag::err_omp_declare_variant_item_expected)
- << "'host', 'nohost', 'cpu', 'gpu', or 'fpga'"
- << "kind"
- << "device";
+ // If anything went wrong we issue an error or warning and then skip the rest
+ // of the selector. However, commas are ambiguous so we look for the nesting
+ // of parentheses here as well.
+ auto FinishSelector = [OuterPC, this]() -> void {
+ bool Done = false;
+ while (!Done) {
+ while (!SkipUntil({tok::r_brace, tok::r_paren, tok::comma,
+ tok::annot_pragma_openmp_end},
+ StopBeforeMatch))
+ ;
+ if (Tok.is(tok::r_paren) && OuterPC > ParenCount)
+ (void)ConsumeParen();
+ if (OuterPC <= ParenCount) {
+ Done = true;
+ break;
}
- if (!P.TryConsumeToken(tok::comma) && Tok.isNot(tok::r_paren)) {
- P.Diag(Tok, diag::err_expected_punc)
- << (KindName.empty() ? "kind of device" : KindName);
+ if (!Tok.is(tok::comma) && !Tok.is(tok::r_paren)) {
+ Done = true;
+ break;
}
- } while (Tok.is(tok::identifier));
- // Parse ')'.
- (void)T.consumeClose();
- if (!Kinds.empty())
- Data.emplace_back(OMP_CTX_SET_device, CSKind, ExprResult(), Kinds);
- break;
- }
- case OMP_CTX_vendor:
- case OMP_CTX_unknown:
- P.Diag(Tok.getLocation(), diag::warn_omp_declare_variant_cs_name_expected)
- << "device";
- // Skip until either '}', ')', or end of directive.
- while (!P.SkipUntil(tok::r_brace, tok::r_paren,
- tok::annot_pragma_openmp_end, Parser::StopBeforeMatch))
- ;
- return;
- }
-}
+ (void)ConsumeAnyToken();
+ }
+ Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_continue_here)
+ << CONTEXT_SELECTOR_LVL;
+ };
-/// Parses clauses for 'declare variant' directive.
-/// clause:
-/// <selector_set_name> '=' '{' <context_selectors> '}'
-/// [ ',' <selector_set_name> '=' '{' <context_selectors> '}' ]
-bool Parser::parseOpenMPContextSelectors(
- SourceLocation Loc, SmallVectorImpl<Sema::OMPCtxSelectorData> &Data) {
- llvm::StringMap<SourceLocation> UsedCtxSets;
+ SourceLocation SelectorLoc = Tok.getLocation();
+ parseOMPTraitSelectorKind(TISelector, Set, SeenSelectors);
+ if (TISelector.Kind == TraitSelector::invalid)
+ return FinishSelector();
+
+ bool AllowsTraitScore = false;
+ bool RequiresProperty = false;
+ if (!isValidTraitSelectorForTraitSet(TISelector.Kind, Set, AllowsTraitScore,
+ RequiresProperty)) {
+ Diag(SelectorLoc, diag::warn_omp_ctx_incompatible_selector_for_set)
+ << getOpenMPContextTraitSelectorName(TISelector.Kind)
+ << getOpenMPContextTraitSetName(Set);
+ Diag(SelectorLoc, diag::note_omp_ctx_compatible_set_for_selector)
+ << getOpenMPContextTraitSelectorName(TISelector.Kind)
+ << getOpenMPContextTraitSetName(
+ getOpenMPContextTraitSetForSelector(TISelector.Kind))
+ << RequiresProperty;
+ return FinishSelector();
+ }
+
+ if (!RequiresProperty) {
+ TISelector.Properties.push_back(
+ {getOpenMPContextTraitPropertyForSelector(TISelector.Kind)});
+ return;
+ }
+
+ if (!Tok.is(tok::l_paren)) {
+ Diag(SelectorLoc, diag::warn_omp_ctx_selector_without_properties)
+ << getOpenMPContextTraitSelectorName(TISelector.Kind)
+ << getOpenMPContextTraitSetName(Set);
+ return FinishSelector();
+ }
+
+ if (TISelector.Kind == TraitSelector::user_condition) {
+ SourceLocation RLoc;
+ ExprResult Condition = ParseOpenMPParensExpr("user condition", RLoc);
+ if (!Condition.isUsable())
+ return FinishSelector();
+ TISelector.ScoreOrCondition = Condition.get();
+ TISelector.Properties.push_back({TraitProperty::user_condition_unknown});
+ return;
+ }
+
+ BalancedDelimiterTracker BDT(*this, tok::l_paren,
+ tok::annot_pragma_openmp_end);
+ // Parse '('.
+ (void)BDT.consumeOpen();
+
+ ExprResult Score = parseContextScore(*this);
+
+ if (!AllowsTraitScore && Score.isUsable()) {
+ Diag(Score.get()->getBeginLoc(),
+ diag::warn_omp_ctx_incompatible_score_for_property)
+ << getOpenMPContextTraitSelectorName(TISelector.Kind)
+ << getOpenMPContextTraitSetName(Set) << Score.get();
+ Score = ExprResult();
+ }
+
+ if (Score.isUsable())
+ TISelector.ScoreOrCondition = Score.get();
+
+ llvm::StringMap<SourceLocation> SeenProperties;
do {
- // Parse inner context selector set name.
- if (!Tok.is(tok::identifier)) {
- Diag(Tok.getLocation(), diag::err_omp_declare_variant_no_ctx_selector)
- << getOpenMPClauseName(OMPC_match);
- return true;
+ parseOMPContextProperty(TISelector, Set, SeenProperties);
+ } while (TryConsumeToken(tok::comma));
+
+ // Parse ')'.
+ BDT.consumeClose();
+}
+
+void Parser::parseOMPTraitSetKind(OMPTraitInfo::OMPTraitSet &TISet,
+ llvm::StringMap<SourceLocation> &Seen) {
+ TISet.Kind = TraitSet::invalid;
+
+ SourceLocation NameLoc = Tok.getLocation();
+ StringRef Name = getNameFromIdOrString(*this, Tok, CONTEXT_SELECTOR_SET_LVL
+ );
+ if (Name.empty()) {
+ Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_options)
+ << CONTEXT_SELECTOR_SET_LVL << listOpenMPContextTraitSets();
+ return;
+ }
+
+ TISet.Kind = getOpenMPContextTraitSetKind(Name);
+ if (TISet.Kind != TraitSet::invalid) {
+ if (checkForDuplicates(*this, Name, NameLoc, Seen,
+ CONTEXT_SELECTOR_SET_LVL))
+ TISet.Kind = TraitSet::invalid;
+ return;
+ }
+
+ // It follows diagnosis and helping notes.
+ Diag(NameLoc, diag::warn_omp_declare_variant_ctx_not_a_set) << Name;
+
+ TraitSelector SelectorForName = getOpenMPContextTraitSelectorKind(Name);
+ if (SelectorForName != TraitSelector::invalid) {
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a)
+ << Name << CONTEXT_SELECTOR_LVL << CONTEXT_SELECTOR_SET_LVL;
+ bool AllowsTraitScore = false;
+ bool RequiresProperty = false;
+ isValidTraitSelectorForTraitSet(
+ SelectorForName, getOpenMPContextTraitSetForSelector(SelectorForName),
+ AllowsTraitScore, RequiresProperty);
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_try)
+ << getOpenMPContextTraitSetName(
+ getOpenMPContextTraitSetForSelector(SelectorForName))
+ << Name << (RequiresProperty ? "(<property-name>)" : "");
+ return;
+ }
+ for (const auto &PotentialSet :
+ {TraitSet::construct, TraitSet::user, TraitSet::implementation,
+ TraitSet::device}) {
+ TraitProperty PropertyForName =
+ getOpenMPContextTraitPropertyKind(PotentialSet, Name);
+ if (PropertyForName == TraitProperty::invalid)
+ continue;
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a)
+ << Name << CONTEXT_TRAIT_LVL << CONTEXT_SELECTOR_SET_LVL;
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_try)
+ << getOpenMPContextTraitSetName(
+ getOpenMPContextTraitSetForProperty(PropertyForName))
+ << getOpenMPContextTraitSelectorName(
+ getOpenMPContextTraitSelectorForProperty(PropertyForName))
+ << ("(" + Name + ")").str();
+ return;
+ }
+ Diag(NameLoc, diag::note_omp_declare_variant_ctx_options)
+ << CONTEXT_SELECTOR_SET_LVL << listOpenMPContextTraitSets();
+}
+
+/// Parses an OpenMP context selector set.
+///
+/// <trait-set-selector-name> '=' '{' <trait-selector> [, <trait-selector>]* '}'
+void Parser::parseOMPContextSelectorSet(
+ OMPTraitInfo::OMPTraitSet &TISet,
+ llvm::StringMap<SourceLocation> &SeenSets) {
+ auto OuterBC = BraceCount;
+
+ // If anything went wrong we issue an error or warning and then skip the rest
+ // of the set. However, commas are ambiguous so we look for the nesting
+ // of braces here as well.
+ auto FinishSelectorSet = [this, OuterBC]() -> void {
+ bool Done = false;
+ while (!Done) {
+ while (!SkipUntil({tok::comma, tok::r_brace, tok::r_paren,
+ tok::annot_pragma_openmp_end},
+ StopBeforeMatch))
+ ;
+ if (Tok.is(tok::r_brace) && OuterBC > BraceCount)
+ (void)ConsumeBrace();
+ if (OuterBC <= BraceCount) {
+ Done = true;
+ break;
+ }
+ if (!Tok.is(tok::comma) && !Tok.is(tok::r_brace)) {
+ Done = true;
+ break;
+ }
+ (void)ConsumeAnyToken();
}
- Sema::OMPCtxStringType Buffer;
- StringRef CtxSelectorSetName = PP.getSpelling(Tok, Buffer);
- auto Res = UsedCtxSets.try_emplace(CtxSelectorSetName, Tok.getLocation());
- if (!Res.second) {
- // OpenMP 5.0, 2.3.2 Context Selectors, Restrictions.
- // Each trait-set-selector-name can only be specified once.
- Diag(Tok.getLocation(), diag::err_omp_declare_variant_ctx_set_mutiple_use)
- << CtxSelectorSetName;
- Diag(Res.first->getValue(),
- diag::note_omp_declare_variant_ctx_set_used_here)
- << CtxSelectorSetName;
- }
- // Parse '='.
- (void)ConsumeToken();
- if (Tok.isNot(tok::equal)) {
- Diag(Tok.getLocation(), diag::err_omp_declare_variant_equal_expected)
- << CtxSelectorSetName;
- return true;
- }
- (void)ConsumeToken();
- // TBD: add parsing of known context selectors.
- // Unknown selector - just ignore it completely.
- {
- // Parse '{'.
- BalancedDelimiterTracker TBr(*this, tok::l_brace,
- tok::annot_pragma_openmp_end);
- if (TBr.expectAndConsume(diag::err_expected_lbrace_after, "="))
- return true;
- OpenMPContextSelectorSetKind CSSKind =
- getOpenMPContextSelectorSet(CtxSelectorSetName);
- llvm::StringMap<SourceLocation> UsedCtx;
- do {
- switch (CSSKind) {
- case OMP_CTX_SET_implementation:
- parseImplementationSelector(*this, Loc, UsedCtx, Data);
- break;
- case OMP_CTX_SET_device:
- parseDeviceSelector(*this, Loc, UsedCtx, Data);
- break;
- case OMP_CTX_SET_unknown:
- // Skip until either '}', ')', or end of directive.
- while (!SkipUntil(tok::r_brace, tok::r_paren,
- tok::annot_pragma_openmp_end, StopBeforeMatch))
- ;
- break;
- }
- const Token PrevTok = Tok;
- if (!TryConsumeToken(tok::comma) && Tok.isNot(tok::r_brace))
- Diag(Tok, diag::err_omp_expected_comma_brace)
- << (PrevTok.isAnnotation() ? "context selector trait"
- : PP.getSpelling(PrevTok));
- } while (Tok.is(tok::identifier));
- // Parse '}'.
- (void)TBr.consumeClose();
- }
- // Consume ','
- if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end))
- (void)ExpectAndConsume(tok::comma);
- } while (Tok.isAnyIdentifier());
+ Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_continue_here)
+ << CONTEXT_SELECTOR_SET_LVL;
+ };
+
+ parseOMPTraitSetKind(TISet, SeenSets);
+ if (TISet.Kind == TraitSet::invalid)
+ return FinishSelectorSet();
+
+ // Parse '='.
+ if (!TryConsumeToken(tok::equal))
+ Diag(Tok.getLocation(), diag::warn_omp_declare_variant_expected)
+ << "="
+ << ("context set name \"" + getOpenMPContextTraitSetName(TISet.Kind) +
+ "\"")
+ .str();
+
+ // Parse '{'.
+ if (Tok.is(tok::l_brace)) {
+ (void)ConsumeBrace();
+ } else {
+ Diag(Tok.getLocation(), diag::warn_omp_declare_variant_expected)
+ << "{"
+ << ("'=' that follows the context set name \"" +
+ getOpenMPContextTraitSetName(TISet.Kind) + "\"")
+ .str();
+ }
+
+ llvm::StringMap<SourceLocation> SeenSelectors;
+ do {
+ OMPTraitInfo::OMPTraitSelector TISelector;
+ parseOMPContextSelector(TISelector, TISet.Kind, SeenSelectors);
+ if (TISelector.Kind != TraitSelector::invalid &&
+ !TISelector.Properties.empty())
+ TISet.Selectors.push_back(TISelector);
+ } while (TryConsumeToken(tok::comma));
+
+ // Parse '}'.
+ if (Tok.is(tok::r_brace)) {
+ (void)ConsumeBrace();
+ } else {
+ Diag(Tok.getLocation(), diag::warn_omp_declare_variant_expected)
+ << "}"
+ << ("context selectors for the context set \"" +
+ getOpenMPContextTraitSetName(TISet.Kind) + "\"")
+ .str();
+ }
+}
+
+/// Parse OpenMP context selectors:
+///
+/// <trait-set-selector> [, <trait-set-selector>]*
+bool Parser::parseOMPContextSelectors(SourceLocation Loc, OMPTraitInfo &TI) {
+ llvm::StringMap<SourceLocation> SeenSets;
+ do {
+ OMPTraitInfo::OMPTraitSet TISet;
+ parseOMPContextSelectorSet(TISet, SeenSets);
+ if (TISet.Kind != TraitSet::invalid && !TISet.Selectors.empty())
+ TI.Sets.push_back(TISet);
+ } while (TryConsumeToken(tok::comma));
+
return false;
}
@@ -1102,9 +1338,6 @@
(void)ConsumeAnnotationToken();
return;
}
- Optional<std::pair<FunctionDecl *, Expr *>> DeclVarData =
- Actions.checkOpenMPDeclareVariantFunction(
- Ptr, AssociatedFunction.get(), SourceRange(Loc, Tok.getLocation()));
// Parse 'match'.
OpenMPClauseKind CKind = Tok.isAnnotation()
@@ -1132,24 +1365,27 @@
}
// Parse inner context selectors.
- SmallVector<Sema::OMPCtxSelectorData, 4> Data;
- if (!parseOpenMPContextSelectors(Loc, Data)) {
- // Parse ')'.
- (void)T.consumeClose();
- // Need to check for extra tokens.
- if (Tok.isNot(tok::annot_pragma_openmp_end)) {
- Diag(Tok, diag::warn_omp_extra_tokens_at_eol)
- << getOpenMPDirectiveName(OMPD_declare_variant);
- }
- }
+ OMPTraitInfo *TI = new OMPTraitInfo();
+ parseOMPContextSelectors(Loc, *TI);
+
+ // Parse ')'
+ (void)T.consumeClose();
+
+ Optional<std::pair<FunctionDecl *, Expr *>> DeclVarData =
+ Actions.checkOpenMPDeclareVariantFunction(
+ Ptr, AssociatedFunction.get(), *TI,
+ SourceRange(Loc, Tok.getLocation()));
// Skip last tokens.
while (Tok.isNot(tok::annot_pragma_openmp_end))
ConsumeAnyToken();
- if (DeclVarData.hasValue())
+ if (DeclVarData.hasValue() && !TI->Sets.empty())
Actions.ActOnOpenMPDeclareVariantDirective(
- DeclVarData.getValue().first, DeclVarData.getValue().second,
- SourceRange(Loc, Tok.getLocation()), Data);
+ DeclVarData.getValue().first, DeclVarData.getValue().second, TI,
+ SourceRange(Loc, Tok.getLocation()));
+ else
+ delete TI;
+
// Skip the last annot_pragma_openmp_end.
(void)ConsumeAnnotationToken();
}
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 5c79eb2..9b3f5d8 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -5369,7 +5369,8 @@
Optional<std::pair<FunctionDecl *, Expr *>>
Sema::checkOpenMPDeclareVariantFunction(Sema::DeclGroupPtrTy DG,
- Expr *VariantRef, SourceRange SR) {
+ Expr *VariantRef, OMPTraitInfo &TI,
+ SourceRange SR) {
if (!DG || DG.get().isNull())
return None;
@@ -5422,12 +5423,41 @@
return None;
}
+ auto ShouldDelayChecks = [](Expr *&E, bool) {
+ return E && (E->isTypeDependent() || E->isValueDependent() ||
+ E->containsUnexpandedParameterPack() ||
+ E->isInstantiationDependent());
+ };
// Do not check templates, wait until instantiation.
- if (VariantRef->isTypeDependent() || VariantRef->isValueDependent() ||
- VariantRef->containsUnexpandedParameterPack() ||
- VariantRef->isInstantiationDependent() || FD->isDependentContext())
+ if (FD->isDependentContext() || ShouldDelayChecks(VariantRef, false) ||
+ TI.anyScoreOrCondition(ShouldDelayChecks))
return std::make_pair(FD, VariantRef);
+ // Deal with non-constant score and user condition expressions.
+ auto HandleNonConstantScoresAndConditions = [this](Expr *&E,
+ bool IsScore) -> bool {
+ llvm::APSInt Result;
+ if (!E || E->isIntegerConstantExpr(Result, Context))
+ return false;
+
+ if (IsScore) {
+ // We warn on non-constant scores and pretend they were not present.
+ Diag(E->getExprLoc(), diag::warn_omp_declare_variant_score_not_constant)
+ << E;
+ E = nullptr;
+ } else {
+ // We could replace a non-constant user condition with "false" but we
+ // will soon need to handle these anyway for the dynamic version of
+ // OpenMP context selectors.
+ Diag(E->getExprLoc(),
+ diag::err_omp_declare_variant_user_condition_not_constant)
+ << E;
+ }
+ return true;
+ };
+ if (TI.anyScoreOrCondition(HandleNonConstantScoresAndConditions))
+ return None;
+
// Convert VariantRef expression to the type of the original function to
// resolve possible conflicts.
ExprResult VariantRefCast;
@@ -5600,75 +5630,13 @@
return std::make_pair(FD, cast<Expr>(DRE));
}
-void Sema::ActOnOpenMPDeclareVariantDirective(
- FunctionDecl *FD, Expr *VariantRef, SourceRange SR,
- ArrayRef<OMPCtxSelectorData> Data) {
- if (Data.empty())
- return;
- SmallVector<Expr *, 4> CtxScores;
- SmallVector<unsigned, 4> CtxSets;
- SmallVector<unsigned, 4> Ctxs;
- SmallVector<StringRef, 4> ImplVendors, DeviceKinds;
- bool IsError = false;
- for (const OMPCtxSelectorData &D : Data) {
- OpenMPContextSelectorSetKind CtxSet = D.CtxSet;
- OpenMPContextSelectorKind Ctx = D.Ctx;
- if (CtxSet == OMP_CTX_SET_unknown || Ctx == OMP_CTX_unknown)
- return;
- Expr *Score = nullptr;
- if (D.Score.isUsable()) {
- Score = D.Score.get();
- if (!Score->isTypeDependent() && !Score->isValueDependent() &&
- !Score->isInstantiationDependent() &&
- !Score->containsUnexpandedParameterPack()) {
- Score =
- PerformOpenMPImplicitIntegerConversion(Score->getExprLoc(), Score)
- .get();
- if (Score)
- Score = VerifyIntegerConstantExpression(Score).get();
- }
- } else {
- // OpenMP 5.0, 2.3.3 Matching and Scoring Context Selectors.
- // The kind, arch, and isa selectors are given the values 2^l, 2^(l+1) and
- // 2^(l+2), respectively, where l is the number of traits in the construct
- // set.
- // TODO: implement correct logic for isa and arch traits.
- // TODO: take the construct context set into account when it is
- // implemented.
- int L = 0; // Currently set the number of traits in construct set to 0,
- // since the construct trait set in not supported yet.
- if (CtxSet == OMP_CTX_SET_device && Ctx == OMP_CTX_kind)
- Score = ActOnIntegerConstant(SourceLocation(), std::pow(2, L)).get();
- else
- Score = ActOnIntegerConstant(SourceLocation(), 0).get();
- }
- switch (Ctx) {
- case OMP_CTX_vendor:
- assert(CtxSet == OMP_CTX_SET_implementation &&
- "Expected implementation context selector set.");
- ImplVendors.append(D.Names.begin(), D.Names.end());
- break;
- case OMP_CTX_kind:
- assert(CtxSet == OMP_CTX_SET_device &&
- "Expected device context selector set.");
- DeviceKinds.append(D.Names.begin(), D.Names.end());
- break;
- case OMP_CTX_unknown:
- llvm_unreachable("Unknown context selector kind.");
- }
- IsError = IsError || !Score;
- CtxSets.push_back(CtxSet);
- Ctxs.push_back(Ctx);
- CtxScores.push_back(Score);
- }
- if (!IsError) {
- auto *NewAttr = OMPDeclareVariantAttr::CreateImplicit(
- Context, VariantRef, CtxScores.begin(), CtxScores.size(),
- CtxSets.begin(), CtxSets.size(), Ctxs.begin(), Ctxs.size(),
- ImplVendors.begin(), ImplVendors.size(), DeviceKinds.begin(),
- DeviceKinds.size(), SR);
- FD->addAttr(NewAttr);
- }
+void Sema::ActOnOpenMPDeclareVariantDirective(FunctionDecl *FD,
+ Expr *VariantRef,
+ OMPTraitInfo *TI,
+ SourceRange SR) {
+ auto *NewAttr =
+ OMPDeclareVariantAttr::CreateImplicit(Context, VariantRef, TI, SR);
+ FD->addAttr(NewAttr);
}
void Sema::markOpenMPDeclareVariantFuncsReferenced(SourceLocation Loc,
@@ -10481,7 +10449,6 @@
CS->getCapturedDecl()->setNothrow();
}
-
OMPLoopDirective::HelperExprs B;
// In presence of clause 'collapse' with number of loops, it will
// define the nested loops number.
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 37dace3..6571c82 100755
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -393,50 +393,43 @@
VariantFuncRef = Subst(E);
}
+ // Copy the template version of the OMPTraitInfo and run substitute on all
+ // score and condition expressiosn.
+ OMPTraitInfo *TI = new OMPTraitInfo();
+ *TI = *Attr.getTraitInfos();
+
+ // Try to substitute template parameters in score and condition expressions.
+ auto SubstScoreOrConditionExpr = [&S, Subst](Expr *&E, bool) {
+ if (E) {
+ EnterExpressionEvaluationContext Unevaluated(
+ S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+ ExprResult ER = Subst(E);
+ if (ER.isUsable())
+ E = ER.get();
+ else
+ return true;
+ }
+ return false;
+ };
+ if (TI->anyScoreOrCondition(SubstScoreOrConditionExpr)) {
+ delete TI;
+ return;
+ }
+
// Check function/variant ref.
Optional<std::pair<FunctionDecl *, Expr *>> DeclVarData =
- S.checkOpenMPDeclareVariantFunction(
- S.ConvertDeclToDeclGroup(New), VariantFuncRef.get(), Attr.getRange());
- if (!DeclVarData)
+ S.checkOpenMPDeclareVariantFunction(S.ConvertDeclToDeclGroup(New),
+ VariantFuncRef.get(), *TI,
+ Attr.getRange());
+
+ if (!DeclVarData) {
+ delete TI;
return;
- SmallVector<Sema::OMPCtxSelectorData, 4> Data;
- for (unsigned I = 0, E = Attr.scores_size(); I < E; ++I) {
- ExprResult Score;
- if (Expr *E = *std::next(Attr.scores_begin(), I))
- Score = Subst(E);
- // Instantiate the attribute.
- auto CtxSet = static_cast<OpenMPContextSelectorSetKind>(
- *std::next(Attr.ctxSelectorSets_begin(), I));
- auto Ctx = static_cast<OpenMPContextSelectorKind>(
- *std::next(Attr.ctxSelectors_begin(), I));
- switch (CtxSet) {
- case OMP_CTX_SET_implementation:
- switch (Ctx) {
- case OMP_CTX_vendor:
- Data.emplace_back(CtxSet, Ctx, Score, Attr.implVendors());
- break;
- case OMP_CTX_kind:
- case OMP_CTX_unknown:
- llvm_unreachable("Unexpected context selector kind.");
- }
- break;
- case OMP_CTX_SET_device:
- switch (Ctx) {
- case OMP_CTX_kind:
- Data.emplace_back(CtxSet, Ctx, Score, Attr.deviceKinds());
- break;
- case OMP_CTX_vendor:
- case OMP_CTX_unknown:
- llvm_unreachable("Unexpected context selector kind.");
- }
- break;
- case OMP_CTX_SET_unknown:
- llvm_unreachable("Unexpected context selector set kind.");
- }
}
+
S.ActOnOpenMPDeclareVariantDirective(DeclVarData.getValue().first,
- DeclVarData.getValue().second,
- Attr.getRange(), Data);
+ DeclVarData.getValue().second, TI,
+ Attr.getRange());
}
static void instantiateDependentAMDGPUFlatWorkGroupSizeAttr(
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index a1161d2..fbd59b9 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -12612,3 +12612,22 @@
C->setLParenLoc(Record.readSourceLocation());
C->setKindKwLoc(Record.readSourceLocation());
}
+
+OMPTraitInfo *ASTRecordReader::readOMPTraitInfo() {
+ OMPTraitInfo *TI = new OMPTraitInfo();
+ TI->Sets.resize(readUInt32());
+ for (auto &Set : TI->Sets) {
+ Set.Kind = readEnum<llvm::omp::TraitSet>();
+ Set.Selectors.resize(readUInt32());
+ for (auto &Selector : Set.Selectors) {
+ Selector.Kind = readEnum<llvm::omp::TraitSelector>();
+ Selector.ScoreOrCondition = nullptr;
+ if (readBool())
+ Selector.ScoreOrCondition = readExprRef();
+ Selector.Properties.resize(readUInt32());
+ for (auto &Property : Selector.Properties)
+ Property.Kind = readEnum<llvm::omp::TraitProperty>();
+ }
+ }
+ return TI;
+}
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 20a4f78..45c10be 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2756,6 +2756,8 @@
return Reader.readVersionTuple();
}
+ OMPTraitInfo *readOMPTraitInfo() { return Reader.readOMPTraitInfo(); }
+
template <typename T> T *GetLocalDeclAs(uint32_t LocalID) {
return Reader.GetLocalDeclAs<T>(LocalID);
}
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index f935a69..018a738 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -6578,3 +6578,19 @@
Record.AddSourceLocation(C->getKindKwLoc());
}
+void ASTRecordWriter::writeOMPTraitInfo(OMPTraitInfo *TI) {
+ writeUInt32(TI->Sets.size());
+ for (const auto &Set : TI->Sets) {
+ writeEnum(Set.Kind);
+ writeUInt32(Set.Selectors.size());
+ for (const auto &Selector : Set.Selectors) {
+ writeEnum(Selector.Kind);
+ writeBool(Selector.ScoreOrCondition);
+ if (Selector.ScoreOrCondition)
+ writeExprRef(Selector.ScoreOrCondition);
+ writeUInt32(Selector.Properties.size());
+ for (const auto &Property : Selector.Properties)
+ writeEnum(Property.Kind);
+ }
+ }
+}