Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[flang] AliasAnalysis: Handle fir.load on fir.alloca #117785

Merged
merged 16 commits into from
Feb 13, 2025

Conversation

jdenny-ornl
Copy link
Collaborator

For example, determine that the address in p below cannot alias the address of v:

subroutine test()
  real, pointer :: p
  real, target :: t
  real :: v
  p => t
  v = p
end subroutine test

For example, determine that the address in p below cannot alias the
address of v:

```
subroutine test()
  real, pointer :: p
  real, target :: t
  real :: v
  p => t
  v = p
end subroutine test
```
@llvmbot llvmbot added flang Flang issues not falling into any other category flang:fir-hlfir labels Nov 26, 2024
@llvmbot
Copy link
Member

llvmbot commented Nov 26, 2024

@llvm/pr-subscribers-flang-fir-hlfir

Author: Joel E. Denny (jdenny-ornl)

Changes

For example, determine that the address in p below cannot alias the address of v:

subroutine test()
  real, pointer :: p
  real, target :: t
  real :: v
  p => t
  v = p
end subroutine test

Patch is 34.61 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/117785.diff

3 Files Affected:

  • (modified) flang/lib/Optimizer/Analysis/AliasAnalysis.cpp (+45-18)
  • (modified) flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir (+8-5)
  • (added) flang/test/Analysis/AliasAnalysis/load-ptr-alloca.fir (+412)
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 2b24791d6c7c52..284b3dadbcef96 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -31,21 +31,6 @@ using namespace mlir;
 // AliasAnalysis: alias
 //===----------------------------------------------------------------------===//
 
-/// Temporary function to skip through all the no op operations
-/// TODO: Generalize support of fir.load
-static mlir::Value getOriginalDef(mlir::Value v) {
-  mlir::Operation *defOp;
-  bool breakFromLoop = false;
-  while (!breakFromLoop && (defOp = v.getDefiningOp())) {
-    llvm::TypeSwitch<Operation *>(defOp)
-        .Case<fir::ConvertOp>([&](fir::ConvertOp op) { v = op.getValue(); })
-        .Case<fir::DeclareOp, hlfir::DeclareOp>(
-            [&](auto op) { v = op.getMemref(); })
-        .Default([&](auto op) { breakFromLoop = true; });
-  }
-  return v;
-}
-
 namespace fir {
 
 void AliasAnalysis::Source::print(llvm::raw_ostream &os) const {
@@ -497,6 +482,29 @@ static Value getPrivateArg(omp::BlockArgOpenMPOpInterface &argIface,
   return privateArg;
 }
 
+/// Temporary function to skip through all the no op operations
+/// TODO: Generalize support of fir.load
+static mlir::Value
+getOriginalDef(mlir::Value v,
+               fir::AliasAnalysis::Source::Attributes &attributes,
+               bool &isCapturedInInternalProcedure) {
+  mlir::Operation *defOp;
+  bool breakFromLoop = false;
+  while (!breakFromLoop && (defOp = v.getDefiningOp())) {
+    llvm::TypeSwitch<Operation *>(defOp)
+        .Case<fir::ConvertOp>([&](fir::ConvertOp op) { v = op.getValue(); })
+        .Case<fir::DeclareOp, hlfir::DeclareOp>([&](auto op) {
+          v = op.getMemref();
+          auto varIf = llvm::cast<fir::FortranVariableOpInterface>(defOp);
+          attributes |= getAttrsFromVariable(varIf);
+          isCapturedInInternalProcedure |=
+              varIf.isCapturedInInternalProcedure();
+        })
+        .Default([&](auto op) { breakFromLoop = true; });
+  }
+  return v;
+}
+
 AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
                                                bool getLastInstantiationPoint) {
   auto *defOp = v.getDefiningOp();
@@ -509,6 +517,17 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
   bool isBoxRef{fir::isa_ref_type(v.getType()) &&
                 mlir::isa<fir::BaseBoxType>(fir::unwrapRefType(v.getType()))};
   bool followingData = !isBoxRef;
+  // "fir.alloca !fir.ptr<...>" returns the address *of* a pointer and is thus
+  // non-data, and yet there's no box.  Don't treat it like data, or it will
+  // appear to alias like the address *in* a pointer.  TODO: That case occurs in
+  // our test suite (alias-analysis-2.fir), but does flang currently generate
+  // such code?  Perhaps we should update docs
+  // (AliasAnalysis::Source::SourceOrigin::isData) and debug output
+  // (AliasAnalysis::Source::print) for isData as the current wording implies
+  // !isData requires a box.
+  if (mlir::isa_and_nonnull<fir::AllocaOp, fir::AllocMemOp>(defOp) &&
+      isPointerReference(v.getType()))
+    followingData = false;
   mlir::SymbolRefAttr global;
   Source::Attributes attributes;
   mlir::Operation *instantiationPoint{nullptr};
@@ -522,6 +541,12 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
         .Case<fir::AllocaOp, fir::AllocMemOp>([&](auto op) {
           // Unique memory allocation.
           type = SourceKind::Allocate;
+          // If there's no DeclareOp, then we need to get the pointer attribute
+          // from the type.  TODO: That case occurs in our test suite
+          // (alias-analysis-2.fir), but does flang currently generate such
+          // code?
+          if (isPointerReference(ty))
+            attributes.set(Attribute::Pointer);
           breakFromLoop = true;
         })
         .Case<fir::ConvertOp>([&](auto op) {
@@ -554,10 +579,12 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
         .Case<fir::LoadOp>([&](auto op) {
           // If the load is from a leaf source, return the leaf. Do not track
           // through indirections otherwise.
-          // TODO: Add support to fir.alloca and fir.allocmem
-          auto def = getOriginalDef(op.getMemref());
+          // TODO: Add support to fir.allocmem.
+          auto def = getOriginalDef(op.getMemref(), attributes,
+                                    isCapturedInInternalProcedure);
           if (isDummyArgument(def) ||
-              def.template getDefiningOp<fir::AddrOfOp>()) {
+              def.template getDefiningOp<fir::AddrOfOp>() ||
+              def.template getDefiningOp<fir::AllocaOp>()) {
             v = def;
             defOp = v.getDefiningOp();
             return;
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir
index d03348efd2a68c..e2cdf8c8ddeb66 100644
--- a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir
@@ -8,9 +8,11 @@
 // They cannot physically alias
 // CHECK-DAG: p1.addr#0 <-> p2.addr#0: NoAlias
 
-// p1.addr and p2.addr could both be wrapped inside boxes
-// CHECK-DAG: p1.addr#0 <-> boxp1.addr#0: MayAlias
-// CHECK-DAG: p2.addr#0 <-> boxp1.addr#0: MayAlias
+// p1.addr is the address returned by an alloca.  It does not have a target
+// attribute, and it is not the address retrieved from a pointer.  It cannot
+// alias anything.  Likewise for p2.addr.
+// CHECK-DAG: p1.addr#0 <-> boxp1.addr#0: NoAlias
+// CHECK-DAG: p2.addr#0 <-> boxp1.addr#0: NoAlias
 
 // TODO: To really see aliasing, we should be looking at a load of p1.addr
 // p1.addr is just a local address holding the address to the data 
@@ -27,10 +29,11 @@
 // CHECK-DAG: p2.addr#0 <-> func.region0#2: NoAlias
 
 // All arguments are either pointers or targets
-// A pointer in a box may alias with both
+// The address *in* a local pointer may alias the address of a target
+// argument, but it does not alias the address *of* a pointer argument.
 // CHECK-DAG: boxp1.addr#0 <-> func.region0#0: MayAlias
 // CHECK-DAG: boxp1.addr#0 <-> func.region0#1: MayAlias
-// CHECK-DAG: boxp1.addr#0 <-> func.region0#2: MayAlias
+// CHECK-DAG: boxp1.addr#0 <-> func.region0#2: NoAlias
 
 // A target dummy may alias with another target
 // CHECK-DAG: func.region0#0 <-> func.region0#1: MayAlias
diff --git a/flang/test/Analysis/AliasAnalysis/load-ptr-alloca.fir b/flang/test/Analysis/AliasAnalysis/load-ptr-alloca.fir
new file mode 100644
index 00000000000000..56c5313d397a51
--- /dev/null
+++ b/flang/test/Analysis/AliasAnalysis/load-ptr-alloca.fir
@@ -0,0 +1,412 @@
+// Check aliasing with the address *in* (not *of*) a local (fir.alloca) pointer
+// variable.
+//
+// Throughout this test, the ".fir" suffix on symbols indicates a version of the
+// MLIR after convert-hlfir-to-fir.  We would like alias analysis results to be
+// the same in both versions.
+
+// RUN: fir-opt %s -split-input-file -o /dev/null --mlir-disable-threading  \
+// RUN:   -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' \
+// RUN:   2>&1 | FileCheck -match-full-lines %s
+
+// subroutine test(n)
+//   integer :: n
+//   real, pointer :: p0, p1
+//   real :: arr(n)
+//   real, target :: t_arr(n)
+//   real, allocatable :: alloc
+//   real, allocatable, target :: t_alloc
+//   real, target :: t
+//   real :: v
+// end subroutine test
+
+// CHECK-LABEL: Testing : "_QPtest"
+
+// The address in a pointer can alias the address in another pointer or the
+// address of a target but not the address of other variables.
+// CHECK-DAG: p0.tgt#0 <-> p1.tgt#0: MayAlias
+// CHECK-DAG: t#0 <-> p0.tgt#0: MayAlias
+// CHECK-DAG: t#0 <-> p1.tgt#0: MayAlias
+// CHECK-DAG: v#0 <-> p0.tgt#0: NoAlias
+// CHECK-DAG: v#0 <-> p1.tgt#0: NoAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> p1.tgt.fir#0: MayAlias
+// CHECK-DAG: t.fir#0 <-> p0.tgt.fir#0: MayAlias
+// CHECK-DAG: t.fir#0 <-> p1.tgt.fir#0: MayAlias
+// CHECK-DAG: v.fir#0 <-> p0.tgt.fir#0: NoAlias
+// CHECK-DAG: v.fir#0 <-> p1.tgt.fir#0: NoAlias
+
+// The address in a pointer cannot alias the address of a pointer.
+// CHECK-DAG: p0#0 <-> p0.tgt#0: NoAlias
+// CHECK-DAG: p0#0 <-> p1.tgt#0: NoAlias
+// CHECK-DAG: p1#0 <-> p0.tgt#0: NoAlias
+// CHECK-DAG: p1#0 <-> p1.tgt#0: NoAlias
+// CHECK-DAG: p0.fir#0 <-> p0.tgt.fir#0: NoAlias
+// CHECK-DAG: p0.fir#0 <-> p1.tgt.fir#0: NoAlias
+// CHECK-DAG: p1.fir#0 <-> p0.tgt.fir#0: NoAlias
+// CHECK-DAG: p1.fir#0 <-> p1.tgt.fir#0: NoAlias
+
+// For some cases, AliasAnalysis analyzes hlfir.designate like fir.box_addr, so
+// make sure it doesn't mistakenly see the address of arr(1) as an address that
+// was loaded from a pointer and that could alias something.  However, t_arr is
+// a target.
+// CHECK-DAG: p0.tgt#0 <-> arr(1)#0: NoAlias
+// CHECK-DAG: p0.tgt#0 <-> t_arr(1)#0: MayAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> arr(1).fir#0: NoAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> t_arr(1).fir#0: MayAlias
+
+// Like a pointer, an allocatable contains an address, but an allocatable is not
+// a pointer and so cannot alias pointers.  However, t_alloc is a target.
+// CHECK-DAG: p0.tgt#0 <-> alloc.tgt#0: NoAlias
+// CHECK-DAG: p0.tgt#0 <-> t_alloc.tgt#0: MayAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> alloc.tgt.fir#0: NoAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> t_alloc.tgt.fir#0: MayAlias
+
+// The address in an allocatable cannot alias the address of that allocatable.
+// CHECK-DAG: alloc#0 <-> alloc.tgt#0: NoAlias
+// CHECK-DAG: alloc.fir#0 <-> alloc.tgt.fir#0: NoAlias
+
+func.func @_QPtest(%arg0: !fir.ref<i32> {fir.bindc_name = "n"}) {
+  %0 = fir.alloca !fir.box<!fir.heap<f32>> {bindc_name = "alloc", uniq_name = "_QFtestEalloc"}
+  %1:2 = hlfir.declare %0 {test.ptr="alloc", fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtestEalloc"} : (!fir.ref<!fir.box<!fir.heap<f32>>>) -> (!fir.ref<!fir.box<!fir.heap<f32>>>, !fir.ref<!fir.box<!fir.heap<f32>>>)
+  %2:2 = hlfir.declare %arg0 {uniq_name = "_QFtestEn"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+  %3 = fir.alloca !fir.box<!fir.ptr<f32>> {bindc_name = "p0", uniq_name = "_QFtestEp0"}
+  %4:2 = hlfir.declare %3 {test.ptr="p0", fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFtestEp0"} : (!fir.ref<!fir.box<!fir.ptr<f32>>>) -> (!fir.ref<!fir.box<!fir.ptr<f32>>>, !fir.ref<!fir.box<!fir.ptr<f32>>>)
+  %5 = fir.alloca !fir.box<!fir.ptr<f32>> {bindc_name = "p1", uniq_name = "_QFtestEp1"}
+  %6:2 = hlfir.declare %5 {test.ptr="p1", fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFtestEp1"} : (!fir.ref<!fir.box<!fir.ptr<f32>>>) -> (!fir.ref<!fir.box<!fir.ptr<f32>>>, !fir.ref<!fir.box<!fir.ptr<f32>>>)
+  %7 = fir.alloca f32 {bindc_name = "t", fir.target, uniq_name = "_QFtestEt"}
+  %8:2 = hlfir.declare %7 {test.ptr="t", fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFtestEt"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
+  %9 = fir.alloca !fir.box<!fir.heap<f32>> {bindc_name = "t_alloc", fir.target, uniq_name = "_QFtestEt_alloc"}
+  %10:2 = hlfir.declare %9 {fortran_attrs = #fir.var_attrs<allocatable, target>, uniq_name = "_QFtestEt_alloc"} : (!fir.ref<!fir.box<!fir.heap<f32>>>) -> (!fir.ref<!fir.box<!fir.heap<f32>>>, !fir.ref<!fir.box<!fir.heap<f32>>>)
+  %11 = fir.alloca f32 {bindc_name = "v", uniq_name = "_QFtestEv"}
+  %12:2 = hlfir.declare %11 {test.ptr="v", uniq_name = "_QFtestEv"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
+  %13 = fir.load %2#0 : !fir.ref<i32>
+  %14 = fir.convert %13 : (i32) -> i64
+  %15 = fir.convert %14 : (i64) -> index
+  %c0 = arith.constant 0 : index
+  %16 = arith.cmpi sgt, %15, %c0 : index
+  %17 = arith.select %16, %15, %c0 : index
+  %18 = fir.alloca !fir.array<?xf32>, %17 {bindc_name = "arr", uniq_name = "_QFtestEarr"}
+  %19 = fir.shape %17 : (index) -> !fir.shape<1>
+  %20:2 = hlfir.declare %18(%19) {uniq_name = "_QFtestEarr"} : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>) -> (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.array<?xf32>>)
+  %21 = fir.load %2#0 : !fir.ref<i32>
+  %22 = fir.convert %21 : (i32) -> i64
+  %23 = fir.convert %22 : (i64) -> index
+  %c0_0 = arith.constant 0 : index
+  %24 = arith.cmpi sgt, %23, %c0_0 : index
+  %25 = arith.select %24, %23, %c0_0 : index
+  %26 = fir.alloca !fir.array<?xf32>, %25 {bindc_name = "t_arr", fir.target, uniq_name = "_QFtestEt_arr"}
+  %27 = fir.shape %25 : (index) -> !fir.shape<1>
+  %28:2 = hlfir.declare %26(%27) {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFtestEt_arr"} : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>) -> (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.array<?xf32>>)
+  %29 = fir.load %4#0 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %30 = fir.box_addr %29 {test.ptr="p0.tgt"} : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
+  %31 = fir.load %6#0 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %32 = fir.box_addr %31 {test.ptr="p1.tgt"} : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
+  %c1 = arith.constant 1 : index
+  %33 = hlfir.designate %20#0 (%c1) {test.ptr="arr(1)"} : (!fir.box<!fir.array<?xf32>>, index) -> !fir.ref<f32>
+  %c1_1 = arith.constant 1 : index
+  %34 = hlfir.designate %28#0 (%c1_1) {test.ptr="t_arr(1)"} : (!fir.box<!fir.array<?xf32>>, index) -> !fir.ref<f32>
+  %35 = fir.load %1#0 : !fir.ref<!fir.box<!fir.heap<f32>>>
+  %36 = fir.box_addr %35 {test.ptr="alloc.tgt"} : (!fir.box<!fir.heap<f32>>) -> !fir.heap<f32>
+  %37 = fir.load %10#0 : !fir.ref<!fir.box<!fir.heap<f32>>>
+  %38 = fir.box_addr %37 {test.ptr="t_alloc.tgt"} : (!fir.box<!fir.heap<f32>>) -> !fir.heap<f32>
+  return
+}
+
+func.func @_QPtest.fir(%arg0: !fir.ref<i32> {fir.bindc_name = "n"}) {
+  %0 = fir.alloca !fir.box<!fir.heap<f32>> {bindc_name = "alloc", uniq_name = "_QFtestEalloc"}
+  %1 = fir.declare %0 {test.ptr = "alloc.fir", fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtestEalloc"} : (!fir.ref<!fir.box<!fir.heap<f32>>>) -> !fir.ref<!fir.box<!fir.heap<f32>>>
+  %2 = fir.declare %arg0 {uniq_name = "_QFtestEn"} : (!fir.ref<i32>) -> !fir.ref<i32>
+  %3 = fir.alloca !fir.box<!fir.ptr<f32>> {bindc_name = "p0", uniq_name = "_QFtestEp0"}
+  %4 = fir.declare %3 {test.ptr = "p0.fir", fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFtestEp0"} : (!fir.ref<!fir.box<!fir.ptr<f32>>>) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %5 = fir.alloca !fir.box<!fir.ptr<f32>> {bindc_name = "p1", uniq_name = "_QFtestEp1"}
+  %6 = fir.declare %5 {test.ptr = "p1.fir", fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFtestEp1"} : (!fir.ref<!fir.box<!fir.ptr<f32>>>) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %7 = fir.alloca f32 {bindc_name = "t", fir.target, uniq_name = "_QFtestEt"}
+  %8 = fir.declare %7 {test.ptr = "t.fir", fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFtestEt"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %9 = fir.alloca !fir.box<!fir.heap<f32>> {bindc_name = "t_alloc", fir.target, uniq_name = "_QFtestEt_alloc"}
+  %10 = fir.declare %9 {fortran_attrs = #fir.var_attrs<allocatable, target>, uniq_name = "_QFtestEt_alloc"} : (!fir.ref<!fir.box<!fir.heap<f32>>>) -> !fir.ref<!fir.box<!fir.heap<f32>>>
+  %11 = fir.alloca f32 {bindc_name = "v", uniq_name = "_QFtestEv"}
+  %12 = fir.declare %11 {test.ptr = "v.fir", uniq_name = "_QFtestEv"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %13 = fir.load %2 : !fir.ref<i32>
+  %14 = fir.convert %13 : (i32) -> i64
+  %15 = fir.convert %14 : (i64) -> index
+  %c0 = arith.constant 0 : index
+  %16 = arith.cmpi sgt, %15, %c0 : index
+  %17 = arith.select %16, %15, %c0 : index
+  %18 = fir.alloca !fir.array<?xf32>, %17 {bindc_name = "arr", uniq_name = "_QFtestEarr"}
+  %19 = fir.shape %17 : (index) -> !fir.shape<1>
+  %20 = fir.declare %18(%19) {uniq_name = "_QFtestEarr"} : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.ref<!fir.array<?xf32>>
+  %21 = fir.embox %20(%19) : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.box<!fir.array<?xf32>>
+  %22 = fir.load %2 : !fir.ref<i32>
+  %23 = fir.convert %22 : (i32) -> i64
+  %24 = fir.convert %23 : (i64) -> index
+  %c0_0 = arith.constant 0 : index
+  %25 = arith.cmpi sgt, %24, %c0_0 : index
+  %26 = arith.select %25, %24, %c0_0 : index
+  %27 = fir.alloca !fir.array<?xf32>, %26 {bindc_name = "t_arr", fir.target, uniq_name = "_QFtestEt_arr"}
+  %28 = fir.shape %26 : (index) -> !fir.shape<1>
+  %29 = fir.declare %27(%28) {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFtestEt_arr"} : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.ref<!fir.array<?xf32>>
+  %30 = fir.embox %29(%28) : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.box<!fir.array<?xf32>>
+  %31 = fir.load %4 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %32 = fir.box_addr %31 {test.ptr = "p0.tgt.fir"} : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
+  %33 = fir.load %6 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %34 = fir.box_addr %33 {test.ptr = "p1.tgt.fir"} : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
+  %c1 = arith.constant 1 : index
+  %35 = fir.array_coor %20(%19) %c1 {test.ptr="arr(1).fir"} : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>, index) -> !fir.ref<f32>
+  %c1_1 = arith.constant 1 : index
+  %36 = fir.array_coor %29(%28) %c1_1 {test.ptr="t_arr(1).fir"} : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>, index) -> !fir.ref<f32>
+  %37 = fir.load %1 : !fir.ref<!fir.box<!fir.heap<f32>>>
+  %38 = fir.box_addr %37 {test.ptr = "alloc.tgt.fir"} : (!fir.box<!fir.heap<f32>>) -> !fir.heap<f32>
+  %39 = fir.load %10 : !fir.ref<!fir.box<!fir.heap<f32>>>
+  %40 = fir.box_addr %39 {test.ptr = "t_alloc.tgt.fir"} : (!fir.box<!fir.heap<f32>>) -> !fir.heap<f32>
+  return
+}
+
+// -----
+
+// Repeat above test except compare the local pointer against dummy args instead
+// of other locals.
+
+// subroutine test(p1, arr, t_arr, alloc, t_alloc, t, v)
+//   real, pointer :: p1
+//   real :: arr(:)
+//   real, target :: t_arr(:)
+//   real, allocatable :: alloc
+//   real, allocatable, target :: t_alloc
+//   real, target :: t
+//   real :: v
+//   real, pointer :: p0
+// end subroutine test
+
+// CHECK-LABEL: Testing : "_QPtest"
+
+// The address in a pointer can alias the address in another pointer or the
+// address of a target but not the address of other variables.
+// CHECK-DAG: p0.tgt#0 <-> p1.tgt#0: MayAlias
+// CHECK-DAG: t#0 <-> p0.tgt#0: MayAlias
+// CHECK-DAG: t#0 <-> p1.tgt#0: MayAlias
+// CHECK-DAG: v#0 <-> p0.tgt#0: NoAlias
+// CHECK-DAG: v#0 <-> p1.tgt#0: NoAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> p1.tgt.fir#0: MayAlias
+// CHECK-DAG: t.fir#0 <-> p0.tgt.fir#0: MayAlias
+// CHECK-DAG: t.fir#0 <-> p1.tgt.fir#0: MayAlias
+// CHECK-DAG: v.fir#0 <-> p0.tgt.fir#0: NoAlias
+// CHECK-DAG: v.fir#0 <-> p1.tgt.fir#0: NoAlias
+
+// The address in a pointer cannot alias the address of a pointer.
+// CHECK-DAG: p0#0 <-> p0.tgt#0: NoAlias
+// CHECK-DAG: p0#0 <-> p1.tgt#0: NoAlias
+// CHECK-DAG: p1#0 <-> p0.tgt#0: NoAlias
+// CHECK-DAG: p1#0 <-> p1.tgt#0: NoAlias
+// CHECK-DAG: p0.fir#0 <-> p0.tgt.fir#0: NoAlias
+// CHECK-DAG: p0.fir#0 <-> p1.tgt.fir#0: NoAlias
+// CHECK-DAG: p1.fir#0 <-> p0.tgt.fir#0: NoAlias
+// CHECK-DAG: p1.fir#0 <-> p1.tgt.fir#0: NoAlias
+
+// For some cases, AliasAnalysis analyzes hlfir.designate like fir.box_addr, so
+// make sure it doesn't mistakenly see the address of arr(1) as an address that
+// was loaded from a pointer and that could alias something.  However, t_arr is
+// a target.
+// CHECK-DAG: p0.tgt#0 <-> arr(1)#0: NoAlias
+// CHECK-DAG: p0.tgt#0 <-> t_arr(1)#0: MayAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> arr(1).fir#0: NoAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> t_arr(1).fir#0: MayAlias
+
+// Like a pointer, an allocatable contains an address, but an allocatable is not
+// a pointer and so cannot alias pointers.  However, t_alloc is a target.
+// CHECK-DAG: p0.tgt#0 <-> alloc.tgt#0: NoAlias
+// CHECK-DAG: p0.tgt#0 <-> t_alloc.tgt#0: MayAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> alloc.tgt.fir#0: NoAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> t_alloc.tgt.fir#0: MayAlias
+
+// The address in an allocatable cannot alias the address of that allocatable.
+// CHECK-DAG: alloc#0 <-> alloc.tgt#0: NoAlias
+// CHECK-DAG: alloc.fir#0 <-> alloc.tgt.fir#0: NoAlias
+
+func.func @_QPtest(%arg0: !fir.ref<!fir.box<!fir.ptr<f32>>> {fir.bindc_name = "p1"}, %arg1: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "arr"}, %arg2: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "t_arr", fir.target}, %arg3: !fir.ref<!fir.box<!fir.heap<f32>>> {fir.bindc_name = "a...
[truncated]

Copy link

github-actions bot commented Nov 26, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@jdenny-ornl
Copy link
Collaborator Author

ping

@Renaud-K Renaud-K self-requested a review January 6, 2025 23:30
@Renaud-K
Copy link
Contributor

Renaud-K commented Jan 6, 2025

I posted comments a while back, as you can see from the date, but they remained in "Pending" status. My bad.

As discussed at
<llvm#117785 (comment)>,
it breaks a test, marked as T1.
Add a failing test marked as T3.
Add another failing test marked as T4.
Renaud-K added a commit that referenced this pull request Feb 6, 2025
To establish a baseline for new tests mentioned in
#117785, adding them here
independently.
github-actions bot pushed a commit to arm/arm-toolchain that referenced this pull request Feb 6, 2025
…25917)

To establish a baseline for new tests mentioned in
llvm/llvm-project#117785, adding them here
independently.
@jdenny-ornl
Copy link
Collaborator Author

For now, I'm just trying to keep this PR up to date with main so it's easy to reference. I understand it might need to be rewritten after @Renaud-K lands his PRs.

Icohedron pushed a commit to Icohedron/llvm-project that referenced this pull request Feb 11, 2025
To establish a baseline for new tests mentioned in
llvm#117785, adding them here
independently.
Don't introduce T1 and T2 comments as I'm not sure they matter so much
going forward.  As for T3, make it clear what T4 references.
@jdenny-ornl jdenny-ornl merged commit eb8ffd6 into llvm:main Feb 13, 2025
8 checks passed
joaosaffran pushed a commit to joaosaffran/llvm-project that referenced this pull request Feb 14, 2025
For example, determine that the address in p below cannot alias the
address of v:

```
subroutine test()
  real, pointer :: p
  real, target :: t
  real :: v
  p => t
  v = p
end subroutine test
```
@kawashima-fj
Copy link
Member

@jdenny-ornl After this commit, a test which uses Cray pointer fails. Could you take a look?

Before this commit (660cdac):

$ flang Fortran/0734/0734_0018.f90
$ ./a.out
 pass

After this commit (eb8ffd6):

$ flang Fortran/0734/0734_0018.f90
$ ./a.out
 erorr-1
 pass

@jdenny-ornl
Copy link
Collaborator Author

@kawashima-fj Thanks for the report, and sorry for the trouble. I am able to reproduce the issue on a local test machine, and I will investigate. If I don't find a simple resolution today, I will try to revert the change. Let me know if you feel I should revert sooner. Thanks.

@jdenny-ornl
Copy link
Collaborator Author

@jdenny-ornl After this commit, a test which uses Cray pointer fails. Could you take a look?

I don't have experience working with cray pointers or forall loops in fortran. However, what I've read suggests the above test is invalid, and so the result is undefined.

Specifically, the test has:

pointer (j,x)
j=loc(a(2))
forall (i=1:3)
 x(i)=a(i)+1
end forall

Because x(i) is an alias for a(i+1), the forall body performs a(i+1)=a(i)+1. From what I've read, in order to enable optimizations, forall doesn't permit this kind of data dependency among iterations.

Furthermore, on my test system, both gfortran 11.4.0 and 13.2.0 produce the same result for this test as flang does with this PR merged.

Let me know if you believe flang's previous behavior for this test is the only correct behavior.

@kawashima-fj
Copy link
Member

@jdenny-ornl Thanks for your investigation. I will check with my colleague who is familiar with the specifications.

@kawashima-fj
Copy link
Member

@jdenny-ornl I and my colleague confirmed. As you saied, it's an invalid test. I'm sorry to bother you.

@jdenny-ornl
Copy link
Collaborator Author

@kawashima-fj No worries. Thanks for confirming.

sivan-shani pushed a commit to sivan-shani/llvm-project that referenced this pull request Feb 24, 2025
For example, determine that the address in p below cannot alias the
address of v:

```
subroutine test()
  real, pointer :: p
  real, target :: t
  real :: v
  p => t
  v = p
end subroutine test
```
YutongZhuu pushed a commit to YutongZhuu/llvm-project that referenced this pull request Mar 8, 2025
For example, determine that the address in p below cannot alias the
address of v:

```
subroutine test()
  real, pointer :: p
  real, target :: t
  real :: v
  p => t
  v = p
end subroutine test
```
YutongZhuu pushed a commit to YutongZhuu/llvm-project that referenced this pull request Mar 8, 2025
For example, determine that the address in p below cannot alias the
address of v:

```
subroutine test()
  real, pointer :: p
  real, target :: t
  real :: v
  p => t
  v = p
end subroutine test
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang:fir-hlfir flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants