[flang-commits] [flang] [llvm] [flang][OpenMP] Track reachable metadirective replacements (PR #219014)

via flang-commits flang-commits at lists.llvm.org
Fri Sep 11 22:52:01 PDT 2026


================
@@ -702,6 +745,164 @@ void OmpStructureChecker::CheckTraitSimd(
   }
 }
 
+void OmpStructureChecker::CollectMetadirectiveConstructSelectors(
+    const parser::ProgramUnit &programUnit) {
+  metadirectiveConstructSelectors_.clear();
+  MetadirectiveConstructSelectorCollector collector{
+      metadirectiveConstructSelectors_};
+  parser::Walk(programUnit, collector);
+}
+
+OmpStructureChecker::ConstructTraitSequence
+OmpStructureChecker::GetConstructTraitsForPath(
+    const EffectiveDirectivePath &path) const {
+  ConstructTraitSequence constructTraits;
+  for (auto directive{path.rbegin()}; directive != path.rend(); ++directive) {
+    // The construct trait set starts at the innermost target construct.
+    if (llvm::omp::allTargetSet.test(*directive)) {
+      constructTraits.clear();
+    }
+    llvm::omp::VariantMatchInfo directiveVMI;
+    AppendConstructTraitsForDirective(*directive, directiveVMI);
+    constructTraits.append(directiveVMI.ConstructTraits.begin(),
+        directiveVMI.ConstructTraits.end());
+  }
+  return constructTraits;
+}
+
+llvm::SmallVector<OmpStructureChecker::EffectiveDirectivePath, 4>
+OmpStructureChecker::GetUniqueEffectiveDirectivePaths(
+    llvm::SmallVector<EffectiveDirectivePath, 4> paths) const {
+  if (paths.size() < 2) {
+    return paths;
+  }
+
+  // Selected directive paths are observed only by construct selectors on
+  // nested metadirectives. If there are none in this program unit, every path
+  // is equivalent for this analysis.
+  if (metadirectiveConstructSelectors_.empty()) {
+    paths.resize(1);
+    return paths;
+  }
+
+  auto getSignature = [&](const EffectiveDirectivePath &path) {
+    ConstructTraitSequence contextTraits{GetConstructTraitsForPath(path)};
+
+    // Matching depends on trait presence and ordered match positions. Retain
+    // matches after a failure for match_any scoring, as well as successful
+    // prefixes for matching after inner directives are appended.
+    std::vector<unsigned> signature;
+    signature.push_back(contextTraits.size());
+    for (const ConstructTraitSequence &selector :
+        metadirectiveConstructSelectors_) {
+      for (llvm::omp::TraitProperty property : selector) {
+        signature.push_back(llvm::is_contained(contextTraits, property));
+      }
+
+      std::size_t contextIndex{0};
+      for (llvm::omp::TraitProperty property : selector) {
+        std::size_t searchStart{contextIndex};
+        while (contextIndex < contextTraits.size() &&
+            contextTraits[contextIndex] != property) {
+          ++contextIndex;
+        }
+        if (contextIndex == contextTraits.size()) {
+          signature.push_back(0);
+          // Like match_any, skip absent traits without consuming the context.
+          contextIndex = searchStart;
+        } else {
+          // Reserve zero for an unmatched property.
+          signature.push_back(++contextIndex);
+        }
+      }
+
+      // Scoring uses the highest-valued complete ordered match. Retain that
+      // match for every selector prefix, since appended inner constructs can
+      // complete a selector that does not yet match the current context.
+      for (std::size_t prefixSize{1}; prefixSize <= selector.size();
+          ++prefixSize) {
+        contextIndex = contextTraits.size();
+        for (std::size_t i{prefixSize}; i > 0; --i) {
+          while (contextIndex > 0 &&
+              contextTraits[contextIndex - 1] != selector[i - 1]) {
+            --contextIndex;
+          }
+          signature.push_back(contextIndex);
+          if (contextIndex > 0) {
+            --contextIndex;
+          }
+        }
+      }
+    }
+    return signature;
+  };
+
+  std::set<std::vector<unsigned>> signatures;
+  llvm::SmallVector<EffectiveDirectivePath, 4> uniquePaths;
+  uniquePaths.reserve(paths.size());
+  for (EffectiveDirectivePath &path : paths) {
+    if (signatures.insert(getSignature(path)).second) {
+      uniquePaths.push_back(std::move(path));
+    }
+  }
+  return uniquePaths;
+}
+
+llvm::SmallVector<OmpStructureChecker::MetadirectiveReplacementBranch, 4>
+OmpStructureChecker::GetReachableMetadirectiveReplacements(
+    const parser::OmpClauseList &clauses) {
+  llvm::SmallVector<MetadirectiveReplacementBranch, 4> result;
+
+  for (const EffectiveDirectivePath &path : GetEnclosingDirectivePaths()) {
+    ConstructTraitSequence constructTraits{GetConstructTraitsForPath(path)};
+    OmpVariantMatchContext matchContext{context_, constructTraits};
+    if (auto candidateSet{
+            BuildMetadirectiveCandidateSet(clauses, context_, matchContext)}) {
----------------
MattPD wrote:

Dynamic `match_any` still drops a reachable candidate when its static selector contains an unknown vendor. The invalid-property check in `BuildMetadirectiveCandidateSet` rejects it before the condition-true applicability check. The [LLVM extension definition](https://github.com/llvm/llvm-project/blob/398137ee50c7291a24752e7411b081c6250382f2/clang/include/clang/Basic/AttrDocs.td#L5949-L5953) makes a single matching trait sufficient.

Save the reproducer below as `repro.f90` and run `flang -fc1 -fopenmp -fopenmp-version=52 -fsyntax-only repro.f90`. Revision `398137ee50c7` accepts it, while [the exact base](https://github.com/llvm/llvm-project/commit/6c68a1661af7dce1f121fd20dfce72f4ddc4afc9) reports the required COLLAPSE depth diagnostic. Changing `bogus_vendor` to the known inactive vendor `gnu`, or making the condition `.true.`, restores that diagnostic on `398137ee50c7`. A `.false.` condition correctly excludes the candidate.

Could the condition-true matching step decide this candidate's applicability rather than treating the unknown property as a veto? This differs from the now-fixed MATCH_NONE ranking crash.

```fortran
subroutine any_vendor(flag, n)
  logical :: flag
  integer :: n, i
  !$omp metadirective &
  !$omp& when(implementation={vendor(bogus_vendor), extension(match_any)}, &
  !$omp& user={condition(flag)}: simd collapse(2)) otherwise(nothing)
  do i = 1, n
  end do
end subroutine
```

https://github.com/llvm/llvm-project/pull/219014


More information about the flang-commits mailing list