[flang-commits] [flang] e67b241 - [flang][OpenMP] Make sure that exec METADIRECTIVE is in execution part (#233147)
via flang-commits
flang-commits at lists.llvm.org
Tue Oct 20 08:01:46 PDT 2026
Author: Krzysztof Parzyszek
Date: 2026-10-20T10:01:39-05:00
New Revision: e67b241027f3694d9089db3b5bdc4372318e52cf
URL: https://github.com/llvm/llvm-project/commit/e67b241027f3694d9089db3b5bdc4372318e52cf
DIFF: https://github.com/llvm/llvm-project/commit/e67b241027f3694d9089db3b5bdc4372318e52cf.diff
LOG: [flang][OpenMP] Make sure that exec METADIRECTIVE is in execution part (#233147)
A METADIRECTIVE can be in either the specification part or the execution
part, depending on whether the variant directives contained in it are
declarative or executable. The OmpMetadirectiveDirective AST node can
hold any combination of variants, that is it can represent both kinds.
When the parser builds the specification part and attempts to create an
OmpMetadirectiveDirective, once it gets one it puts it in the
specification part regardless of the contents.
When an executable METADIRECTIVE is the first statement of the execution
part, it will end up in the specification part. This is an identical
situation as that for utility directives.
Recognize executable METADIRECTIVEs at the end of the specification part
and move them over to the beginning of the execution part.
Added:
Modified:
flang/lib/Semantics/canonicalize-omp.cpp
flang/test/Parser/OpenMP/metadirective-dirspec.f90
Removed:
################################################################################
diff --git a/flang/lib/Semantics/canonicalize-omp.cpp b/flang/lib/Semantics/canonicalize-omp.cpp
index 14316c6e180ede..3a93ba33179421 100644
--- a/flang/lib/Semantics/canonicalize-omp.cpp
+++ b/flang/lib/Semantics/canonicalize-omp.cpp
@@ -162,16 +162,18 @@ void CanonicalizationOfOmp::CanonicalizeAllocateDirectives(
}
}
+template <typename T> parser::ExecutionPartConstruct MakeExec(T &&x) {
+ return parser::ExecutionPartConstruct(parser::ExecutableConstruct(
+ common::Indirection(parser::OpenMPConstruct(std::move(x)))));
+}
+
parser::ExecutionPartConstruct CanonicalizationOfOmp::EmbedInExec(
parser::OmpAllocateDirective *alo, parser::ExecutionPartConstruct &&epc) {
// Nest current epc inside the allocate directive.
std::get<parser::Block>(alo->t).push_front(std::move(epc));
// Set the new epc to be the ExecutionPartConstruct made from
// the allocate directive.
- parser::OpenMPConstruct opc(std::move(*alo));
- common::Indirection<parser::OpenMPConstruct> ind(std::move(opc));
- parser::ExecutableConstruct ec(std::move(ind));
- return parser::ExecutionPartConstruct(std::move(ec));
+ return MakeExec(std::move(*alo));
}
void CanonicalizationOfOmp::MakeExecutableAllocateFromDecls(
@@ -226,7 +228,7 @@ void CanonicalizationOfOmp::MakeExecutableAllocateFromOmps(
}
}
-// Canonicalization of utility directives.
+// Canonicalization of utility and metadirectives.
//
// This addresses the issue of directives that appear at the boundary
// between the specification and the execution parts, e.g.
@@ -280,6 +282,80 @@ void CanonicalizationOfOmp::CanonicalizeUtilityDirectives(
}
}
+static const parser::OmpDirectiveSpecification *GetODS(
+ const parser::OmpClause &clause) {
+ if (auto *other = parser::Unwrap<parser::OmpOtherwiseClause>(clause)) {
+ return other->v.has_value() ? &other->v->value() : nullptr;
+ }
+ if (auto *when = parser::Unwrap<parser::OmpWhenClause>(clause)) {
+ using OmpDirectiveSpecification =
+ std::optional<common::Indirection<parser::OmpDirectiveSpecification>>;
+ if (auto &ods = std::get<OmpDirectiveSpecification>(when->t)) {
+ return &ods->value();
+ }
+ return nullptr;
+ }
+ if (auto *dv = parser::Unwrap<parser::OmpDefaultVariantClause>(clause)) {
+ return &dv->v.value();
+ }
+ return nullptr;
+}
+
+static bool IsConsideredExecutable(const parser::OmpDirectiveSpecification &x) {
+ switch (llvm::omp::getDirectiveCategory(x.DirId())) {
+ case llvm::omp::Category::Executable:
+ case llvm::omp::Category::Utility:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool IsConsideredExecutable(
+ const parser::OmpMetadirectiveDirective &md) {
+ // NOTHING as a variant by itself should not decide whether the METADIRECTIVE
+ // as a whole executable or not. But when all variants are NOTHING then the
+ // METADIRECTIVE should be treated as executable.
+ bool something = false;
+ for (const parser::OmpClause &clause : md.v.Clauses().v) {
+ if (auto *ods = GetODS(clause)) {
+ if (ods->DirId() != llvm::omp::Directive::OMPD_nothing) {
+ something = true;
+ if (IsConsideredExecutable(*ods)) {
+ return true;
+ }
+ }
+ }
+ // If the directive specification is missing, it's NOTHING.
+ }
+ // If everything is NOTHING, return true.
+ return !something;
+}
+
+static bool IsConsideredExecutable(
+ const parser::OpenMPDeclarativeConstruct &x) {
+ if (std::holds_alternative<parser::OmpUtilityDirective>(x.u)) {
+ return true;
+ }
+ if (auto *md = parser::Unwrap<parser::OmpMetadirectiveDirective>(x)) {
+ if (IsConsideredExecutable(*md)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static parser::ExecutionPartConstruct MakeExec(
+ parser::OpenMPDeclarativeConstruct &x) {
+ if (auto *s = std::get_if<parser::OmpMetadirectiveDirective>(&x.u)) {
+ return MakeExec(parser::OpenMPStandaloneConstruct(std::move(*s)));
+ }
+ if (auto *s = std::get_if<parser::OmpUtilityDirective>(&x.u)) {
+ return MakeExec(std::move(*s));
+ }
+ llvm_unreachable("Unexpected OpenMPDeclarativeConstruct");
+}
+
void CanonicalizationOfOmp::MoveUtilityDirectivesFromDecls(
std::list<parser::DeclarationConstruct> &decls, parser::Block &block) {
// Find the trailing range of DeclarationConstructs that are OpenMP
@@ -287,11 +363,8 @@ void CanonicalizationOfOmp::MoveUtilityDirectivesFromDecls(
std::list<parser::DeclarationConstruct>::reverse_iterator rlast = [&]() {
for (auto rit = decls.rbegin(), rend = decls.rend(); rit != rend; ++rit) {
parser::DeclarationConstruct &dc = *rit;
- if (auto *odc = parser::Unwrap<parser::OpenMPDeclarativeConstruct>(dc)) {
- if (!std::holds_alternative<parser::OmpUtilityDirective>(odc->u)) {
- return rit;
- }
- } else {
+ auto *odc = parser::Unwrap<parser::OpenMPDeclarativeConstruct>(dc);
+ if (!odc || !IsConsideredExecutable(*odc)) {
return rit;
}
}
@@ -304,10 +377,7 @@ void CanonicalizationOfOmp::MoveUtilityDirectivesFromDecls(
using OpenMPDeclarativeConstruct =
common::Indirection<parser::OpenMPDeclarativeConstruct>;
auto &oc = std::get<OpenMPDeclarativeConstruct>(sc.u).value();
- auto &ut = std::get<parser::OmpUtilityDirective>(oc.u);
-
- return parser::ExecutionPartConstruct(parser::ExecutableConstruct(
- common::Indirection(parser::OpenMPConstruct(std::move(ut)))));
+ return MakeExec(oc);
});
decls.erase(rlast.base(), decls.end());
@@ -320,8 +390,7 @@ void CanonicalizationOfOmp::MoveUtilityDirectivesFromOmps(
// utility directive, that are to be moved to the execution part.
std::list<OpenMPDeclarativeConstruct>::reverse_iterator rlast = [&]() {
for (auto rit = omps.rbegin(), rend = omps.rend(); rit != rend; ++rit) {
- OpenMPDeclarativeConstruct &dc = *rit;
- if (!std::holds_alternative<parser::OmpUtilityDirective>(dc.u)) {
+ if (!IsConsideredExecutable(*rit)) {
return rit;
}
}
@@ -329,11 +398,7 @@ void CanonicalizationOfOmp::MoveUtilityDirectivesFromOmps(
}();
std::transform(omps.rbegin(), rlast, std::front_inserter(block),
- [](parser::OpenMPDeclarativeConstruct &dc) {
- auto &ut = std::get<parser::OmpUtilityDirective>(dc.u);
- return parser::ExecutionPartConstruct(parser::ExecutableConstruct(
- common::Indirection(parser::OpenMPConstruct(std::move(ut)))));
- });
+ [](parser::OpenMPDeclarativeConstruct &dc) { return MakeExec(dc); });
omps.erase(rlast.base(), omps.end());
}
diff --git a/flang/test/Parser/OpenMP/metadirective-dirspec.f90 b/flang/test/Parser/OpenMP/metadirective-dirspec.f90
index d63db6c93cf404..837c54f4fecbb2 100644
--- a/flang/test/Parser/OpenMP/metadirective-dirspec.f90
+++ b/flang/test/Parser/OpenMP/metadirective-dirspec.f90
@@ -40,7 +40,8 @@ subroutine f01(x)
!UNPARSE: !$OMP METADIRECTIVE WHEN(USER={CONDITION(.true._4)}: CRITICAL(x))
!UNPARSE: END SUBROUTINE
-!PARSE-TREE: DeclarationConstruct -> SpecificationConstruct -> OpenMPDeclarativeConstruct -> OmpMetadirectiveDirective
+!PARSE-TREE: OpenMPConstruct -> OpenMPStandaloneConstruct -> OmpMetadirectiveDirective -> OmpDirectiveSpecification
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = metadirective
!PARSE-TREE: | OmpClauseList -> OmpClause -> When -> OmpWhenClause
!PARSE-TREE: | | Modifier -> OmpContextSelectorSpecification -> OmpTraitSetSelector
!PARSE-TREE: | | | OmpTraitSetSelectorName -> Value = User
@@ -227,7 +228,8 @@ subroutine f06(x, y)
!UNPARSE: !$OMP METADIRECTIVE WHEN(USER={CONDITION(.true._4)}: FLUSH(x, y))
!UNPARSE: END SUBROUTINE
-!PARSE-TREE: DeclarationConstruct -> SpecificationConstruct -> OpenMPDeclarativeConstruct -> OmpMetadirectiveDirective
+!PARSE-TREE: OpenMPConstruct -> OpenMPStandaloneConstruct -> OmpMetadirectiveDirective -> OmpDirectiveSpecification
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = metadirective
!PARSE-TREE: | OmpClauseList -> OmpClause -> When -> OmpWhenClause
!PARSE-TREE: | | Modifier -> OmpContextSelectorSpecification -> OmpTraitSetSelector
!PARSE-TREE: | | | OmpTraitSetSelectorName -> Value = User
@@ -268,3 +270,58 @@ subroutine f07
!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = threadprivate
!PARSE-TREE: | | | OmpArgumentList -> OmpArgument -> OmpObject -> Designator -> DataRef -> Name = 't'
!PARSE-TREE: | | | OmpClauseList ->
+
+subroutine f08
+ !$omp metadirective when(user={condition(.true.)}: barrier)
+end
+
+!UNPARSE: SUBROUTINE f08
+!UNPARSE: !$OMP METADIRECTIVE WHEN(USER={CONDITION(.true._4)}: BARRIER)
+!UNPARSE: END SUBROUTINE
+
+!PARSE-TREE: OpenMPConstruct -> OpenMPStandaloneConstruct -> OmpMetadirectiveDirective -> OmpDirectiveSpecification
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = metadirective
+!PARSE-TREE: | OmpClauseList -> OmpClause -> When -> OmpWhenClause
+!PARSE-TREE: | | Modifier -> OmpContextSelectorSpecification -> OmpTraitSetSelector
+!PARSE-TREE: | | | OmpTraitSetSelectorName -> Value = User
+!PARSE-TREE: | | | OmpTraitSelector
+!PARSE-TREE: | | | | OmpTraitSelectorName -> Value = Condition
+!PARSE-TREE: | | | | Properties
+!PARSE-TREE: | | | | | OmpTraitProperty -> Scalar -> Expr = '.true._4'
+!PARSE-TREE: | | | | | | LiteralConstant -> LogicalLiteralConstant
+!PARSE-TREE: | | | | | | | bool = 'true'
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = barrier
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+!PARSE-TREE: | Flags = {}
+
+subroutine f09
+ !$omp metadirective when(user={condition(.true.)}: nothing) &
+ !$omp & otherwise(nothing)
+end
+
+!UNPARSE: SUBROUTINE f09
+!UNPARSE: !$OMP METADIRECTIVE WHEN(USER={CONDITION(.true._4)}: NOTHING) OTHERWISE(NOTHING)
+!UNPARSE: END SUBROUTINE
+
+!PARSE-TREE: OpenMPConstruct -> OpenMPStandaloneConstruct -> OmpMetadirectiveDirective -> OmpDirectiveSpecification
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = metadirective
+!PARSE-TREE: | OmpClauseList -> OmpClause -> When -> OmpWhenClause
+!PARSE-TREE: | | Modifier -> OmpContextSelectorSpecification -> OmpTraitSetSelector
+!PARSE-TREE: | | | OmpTraitSetSelectorName -> Value = User
+!PARSE-TREE: | | | OmpTraitSelector
+!PARSE-TREE: | | | | OmpTraitSelectorName -> Value = Condition
+!PARSE-TREE: | | | | Properties
+!PARSE-TREE: | | | | | OmpTraitProperty -> Scalar -> Expr = '.true._4'
+!PARSE-TREE: | | | | | | LiteralConstant -> LogicalLiteralConstant
+!PARSE-TREE: | | | | | | | bool = 'true'
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = nothing
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+!PARSE-TREE: | OmpClause -> Otherwise -> OmpOtherwiseClause -> OmpDirectiveSpecification
+!PARSE-TREE: | | OmpDirectiveName -> llvm::omp::Directive = nothing
+!PARSE-TREE: | | OmpClauseList ->
+!PARSE-TREE: | | Flags = {}
+!PARSE-TREE: | Flags = {}
More information about the flang-commits
mailing list