[flang-commits] [flang] [flang][OpenMP] Lower DO, SIMD, and DO SIMD metadirective variants (PR #218555)
via flang-commits
flang-commits at lists.llvm.org
Mon Aug 24 20:57:40 PDT 2026
================
@@ -6908,126 +7211,87 @@ static void genMetadirective(lower::AbstractConverter &converter,
case semantics::omp::UnsupportedSelectorFeature::None:
break;
}
+ }
+ }
- llvm::omp::VariantMatchInfo rawVMI;
- std::optional<semantics::omp::DynamicUserCondition> dynamicCond =
- semantics::omp::MakeVariantMatchInfo(rawVMI, ctxSel, semaCtx);
-
- if (dynamicCond) {
- constexpr llvm::omp::TraitProperty dynamicConditionTrait =
- llvm::omp::TraitProperty::user_condition_unknown;
- constexpr llvm::omp::TraitProperty matchAnyTrait =
- llvm::omp::TraitProperty::implementation_extension_match_any;
- constexpr llvm::omp::TraitProperty matchNoneTrait =
- llvm::omp::TraitProperty::implementation_extension_match_none;
-
- // Static applicability must only use traits known at lowering time.
- // For example, in
- // when(implementation={vendor(llvm)},
- // user={condition(score(5): flag)}: barrier)
- // vendor(llvm) can be checked now, but flag cannot. Drop the
- // runtime-only user_condition_unknown for applicability, while keeping
- // score(5) so ranking can still honor the user-condition selector.
- llvm::omp::VariantMatchInfo staticVMI = rawVMI;
- std::optional<llvm::APInt> conditionScore;
- auto scoreIt = staticVMI.ScoreMap.find(dynamicConditionTrait);
- if (scoreIt != staticVMI.ScoreMap.end()) {
- conditionScore = scoreIt->second;
- staticVMI.ScoreMap.erase(scoreIt);
- }
- staticVMI.RequiredTraits.reset(unsigned(dynamicConditionTrait));
- llvm::APInt *conditionScorePtr =
- conditionScore ? &*conditionScore : nullptr;
-
- bool hasMatchAny = rawVMI.RequiredTraits.test(unsigned(matchAnyTrait));
- bool hasMatchNone =
- rawVMI.RequiredTraits.test(unsigned(matchNoneTrait));
- bool isStaticVMIApplicable =
- llvm::omp::isVariantApplicableInContext(staticVMI, ompCtx);
- // If staticVMI does not match, only match_any can still apply. Check
- // conditionTrueVMI because the runtime condition may satisfy match_any.
- if (!isStaticVMIApplicable) {
- if (!hasMatchAny || staticVMI.RequiredTraits.test(
- unsigned(llvm::omp::TraitProperty::invalid)))
- continue;
-
- llvm::omp::VariantMatchInfo conditionTrueVMI = staticVMI;
- conditionTrueVMI.addTrait(
- llvm::omp::TraitProperty::user_condition_true, "<condition>",
- conditionScorePtr);
- if (!llvm::omp::isVariantApplicableInContext(conditionTrueVMI,
- ompCtx))
- continue;
- }
+ std::optional<semantics::omp::MetadirectiveCandidateSet> candidateSet =
+ semantics::omp::BuildMetadirectiveCandidateSet(clauseList, semaCtx,
+ ompCtx);
+ assert(candidateSet && "unsupported selector reached candidate planning");
+ auto &candidates = candidateSet->candidates;
+ const parser::OmpDirectiveSpecification *fallback = candidateSet->fallback;
- auto addConditionTraitForRanking =
- [&](llvm::omp::VariantMatchInfo &rankingVMI) {
- rankingVMI.addTrait(
- hasMatchNone ? dynamicConditionTrait
- : llvm::omp::TraitProperty::user_condition_true,
- "<condition>", conditionScorePtr);
- };
-
- if (hasMatchAny && isStaticVMIApplicable) {
- // A statically matched match_any selector needs two candidates: a
- // guarded candidate with the user condition and score, and an
- // unguarded candidate with only the statically matched traits. If the
- // when clause omits its directive, only add the unguarded candidate.
- if (isExplicit) {
- llvm::omp::VariantMatchInfo conditionTrueVMI = staticVMI;
- addConditionTraitForRanking(conditionTrueVMI);
- candidates.emplace_back(spec, conditionTrueVMI, isExplicit,
- dynamicCond);
- }
- candidates.emplace_back(spec, staticVMI, isExplicit);
- continue;
- }
+ llvm::SmallVector<unsigned, 4> allCandidateIndices;
+ allCandidateIndices.reserve(candidates.size());
+ for (unsigned idx = 0, end = candidates.size(); idx < end; ++idx)
+ allCandidateIndices.push_back(idx);
- llvm::omp::VariantMatchInfo rankingVMI = staticVMI;
- // An omitted directive is implicit nothing, so do not let the runtime
- // condition raise its rank. Explicit `nothing` is still a variant.
- if (!isExplicit && hasMatchAny && !isStaticVMIApplicable)
- rankingVMI = llvm::omp::VariantMatchInfo();
- else if (isExplicit)
- addConditionTraitForRanking(rankingVMI);
- candidates.emplace_back(spec, rankingVMI, isExplicit, dynamicCond,
- /*conditionShouldBeTrue=*/!hasMatchNone);
- continue;
- }
+ llvm::SmallVector<const parser::OmpDirectiveSpecification *, 4>
+ reachableVariantSpecs = semantics::omp::GetReachableMetadirectiveVariants(
+ *candidateSet, ompCtx, semaCtx);
- if (!llvm::omp::isVariantApplicableInContext(rawVMI, ompCtx))
- continue;
+ bool hasLoopAssociatedCandidate =
+ llvm::any_of(reachableVariantSpecs, [](const auto *spec) {
+ return spec && hasDirectiveAssociation(
+ spec->DirId(), llvm::omp::Association::LoopNest);
+ });
+ SplicedAssociatedEvaluations splicedAssociatedEvaluations;
+ lower::pft::Evaluation *associatedLoopEval = nullptr;
+ llvm::scope_exit restoreEvaluationOwnership([&]() {
+ if (eval.hasNestedEvaluations())
+ splicedAssociatedEvaluations.restore(eval.getNestedEvaluations());
+ });
+ if (hasLoopAssociatedCandidate) {
+ if (lower::pft::Evaluation *loopEval =
+ spliceAssociatedDoEval(eval, &splicedAssociatedEvaluations)) {
+ associatedLoopEval = loopEval;
+ if (lower::pft::FunctionLikeUnit *owningProc =
+ eval.getOwningProcedure()) {
+ if (owningProc->getEntryEval() &&
+ splicedAssociatedEvaluations.getEntryBlock()) {
+ // Alternate ENTRY lowering starts with a branch. Emit selection in
+ // the detached associated block, which is either that branch's
+ // destination or unreachable for an ENTRY after the metadirective.
+ builder.setInsertionPointToStart(
+ splicedAssociatedEvaluations.getEntryBlock());
+ }
+ }
- candidates.emplace_back(spec, rawVMI, isExplicit);
- } else if (const auto *otherwiseClause =
- std::get_if<parser::OmpClause::Otherwise>(&clause.u)) {
- if (otherwiseClause->v && otherwiseClause->v->v)
- fallback = getFallbackVariant(otherwiseClause->v->v->value());
- } else if (const auto *defaultVariantClause =
- std::get_if<parser::OmpClause::DefaultVariant>(&clause.u)) {
- const auto &dirSpec = defaultVariantClause->v.v;
- fallback = getFallbackVariant(dirSpec.value());
+ auto &nested = eval.getNestedEvaluations();
+ auto loopIt =
+ llvm::find_if(nested, [loopEval](lower::pft::Evaluation &e) {
+ return &e == loopEval;
+ });
+ assert(loopIt != nested.end() && "associated loop not nested");
+
+ // Attach compiler directives to the loop before any selected variant
+ // lowers it. Variant bodies skip them below to avoid processing them a
+ // second time.
+ for (auto it = nested.begin(); it != loopIt; ++it)
+ if (it->getIf<parser::CompilerDirective>())
+ converter.genEval(*it);
----------------
MattPD wrote:
Lowering eagerly emits every interposed compiler directive before it evaluates the metadirective's runtime condition. `PREFETCH` evaluates its designator and emits executable IR. The frontend accepts this source, which exposes observable call reordering:
```fortran
subroutine s(a, n)
integer :: a(n), n, i, idx
logical :: choose
external :: idx, choose
!$omp metadirective &
!$omp& when(user={condition(choose())}: do) &
!$omp& otherwise(nothing)
!dir$ prefetch a(idx())
do i = 1, n
a(i) = i
end do
end
```
Running `flang -fc1 -fopenmp -fopenmp-version=52 -emit-hlfir` emits operations in this order: `idx()`, `fir.prefetch`, `choose()`, `fir.if`. The comparable non-loop metadirective evaluates `choose()` first.
Semantic analysis rejects the same interposed `PREFETCH` when it follows a directly written `!$omp do`. The metadirective form passes that check.
Lowering should emit each effectful interposed directive only in the branch selected at runtime. If lowering cannot support the directive, it should issue a targeted not-yet-implemented error before it emits executable IR.
https://github.com/llvm/llvm-project/pull/218555
More information about the flang-commits
mailing list