================
@@ -763,46 +966,112 @@ void OmpStructureChecker::Enter(const
parser::OmpDirectiveSpecification &x) {
void OmpStructureChecker::Leave(const parser::OmpDirectiveSpecification &x) {
if (GetDirectiveNest(MetadirectiveNest) || GetDirectiveNest(ApplyNest)) {
+ CHECK(!directiveSpecificationReachability_.empty());
+ directiveSpecificationReachability_.pop_back();
dirContext_.pop_back();
}
}
void OmpStructureChecker::Enter(const parser::OmpMetadirectiveDirective &x) {
+ auto branches{GetReachableMetadirectiveReplacements(x.v.Clauses())};
+ pendingLoopDirectiveGroups_.push_back(
+ {std::move(branches), /*isStandaloneMetadirective=*/true});
EnterDirectiveNest(MetadirectiveNest);
}
void OmpStructureChecker::Leave(const parser::OmpMetadirectiveDirective &) {
ExitDirectiveNest(MetadirectiveNest);
}
+void OmpStructureChecker::Enter(
+ const parser::OmpDelimitedMetadirectiveDirective &x) {
+ auto branches{GetReachableMetadirectiveReplacements(x.BeginDir().Clauses())};
+ llvm::SmallVector<EffectiveDirectivePath, 4> paths;
+ for (const MetadirectiveReplacementBranch &branch : branches) {
+ EffectiveDirectivePath path{branch.enclosingPath};
+ if (branch.spec) {
+ path.insert(path.begin(), branch.spec->DirId());
+ }
+ paths.push_back(std::move(path));
+ }
+ paths = GetUniqueEffectiveDirectivePaths(std::move(paths));
+ activeMetadirectiveReplacements_.push_back(
+ {dirContext_.size(), std::move(paths)});
+ pendingLoopDirectiveGroups_.push_back({std::move(branches)});
+}
+
+void OmpStructureChecker::Leave(
+ const parser::OmpDelimitedMetadirectiveDirective &) {
+ CHECK(!activeMetadirectiveReplacements_.empty());
+ activeMetadirectiveReplacements_.pop_back();
+}
+
// Check a loop-associated metadirective's variants against the loop nest they
// apply to. The nest is not attached to the directive in the parse tree. It is
// the next executable construct, either a following sibling or the first
// execution-part construct for a declarative metadirective.
void OmpStructureChecker::Enter(const parser::ExecutionPartConstruct &x) {
- if (metadirectiveLoopVariants_.empty()) {
+ executionPartReplacementDepths_.push_back(
+ activeMetadirectiveReplacements_.size());
+ if (pendingLoopDirectiveGroups_.empty()) {
return;
}
if (parser::Unwrap<parser::CompilerDirective>(x)) {
return;
}
+
+ const parser::DoConstruct *rootLoop{parser::Unwrap<parser::DoConstruct>(x)};
+ bool isStrictlyStructuredBlock{
+ parser::Unwrap<parser::BlockConstruct>(x) != nullptr};
+
+ // Keep standalone replacements active throughout their associated DO or
+ // BLOCK construct so nested construct selectors see the selected path.
+ if (rootLoop || isStrictlyStructuredBlock) {
----------------
MattPD wrote:
When a standalone metadirective selects a block-associated replacement,
semantics keeps that replacement active through the following strictly
structured BLOCK. Test `f28` asserts this behavior.
Lowering does not enclose that BLOCK in the selected region. `genMetadirective`
dispatches the variant on the metadirective's own evaluation and keeps the
selected construct context active only during that dispatch. It includes a
following sibling in the dispatch only when the sibling is a loop. The selected
region is already dropped at the merge base, so `!$omp metadirective
when(implementation={vendor(llvm)}: parallel)` followed by a BLOCK does not
lower to `omp.parallel` at either the merge base or this PR's head.
With this PR, the disagreement has a second symptom: Semantics accepts an input
triggering a lowering abort. You can reproduce the abort by saving the
following to `standalone.f90` and running `flang -fc1 -fopenmp
-fopenmp-version=52 -emit-hlfir -o - standalone.f90`:
```fortran
subroutine standalone_parallel_gap(n)
integer :: n, i
!$omp metadirective when(implementation={vendor(llvm)}: parallel) &
!$omp& otherwise(nothing)
block
!$omp metadirective &
!$omp& when(construct={parallel}: nothing) &
!$omp& when(implementation={vendor(score(0): llvm)}: simd collapse(2)) &
!$omp& otherwise(nothing)
do i = 1, n
end do
end block
end subroutine
```
With this PR, `-fsyntax-only` accepts the program. In semantics, the selected
`parallel` context causes the inner metadirective to select `nothing`. The SIMD
candidate is therefore unreachable, so semantics does not check its loop.
Lowering lacks that `parallel` context, selects the SIMD candidate, and aborts
with "not yet implemented: METADIRECTIVE variant with COLLAPSE or ORDERED
requires a deeper perfectly-nested loop nest than is present". On
https://github.com/llvm/llvm-project/pull/224431 alone, the program is rejected
in semantics. The `begin metadirective` / `end metadirective` spelling lowers
to `omp.parallel` and agrees with semantics.
The semantics model follows the strictly-structured-block rule, so the fix
belongs in lowering. This comment tracks that gap and does not ask for a change
in this PR. A lowering fix could process the following BLOCK while the selected
construct context remains active. It would then move the BLOCK back to its
original position and skip its normal lowering, as the existing loop handling
does. Until then, `f28` asserts only the semantics model. It does not test the
corresponding lowering behavior.
https://github.com/llvm/llvm-project/pull/219014
_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits