[flang-commits] [flang] [flang][OpenMP][NFC] Hoist variant match-info construction into Semantics (PR #204387)
Abid Qadeer via flang-commits
flang-commits at lists.llvm.org
Thu Jun 18 07:23:28 PDT 2026
https://github.com/abidh updated https://github.com/llvm/llvm-project/pull/204387
>From 48c288b4596e04f0872363a506ad0d962b774c73 Mon Sep 17 00:00:00 2001
From: Abid Qadeer <haqadeer at amd.com>
Date: Fri, 12 Jun 2026 12:55:56 +0100
Subject: [PATCH 1/2] [flang][OpenMP][NFC] Hoist variant match-info
construction into Semantics
Replace the lowering-only makeVariantMatchInfo helper with a single shared
semantics::omp::MakeVariantMatchInfo in Semantics/openmp-utils. It builds the
VariantMatchInfo from a parsed context selector and returns the optional
non-constant user condition (as before) for the caller to lower as a runtime
condition. Update metadirective lowering to use it and drop the duplicated
Lower/OpenMP copy.
Selector features that variant selection cannot yet honour (target_device
selectors, and clause/extension trait properties) are not match-info concerns,
so they are kept out of MakeVariantMatchInfo. Detection lives in a separate,
pure helper FindUnsupportedSelectorFeature; the caller diagnoses the feature in
its own terms (metadirective lowering emits a TODO) before building the match
info. MakeVariantMatchInfo CHECKs the precondition. NFC for metadirective.
Co-authored-by: Cursor <cursoragent at cursor.com>
---
flang/include/flang/Semantics/openmp-utils.h | 40 +++++++
flang/lib/Lower/OpenMP/OpenMP.cpp | 32 ++++--
flang/lib/Lower/OpenMP/Utils.cpp | 112 +------------------
flang/lib/Lower/OpenMP/Utils.h | 19 +---
flang/lib/Semantics/openmp-utils.cpp | 97 ++++++++++++++++
5 files changed, 164 insertions(+), 136 deletions(-)
diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index e0358eafe487c..d2bfeca68bf84 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -163,6 +163,46 @@ std::optional<bool> GetLogicalArgument(
std::optional<bool> IsContiguous(
SemanticsContext &semaCtx, const parser::OmpObject &object);
+/// Non-constant user condition expression and source for runtime lowering.
+struct DynamicUserCondition {
+ const parser::ScalarExpr *expr;
+ parser::CharBlock source;
+};
+
+/// A context-selector feature that variant matching accepts syntactically but
+/// cannot yet honour during selection. Callers are expected to diagnose these
+/// (a lowering \c TODO or a semantic error) before calling
+/// \c MakeVariantMatchInfo, which asserts none are present.
+enum class UnsupportedSelectorFeature {
+ None,
+ /// A `target_device={...}` selector set.
+ TargetDevice,
+ /// A clause property (e.g. \c simdlen(8) in \c construct={simd(simdlen(8))})
+ /// or an extension property (e.g. \c foo(bar) in
+ /// \c implementation={my_trait(foo(bar))}).
+ ClauseOrExtensionProperty,
+};
+
+/// Scan a parsed context selector for the first feature that variant matching
+/// cannot yet honour (see \c UnsupportedSelectorFeature). Pure detection: emits
+/// no diagnostics and has no side effects on any match info.
+UnsupportedSelectorFeature FindUnsupportedSelectorFeature(
+ const parser::traits::OmpContextSelectorSpecification &ctxSel,
+ SemanticsContext &semaCtx);
+
+/// Populate \p vmi from a parsed context selector. Score modifiers are
+/// honoured (including on `condition(...)` selectors). Constant user
+/// conditions are folded into user_condition_true/false traits; a non-constant
+/// user condition is recorded as user_condition_unknown and the first such
+/// expression is returned for the caller to lower as a runtime condition.
+///
+/// The caller must first reject unsupported selector features (see
+/// \c FindUnsupportedSelectorFeature); this function asserts none are present.
+std::optional<DynamicUserCondition> MakeVariantMatchInfo(
+ llvm::omp::VariantMatchInfo &vmi,
+ const parser::traits::OmpContextSelectorSpecification &ctxSel,
+ SemanticsContext &semaCtx);
+
std::vector<SomeExpr> GetTopLevelDesignators(const SomeExpr &expr);
const SomeExpr *HasStorageOverlap(
const SomeExpr &base, llvm::ArrayRef<SomeExpr> exprs);
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index facca9867e4bb..b9c8071a13f77 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -4763,18 +4763,18 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
namespace {
struct MetadirectiveCandidate {
- MetadirectiveCandidate(
- const parser::OmpDirectiveSpecification *spec,
- llvm::omp::VariantMatchInfo vmi, bool isExplicit,
- std::optional<DynamicUserCondition> dynamicCond = std::nullopt,
- bool conditionShouldBeTrue = true)
+ MetadirectiveCandidate(const parser::OmpDirectiveSpecification *spec,
+ llvm::omp::VariantMatchInfo vmi, bool isExplicit,
+ std::optional<semantics::omp::DynamicUserCondition>
+ dynamicCond = std::nullopt,
+ bool conditionShouldBeTrue = true)
: spec(spec), vmi(vmi), isExplicit(isExplicit), dynamicCond(dynamicCond),
conditionShouldBeTrue(conditionShouldBeTrue) {}
const parser::OmpDirectiveSpecification *spec = nullptr;
llvm::omp::VariantMatchInfo vmi;
bool isExplicit = false;
- std::optional<DynamicUserCondition> dynamicCond;
+ std::optional<semantics::omp::DynamicUserCondition> dynamicCond;
bool conditionShouldBeTrue = true;
};
} // namespace
@@ -4832,9 +4832,25 @@ static void genMetadirective(lower::AbstractConverter &converter,
const auto &ctxSel = getContextSelector(*whenClause);
auto [spec, isExplicit] = getDirectiveVariant(*whenClause);
+ // METADIRECTIVE cannot yet honour some selector features that are
+ // otherwise accepted; reject them before building the match info.
+ switch (semantics::omp::FindUnsupportedSelectorFeature(ctxSel, semaCtx)) {
+ case semantics::omp::UnsupportedSelectorFeature::TargetDevice:
+ TODO(converter.genLocation(clause.source),
+ "target_device selector in METADIRECTIVE");
+ break;
+ case semantics::omp::UnsupportedSelectorFeature::
+ ClauseOrExtensionProperty:
+ TODO(converter.genLocation(clause.source),
+ "clause or extension trait matching in METADIRECTIVE");
+ break;
+ case semantics::omp::UnsupportedSelectorFeature::None:
+ break;
+ }
+
llvm::omp::VariantMatchInfo rawVMI;
- std::optional<DynamicUserCondition> dynamicCond = makeVariantMatchInfo(
- rawVMI, ctxSel, semaCtx, converter.genLocation(clause.source));
+ std::optional<semantics::omp::DynamicUserCondition> dynamicCond =
+ semantics::omp::MakeVariantMatchInfo(rawVMI, ctxSel, semaCtx);
if (dynamicCond) {
constexpr llvm::omp::TraitProperty dynamicConditionTrait =
diff --git a/flang/lib/Lower/OpenMP/Utils.cpp b/flang/lib/Lower/OpenMP/Utils.cpp
index 7a532e10f1a1e..bbe1b47fcf7d4 100644
--- a/flang/lib/Lower/OpenMP/Utils.cpp
+++ b/flang/lib/Lower/OpenMP/Utils.cpp
@@ -70,6 +70,7 @@ llvm::cl::opt<bool> treatIndexAsSection(
namespace Fortran {
namespace lower {
namespace omp {
+
bool requiresImplicitDefaultDeclareMapper(
const semantics::DerivedTypeSpec &typeSpec) {
// ISO C interoperable types (e.g., c_ptr, c_funptr) must always have implicit
@@ -1286,117 +1287,6 @@ mlir::Value genIteratorCoordinate(Fortran::lower::AbstractConverter &converter,
/*typeparams=*/mlir::ValueRange{});
}
-/// Collect trait property names (vendor, kind, arch, isa, etc.) into a VMI.
-static void processTraitProperties(
- llvm::omp::VariantMatchInfo &vmi, llvm::omp::TraitSet set,
- llvm::omp::TraitSelector selector,
- const std::optional<parser::OmpTraitSelector::Properties> &props,
- llvm::APInt *scorePtr, mlir::Location loc) {
- if (!props)
- return;
-
- for (const auto &prop :
- std::get<std::list<parser::OmpTraitProperty>>(props->t)) {
- const auto *name = std::get_if<parser::OmpTraitPropertyName>(&prop.u);
- // Clause properties and extension properties (e.g. `simdlen(8)` in
- // `construct={simd(simdlen(8))}`) and `foo(bar)` in
- // `implementation={my_trait(foo(bar))}` are not matched yet.
- if (!name)
- TODO(loc, "clause or extension trait matching in METADIRECTIVE");
- }
- semantics::omp::ProcessTraitProperties(vmi, set, selector, props, scorePtr);
-}
-
-/// Process user={condition(...)} trait properties. Constant conditions are
-/// resolved to user_condition_true/false. Non-constant conditions are marked
-/// as user_condition_unknown and returned for later use in fir.if lowering.
-static std::optional<DynamicUserCondition> processUserConditionTrait(
- llvm::omp::VariantMatchInfo &vmi,
- const std::optional<parser::OmpTraitSelector::Properties> &props,
- semantics::SemanticsContext &semaCtx, llvm::APInt *scorePtr) {
- std::optional<DynamicUserCondition> dynamicCond;
- if (!props)
- return dynamicCond;
-
- for (const auto &prop :
- std::get<std::list<parser::OmpTraitProperty>>(props->t)) {
- const auto *scalarExpr = std::get_if<parser::ScalarExpr>(&prop.u);
- if (!scalarExpr)
- continue;
-
- if (auto constValue =
- semantics::omp::EvaluateUserCondition(semaCtx, *scalarExpr)) {
- vmi.addTrait(*constValue ? llvm::omp::TraitProperty::user_condition_true
- : llvm::omp::TraitProperty::user_condition_false,
- "<condition>", scorePtr);
- continue;
- }
-
- dynamicCond = DynamicUserCondition{scalarExpr, prop.source};
- vmi.addTrait(llvm::omp::TraitProperty::user_condition_unknown,
- "<condition>", scorePtr);
- }
-
- return dynamicCond;
-}
-
-/// Populate a VariantMatchInfo from context selector.
-/// For user conditions, attempts constant folding. Non-constant conditions
-/// are recorded as user_condition_unknown and returned for later use in
-/// fir.if lowering.
-std::optional<DynamicUserCondition>
-makeVariantMatchInfo(llvm::omp::VariantMatchInfo &vmi,
- const parser::modifier::OmpContextSelector &ctxSel,
- semantics::SemanticsContext &semaCtx, mlir::Location loc) {
- std::optional<DynamicUserCondition> dynamicCond;
-
- for (const auto &traitSet : ctxSel.v) {
- using TSSName = parser::OmpTraitSetSelectorName;
- auto setName = std::get<TSSName>(traitSet.t).v;
- llvm::omp::TraitSet set = semantics::omp::MapTraitSet(setName);
-
- for (const auto &trait :
- std::get<std::list<parser::OmpTraitSelector>>(traitSet.t)) {
- const auto &selectorName =
- std::get<parser::OmpTraitSelectorName>(trait.t);
- llvm::omp::TraitSelector selector =
- semantics::omp::MapTraitSelector(selectorName, set);
- const auto &props =
- std::get<std::optional<parser::OmpTraitSelector::Properties>>(
- trait.t);
-
- // target_device selectors require runtime target device queries not yet
- // supported.
- if (set == llvm::omp::TraitSet::target_device)
- TODO(loc, "target_device selector in METADIRECTIVE");
-
- std::optional<llvm::APInt> score;
- llvm::APInt *scorePtr =
- semantics::omp::GetTraitScore(props, semaCtx, score);
-
- if (selector == llvm::omp::TraitSelector::user_condition) {
- if (std::optional<DynamicUserCondition> userCond =
- processUserConditionTrait(vmi, props, semaCtx, scorePtr))
- dynamicCond = userCond;
- continue;
- }
-
- processTraitProperties(vmi, set, selector, props, scorePtr, loc);
-
- if (props || set != llvm::omp::TraitSet::construct)
- continue;
-
- // Construct traits with no properties: the selector is the property.
- llvm::omp::TraitProperty propKind =
- llvm::omp::getOpenMPContextTraitPropertyForSelector(selector);
- if (propKind != llvm::omp::TraitProperty::invalid)
- vmi.addTrait(set, propKind, selectorName.ToString(), scorePtr);
- }
- }
-
- return dynamicCond;
-}
-
// ---------------------------------------------------------------------------
// FlangOMPContext — shared OMPContext for metadirective variant-matching
// ---------------------------------------------------------------------------
diff --git a/flang/lib/Lower/OpenMP/Utils.h b/flang/lib/Lower/OpenMP/Utils.h
index e7f477ff44739..efe6c963a3778 100644
--- a/flang/lib/Lower/OpenMP/Utils.h
+++ b/flang/lib/Lower/OpenMP/Utils.h
@@ -258,27 +258,12 @@ std::optional<llvm::SmallVector<mlir::Value>> getIteratorElementIndices(
/// Walk the already-emitted MLIR parent operations starting from \p op and
/// collect the implied OpenMP construct traits in outermost-to-innermost
-/// order. Used by metadirective lowering to build the `ConstructTraits` of an
-/// `OMPContext`.
+/// order. Used by metadirective lowering and declare-variant call resolution
+/// to build the `ConstructTraits` of an `OMPContext`.
void collectEnclosingConstructTraits(
mlir::Operation *op,
llvm::SmallVectorImpl<llvm::omp::TraitProperty> &constructTraits);
-/// Non-constant user condition expression and source for runtime lowering.
-struct DynamicUserCondition {
- const parser::ScalarExpr *expr;
- parser::CharBlock source;
-};
-
-/// Populate \p vmi from a parsed OpenMP context selector. Constant user
-/// conditions are folded into user_condition_true/false traits. A non-constant
-/// user condition is recorded as user_condition_unknown and returned for later
-/// lowering as a runtime condition.
-std::optional<DynamicUserCondition>
-makeVariantMatchInfo(llvm::omp::VariantMatchInfo &vmi,
- const parser::modifier::OmpContextSelector &ctxSel,
- semantics::SemanticsContext &semaCtx, mlir::Location loc);
-
/// `OMPContext` flavour used by Flang's OpenMP variant matching. Adds an
/// ISA-trait override based on the module's target-features attribute.
class FlangOMPContext final : public llvm::omp::OMPContext {
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index b02ef81176a63..70b1b2c65177f 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -2230,4 +2230,101 @@ void ProcessTraitProperties(llvm::omp::VariantMatchInfo &vmi,
}
}
+UnsupportedSelectorFeature FindUnsupportedSelectorFeature(
+ const parser::traits::OmpContextSelectorSpecification &ctxSel,
+ SemanticsContext &semaCtx) {
+ for (const parser::OmpTraitSetSelector &traitSet : ctxSel.v) {
+ using TSSName = parser::OmpTraitSetSelectorName;
+ auto setName{std::get<TSSName>(traitSet.t).v};
+ if (MapTraitSet(setName) == llvm::omp::TraitSet::target_device)
+ return UnsupportedSelectorFeature::TargetDevice;
+
+ for (const parser::OmpTraitSelector &selector :
+ std::get<std::list<parser::OmpTraitSelector>>(traitSet.t)) {
+ const auto &props{
+ std::get<std::optional<parser::OmpTraitSelector::Properties>>(
+ selector.t)};
+ if (!props)
+ continue;
+ for (const auto &prop :
+ std::get<std::list<parser::OmpTraitProperty>>(props->t))
+ if (std::holds_alternative<common::Indirection<parser::OmpClause>>(
+ prop.u) ||
+ std::holds_alternative<parser::OmpTraitPropertyExtension>(prop.u))
+ return UnsupportedSelectorFeature::ClauseOrExtensionProperty;
+ }
+ }
+ return UnsupportedSelectorFeature::None;
+}
+
+static void AddTraitPropertiesFromSelector(llvm::omp::TraitSet set,
+ const parser::OmpTraitSelector &selector, llvm::omp::VariantMatchInfo &vmi,
+ SemanticsContext &semaCtx,
+ std::optional<DynamicUserCondition> &dynamicCond) {
+ const auto &traitName{std::get<parser::OmpTraitSelectorName>(selector.t)};
+ const auto &props{
+ std::get<std::optional<parser::OmpTraitSelector::Properties>>(
+ selector.t)};
+
+ std::optional<llvm::APInt> scoreStorage;
+ llvm::APInt *scorePtr{GetTraitScore(props, semaCtx, scoreStorage)};
+
+ // user={condition(...)}: constant-fold to user_condition_true/false. A
+ // non-constant expression is recorded as user_condition_unknown and the
+ // first such expression is captured for later runtime lowering.
+ llvm::omp::TraitSelector selectorKind{MapTraitSelector(traitName, set)};
+ if (selectorKind == llvm::omp::TraitSelector::user_condition) {
+ if (!props)
+ return;
+ for (const auto &prop :
+ std::get<std::list<parser::OmpTraitProperty>>(props->t)) {
+ const auto *scalarExpr{std::get_if<parser::ScalarExpr>(&prop.u)};
+ if (!scalarExpr)
+ continue;
+ if (auto constValue{EvaluateUserCondition(semaCtx, *scalarExpr)}) {
+ vmi.addTrait(set,
+ *constValue ? llvm::omp::TraitProperty::user_condition_true
+ : llvm::omp::TraitProperty::user_condition_false,
+ "<condition>", scorePtr);
+ continue;
+ }
+ if (!dynamicCond)
+ dynamicCond = DynamicUserCondition{scalarExpr, prop.source};
+ vmi.addTrait(set, llvm::omp::TraitProperty::user_condition_unknown,
+ "<condition>", scorePtr);
+ }
+ return;
+ }
+
+ ProcessTraitProperties(vmi, set, selectorKind, props, scorePtr);
+
+ if (props || set != llvm::omp::TraitSet::construct)
+ return;
+
+ // Construct trait selector with no properties (e.g. `construct={simd}`):
+ // the selector itself implies the property.
+ llvm::omp::TraitProperty propKind{
+ llvm::omp::getOpenMPContextTraitPropertyForSelector(selectorKind)};
+ if (propKind != llvm::omp::TraitProperty::invalid)
+ vmi.addTrait(set, propKind, traitName.ToString(), scorePtr);
+}
+
+std::optional<DynamicUserCondition> MakeVariantMatchInfo(
+ llvm::omp::VariantMatchInfo &vmi,
+ const parser::traits::OmpContextSelectorSpecification &ctxSel,
+ SemanticsContext &semaCtx) {
+ CHECK(FindUnsupportedSelectorFeature(ctxSel, semaCtx) ==
+ UnsupportedSelectorFeature::None);
+ std::optional<DynamicUserCondition> dynamicCond;
+ for (const parser::OmpTraitSetSelector &traitSet : ctxSel.v) {
+ using TSSName = parser::OmpTraitSetSelectorName;
+ auto setName{std::get<TSSName>(traitSet.t).v};
+ llvm::omp::TraitSet set{MapTraitSet(setName)};
+
+ for (const parser::OmpTraitSelector &selector :
+ std::get<std::list<parser::OmpTraitSelector>>(traitSet.t))
+ AddTraitPropertiesFromSelector(set, selector, vmi, semaCtx, dynamicCond);
+ }
+ return dynamicCond;
+}
} // namespace Fortran::semantics::omp
>From c0c1fe66f24e314454e4eac291902331e1c06408 Mon Sep 17 00:00:00 2001
From: Abid Qadeer <haqadeer at amd.com>
Date: Thu, 18 Jun 2026 15:22:50 +0100
Subject: [PATCH 2/2] Remove a spurious whitespace change.
---
flang/lib/Lower/OpenMP/Utils.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/flang/lib/Lower/OpenMP/Utils.cpp b/flang/lib/Lower/OpenMP/Utils.cpp
index bbe1b47fcf7d4..382292b6c6c13 100644
--- a/flang/lib/Lower/OpenMP/Utils.cpp
+++ b/flang/lib/Lower/OpenMP/Utils.cpp
@@ -70,7 +70,6 @@ llvm::cl::opt<bool> treatIndexAsSection(
namespace Fortran {
namespace lower {
namespace omp {
-
bool requiresImplicitDefaultDeclareMapper(
const semantics::DerivedTypeSpec &typeSpec) {
// ISO C interoperable types (e.g., c_ptr, c_funptr) must always have implicit
More information about the flang-commits
mailing list