================
@@ -1473,27 +1490,82 @@ semantics::omp::OmpVariantMatchContext 
makeVariantMatchContext(
 }
 
 void collectEnclosingConstructTraits(
-    mlir::Operation *op,
+    AbstractConverter &converter, const pft::Evaluation *evaluation,
     llvm::SmallVectorImpl<llvm::omp::TraitProperty> &constructTraits) {
-  // Collect enclosing OpenMP operations so variants chosen by an outer
-  // metadirective are part of this metadirective's context. For example, an
-  // inner metadirective inside `target` and an outer-selected `parallel` must
-  // be able to match construct={target, parallel}. The final reverse yields
-  // outermost-to-innermost order as required by OMPContext.
-  for (; op; op = op->getParentOp()) {
-    if (mlir::isa<mlir::omp::WsloopOp>(op))
-      constructTraits.push_back(llvm::omp::TraitProperty::construct_for_for);
-    if (mlir::isa<mlir::omp::ParallelOp>(op))
-      constructTraits.push_back(
-          llvm::omp::TraitProperty::construct_parallel_parallel);
-    if (mlir::isa<mlir::omp::TeamsOp>(op))
-      constructTraits.push_back(
-          llvm::omp::TraitProperty::construct_teams_teams);
-    if (mlir::isa<mlir::omp::TargetOp>(op))
-      constructTraits.push_back(
-          llvm::omp::TraitProperty::construct_target_target);
+  const auto *loopControl =
+      converter.getStateStack().getStackTop<LoopControlContext>();
+  // Use the loop owner's ancestors: host evaluation may have TARGET current
+  // while evaluating a nested loop's bounds.
+  if (loopControl)
+    evaluation = &loopControl->evaluation;
+
+  llvm::SmallVector<const OpenMPContextFrame *, 4> frames;
+  converter.getStateStack().stackWalk<OpenMPContextFrame>(
+      [&](OpenMPContextFrame &frame) {
+        frames.push_back(&frame);
+        return mlir::WalkResult::advance();
+      });
+  std::reverse(frames.begin(), frames.end());
+  llvm::SmallVector<bool, 4> usedFrames(frames.size(), false);
+
+  llvm::SmallVector<const pft::Evaluation *, 8> ancestors;
+  for (const pft::Evaluation *parent = evaluation ? evaluation->parentConstruct
+                                                  : nullptr;
+       parent; parent = parent->parentConstruct) {
+    ancestors.push_back(parent);
+  }
+  std::reverse(ancestors.begin(), ancestors.end());
+
+  auto append = [&](llvm::omp::Directive directive) {
+    semantics::omp::AppendDirectiveContextTraits(directive, constructTraits);
+  };
+  auto getDirective = [&](const pft::Evaluation &eval,
+                          const parser::OpenMPConstruct &omp) {
+    llvm::omp::Directive directive = parser::omp::GetOmpDirectiveName(omp).v;
+    if (directive == llvm::omp::Directive::OMPD_metadirective)
+      for (const OpenMPContextFrame *frame : frames)
+        if (&frame->evaluation == &eval && frame->isReplacement)
+          return frame->directive;
+    return directive;
+  };
+  for (const pft::Evaluation *ancestor : ancestors) {
+    const auto *omp = ancestor->getIf<parser::OpenMPConstruct>();
----------------
MattPD wrote:

When a metadirective appears before any executable statement, its evaluation is 
an `OpenMPDeclarativeConstruct`, so the ancestor walk skips it without 
consuming the replacement's entered frames. The collector then appends the 
selected PARALLEL after a nested TARGET has cleared the outer context.

You can reproduce this by saving the following to `repro.f90` and running 
`flang -fc1 -fopenmp -fopenmp-version=52 -emit-hlfir -module-dir /tmp repro.f90 
-o -`:

```fortran
module m
contains
  subroutine par()
  end subroutine
  subroutine base()
    !$omp declare variant(par) match(construct={parallel})
  end subroutine
  subroutine s()
    !$omp metadirective when(implementation={vendor(llvm)}: parallel)
    block
      !$omp target
        call base()
      !$omp end target
    end block
  end subroutine
end module
```

At 1850727, `s` calls `par` inside TARGET. It should call `base`, because 
[OpenMP 5.2 7.1](https://www.openmp.org/spec-html/5.2/openmpse42.html) excludes 
constructs outside the innermost TARGET. Adding an executable statement before 
the metadirective, or writing PARALLEL directly, preserves the TARGET boundary. 
At 9d79b83, `s` also calls `base`, although Flang omits the selected PARALLEL.

Could the ancestor walk consume a replacement's entered frames at the 
metadirective's source position for both `OpenMPConstruct` and 
`OpenMPDeclarativeConstruct` evaluations?

https://github.com/llvm/llvm-project/pull/224431
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to