[flang-commits] [flang] [flang] Follow memory source through more operations (PR #66713)

Renaud Kauffmann via flang-commits flang-commits at lists.llvm.org
Tue Sep 19 09:38:13 PDT 2023


https://github.com/Renaud-K updated https://github.com/llvm/llvm-project/pull/66713

>From af689f061df81073ba227e0b8859d39e3c562c09 Mon Sep 17 00:00:00 2001
From: Renaud-K <rkauffmann at nvidia.com>
Date: Mon, 18 Sep 2023 15:11:35 -0700
Subject: [PATCH 1/2] Follow memory source through more operations

---
 .../flang/Optimizer/Analysis/AliasAnalysis.h  |   3 +-
 .../lib/Optimizer/Analysis/AliasAnalysis.cpp  | 112 ++-
 .../AliasAnalysis/alias-analysis-2.fir        |  30 +-
 .../AliasAnalysis/alias-analysis-5.fir        | 671 ++++++++++++++++++
 .../AliasAnalysis/alias-analysis-6.fir        |  24 +
 5 files changed, 805 insertions(+), 35 deletions(-)
 create mode 100644 flang/test/Analysis/AliasAnalysis/alias-analysis-5.fir
 create mode 100644 flang/test/Analysis/AliasAnalysis/alias-analysis-6.fir

diff --git a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
index ddde328f5cb5a4d..e96942abc22f3ee 100644
--- a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
+++ b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
@@ -20,7 +20,7 @@ namespace fir {
 //===----------------------------------------------------------------------===//
 // AliasAnalysis
 //===----------------------------------------------------------------------===//
-class AliasAnalysis {
+struct AliasAnalysis {
   // Structures to describe the memory source of a value.
 
   /// Kind of the memory source referenced by a value.
@@ -81,7 +81,6 @@ class AliasAnalysis {
   friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
                                        const AliasAnalysis::Source &op);
 
-public:
   /// Given two values, return their aliasing behavior.
   mlir::AliasResult alias(mlir::Value lhs, mlir::Value rhs);
 
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 767ef2e8fa7dc08..6f8b8b441c5dfe5 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -36,6 +36,21 @@ static bool isDummyArgument(mlir::Value v) {
   return blockArg.getOwner()->isEntryBlock();
 }
 
+/// 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 {
@@ -80,12 +95,27 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
              llvm::dbgs() << "  rhsSrc: " << rhsSrc << "\n";
              llvm::dbgs() << "\n";);
 
+  // SourceKind::Unknown is set for the addresses wrapped in a global boxes.
+  // ie: fir.global @_QMpointersEp : !fir.box<!fir.ptr<f32>>
+  // Though nothing is known about them, they would only alias with targets or
+  // pointers
+  bool unknownSourceToNonTargetOrPointer = false;
+  if (lhsSrc.u != rhsSrc.u) {
+    if ((lhsSrc.kind == SourceKind::Unknown && !rhsSrc.isTargetOrPointer()) ||
+        (rhsSrc.kind == SourceKind::Unknown && !lhsSrc.isTargetOrPointer())) {
+      unknownSourceToNonTargetOrPointer = true;
+    }
+  }
+
   // Indirect case currently not handled. Conservatively assume
   // it aliases with everything
   if (lhsSrc.kind == SourceKind::Indirect ||
       lhsSrc.kind == SourceKind::Unknown ||
-      rhsSrc.kind == SourceKind::Indirect || rhsSrc.kind == SourceKind::Unknown)
-    return AliasResult::MayAlias;
+      rhsSrc.kind == SourceKind::Indirect ||
+      rhsSrc.kind == SourceKind::Unknown) {
+    if (!unknownSourceToNonTargetOrPointer)
+      return AliasResult::MayAlias;
+  }
 
   if (lhsSrc.kind == rhsSrc.kind) {
     if (lhsSrc.u == rhsSrc.u) {
@@ -103,9 +133,6 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
         lhsSrc.kind == SourceKind::Global)
       return AliasResult::NoAlias;
 
-    assert(lhsSrc.kind == SourceKind::Argument &&
-           "unexpected memory source kind");
-
     // Dummy TARGET/POINTER arguments may alias.
     if (lhsSrc.isTargetOrPointer() && rhsSrc.isTargetOrPointer())
       return AliasResult::MayAlias;
@@ -122,7 +149,7 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
     return AliasResult::NoAlias;
   }
 
-  assert(lhsSrc.kind != rhsSrc.kind && "memory source kinds must be the same");
+  assert(lhsSrc.kind != rhsSrc.kind && "memory source kinds must be different");
 
   Source *src1, *src2;
   if (lhsSrc.kind < rhsSrc.kind) {
@@ -133,18 +160,6 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
     src2 = &lhsSrc;
   }
 
-  assert(src2->kind <= SourceKind::HostAssoc &&
-         "unexpected memory source kind");
-  if (src1->kind == SourceKind::Allocate)
-    return AliasResult::NoAlias;
-
-  assert(((src1->kind == SourceKind::Global &&
-           (src2->kind == SourceKind::Argument ||
-            src2->kind == SourceKind::HostAssoc)) ||
-          (src1->kind == SourceKind::Argument &&
-           src2->kind == SourceKind::HostAssoc)) &&
-         "unexpected memory source kinds");
-
   if (src1->kind == SourceKind::Argument &&
       src2->kind == SourceKind::HostAssoc) {
     // Treat the host entity as TARGET for the purpose of disambiguating
@@ -229,6 +244,7 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) {
   mlir::Type ty;
   bool breakFromLoop{false};
   bool approximateSource{false};
+  bool followBoxAddr{false};
   mlir::SymbolRefAttr global;
   Source::Attributes attributes;
   while (defOp && !breakFromLoop) {
@@ -244,22 +260,74 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) {
           v = op->getOperand(0);
           defOp = v.getDefiningOp();
         })
+        .Case<fir::BoxAddrOp>([&](auto op) {
+          v = op->getOperand(0);
+          defOp = v.getDefiningOp();
+          if (mlir::isa<fir::BaseBoxType>(v.getType()))
+            followBoxAddr = true;
+        })
+        .Case<fir::ArrayCoorOp, fir::CoordinateOp>([&](auto op) {
+          v = op->getOperand(0);
+          defOp = v.getDefiningOp();
+          if (mlir::isa<fir::BaseBoxType>(v.getType()))
+            followBoxAddr = true;
+          approximateSource = true;
+        })
+        .Case<fir::EmboxOp, fir::ReboxOp>([&](auto op) {
+          if (followBoxAddr) {
+            v = op->getOperand(0);
+            defOp = v.getDefiningOp();
+          } else
+            breakFromLoop = true;
+        })
         .Case<fir::LoadOp>([&](auto op) {
-          // No further tracking for addresses loaded from memory (e.g. a box)
-          // right now.
+          if (followBoxAddr && mlir::isa<fir::BaseBoxType>(op.getType())) {
+            // For now, support the load of an argument or fir.address_of
+            // TODO: generalize to all operations (in particular fir.alloca and
+            // fir.allocmem)
+            auto def = getOriginalDef(op.getMemref());
+            if (isDummyArgument(def) ||
+                def.template getDefiningOp<fir::AddrOfOp>()) {
+              v = def;
+              defOp = v.getDefiningOp();
+              return;
+            }
+          }
+          // No further tracking for addresses loaded from memory for now.
           type = SourceKind::Indirect;
           breakFromLoop = true;
         })
         .Case<fir::AddrOfOp>([&](auto op) {
           // Address of a global scope object.
-          type = SourceKind::Global;
           ty = v.getType();
+
+          // When the global is a
+          // fir.global @_QMpointersEp : !fir.box<!fir.ptr<f32>>
+          //   or
+          // fir.global @_QMpointersEp : !fir.box<!fir.heap<f32>>
+          //
+          // and when following through the wrapped address, capture
+          // the fact that there is nothing known about it. Therefore setting
+          // the source to unknown.
+          //
+          // When not following the wrapped address, then consider the address
+          // of the box, which has nothing to do with the wrapped address and
+          // lies in the global memory space.
+          if (followBoxAddr &&
+              mlir::isa<fir::BaseBoxType>(fir::unwrapRefType(ty)))
+            type = SourceKind::Unknown;
+          else
+            type = SourceKind::Global;
+
           if (fir::valueHasFirAttribute(v,
                                         fir::GlobalOp::getTargetAttrNameStr()))
             attributes.set(Attribute::Target);
 
+          // TODO: Take followBoxAddr into account when setting the pointer
+          // attribute
           if (Source::isPointerReference(ty))
             attributes.set(Attribute::Pointer);
+
           global = llvm::cast<fir::AddrOfOp>(op).getSymbol();
           breakFromLoop = true;
         })
@@ -278,7 +346,7 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) {
             breakFromLoop = true;
             return;
           }
-
+          // TODO: Look for the fortran attributes present on the operation
           // Track further through the operand
           v = op.getMemref();
           defOp = v.getDefiningOp();
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir
index ca720cf0ec813a5..31459ef21d947c3 100644
--- a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir
@@ -11,8 +11,11 @@
 // 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
-// CHECK-DAG: p1.addr#0 <-> arg2.addr#0: MayAlias
-// CHECK-DAG: p2.addr#0 <-> arg2.addr#0: MayAlias
+
+// 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 
+// CHECK-DAG: p1.addr#0 <-> arg2.addr#0: NoAlias
+// CHECK-DAG: p2.addr#0 <-> arg2.addr#0: NoAlias
 
 // p1.addr and p2.addr are the result of an allocation
 // They cannot physically alias with an argument
@@ -41,7 +44,7 @@
 // pointer arguments
 // CHECK-DAG: arg2.addr#0 <-> func.region0#0: MayAlias
 // CHECK-DAG: arg2.addr#0 <-> func.region0#1: MayAlias
-// CHECK-DAG: arg2.addr#0 <-> func.region0#2: MayAlias
+// CHECK-DAG: arg2.addr#0 <-> func.region0#2: MustAlias
 // CHECK-DAG: boxp1.addr#0 <-> arg2.addr#0: MayAlias
 
 func.func @_QFPtest(%arg0: !fir.ref<f32> {fir.bindc_name = "v1", fir.target}, %arg1: !fir.ref<f32> {fir.bindc_name = "v2", fir.target}, %arg2: !fir.ref<!fir.box<!fir.ptr<f32>>> ) attributes {test.ptr = "func"} {
@@ -99,7 +102,7 @@ func.func @_QFPtest(%arg0: !fir.ref<f32> {fir.bindc_name = "v1", fir.target}, %a
 // CHECK-DAG: func.region0#0 <-> func.region0#1: MayAlias
 // CHECK-DAG: func.region0#0 <-> func.region0#2: MayAlias
 
-func.func @_QFPtest2(%arg0: !fir.ref<f32> {fir.bindc_name = "v1", fir.target}, %arg1: !fir.ref<!fir.box<!fir.ptr<f32>>>, %arg2: !fir.ref<!fir.ptr<f32>> ) attributes {test.ptr = "func"} {
+func.func @_QFPtest2(%arg0: !fir.ref<f32> {fir.bindc_name = "v1", fir.target}, %arg1: !fir.ref<!fir.box<!fir.ptr<f32>>>, %arg2: !fir.ref<!fir.box<!fir.ptr<f32>>> ) attributes {test.ptr = "func"} {
   return
 }
 
@@ -131,9 +134,14 @@ func.func @_QFPtest2(%arg0: !fir.ref<f32> {fir.bindc_name = "v1", fir.target}, %
 // CHECK-DAG: p#0 <-> func.region0#0: MayAlias
 // CHECK-DAG: p#0 <-> func.region0#1: NoAlias
 
-// FIXME: p and p1 are pointers, they cannot alias with a wrapped address.
-//        Only the addresses they wrap could alias with the address wrapped by the box
-// CHECK-DAG: p#0 <-> box.addr#0: MayAlias
+// p could be pointing to var2 
+// var2, being a target, could also be passed as argument arg0 
+
+// This was the wrong question to ask. We are asking if the address of box _QMpointersEp can 
+// alias with the wrapped scalar _QFEvar2. We meant box_addr of _QMpointersEp 
+// CHECK-DAG: p#0 <-> box.addr#0: NoAlias
+
+// TODO: Still need to handle more gracefully the difference between !fir.ref<!fir.box<>> and !fir.box<> 
 // CHECK-DAG: box.addr#0 <-> func.region0#0: MayAlias
 
 // var2, although it is a target, cannot alias with p
@@ -141,14 +149,14 @@ func.func @_QFPtest2(%arg0: !fir.ref<f32> {fir.bindc_name = "v1", fir.target}, %
 // CHECK-DAG: var2#0 <-> p#0: NoAlias
 // It can alias with p1, if p1 is a pointer component
 // CHECK-DAG: var2#0 <-> func.region0#0: MayAlias
-// It can alias with a box.addr
-// CHECK-DAG: var2#0 <-> box.addr#0: MayAlias
+// It is the same as box.addr
+// CHECK-DAG: var2#0 <-> box.addr#0: MustAlias
 
 // A global may not alias with a dummy
 // CHECK-DAG: var2#0 <-> func.region0#1: NoAlias
 
-// FIXME: a pointer may only alias with a target but arg1 is a regular dummy
-// CHECK-DAG: box.addr#0 <-> func.region0#1: MayAlias
+// A pointer may only alias with a target but arg1 is a regular dummy
+// CHECK-DAG: box.addr#0 <-> func.region0#1: NoAlias
 
 // Dummy argument do not alias
 // CHECK-DAG: func.region0#0 <-> func.region0#1: NoAlias
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-5.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-5.fir
new file mode 100644
index 000000000000000..062413858691204
--- /dev/null
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-5.fir
@@ -0,0 +1,671 @@
+// 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
+
+// CHECK-LABEL: Testing : "_QPtest1"
+// CHECK: arraya(ii)#0 <-> arrayc(ii)#0: NoAlias
+
+// Generated from 
+// subroutine test1(arrayA, arrayB, arrayC, N)
+//   integer, dimension(:) :: arrayA, arrayB, arrayC
+//   integer N
+//   do ii = 1, N
+//     arrayC(ii) = arrayB(ii) + arrayA(ii)
+//   end do
+// end subroutine
+
+func.func @_QPtest1(%arg0: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "arraya"}, %arg1: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "arrayb"}, %arg2: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "arrayc"}, %arg3: !fir.ref<i32> {fir.bindc_name = "n"}) {
+  %c1 = arith.constant 1 : index
+  %c1_i64 = arith.constant 1 : i64
+  %0 = fir.alloca i32 {bindc_name = "ii", uniq_name = "_QFtest1Eii"}
+  %1 = fir.load %arg3 : !fir.ref<i32>
+  %2 = fir.convert %1 : (i32) -> index
+  %3 = fir.convert %c1 : (index) -> i32
+  %4:2 = fir.do_loop %arg4 = %c1 to %2 step %c1 iter_args(%arg5 = %3) -> (index, i32) {
+    fir.store %arg5 to %0 : !fir.ref<i32>
+    %5 = fir.load %0 : !fir.ref<i32>
+    %6 = fir.convert %5 : (i32) -> i64
+    %7 = arith.subi %6, %c1_i64 : i64
+    %8 = fir.coordinate_of %arg1, %7 : (!fir.box<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %9 = fir.load %8 : !fir.ref<i32>
+    %10 = fir.coordinate_of %arg0, %7 {test.ptr = "arraya(ii)"} : (!fir.box<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %11 = fir.load %10 : !fir.ref<i32>
+    %12 = arith.addi %9, %11 : i32
+    %13 = fir.coordinate_of %arg2, %7 {test.ptr = "arrayc(ii)"} : (!fir.box<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    fir.store %12 to %13 : !fir.ref<i32>
+    %14 = arith.addi %arg4, %c1 : index
+    %15 = fir.load %0 : !fir.ref<i32>
+    %16 = arith.addi %15, %3 : i32
+    fir.result %14, %16 : index, i32
+  }
+  fir.store %4#1 to %0 : !fir.ref<i32>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "_QPtest3"
+// CHECK: arraya(ii)#0 <-> arrayc(ii)#0: MayAlias
+
+// subroutine test3(arrayA, arrayB, arrayC, N)
+//   integer, dimension(:), target :: arrayA
+//   integer, dimension(:) :: arrayB
+//   integer, dimension(:), pointer :: arrayC
+//   do ii = 1, N
+//     arrayC(ii) = arrayB(ii) + arrayA(ii)
+//   end do
+// end subroutine
+
+func.func @_QPtest3(%arg0: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "arraya", fir.target}, %arg1: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "arrayb"}, %arg2: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>> {fir.bindc_name = "arrayc"}, %arg3: !fir.ref<i32> {fir.bindc_name = "n"}) {
+  %c1 = arith.constant 1 : index
+  %c0 = arith.constant 0 : index
+  %c1_i64 = arith.constant 1 : i64
+  %0 = fir.alloca i32 {bindc_name = "ii", uniq_name = "_QFtest3Eii"}
+  %1 = fir.load %arg3 : !fir.ref<i32>
+  %2 = fir.convert %1 : (i32) -> index
+  %3 = fir.convert %c1 : (index) -> i32
+  %4:2 = fir.do_loop %arg4 = %c1 to %2 step %c1 iter_args(%arg5 = %3) -> (index, i32) {
+    fir.store %arg5 to %0 : !fir.ref<i32>
+    %5 = fir.load %0 : !fir.ref<i32>
+    %6 = fir.convert %5 : (i32) -> i64
+    %7 = arith.subi %6, %c1_i64 : i64
+    %8 = fir.coordinate_of %arg1, %7 : (!fir.box<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %9 = fir.load %8 : !fir.ref<i32>
+    %10 = fir.coordinate_of %arg0, %7 {test.ptr = "arraya(ii)"} : (!fir.box<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %11 = fir.load %10 : !fir.ref<i32>
+    %12 = arith.addi %9, %11 : i32
+    %13 = fir.load %arg2 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+    %14:3 = fir.box_dims %13, %c0 : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %15 = fir.convert %14#0 : (index) -> i64
+    %16 = arith.subi %6, %15 : i64
+    %17 = fir.coordinate_of %13, %16 {test.ptr = "arrayc(ii)"} : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, i64) -> !fir.ref<i32>
+    fir.store %12 to %17 : !fir.ref<i32>
+    %18 = arith.addi %arg4, %c1 : index
+    %19 = fir.load %0 : !fir.ref<i32>
+    %20 = arith.addi %19, %3 : i32
+    fir.result %18, %20 : index, i32
+  }
+  fir.store %4#1 to %0 : !fir.ref<i32>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "_QMtest4Ptest"
+// CHECK: arraya(ii)#0 <-> arrayc(ii)#0: NoAlias
+
+// module test4
+//   integer, dimension(:), allocatable :: arrayA
+//   integer, dimension(:), allocatable :: arrayB
+//   integer, dimension(:), allocatable :: arrayC
+//   integer :: N
+// contains
+// subroutine test
+//   do ii = 1, N
+//     arrayC(ii) = arrayB(ii) + arrayA(ii)
+//   end do
+// end subroutine
+// endmodule
+
+fir.global @_QMtest4Earraya : !fir.box<!fir.heap<!fir.array<?xi32>>> 
+fir.global @_QMtest4Earrayb : !fir.box<!fir.heap<!fir.array<?xi32>>> 
+fir.global @_QMtest4Earrayc : !fir.box<!fir.heap<!fir.array<?xi32>>> 
+fir.global @_QMtest4En : i32
+
+func.func @_QMtest4Ptest() {
+  %c1 = arith.constant 1 : index
+  %c0 = arith.constant 0 : index
+  %0 = fir.address_of(@_QMtest4Earraya) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+  %1 = fir.address_of(@_QMtest4Earrayb) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+  %2 = fir.address_of(@_QMtest4Earrayc) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+  %3 = fir.address_of(@_QMtest4En) : !fir.ref<i32>
+  %4 = fir.alloca i32 {bindc_name = "ii", uniq_name = "_QMtest4FtestEii"}
+  %5 = fir.load %3 : !fir.ref<i32>
+  %6 = fir.convert %5 : (i32) -> index
+  %7 = fir.convert %c1 : (index) -> i32
+  %8:2 = fir.do_loop %arg0 = %c1 to %6 step %c1 iter_args(%arg1 = %7) -> (index, i32) {
+    fir.store %arg1 to %4 : !fir.ref<i32>
+    %9 = fir.load %1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+    %10:3 = fir.box_dims %9, %c0 : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %11 = fir.box_addr %9 : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+    %12 = fir.load %4 : !fir.ref<i32>
+    %13 = fir.convert %12 : (i32) -> i64
+    %14 = fir.convert %10#0 : (index) -> i64
+    %15 = arith.subi %13, %14 : i64
+    %16 = fir.coordinate_of %11, %15 : (!fir.heap<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %17 = fir.load %16 : !fir.ref<i32>
+    %18 = fir.load %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+    %19:3 = fir.box_dims %18, %c0 : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %20 = fir.box_addr %18 : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+    %21 = fir.convert %19#0 : (index) -> i64
+    %22 = arith.subi %13, %21 : i64
+    %23 = fir.coordinate_of %20, %22 {test.ptr = "arraya(ii)"} : (!fir.heap<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %24 = fir.load %23 : !fir.ref<i32>
+    %25 = arith.addi %17, %24 : i32
+    %26 = fir.load %2 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+    %27:3 = fir.box_dims %26, %c0 : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %28 = fir.box_addr %26 : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+    %29 = fir.convert %27#0 : (index) -> i64
+    %30 = arith.subi %13, %29 : i64
+    %31 = fir.coordinate_of %28, %30 {test.ptr = "arrayc(ii)"} : (!fir.heap<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    fir.store %25 to %31 : !fir.ref<i32>
+    %32 = arith.addi %arg0, %c1 : index
+    %33 = fir.load %4 : !fir.ref<i32>
+    %34 = arith.addi %33, %7 : i32
+    fir.result %32, %34 : index, i32
+  }
+  fir.store %8#1 to %4 : !fir.ref<i32>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "_QMtest5Ptest"
+// CHECK: arraya(ii)#0 <-> arrayc(ii)#0: NoAlias
+
+// module test5
+//   integer, dimension(:), allocatable :: arrayA
+//   integer, dimension(:), allocatable :: arrayB
+//   integer, dimension(:), pointer :: arrayC
+//   integer :: N
+// contains
+// subroutine test
+//   do ii = 1, N
+//     arrayC(ii) = arrayB(ii) + arrayA(ii)
+//   end do
+// end subroutine
+// endmodule
+
+fir.global @_QMtest5Earraya : !fir.box<!fir.heap<!fir.array<?xi32>>> 
+fir.global @_QMtest5Earrayb : !fir.box<!fir.heap<!fir.array<?xi32>>>
+fir.global @_QMtest5Earrayc : !fir.box<!fir.ptr<!fir.array<?xi32>>>
+fir.global @_QMtest5En : i32
+
+func.func @_QMtest5Ptest() {
+  %c1 = arith.constant 1 : index
+  %c0 = arith.constant 0 : index
+  %0 = fir.address_of(@_QMtest5Earraya) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+  %1 = fir.address_of(@_QMtest5Earrayb) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+  %2 = fir.address_of(@_QMtest5Earrayc) : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+  %3 = fir.address_of(@_QMtest5En) : !fir.ref<i32>
+  %4 = fir.alloca i32 {bindc_name = "ii", uniq_name = "_QMtest5FtestEii"}
+  %5 = fir.load %3 : !fir.ref<i32>
+  %6 = fir.convert %5 : (i32) -> index
+  %7 = fir.convert %c1 : (index) -> i32
+  %8:2 = fir.do_loop %arg0 = %c1 to %6 step %c1 iter_args(%arg1 = %7) -> (index, i32) {
+    fir.store %arg1 to %4 : !fir.ref<i32>
+    %9 = fir.load %1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+    %10:3 = fir.box_dims %9, %c0 : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %11 = fir.box_addr %9 : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+    %12 = fir.load %4 : !fir.ref<i32>
+    %13 = fir.convert %12 : (i32) -> i64
+    %14 = fir.convert %10#0 : (index) -> i64
+    %15 = arith.subi %13, %14 : i64
+    %16 = fir.coordinate_of %11, %15 : (!fir.heap<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %17 = fir.load %16 : !fir.ref<i32>
+    %18 = fir.load %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+    %19:3 = fir.box_dims %18, %c0 : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %20 = fir.box_addr %18 : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+    %21 = fir.convert %19#0 : (index) -> i64
+    %22 = arith.subi %13, %21 : i64
+    %23 = fir.coordinate_of %20, %22 {test.ptr = "arraya(ii)"} : (!fir.heap<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %24 = fir.load %23 : !fir.ref<i32>
+    %25 = arith.addi %17, %24 : i32
+    %26 = fir.load %2 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+    %27:3 = fir.box_dims %26, %c0 : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %28 = fir.convert %27#0 : (index) -> i64
+    %29 = arith.subi %13, %28 : i64
+    %30 = fir.coordinate_of %26, %29 {test.ptr = "arrayc(ii)"}  : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, i64) -> !fir.ref<i32>
+    fir.store %25 to %30 : !fir.ref<i32>
+    %31 = arith.addi %arg0, %c1 : index
+    %32 = fir.load %4 : !fir.ref<i32>
+    %33 = arith.addi %32, %7 : i32
+    fir.result %31, %33 : index, i32
+  }
+  fir.store %8#1 to %4 : !fir.ref<i32>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "_QMtest6Ptest"
+// CHECK: arraya(ii)#0 <-> arrayc(ii)#0: MayAlias
+
+// module test6
+//   integer, dimension(:), allocatable, target :: arrayA
+//   integer, dimension(:), allocatable :: arrayB
+//   integer, dimension(:), pointer :: arrayC
+//   integer :: N
+// contains
+// subroutine test
+//   do ii = 1, N
+//     arrayC(ii) = arrayB(ii) + arrayA(ii)
+//   end do
+// end subroutine
+// endmodule
+
+fir.global @_QMtest6Earraya target : !fir.box<!fir.heap<!fir.array<?xi32>>> 
+fir.global @_QMtest6Earrayb : !fir.box<!fir.heap<!fir.array<?xi32>>>
+fir.global @_QMtest6Earrayc : !fir.box<!fir.ptr<!fir.array<?xi32>>>
+fir.global @_QMtest6En : i32
+
+func.func @_QMtest6Ptest() {
+  %c1 = arith.constant 1 : index
+  %c0 = arith.constant 0 : index
+  %0 = fir.address_of(@_QMtest6Earraya) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+  %1 = fir.address_of(@_QMtest6Earrayb) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+  %2 = fir.address_of(@_QMtest6Earrayc) : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+  %3 = fir.address_of(@_QMtest6En) : !fir.ref<i32>
+  %4 = fir.alloca i32 {bindc_name = "ii", uniq_name = "_QMtest6FtestEii"}
+  %5 = fir.load %3 : !fir.ref<i32>
+  %6 = fir.convert %5 : (i32) -> index
+  %7 = fir.convert %c1 : (index) -> i32
+  %8:2 = fir.do_loop %arg0 = %c1 to %6 step %c1 iter_args(%arg1 = %7) -> (index, i32) {
+    fir.store %arg1 to %4 : !fir.ref<i32>
+    %9 = fir.load %1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+    %10:3 = fir.box_dims %9, %c0 : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %11 = fir.box_addr %9 : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+    %12 = fir.load %4 : !fir.ref<i32>
+    %13 = fir.convert %12 : (i32) -> i64
+    %14 = fir.convert %10#0 : (index) -> i64
+    %15 = arith.subi %13, %14 : i64
+    %16 = fir.coordinate_of %11, %15 : (!fir.heap<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %17 = fir.load %16 : !fir.ref<i32>
+    %18 = fir.load %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+    %19:3 = fir.box_dims %18, %c0 : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %20 = fir.box_addr %18 : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+    %21 = fir.convert %19#0 : (index) -> i64
+    %22 = arith.subi %13, %21 : i64
+    %23 = fir.coordinate_of %20, %22 {test.ptr = "arraya(ii)"} : (!fir.heap<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %24 = fir.load %23 : !fir.ref<i32>
+    %25 = arith.addi %17, %24 : i32
+    %26 = fir.load %2 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+    %27:3 = fir.box_dims %26, %c0 : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %28 = fir.convert %27#0 : (index) -> i64
+    %29 = arith.subi %13, %28 : i64
+    %30 = fir.coordinate_of %26, %29 {test.ptr = "arrayc(ii)"} : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, i64) -> !fir.ref<i32>
+    fir.store %25 to %30 : !fir.ref<i32>
+    %31 = arith.addi %arg0, %c1 : index
+    %32 = fir.load %4 : !fir.ref<i32>
+    %33 = arith.addi %32, %7 : i32
+    fir.result %31, %33 : index, i32
+  }
+  fir.store %8#1 to %4 : !fir.ref<i32>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "_QMtest7Ptest"
+// CHECK: arraya(ii)#0 <-> arrayc(ii)#0: MayAlias
+
+// module test7
+//   integer, dimension(1000), target :: arrayA
+//   integer, dimension(:), allocatable :: arrayB
+//   integer, dimension(:), pointer :: arrayC
+//   integer :: N
+// contains
+// subroutine test
+//   do ii = 1, N
+//     arrayC(ii) = arrayB(ii) + arrayA(ii)
+//   end do
+// end subroutine
+// endmodule
+
+fir.global @_QMtest7Earraya target : !fir.array<1000xi32> 
+fir.global @_QMtest7Earrayb : !fir.box<!fir.heap<!fir.array<?xi32>>> 
+fir.global @_QMtest7Earrayc : !fir.box<!fir.ptr<!fir.array<?xi32>>> 
+fir.global @_QMtest7En : i32 
+
+func.func @_QMtest7Ptest() {
+  %c1 = arith.constant 1 : index
+  %c1_i64 = arith.constant 1 : i64
+  %c0 = arith.constant 0 : index
+  %0 = fir.address_of(@_QMtest7Earraya) : !fir.ref<!fir.array<1000xi32>>
+  %1 = fir.address_of(@_QMtest7Earrayb) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+  %2 = fir.address_of(@_QMtest7Earrayc) : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+  %3 = fir.address_of(@_QMtest7En) : !fir.ref<i32>
+  %4 = fir.alloca i32 {bindc_name = "ii", uniq_name = "_QMtest7FtestEii"}
+  %5 = fir.load %3 : !fir.ref<i32>
+  %6 = fir.convert %5 : (i32) -> index
+  %7 = fir.convert %c1 : (index) -> i32
+  %8:2 = fir.do_loop %arg0 = %c1 to %6 step %c1 iter_args(%arg1 = %7) -> (index, i32) {
+    fir.store %arg1 to %4 : !fir.ref<i32>
+    %9 = fir.load %1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+    %10:3 = fir.box_dims %9, %c0 : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %11 = fir.box_addr %9 : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+    %12 = fir.load %4 : !fir.ref<i32>
+    %13 = fir.convert %12 : (i32) -> i64
+    %14 = fir.convert %10#0 : (index) -> i64
+    %15 = arith.subi %13, %14 : i64
+    %16 = fir.coordinate_of %11, %15 : (!fir.heap<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %17 = fir.load %16 : !fir.ref<i32>
+    %18 = arith.subi %13, %c1_i64 : i64
+    %19 = fir.coordinate_of %0, %18 {test.ptr = "arraya(ii)"} : (!fir.ref<!fir.array<1000xi32>>, i64) -> !fir.ref<i32>
+    %20 = fir.load %19 : !fir.ref<i32>
+    %21 = arith.addi %17, %20 : i32
+    %22 = fir.load %2 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+    %23:3 = fir.box_dims %22, %c0 : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %24 = fir.convert %23#0 : (index) -> i64
+    %25 = arith.subi %13, %24 : i64
+    %26 = fir.coordinate_of %22, %25 {test.ptr = "arrayc(ii)"} : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, i64) -> !fir.ref<i32>
+    fir.store %21 to %26 : !fir.ref<i32>
+    %27 = arith.addi %arg0, %c1 : index
+    %28 = fir.load %4 : !fir.ref<i32>
+    %29 = arith.addi %28, %7 : i32
+    fir.result %27, %29 : index, i32
+  }
+  fir.store %8#1 to %4 : !fir.ref<i32>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "_QMtest8Ptest"
+// CHECK: arraya(ii)#0 <-> arrayc(ii)#0: NoAlias
+
+// module test8
+//   integer, dimension(1000) :: arrayA
+//   integer, dimension(:), allocatable :: arrayB
+//   integer, dimension(:), pointer :: arrayC
+//   integer :: N
+// contains
+// subroutine test
+//   do ii = 1, N
+//     arrayC(ii) = arrayB(ii) + arrayA(ii)
+//   end do
+// end subroutine
+// endmodule
+
+fir.global @_QMtest8Earraya : !fir.array<1000xi32> 
+fir.global @_QMtest8Earrayb : !fir.box<!fir.heap<!fir.array<?xi32>>> 
+fir.global @_QMtest8Earrayc : !fir.box<!fir.ptr<!fir.array<?xi32>>> 
+fir.global @_QMtest8En : i32 
+
+func.func @_QMtest8Ptest() {
+  %c1 = arith.constant 1 : index
+  %c1_i64 = arith.constant 1 : i64
+  %c0 = arith.constant 0 : index
+  %0 = fir.address_of(@_QMtest8Earraya) : !fir.ref<!fir.array<1000xi32>>
+  %1 = fir.address_of(@_QMtest8Earrayb) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+  %2 = fir.address_of(@_QMtest8Earrayc) : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+  %3 = fir.address_of(@_QMtest8En) : !fir.ref<i32>
+  %4 = fir.alloca i32 {bindc_name = "ii", uniq_name = "_QMtest8FtestEii"}
+  %5 = fir.load %3 : !fir.ref<i32>
+  %6 = fir.convert %5 : (i32) -> index
+  %7 = fir.convert %c1 : (index) -> i32
+  %8:2 = fir.do_loop %arg0 = %c1 to %6 step %c1 iter_args(%arg1 = %7) -> (index, i32) {
+    fir.store %arg1 to %4 : !fir.ref<i32>
+    %9 = fir.load %1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+    %10:3 = fir.box_dims %9, %c0 : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %11 = fir.box_addr %9 : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+    %12 = fir.load %4 : !fir.ref<i32>
+    %13 = fir.convert %12 : (i32) -> i64
+    %14 = fir.convert %10#0 : (index) -> i64
+    %15 = arith.subi %13, %14 : i64
+    %16 = fir.coordinate_of %11, %15 : (!fir.heap<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %17 = fir.load %16 : !fir.ref<i32>
+    %18 = arith.subi %13, %c1_i64 : i64
+    %19 = fir.coordinate_of %0, %18 {test.ptr = "arraya(ii)"} : (!fir.ref<!fir.array<1000xi32>>, i64) -> !fir.ref<i32>
+    %20 = fir.load %19 : !fir.ref<i32>
+    %21 = arith.addi %17, %20 : i32
+    %22 = fir.load %2 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+    %23:3 = fir.box_dims %22, %c0 : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %24 = fir.convert %23#0 : (index) -> i64
+    %25 = arith.subi %13, %24 : i64
+    %26 = fir.coordinate_of %22, %25 {test.ptr = "arrayc(ii)"} : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, i64) -> !fir.ref<i32>
+    fir.store %21 to %26 : !fir.ref<i32>
+    %27 = arith.addi %arg0, %c1 : index
+    %28 = fir.load %4 : !fir.ref<i32>
+    %29 = arith.addi %28, %7 : i32
+    fir.result %27, %29 : index, i32
+  }
+  fir.store %8#1 to %4 : !fir.ref<i32>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "_QMtest9Ptest"
+// CHECK: arraya(ii)#0 <-> arrayc(ii)#0: NoAlias
+
+// module test9
+//   integer, dimension(:), allocatable :: arrayB
+//   integer, dimension(:), pointer :: arrayC
+//   integer :: N
+// contains
+// subroutine test(arrayA)
+//   integer, dimension(:) :: arrayA
+//   do ii = 1, N
+//     arrayC(ii) = arrayB(ii) + arrayA(ii)
+//   end do
+// end subroutine
+// endmodule
+
+fir.global @_QMtest9Earrayb : !fir.box<!fir.heap<!fir.array<?xi32>>> 
+fir.global @_QMtest9Earrayc : !fir.box<!fir.ptr<!fir.array<?xi32>>>
+fir.global @_QMtest9En : i32
+
+func.func @_QMtest9Ptest(%arg0: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "arraya"}) {
+  %c1 = arith.constant 1 : index
+  %c1_i64 = arith.constant 1 : i64
+  %c0 = arith.constant 0 : index
+  %0 = fir.address_of(@_QMtest9Earrayb) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+  %1 = fir.address_of(@_QMtest9Earrayc) : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+  %2 = fir.address_of(@_QMtest9En) : !fir.ref<i32>
+  %3 = fir.alloca i32 {bindc_name = "ii", uniq_name = "_QMtest9FtestEii"}
+  %4 = fir.load %2 : !fir.ref<i32>
+  %5 = fir.convert %4 : (i32) -> index
+  %6 = fir.convert %c1 : (index) -> i32
+  %7:2 = fir.do_loop %arg1 = %c1 to %5 step %c1 iter_args(%arg2 = %6) -> (index, i32) {
+    fir.store %arg2 to %3 : !fir.ref<i32>
+    %8 = fir.load %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+    %9:3 = fir.box_dims %8, %c0 : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %10 = fir.box_addr %8 : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+    %11 = fir.load %3 : !fir.ref<i32>
+    %12 = fir.convert %11 : (i32) -> i64
+    %13 = fir.convert %9#0 : (index) -> i64
+    %14 = arith.subi %12, %13 : i64
+    %15 = fir.coordinate_of %10, %14 : (!fir.heap<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %16 = fir.load %15 : !fir.ref<i32>
+    %17 = arith.subi %12, %c1_i64 : i64
+    %18 = fir.coordinate_of %arg0, %17 {test.ptr = "arraya(ii)"} : (!fir.box<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %19 = fir.load %18 : !fir.ref<i32>
+    %20 = arith.addi %16, %19 : i32
+    %21 = fir.load %1 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+    %22:3 = fir.box_dims %21, %c0 : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %23 = fir.convert %22#0 : (index) -> i64
+    %24 = arith.subi %12, %23 : i64
+    %25 = fir.coordinate_of %21, %24 {test.ptr = "arrayc(ii)"} : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, i64) -> !fir.ref<i32>
+    fir.store %20 to %25 : !fir.ref<i32>
+    %26 = arith.addi %arg1, %c1 : index
+    %27 = fir.load %3 : !fir.ref<i32>
+    %28 = arith.addi %27, %6 : i32
+    fir.result %26, %28 : index, i32
+  }
+  fir.store %7#1 to %3 : !fir.ref<i32>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "_QMtest10Ptest"
+// CHECK: arraya(ii)#0 <-> arrayc(ii)#0: MayAlias
+
+// module test10
+//   integer, dimension(:), allocatable :: arrayB
+//   integer, dimension(:), pointer :: arrayC
+//   integer :: N
+// contains
+// subroutine test(arrayA)
+//   integer, dimension(:), target :: arrayA
+//   do ii = 1, N
+//     arrayC(ii) = arrayB(ii) + arrayA(ii)
+//   end do
+// end subroutine
+// endmodule
+
+fir.global @_QMtest10Earrayb : !fir.box<!fir.heap<!fir.array<?xi32>>> 
+fir.global @_QMtest10Earrayc : !fir.box<!fir.ptr<!fir.array<?xi32>>> 
+fir.global @_QMtest10En : i32
+
+func.func @_QMtest10Ptest(%arg0: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "arraya", fir.target}) {
+  %c1 = arith.constant 1 : index
+  %c1_i64 = arith.constant 1 : i64
+  %c0 = arith.constant 0 : index
+  %0 = fir.address_of(@_QMtest10Earrayb) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+  %1 = fir.address_of(@_QMtest10Earrayc) : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+  %2 = fir.address_of(@_QMtest10En) : !fir.ref<i32>
+  %3 = fir.alloca i32 {bindc_name = "ii", uniq_name = "_QMtest10FtestEii"}
+  %4 = fir.load %2 : !fir.ref<i32>
+  %5 = fir.convert %4 : (i32) -> index
+  %6 = fir.convert %c1 : (index) -> i32
+  %7:2 = fir.do_loop %arg1 = %c1 to %5 step %c1 iter_args(%arg2 = %6) -> (index, i32) {
+    fir.store %arg2 to %3 : !fir.ref<i32>
+    %8 = fir.load %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+    %9:3 = fir.box_dims %8, %c0 : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %10 = fir.box_addr %8 : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+    %11 = fir.load %3 : !fir.ref<i32>
+    %12 = fir.convert %11 : (i32) -> i64
+    %13 = fir.convert %9#0 : (index) -> i64
+    %14 = arith.subi %12, %13 : i64
+    %15 = fir.coordinate_of %10, %14 : (!fir.heap<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %16 = fir.load %15 : !fir.ref<i32>
+    %17 = arith.subi %12, %c1_i64 : i64
+    %18 = fir.coordinate_of %arg0, %17 {test.ptr = "arraya(ii)"} : (!fir.box<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %19 = fir.load %18 : !fir.ref<i32>
+    %20 = arith.addi %16, %19 : i32
+    %21 = fir.load %1 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+    %22:3 = fir.box_dims %21, %c0 : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %23 = fir.convert %22#0 : (index) -> i64
+    %24 = arith.subi %12, %23 : i64
+    %25 = fir.coordinate_of %21, %24 {test.ptr = "arrayc(ii)"} : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, i64) -> !fir.ref<i32>
+    fir.store %20 to %25 : !fir.ref<i32>
+    %26 = arith.addi %arg1, %c1 : index
+    %27 = fir.load %3 : !fir.ref<i32>
+    %28 = arith.addi %27, %6 : i32
+    fir.result %26, %28 : index, i32
+  }
+  fir.store %7#1 to %3 : !fir.ref<i32>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "_QMtest11Ptest"
+// CHECK: arraya(ii)#0 <-> arrayc(ii)#0: MayAlias
+
+// module test11
+//   integer, dimension(:), allocatable :: arrayB
+//   integer, dimension(1000), target :: arrayC
+//   integer :: N
+// contains
+// subroutine test(arrayA)
+//   integer, dimension(:), target :: arrayA
+//   do ii = 1, N
+//     arrayC(ii) = arrayB(ii) + arrayA(ii)
+//   end do
+// end subroutine
+// endmodule
+
+fir.global @_QMtest11Earrayb : !fir.box<!fir.heap<!fir.array<?xi32>>> 
+fir.global @_QMtest11Earrayc target : !fir.array<1000xi32> 
+fir.global @_QMtest11En : i32 
+
+func.func @_QMtest11Ptest(%arg0: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "arraya", fir.target}) {
+  %c1 = arith.constant 1 : index
+  %c1_i64 = arith.constant 1 : i64
+  %c0 = arith.constant 0 : index
+  %0 = fir.address_of(@_QMtest11Earrayb) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+  %1 = fir.address_of(@_QMtest11Earrayc) : !fir.ref<!fir.array<1000xi32>>
+  %2 = fir.address_of(@_QMtest11En) : !fir.ref<i32>
+  %3 = fir.alloca i32 {bindc_name = "ii", uniq_name = "_QMtest11FtestEii"}
+  %4 = fir.load %2 : !fir.ref<i32>
+  %5 = fir.convert %4 : (i32) -> index
+  %6 = fir.convert %c1 : (index) -> i32
+  %7:2 = fir.do_loop %arg1 = %c1 to %5 step %c1 iter_args(%arg2 = %6) -> (index, i32) {
+    fir.store %arg2 to %3 : !fir.ref<i32>
+    %8 = fir.load %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+    %9:3 = fir.box_dims %8, %c0 : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %10 = fir.box_addr %8 : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+    %11 = fir.load %3 : !fir.ref<i32>
+    %12 = fir.convert %11 : (i32) -> i64
+    %13 = fir.convert %9#0 : (index) -> i64
+    %14 = arith.subi %12, %13 : i64
+    %15 = fir.coordinate_of %10, %14 : (!fir.heap<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %16 = fir.load %15 : !fir.ref<i32>
+    %17 = arith.subi %12, %c1_i64 : i64
+    %18 = fir.coordinate_of %arg0, %17 {test.ptr = "arraya(ii)"} : (!fir.box<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %19 = fir.load %18 : !fir.ref<i32>
+    %20 = arith.addi %16, %19 : i32
+    %21 = fir.coordinate_of %1, %17 {test.ptr = "arrayc(ii)"} : (!fir.ref<!fir.array<1000xi32>>, i64) -> !fir.ref<i32>
+    fir.store %20 to %21 : !fir.ref<i32>
+    %22 = arith.addi %arg1, %c1 : index
+    %23 = fir.load %3 : !fir.ref<i32>
+    %24 = arith.addi %23, %6 : i32
+    fir.result %22, %24 : index, i32
+  }
+  fir.store %7#1 to %3 : !fir.ref<i32>
+  return
+}
+
+
+// -----
+
+// CHECK-LABEL: Testing : "_QMtest12Ptest"
+// CHECK: arraya(ii)#0 <-> arrayc(ii)#0: NoAlias
+
+// module test12
+//   integer, dimension(:), allocatable :: arrayB
+//   integer, dimension(1000) :: arrayC
+//   integer :: N
+// contains
+// subroutine test(arrayA)
+//   integer, dimension(:), target :: arrayA
+//   do ii = 1, N
+//     arrayC(ii) = arrayB(ii) + arrayA(ii)
+//   end do
+// end subroutine
+// endmodule
+
+fir.global @_QMtest12Earrayb : !fir.box<!fir.heap<!fir.array<?xi32>>>
+fir.global @_QMtest12Earrayc : !fir.array<1000xi32> 
+fir.global @_QMtest12En : i32
+
+func.func @_QMtest12Ptest(%arg0: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "arraya", fir.target}) {
+  %c1 = arith.constant 1 : index
+  %c1_i64 = arith.constant 1 : i64
+  %c0 = arith.constant 0 : index
+  %0 = fir.address_of(@_QMtest12Earrayb) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+  %1 = fir.address_of(@_QMtest12Earrayc) : !fir.ref<!fir.array<1000xi32>>
+  %2 = fir.address_of(@_QMtest12En) : !fir.ref<i32>
+  %3 = fir.alloca i32 {bindc_name = "ii", uniq_name = "_QMtest12FtestEii"}
+  %4 = fir.load %2 : !fir.ref<i32>
+  %5 = fir.convert %4 : (i32) -> index
+  %6 = fir.convert %c1 : (index) -> i32
+  %7:2 = fir.do_loop %arg1 = %c1 to %5 step %c1 iter_args(%arg2 = %6) -> (index, i32) {
+    fir.store %arg2 to %3 : !fir.ref<i32>
+    %8 = fir.load %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+    %9:3 = fir.box_dims %8, %c0 : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+    %10 = fir.box_addr %8 : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+    %11 = fir.load %3 : !fir.ref<i32>
+    %12 = fir.convert %11 : (i32) -> i64
+    %13 = fir.convert %9#0 : (index) -> i64
+    %14 = arith.subi %12, %13 : i64
+    %15 = fir.coordinate_of %10, %14 : (!fir.heap<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %16 = fir.load %15 : !fir.ref<i32>
+    %17 = arith.subi %12, %c1_i64 : i64
+    %18 = fir.coordinate_of %arg0, %17 {test.ptr = "arraya(ii)"} : (!fir.box<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
+    %19 = fir.load %18 : !fir.ref<i32>
+    %20 = arith.addi %16, %19 : i32
+    %21 = fir.coordinate_of %1, %17 {test.ptr = "arrayc(ii)"} : (!fir.ref<!fir.array<1000xi32>>, i64) -> !fir.ref<i32>
+    fir.store %20 to %21 : !fir.ref<i32>
+    %22 = arith.addi %arg1, %c1 : index
+    %23 = fir.load %3 : !fir.ref<i32>
+    %24 = arith.addi %23, %6 : i32
+    fir.result %22, %24 : index, i32
+  }
+  fir.store %7#1 to %3 : !fir.ref<i32>
+  return
+}
+
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-6.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-6.fir
new file mode 100644
index 000000000000000..82d89989c9cf726
--- /dev/null
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-6.fir
@@ -0,0 +1,24 @@
+// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))'
+
+// CHECK: test_y(1)#0 <-> test_x(1)#0: MayAlias
+func.func @_QPtest(%arg0: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>> {fir.bindc_name = "y"}) {
+  %c1 = arith.constant 1 : index
+  %0 = fir.address_of(@_QMdataEx) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
+  %1 = fir.declare %0 {fortran_attrs = #fir.var_attrs<allocatable, target>, uniq_name = "_QMdataEx"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
+  %2 = fir.declare %arg0 {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFtestEy"} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
+  %3 = fir.load %2 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
+  %c0 = arith.constant 0 : index
+  %4:3 = fir.box_dims %3, %c0 : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, index) -> (index, index, index)
+  %5 = fir.shift %4#0 : (index) -> !fir.shift<1>
+  %6 = fir.array_coor %3(%5) %c1 {test.ptr = "test_y(1)"} : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, !fir.shift<1>, index) -> !fir.ref<f32>
+  %7 = fir.load %6 : !fir.ref<f32>
+  %8 = fir.load %1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
+  %9 = fir.box_addr %8 : (!fir.box<!fir.heap<!fir.array<?xf32>>>) -> !fir.heap<!fir.array<?xf32>>
+  %10:3 = fir.box_dims %8, %c0 : (!fir.box<!fir.heap<!fir.array<?xf32>>>, index) -> (index, index, index)
+  %11 = fir.shape_shift %10#0, %10#1 : (index, index) -> !fir.shapeshift<1>
+  %12 = fir.array_coor %9(%11) %c1 {test.ptr = "test_x(1)"} : (!fir.heap<!fir.array<?xf32>>, !fir.shapeshift<1>, index) -> !fir.ref<f32>
+  fir.store %7 to %12 : !fir.ref<f32>
+  return
+}
+fir.global @_QMdataEx target : !fir.box<!fir.heap<!fir.array<?xf32>>> 
+

>From da2d755dde7eb2e6827111f21747894a856374c2 Mon Sep 17 00:00:00 2001
From: Renaud-K <rkauffmann at nvidia.com>
Date: Tue, 19 Sep 2023 09:37:52 -0700
Subject: [PATCH 2/2] Introducing the Direct SourceKind to contrast with the
 Unknown and Indirect kinds

---
 .../flang/Optimizer/Analysis/AliasAnalysis.h  |  6 +++-
 .../lib/Optimizer/Analysis/AliasAnalysis.cpp  | 31 ++++++++++---------
 2 files changed, 21 insertions(+), 16 deletions(-)

diff --git a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
index e96942abc22f3ee..dfcafe88fee1b5d 100644
--- a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
+++ b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
@@ -36,11 +36,15 @@ struct AliasAnalysis {
              /// Represents memory allocated outside of a function
              /// and passed to the function via host association tuple.
              HostAssoc,
+             /// Represents direct memory access whose source cannot be further
+             /// determined
+             Direct,
              /// Represents memory allocated by unknown means and
              /// with the memory address defined by a memory reading
              /// operation (e.g. fir::LoadOp).
              Indirect,
-             /// Represents memory allocated by unknown means.
+             /// Starting point to the analysis whereby nothing is known about
+             /// the source
              Unknown);
 
   /// Attributes of the memory source object.
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 6f8b8b441c5dfe5..aeb6e692784f769 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -95,25 +95,26 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
              llvm::dbgs() << "  rhsSrc: " << rhsSrc << "\n";
              llvm::dbgs() << "\n";);
 
-  // SourceKind::Unknown is set for the addresses wrapped in a global boxes.
+  // Indirect case currently not handled. Conservatively assume
+  // it aliases with everything
+  if (lhsSrc.kind > SourceKind::Direct || rhsSrc.kind > SourceKind::Direct) {
+    return AliasResult::MayAlias;
+  }
+
+  // SourceKind::Direct is set for the addresses wrapped in a global boxes.
   // ie: fir.global @_QMpointersEp : !fir.box<!fir.ptr<f32>>
   // Though nothing is known about them, they would only alias with targets or
   // pointers
-  bool unknownSourceToNonTargetOrPointer = false;
+  bool directSourceToNonTargetOrPointer = false;
   if (lhsSrc.u != rhsSrc.u) {
-    if ((lhsSrc.kind == SourceKind::Unknown && !rhsSrc.isTargetOrPointer()) ||
-        (rhsSrc.kind == SourceKind::Unknown && !lhsSrc.isTargetOrPointer())) {
-      unknownSourceToNonTargetOrPointer = true;
-    }
+    if ((lhsSrc.kind == SourceKind::Direct && !rhsSrc.isTargetOrPointer()) ||
+        (rhsSrc.kind == SourceKind::Direct && !lhsSrc.isTargetOrPointer()))
+      directSourceToNonTargetOrPointer = true;
   }
 
-  // Indirect case currently not handled. Conservatively assume
-  // it aliases with everything
-  if (lhsSrc.kind == SourceKind::Indirect ||
-      lhsSrc.kind == SourceKind::Unknown ||
-      rhsSrc.kind == SourceKind::Indirect ||
-      rhsSrc.kind == SourceKind::Unknown) {
-    if (!unknownSourceToNonTargetOrPointer)
+  if (lhsSrc.kind == SourceKind::Direct ||
+      rhsSrc.kind == SourceKind::Direct) {
+    if (!directSourceToNonTargetOrPointer)
       return AliasResult::MayAlias;
   }
 
@@ -308,14 +309,14 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) {
           //
           // and when following through the wrapped address, capture
           // the fact that there is nothing known about it. Therefore setting
-          // the source to unknown.
+          // the source to Direct.
           //
           // When not following the wrapped address, then consider the address
           // of the box, which has nothing to do with the wrapped address and
           // lies in the global memory space.
           if (followBoxAddr &&
               mlir::isa<fir::BaseBoxType>(fir::unwrapRefType(ty)))
-            type = SourceKind::Unknown;
+            type = SourceKind::Direct;
           else
             type = SourceKind::Global;
 



More information about the flang-commits mailing list