[flang-commits] [flang] [flang][OpenMP] Lower DO and SIMD variants in metadirectives (PR #210810)
via flang-commits
flang-commits at lists.llvm.org
Mon Jul 27 22:07:25 PDT 2026
================
@@ -2521,6 +2543,239 @@ std::optional<DynamicUserCondition> MakeVariantMatchInfo(
return dynamicCond;
}
+std::optional<MetadirectiveCandidateSet> BuildMetadirectiveCandidateSet(
+ const parser::OmpClauseList &clauses, SemanticsContext &context,
+ const OmpVariantMatchContext &matchContext) {
+ MetadirectiveCandidateSet result;
+
+ auto getContextSelector = [](const parser::OmpClause::When &whenClause)
+ -> const parser::modifier::OmpContextSelector * {
+ const auto &modifiers{std::get<0>(whenClause.v.t)};
+ if (!modifiers || modifiers->size() != 1) {
+ return nullptr;
+ }
+ return std::get_if<parser::modifier::OmpContextSelector>(
+ &modifiers->front().u);
+ };
+
+ auto getDirectiveVariant = [](const parser::OmpClause::When &whenClause)
+ -> std::pair<const parser::OmpDirectiveSpecification *, bool> {
+ const auto &optionalSpec{std::get<1>(whenClause.v.t)};
+ if (!optionalSpec) {
+ return {nullptr, false};
+ }
+ if (optionalSpec->value().DirId() == llvm::omp::Directive::OMPD_nothing) {
+ return {nullptr, true};
+ }
+ return {&optionalSpec->value(), true};
+ };
+
+ auto getFallbackVariant = [](const parser::OmpDirectiveSpecification &spec) {
+ return spec.DirId() == llvm::omp::Directive::OMPD_nothing ? nullptr : &spec;
+ };
+
+ for (const parser::OmpClause &clause : clauses.v) {
+ if (const auto *whenClause{
+ std::get_if<parser::OmpClause::When>(&clause.u)}) {
+ const auto *ctxSel{getContextSelector(*whenClause)};
+ if (!ctxSel ||
+ FindUnsupportedSelectorFeature(*ctxSel, context) !=
+ UnsupportedSelectorFeature::None) {
+ return std::nullopt;
+ }
+
+ auto [spec, isExplicit]{getDirectiveVariant(*whenClause)};
+ llvm::omp::VariantMatchInfo rawVMI;
+ std::optional<DynamicUserCondition> dynamicCondition{
+ MakeVariantMatchInfo(rawVMI, *ctxSel, context)};
+ if (llvm::any_of(
+ rawVMI.ConstructTraits, [](llvm::omp::TraitProperty property) {
+ return llvm::omp::getOpenMPContextTraitSetForProperty(
+ property) != llvm::omp::TraitSet::construct;
+ })) {
+ return std::nullopt;
+ }
+
+ if (dynamicCondition) {
+ 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 uses only traits known at compile time. Keep
+ // the condition's score so a true runtime condition is still ranked
+ // correctly.
+ 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, matchContext)};
+ // Only match_any can remain applicable when the static traits do not
+ // match, because a true runtime condition may satisfy the selector.
+ 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, matchContext)) {
+ continue;
+ }
+ }
+
+ auto addConditionTraitForRanking =
+ [&](llvm::omp::VariantMatchInfo &rankingVMI) {
+ rankingVMI.addTrait(hasMatchNone
+ ? dynamicConditionTrait
+ : llvm::omp::TraitProperty::user_condition_true,
+ "<condition>", conditionScorePtr);
+ };
+
+ if (hasMatchAny && isStaticVMIApplicable) {
+ // Represent both outcomes: a guarded candidate with the condition's
+ // score and an unguarded candidate with only the static traits.
+ if (isExplicit) {
+ llvm::omp::VariantMatchInfo conditionTrueVMI{staticVMI};
+ addConditionTraitForRanking(conditionTrueVMI);
+ result.candidates.push_back({spec, std::move(conditionTrueVMI),
+ isExplicit, dynamicCondition});
+ }
+ result.candidates.push_back({spec, std::move(staticVMI), isExplicit});
+ continue;
+ }
+
+ llvm::omp::VariantMatchInfo rankingVMI{staticVMI};
+ // An omitted directive is implicit NOTHING and must not gain rank from
+ // the runtime condition. Explicit NOTHING remains a normal variant.
+ if (!isExplicit && hasMatchAny && !isStaticVMIApplicable) {
+ rankingVMI = llvm::omp::VariantMatchInfo();
+ } else if (isExplicit) {
+ addConditionTraitForRanking(rankingVMI);
+ }
+ result.candidates.push_back({spec, std::move(rankingVMI), isExplicit,
+ dynamicCondition, /*conditionShouldBeTrue=*/!hasMatchNone});
+ continue;
+ }
+
+ if (!llvm::omp::isVariantApplicableInContext(rawVMI, matchContext)) {
+ continue;
+ }
+ result.candidates.push_back({spec, std::move(rawVMI), isExplicit});
+ } else if (const auto *otherwiseClause{
+ std::get_if<parser::OmpClause::Otherwise>(&clause.u)}) {
+ if (otherwiseClause->v && otherwiseClause->v->v) {
+ result.fallback = getFallbackVariant(otherwiseClause->v->v->value());
+ }
+ } else if (const auto *defaultVariantClause{
+ std::get_if<parser::OmpClause::DefaultVariant>(&clause.u)}) {
+ result.fallback = getFallbackVariant(defaultVariantClause->v.v.value());
+ }
+ }
+ return result;
+}
+
+std::optional<unsigned> SelectBestMetadirectiveCandidate(
+ llvm::ArrayRef<unsigned> candidateIndices,
+ llvm::ArrayRef<MetadirectiveCandidate> candidates,
+ const OmpVariantMatchContext &matchContext) {
+ if (candidateIndices.empty()) {
+ return std::nullopt;
+ }
+ if (candidateIndices.size() == 1) {
+ return candidateIndices.front();
+ }
+
+ // The context scorer preserves input order for ties. Explicit replacements
+ // take precedence over an omitted directive's implicit NOTHING.
+ llvm::SmallVector<unsigned, 4> candidateOrder;
+ candidateOrder.reserve(candidateIndices.size());
+ for (unsigned index : candidateIndices) {
+ if (candidates[index].isExplicit) {
+ candidateOrder.push_back(index);
+ }
+ }
+ for (unsigned index : candidateIndices) {
+ if (!candidates[index].isExplicit) {
+ candidateOrder.push_back(index);
+ }
+ }
+
+ llvm::SmallVector<llvm::omp::VariantMatchInfo, 4> orderedVMIs;
+ orderedVMIs.reserve(candidateOrder.size());
+ for (unsigned index : candidateOrder) {
+ orderedVMIs.push_back(candidates[index].vmi);
+ }
+
+ int bestIndex{
+ llvm::omp::getBestVariantMatchForContext(orderedVMIs, matchContext)};
+ if (bestIndex < 0) {
+ return std::nullopt;
+ }
+ CHECK(static_cast<std::size_t>(bestIndex) < candidateOrder.size());
+ return candidateOrder[bestIndex];
+}
+
+llvm::SmallVector<const parser::OmpDirectiveSpecification *, 4>
+GetReachableMetadirectiveVariants(const MetadirectiveCandidateSet &candidateSet,
----------------
MattPD wrote:
`GetReachableMetadirectiveVariants` treats each non-constant `condition()` arm as independently selectable, so two arms guarded by the same expression both stay reachable. A lower-ranked loop variant that can never be selected then still contributes its constraints.
The case I hit is `unreachable_ranked_iteration_variable`: with `condition(score(2): .true.)` and `condition(score(1): .true.)` it passes, and changing both `.true.` to a runtime `flag` turns it into `error: The DO loop iteration variable must be of integer type`. The constant-condition case is the behaviour I would expect. The runtime-condition case looks like a false rejection, since `do` is unreachable either way. The same shape also produces the collapse-depth error, the interrupted-association error, and the enclosing-data-environment TODO.
Every runtime-selection test in `metadirective-loop.f90` uses a constant condition, so this path is currently unexercised. Would you prefer to fold identical guards, or to hold back fatal errors for a variant whose selection depends on a runtime condition?
https://github.com/llvm/llvm-project/pull/210810
More information about the flang-commits
mailing list