[flang-commits] [flang] [flang][OpenMP] Lower DO and SIMD variants in metadirectives (PR #210810)
via flang-commits
flang-commits at lists.llvm.org
Wed Jul 29 21:49:46 PDT 2026
================
@@ -753,11 +754,253 @@ void OmpStructureChecker::Leave(const parser::OmpDirectiveSpecification &x) {
}
}
+void OmpStructureChecker::BeginMetadirectiveSelection() {
+ metadirectiveSelectionStarts_.push_back(metadirectiveLoopVariants_.size());
+}
+
+void OmpStructureChecker::EndMetadirectiveSelection(
+ const parser::OmpClauseList &clauses) {
+ CHECK(!metadirectiveSelectionStarts_.empty());
+ std::size_t firstVariant{metadirectiveSelectionStarts_.back()};
+ metadirectiveSelectionStarts_.pop_back();
+ CHECK(!metadirectiveConstructContexts_.empty());
+
+ // Nested dynamic selections form a cross product. Bound it and leave all
+ // variants conservatively reachable if the exact context set grows larger.
+ constexpr std::size_t maxConstructTraitAlternatives{64};
+ auto isInvariantCondition =
+ [&](const MetadirectiveConditionConstraint &condition) {
+ const SomeExpr *expr{GetExpr(context_, *condition.expr)};
+ if (!expr)
+ return false;
+ auto symbols{evaluate::CollectSymbols(*expr)};
+ return !symbols.empty() &&
+ llvm::all_of(symbols, [](const Symbol &symbol) {
+ if (IsNamedConstant(symbol))
+ return true;
+ return IsIntentIn(symbol) && !IsPointer(symbol) &&
+ !evaluate::IsCoarray(symbol) &&
+ !symbol.attrs().HasAny({Attr::ASYNCHRONOUS, Attr::VOLATILE});
+ });
+ };
+ auto sameCondition = [&](const MetadirectiveConditionConstraint &left,
+ const MetadirectiveConditionConstraint &right) {
+ // Repeated conditions within one metadirective are evaluated at the same
+ // selection point. Across nested metadirectives, only correlate expressions
+ // whose values cannot have changed between the two selections.
+ if (left.owner != right.owner &&
+ (!isInvariantCondition(left) || !isInvariantCondition(right)))
+ return false;
+ if (left.expr == right.expr)
+ return true;
+ const SomeExpr *leftExpr{GetExpr(context_, *left.expr)};
+ const SomeExpr *rightExpr{GetExpr(context_, *right.expr)};
+ return leftExpr && rightExpr && *leftExpr == *rightExpr;
+ };
+ auto addCondition =
+ [&](llvm::SmallVectorImpl<MetadirectiveConditionConstraint> &conditions,
+ MetadirectiveConditionConstraint condition) {
+ for (const MetadirectiveConditionConstraint &existing : conditions) {
+ if (sameCondition(existing, condition))
+ return existing.value == condition.value;
+ }
+ conditions.push_back(condition);
+ return true;
+ };
+ auto sameConditions =
+ [&](llvm::ArrayRef<MetadirectiveConditionConstraint> left,
+ llvm::ArrayRef<MetadirectiveConditionConstraint> right) {
+ if (left.size() != right.size())
+ return false;
+ return llvm::all_of(left, [&](const auto &leftCondition) {
+ return llvm::any_of(right, [&](const auto &rightCondition) {
+ return leftCondition.value == rightCondition.value &&
+ sameCondition(leftCondition, rightCondition);
+ });
+ });
+ };
+ auto containsAlternative =
+ [&](llvm::ArrayRef<MetadirectiveConstructAlternative> alternatives,
+ const MetadirectiveConstructAlternative &alternative) {
+ return llvm::any_of(alternatives, [&](const auto &existing) {
+ return existing.traits == alternative.traits &&
+ sameConditions(existing.conditions, alternative.conditions);
+ });
+ };
+
+ llvm::SmallVector<MetadirectiveConstructAlternative, 4>
+ constructTraitAlternatives(1);
+ auto appendAlternatives =
+ [&](llvm::ArrayRef<MetadirectiveConstructAlternative> suffixes) {
+ if (suffixes.empty())
+ return false;
+
+ llvm::SmallVector<MetadirectiveConstructAlternative, 4>
+ combinedAlternatives;
+ for (const MetadirectiveConstructAlternative &prefix :
+ constructTraitAlternatives) {
+ for (const MetadirectiveConstructAlternative &suffix : suffixes) {
+ MetadirectiveConstructAlternative combined{prefix};
+ combined.traits.append(suffix.traits);
+ bool isCompatible{llvm::all_of(suffix.conditions,
+ [&](MetadirectiveConditionConstraint condition) {
+ return addCondition(combined.conditions, condition);
+ })};
+ if (!isCompatible ||
+ containsAlternative(combinedAlternatives, combined))
+ continue;
+ if (combinedAlternatives.size() == maxConstructTraitAlternatives)
+ return false;
+ combinedAlternatives.push_back(std::move(combined));
+ }
+ }
+ if (combinedAlternatives.empty())
+ return false;
+ constructTraitAlternatives = std::move(combinedAlternatives);
+ return true;
+ };
+
+ // Interleave each enclosing metadirective's selected traits with source
+ // constructs at the metadirective's position in the construct stack.
+ std::size_t metadirectiveContextIndex{0};
+ for (const LoopOrConstruct &item : constructStack_) {
+ if (const auto *construct{
+ std::get_if<const parser::OpenMPConstruct *>(&item)}) {
+ llvm::omp::Directive directive{
+ parser::omp::GetOmpDirectiveName(**construct).v};
+ if (directive == llvm::omp::Directive::OMPD_metadirective) {
+ CHECK(
+ metadirectiveContextIndex < metadirectiveConstructContexts_.size());
+ const MetadirectiveConstructContext &context{
+ metadirectiveConstructContexts_[metadirectiveContextIndex++]};
+ bool isCurrentMetadirective{metadirectiveContextIndex ==
+ metadirectiveConstructContexts_.size()};
+ if (!isCurrentMetadirective &&
+ !appendAlternatives(context.alternatives))
+ return;
+ continue;
+ }
+
+ ConstructTraitSequence sourceTraits;
+ AppendConstructTraitsForDirective(directive, sourceTraits);
+ for (MetadirectiveConstructAlternative &alternative :
+ constructTraitAlternatives)
+ alternative.traits.append(sourceTraits);
+ }
+ }
+
+ struct ReachableVariantPath {
+ const parser::OmpDirectiveSpecification *specification;
+ llvm::SmallVector<MetadirectiveConditionConstraint, 2> conditions;
+ };
+ auto getReachableVariantPaths =
+ [&](const MetadirectiveCandidateSet &candidateSet,
+ const OmpVariantMatchContext &matchContext) {
+ llvm::SmallVector<unsigned, 4> candidates;
+ candidates.reserve(candidateSet.candidates.size());
+ for (unsigned i{0}; i < candidateSet.candidates.size(); ++i)
+ candidates.push_back(i);
+
+ llvm::SmallVector<ReachableVariantPath, 4> paths;
+ llvm::SmallVector<MetadirectiveConditionConstraint, 2> pathConditions;
+ while (true) {
+ std::optional<unsigned> selected{SelectBestMetadirectiveCandidate(
+ candidates, candidateSet.candidates, matchContext)};
+ if (!selected) {
+ paths.push_back({candidateSet.fallback, pathConditions});
+ break;
+ }
+
+ const MetadirectiveCandidate &candidate{
+ candidateSet.candidates[*selected]};
+ if (!candidate.dynamicCondition) {
+ paths.push_back({candidate.spec, pathConditions});
+ break;
+ }
+
+ MetadirectiveConditionConstraint selectedCondition{
+ candidate.dynamicCondition->expr, candidate.conditionShouldBeTrue,
+ &clauses};
+ auto selectedPathConditions{pathConditions};
+ if (addCondition(selectedPathConditions, selectedCondition))
+ paths.push_back(
+ {candidate.spec, std::move(selectedPathConditions)});
+
+ MetadirectiveConditionConstraint failedCondition{
+ candidate.dynamicCondition->expr,
+ !candidate.conditionShouldBeTrue, &clauses};
+ if (!addCondition(pathConditions, failedCondition))
+ break;
+ candidates = GetMetadirectiveElsePathCandidates(
+ *selected, candidates, candidateSet.candidates, context_);
+ }
+ return paths;
+ };
+
+ llvm::SmallVector<const parser::OmpDirectiveSpecification *, 4>
+ reachableVariants;
+ llvm::SmallVector<MetadirectiveConstructAlternative, 2> currentAlternatives;
+ for (const MetadirectiveConstructAlternative &constructAlternative :
+ constructTraitAlternatives) {
+ OmpVariantMatchContext matchContext{context_, constructAlternative.traits};
+ std::optional<MetadirectiveCandidateSet> candidateSet{
+ BuildMetadirectiveCandidateSet(clauses, context_, matchContext)};
+ if (!candidateSet) {
+ // Keep every variant when selection cannot yet model a selector.
+ return;
+ }
+
+ for (const ReachableVariantPath &path :
+ getReachableVariantPaths(*candidateSet, matchContext)) {
+ MetadirectiveConstructAlternative currentAlternative;
+ if (path.specification)
+ AppendConstructTraitsForDirective(
+ path.specification->DirId(), currentAlternative.traits);
+ currentAlternative.conditions = constructAlternative.conditions;
+ bool isCompatible{llvm::all_of(
+ path.conditions, [&](MetadirectiveConditionConstraint condition) {
+ return addCondition(currentAlternative.conditions, condition);
+ })};
+ if (!isCompatible)
+ continue;
+
+ if (!llvm::is_contained(reachableVariants, path.specification))
+ reachableVariants.push_back(path.specification);
+ if (!containsAlternative(currentAlternatives, currentAlternative)) {
+ if (currentAlternatives.size() == maxConstructTraitAlternatives)
+ return;
+ currentAlternatives.push_back(std::move(currentAlternative));
+ }
+ }
+ }
+ if (currentAlternatives.empty())
+ return;
+
+ // Make every reachable replacement and its accumulated path constraints
+ // available to metadirectives in a delimited body.
+ MetadirectiveConstructContext ¤tContext{
+ metadirectiveConstructContexts_.back()};
----------------
MattPD wrote:
This context reaches a metadirective in a delimited body, but not one whose loop is a parse-tree sibling. Such a metadirective never reaches `constructStack_`, and `Leave(parser::OmpMetadirectiveDirective)` pops the context before the loop body is walked:
```fortran
subroutine sp(n, a)
integer :: n, a(n), i
real :: r
!$omp metadirective when(implementation={vendor(llvm)}: do) otherwise(nothing)
do i = 1, n
!$omp metadirective when(construct={do}: nothing) otherwise(do)
do r = 1, 2
a(i) = int(r)
end do
end do
end subroutine
```
The inner metadirective reports `The DO loop iteration variable must be of integer type`. Declaring `r` as an integer shows what lowering actually chose: a single `omp.wsloop`, privatizing `i`, with the inner loop emitted as a plain `fir.do_loop`. Lowering selected `nothing` for the inner metadirective, because the enclosing construct is a `do`, so semantics rejected a variant that lowering had already discarded.
`Enter(parser::ExecutionPartConstruct)` in this file already walks from a metadirective to its sibling loop to validate `metadirectiveLoopVariants_`. Would holding the selected context across that same window cover the non-delimited spelling too?
https://github.com/llvm/llvm-project/pull/210810
More information about the flang-commits
mailing list