[flang-commits] [flang] [flang][OpenMP] Resolve private array source from block-arg owner in alias analysis (PR #208227)

Caroline Newcombe via flang-commits flang-commits at lists.llvm.org
Wed Jul 8 07:11:23 PDT 2026


https://github.com/cenewcombe created https://github.com/llvm/llvm-project/pull/208227

**Summary**
Part of #208086. `fir::AliasAnalysis::getSource` fails to recognize an OpenMP private array as an `Allocate` source when its `hlfir.declare` is nested inside an omp.loop_nest, and conservatively returns `MayAlias`. This changes `getSource` to resolve the clause-carrying OpenMP op from the private block argument's owner region, so the private is correctly classified and does not spuriously alias other objects.

The subsequent `BlockArgOpenMPOpInterface` check and `TypeSwitch` are unchanged. This is conservative in every direction:

- If the memref is not a block argument, behavior is identical to before.
- The `TypeSwitch` has no default case, so an unlisted op type simply leaves the item unrecognized (a missed-precision fallback to `MayAlias`, never a miscompile).
- `isPrivateArg` still verifies the block argument is actually the declare's memref among the op's private block args.
- This also improves precision for pre-existing boxed nested privates, independent of any lowering change.

**Tests**
Two test-fir-alias-analysis tests, both producing `NoAlias` between a dummy-argument array and a private array declared inside omp.loop_nest of an omp.wsloop:

- `alias-analysis-omp-private-boxed-wsloop.mlir` — a boxed dynamic-extent private (the descriptor form lowering emits today).
- `alias-analysis-omp-private-unboxed-wsloop.mlir` — see the note below.
- Without this patch both queries return `MayAlias` and the tests fail.

Note: the unboxed-private IR in the second test is the form produced by a follow-up lowering change (privatizing constant-shape trivial arrays unboxed, part 2 of #208086 ); this alias fix is a prerequisite for that IR to be analyzed correctly.

The block-arg-owner resolution also makes nested-map target items resolve correctly when a mapped variable's declare is reached below the omp.target region. This is a conservative-preserving side effect (an unrecognized item still falls through to `MayAlias`; it isn't given a dedicated test because current lowering emits mapped-variable declares at the omp.target entry, so the existing target-map tests already cover the resolved shape.

**Assisted-by:** Copilot

>From bf1e65a200509b968fd00309c37b2f8e38b84070 Mon Sep 17 00:00:00 2001
From: Caroline Newcombe <caroline.newcombe at hpe.com>
Date: Tue, 7 Jul 2026 15:14:15 -0500
Subject: [PATCH] [flang][OpenMP] Resolve private array source from block-arg
 owner in alias analysis

---
 .../lib/Optimizer/Analysis/AliasAnalysis.cpp  | 12 +++-
 ...ias-analysis-omp-private-boxed-wsloop.mlir | 61 +++++++++++++++++++
 ...s-analysis-omp-private-unboxed-wsloop.mlir | 50 +++++++++++++++
 3 files changed, 121 insertions(+), 2 deletions(-)
 create mode 100644 flang/test/Analysis/AliasAnalysis/alias-analysis-omp-private-boxed-wsloop.mlir
 create mode 100644 flang/test/Analysis/AliasAnalysis/alias-analysis-omp-private-unboxed-wsloop.mlir

diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 02a628968ddc4..2e1aaf14b6a62 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -1363,10 +1363,18 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
           // but their handling is more complex. Maybe we can find better
           // abstractions to handle them in a general fashion.
           bool isPrivateItem = false;
+          // The private/map block argument is owned by the clause-carrying op
+          // (e.g. omp.wsloop), but the declare may be nested deeper (e.g. in an
+          // omp.loop_nest). Resolve the op from the block arg's owner rather
+          // than the declare's immediate parent to handle that nesting.
+          mlir::Operation *ompParentOp = op->getParentOp();
+          if (auto blockArg =
+                  mlir::dyn_cast<mlir::BlockArgument>(op.getMemref()))
+            ompParentOp = blockArg.getOwner()->getParentOp();
           if (omp::BlockArgOpenMPOpInterface argIface =
-                  dyn_cast<omp::BlockArgOpenMPOpInterface>(op->getParentOp())) {
+                  dyn_cast<omp::BlockArgOpenMPOpInterface>(ompParentOp)) {
             Value ompValArg;
-            llvm::TypeSwitch<Operation *>(op->getParentOp())
+            llvm::TypeSwitch<Operation *>(ompParentOp)
                 .Case([&](omp::TargetOp targetOp) {
                   // If declare operation is inside omp target region,
                   // continue alias analysis outside the target region
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-omp-private-boxed-wsloop.mlir b/flang/test/Analysis/AliasAnalysis/alias-analysis-omp-private-boxed-wsloop.mlir
new file mode 100644
index 0000000000000..cc69d3f86ccac
--- /dev/null
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-omp-private-boxed-wsloop.mlir
@@ -0,0 +1,61 @@
+// Use --mlir-disable-threading so that the AA queries are serialized
+// as well as its diagnostic output.
+// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s
+
+// A PRIVATE array is boxed during OMP privatization and its hlfir.declare is
+// nested inside the omp.loop_nest of an omp.wsloop. The alias analysis must
+// resolve the clause-carrying omp.wsloop from the private block argument's
+// owner region -- not the declare's immediate parent (the omp.loop_nest) --
+// so that the loaded box data (from a private/Allocate source) is recognized
+// as not aliasing a dummy argument array.
+//
+// The private is a dynamic-extent array, so it stays boxed independently of
+// how constant-shape arrays are privatized.
+//
+// Fortran source:
+//   subroutine test(a, n, m)
+//     real(8) :: a(10)
+//     integer :: n, m, i
+//     real(8) :: xx(m)
+//   !$omp parallel do private(xx)
+//     do i = 1, n
+//       xx(1:3) = a(1:3)
+//     enddo
+//   end subroutine
+
+// CHECK-LABEL: Testing : "test_boxed_private_wsloop_vs_arg"
+// CHECK: arg_designate#0 <-> private_designate#0: NoAlias
+
+omp.private {type = private} @xx_privatizer : !fir.box<!fir.array<?xf64>>
+
+func.func @test_boxed_private_wsloop_vs_arg(
+    %arg0: !fir.ref<!fir.array<10xf64>> {fir.bindc_name = "a"},
+    %arg1: !fir.ref<i32> {fir.bindc_name = "n"},
+    %arg2: !fir.ref<i32> {fir.bindc_name = "m"}) {
+  %c1 = arith.constant 1 : index
+  %c3 = arith.constant 3 : index
+  %c10 = arith.constant 10 : index
+  %c1_i32 = arith.constant 1 : i32
+  %sh_a = fir.shape %c10 : (index) -> !fir.shape<1>
+  %sh3 = fir.shape %c3 : (index) -> !fir.shape<1>
+  %adecl:2 = hlfir.declare %arg0(%sh_a) {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtestEa"} : (!fir.ref<!fir.array<10xf64>>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xf64>>, !fir.ref<!fir.array<10xf64>>)
+  %m = fir.load %arg2 : !fir.ref<i32>
+  %m_idx = fir.convert %m : (i32) -> index
+  %xx = fir.alloca !fir.array<?xf64>, %m_idx {bindc_name = "xx", uniq_name = "_QFtestExx"}
+  %shm = fir.shape %m_idx : (index) -> !fir.shape<1>
+  %xxbox = fir.embox %xx(%shm) : (!fir.ref<!fir.array<?xf64>>, !fir.shape<1>) -> !fir.box<!fir.array<?xf64>>
+  %xxbox_ref = fir.alloca !fir.box<!fir.array<?xf64>>
+  fir.store %xxbox to %xxbox_ref : !fir.ref<!fir.box<!fir.array<?xf64>>>
+  %n = fir.load %arg1 : !fir.ref<i32>
+
+  omp.wsloop private(@xx_privatizer %xxbox_ref -> %parg : !fir.ref<!fir.box<!fir.array<?xf64>>>) {
+    omp.loop_nest (%iv) : i32 = (%c1_i32) to (%n) inclusive step (%c1_i32) {
+      %pdecl:2 = hlfir.declare %parg {uniq_name = "_QFtestExx"} : (!fir.ref<!fir.box<!fir.array<?xf64>>>) -> (!fir.ref<!fir.box<!fir.array<?xf64>>>, !fir.ref<!fir.box<!fir.array<?xf64>>>)
+      %ad = hlfir.designate %adecl#0 (%c1:%c3:%c1) shape %sh3 {test.ptr = "arg_designate"} : (!fir.ref<!fir.array<10xf64>>, index, index, index, !fir.shape<1>) -> !fir.ref<!fir.array<3xf64>>
+      %pbox = fir.load %pdecl#0 : !fir.ref<!fir.box<!fir.array<?xf64>>>
+      %pd = hlfir.designate %pbox (%c1:%c3:%c1) shape %sh3 {test.ptr = "private_designate"} : (!fir.box<!fir.array<?xf64>>, index, index, index, !fir.shape<1>) -> !fir.ref<!fir.array<3xf64>>
+      omp.yield
+    }
+  }
+  return
+}
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-omp-private-unboxed-wsloop.mlir b/flang/test/Analysis/AliasAnalysis/alias-analysis-omp-private-unboxed-wsloop.mlir
new file mode 100644
index 0000000000000..7017050310bea
--- /dev/null
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-omp-private-unboxed-wsloop.mlir
@@ -0,0 +1,50 @@
+// Use --mlir-disable-threading so that the AA queries are serialized
+// as well as its diagnostic output.
+// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s
+
+// A PRIVATE array is unboxed (a plain fir.array) and its hlfir.declare is
+// nested inside the omp.loop_nest of an omp.wsloop. The alias analysis must
+// resolve the clause-carrying omp.wsloop from the private block argument's
+// owner region -- not the declare's immediate parent (the omp.loop_nest) --
+// so that the private is recognized as an Allocate source that does not alias
+// a dummy argument array.
+//
+// Fortran source:
+//   subroutine test(a, n)
+//     real(8) :: a(10)
+//     integer :: n, i
+//     real(8) :: xx(3)
+//   !$omp parallel do private(xx)
+//     do i = 1, n
+//       xx(1:3) = a(1:3)
+//     enddo
+//   end subroutine
+
+// CHECK-LABEL: Testing : "test_unboxed_private_wsloop_vs_arg"
+// CHECK: arg_designate#0 <-> private_designate#0: NoAlias
+
+omp.private {type = private} @xx_privatizer : !fir.array<3xf64>
+
+func.func @test_unboxed_private_wsloop_vs_arg(
+    %arg0: !fir.ref<!fir.array<10xf64>> {fir.bindc_name = "a"},
+    %arg1: !fir.ref<i32> {fir.bindc_name = "n"}) {
+  %c1 = arith.constant 1 : index
+  %c3 = arith.constant 3 : index
+  %c10 = arith.constant 10 : index
+  %c1_i32 = arith.constant 1 : i32
+  %sh_a = fir.shape %c10 : (index) -> !fir.shape<1>
+  %adecl:2 = hlfir.declare %arg0(%sh_a) {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtestEa"} : (!fir.ref<!fir.array<10xf64>>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xf64>>, !fir.ref<!fir.array<10xf64>>)
+  %xx = fir.alloca !fir.array<3xf64> {bindc_name = "xx", uniq_name = "_QFtestExx"}
+  %sh3 = fir.shape %c3 : (index) -> !fir.shape<1>
+  %n = fir.load %arg1 : !fir.ref<i32>
+
+  omp.wsloop private(@xx_privatizer %xx -> %parg : !fir.ref<!fir.array<3xf64>>) {
+    omp.loop_nest (%iv) : i32 = (%c1_i32) to (%n) inclusive step (%c1_i32) {
+      %xdecl:2 = hlfir.declare %parg(%sh3) {uniq_name = "_QFtestExx"} : (!fir.ref<!fir.array<3xf64>>, !fir.shape<1>) -> (!fir.ref<!fir.array<3xf64>>, !fir.ref<!fir.array<3xf64>>)
+      %ad = hlfir.designate %adecl#0 (%c1:%c3:%c1) shape %sh3 {test.ptr = "arg_designate"} : (!fir.ref<!fir.array<10xf64>>, index, index, index, !fir.shape<1>) -> !fir.ref<!fir.array<3xf64>>
+      %pd = hlfir.designate %xdecl#0 (%c1:%c3:%c1) shape %sh3 {test.ptr = "private_designate"} : (!fir.ref<!fir.array<3xf64>>, index, index, index, !fir.shape<1>) -> !fir.ref<!fir.array<3xf64>>
+      omp.yield
+    }
+  }
+  return
+}



More information about the flang-commits mailing list