[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
================
@@ -5924,125 +6180,99 @@ 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);
}
}
+ auto genMetadirectiveBody = [&]() {
+ for (lower::pft::Evaluation &nested : eval.getNestedEvaluations())
+ if (!hasLoopAssociatedCandidate ||
+ !nested.getIf<parser::CompilerDirective>())
+ converter.genEval(nested);
+ };
+
// Lower a single resolved candidate.
auto genVariant = [&](const parser::OmpDirectiveSpecification *spec) {
if (!spec) {
- genNestedEvaluations(converter, eval);
+ genMetadirectiveBody();
return;
}
- List<Clause> variantClauses = makeClauses(spec->Clauses(), semaCtx);
mlir::Location variantLoc = converter.genLocation(spec->source);
+ List<Clause> variantClauses = makeClauses(spec->Clauses(), semaCtx);
ConstructQueue queue{
buildConstructQueue(converter.getFirOpBuilder().getModule(), semaCtx,
eval, spec->source, spec->DirId(), variantClauses)};
+ unsigned ompVersion{semaCtx.langOptions().OpenMPVersion};
+
+ // Eager privatization relies on variant-local host associations that name
+ // resolution cannot create for a metadirective replacement.
+ if (!enableDelayedPrivatization &&
+ hasEagerPrivatizationClause(queue, ompVersion))
----------------
MattPD wrote:
`firstprivate` needs the variant-local host association in delayed mode as well, so the `!enableDelayedPrivatization` condition leaves the default configuration aborting. This is `Todo/metadirective-block-eager-privatization.f90` with `private(x)` changed to `firstprivate(x)`:
```fortran
subroutine fp(x)
integer :: x
!$omp begin metadirective &
!$omp & when(implementation={vendor(llvm)}: parallel firstprivate(x)) &
!$omp & otherwise(nothing)
x = x + 1
!$omp end metadirective
end subroutine
```
Compiled with `%flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52` and no `-mmlir` flag, it aborts:
```
Bridge.cpp:1065: Assertion `sym.has<HostAssocDetails>() && "No host-association found"' failed.
```
Both new tests force eager privatization, so the suite stays green while the default configuration aborts: `Todo/metadirective-block-eager-privatization.f90` and `Todo/metadirective-block-eager-default-private.f90`.
A `copyin` clause aborts in both modes, at `ClauseProcessor.cpp:1175`, and this predicate does not reach it. It enumerates `Default` plus `isPrivatizingClause`, rather than the property the lowering depends on.
Would checking whether a clause needs a variant-local host association cover `firstprivate` and `copyin` together? A second RUN line without the `-mmlir` flag would then pin the default configuration.
https://github.com/llvm/llvm-project/pull/210810
More information about the flang-commits
mailing list