================
@@ -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

Reply via email to