[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