[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