[flang-commits] [flang] [llvm] [flang][OpenMP] Track reachable metadirective replacements (PR #219014)
via flang-commits
flang-commits at lists.llvm.org
Wed Sep 9 19:47:40 PDT 2026
================
@@ -702,6 +744,148 @@ 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.reserve(
+ 1 + 2 * contextTraits.size() * metadirectiveConstructSelectors_.size());
+ 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);
+ }
+ }
+ }
+ 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:
Could the dynamic implicit-NOTHING candidate retain its condition score before candidate ranking is used to suppress semantic checks? [OpenMP 5.2, sections 7.3 and 7.4](https://www.openmp.org/wp-content/uploads/OpenMP-API-Specification-5-2.pdf) rank candidates by score before explicit-versus-implicit precedence. When `flag` is true, the implicit NOTHING scores 11 and wins over the DO's 6, so the inner metadirective must retain its invalid fallback.
Save this as `repro.f90` and run `flang -fc1 -fopenmp -fopenmp-version=52 -fsyntax-only repro.f90`. Revision `7f525837ea63` accepts it, whereas [the base revision](https://github.com/llvm/llvm-project/commit/89082772bed1ed16c0311b12147a96c35bb063d8) reports the inner COLLAPSE depth error. An explicit `nothing` in the first WHEN also restores that diagnostic on `7f525837ea63`.
```fortran
subroutine omitted_score(flag, n)
logical :: flag
integer :: n, i, j
!$omp metadirective &
!$omp& when(user={condition(score(10): flag)}:) &
!$omp& when(user={condition(score(5): .true.)}: do) otherwise(nothing)
do i = 1, n
!$omp metadirective when(construct={do}: nothing) &
!$omp& otherwise(simd collapse(2))
do j = 1, n
end do
end do
end subroutine
```
https://github.com/llvm/llvm-project/pull/219014
More information about the flang-commits
mailing list