[flang-commits] [flang] WIP: [flang] AliasAnalysis: Fix pointer component logic (PR #94242)

Joel E. Denny via flang-commits flang-commits at lists.llvm.org
Wed Jun 5 09:19:49 PDT 2024


https://github.com/jdenny-ornl updated https://github.com/llvm/llvm-project/pull/94242

>From 484cb901cb3f5ae934b55629bb8537e42d3a832d Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Tue, 28 May 2024 19:14:22 -0400
Subject: [PATCH 1/3] WIP: [flang] AliasAnalysis: Fix pointer component logic

---
 .../lib/Optimizer/Analysis/AliasAnalysis.cpp  | 30 ++++++++++++-------
 .../AliasAnalysis/alias-analysis-3.fir        | 19 ++++++------
 .../AliasAnalysis/alias-analysis-9.fir        | 12 ++++----
 3 files changed, 34 insertions(+), 27 deletions(-)

diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 9d0d706a85c5e..571b78dc64aa5 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -122,6 +122,18 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
   }
 
   if (lhsSrc.kind == rhsSrc.kind) {
+    // If the kinds and origins are the same, then lhs and rhs must alias unless
+    // either source is approximate.  Approximate sources are for parts of the
+    // origin, but we don't have info here on which parts and whether they
+    // overlap, so we normally return MayAlias in that case.
+    //
+    // There is an exceptional case: The origins might compare unequal because
+    // only one has !isData().  If that source is approximate and the other is
+    // not, then the former is the source for the address *of* a pointer
+    // component in a composite, and the latter is for the address of that
+    // composite.  As for the address of any composite vs. the address of one of
+    // its components, a store to one can affect a load from the other, so the
+    // result is MayAlias.
     if (lhsSrc.origin == rhsSrc.origin) {
       LLVM_DEBUG(llvm::dbgs()
                  << "  aliasing because same source kind and origin\n");
@@ -129,6 +141,13 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
         return AliasResult::MayAlias;
       return AliasResult::MustAlias;
     }
+    if (lhsSrc.origin.u == rhsSrc.origin.u &&
+        ((lhsSrc.approximateSource && !lhsSrc.isData() && !rhsSrc.approximateSource) ||
+         (rhsSrc.approximateSource && !rhsSrc.isData() && !lhsSrc.approximateSource))) {
+      LLVM_DEBUG(llvm::dbgs()
+                 << "  aliasing between composite and pointer component\n");
+      return AliasResult::MayAlias;
+    }
 
     // Two host associated accesses may overlap due to an equivalence.
     if (lhsSrc.kind == SourceKind::HostAssoc) {
@@ -173,17 +192,6 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
     return AliasResult::MayAlias;
   }
 
-  // Box for POINTER component inside an object of a derived type
-  // may alias box of a POINTER object, as well as boxes for POINTER
-  // components inside two objects of derived types may alias.
-  if ((src1->isRecordWithPointerComponent() && src2->isTargetOrPointer()) ||
-      (src2->isRecordWithPointerComponent() && src1->isTargetOrPointer()) ||
-      (src1->isRecordWithPointerComponent() &&
-       src2->isRecordWithPointerComponent())) {
-    LLVM_DEBUG(llvm::dbgs() << "  aliasing because of pointer components\n");
-    return AliasResult::MayAlias;
-  }
-
   return AliasResult::NoAlias;
 }
 
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir
index 91829a461dc72..2bf326f2d2af2 100644
--- a/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir
@@ -16,12 +16,12 @@
 //   end subroutine test
 // end module m
 
-// A composite with a pointer component may alias with a dummy pointer
+// A composite with a pointer component does not alias with a dummy pointer
 // CHECK-LABEL: Testing : "_QMmPtest
-// CHECK: a#0 <-> func.region0#0: MayAlias
+// CHECK: a#0 <-> func.region0#0: NoAlias
 
-// FIXME: a's box cannot alias with raw reference to f32 (x), so MayAlias below must be NoAlias:
-// CHECK: a#0 <-> func.region0#1: MayAlias
+// a's box cannot alias with raw reference to f32 (x)
+// CHECK: a#0 <-> func.region0#1: NoAlias
 
 // pointer_dummy's box cannot alias with raw reference to f32 (x)
 // CHECK: func.region0#0 <-> func.region0#1: NoAlias
@@ -46,7 +46,7 @@ func.func private @_QPtest2(!fir.ref<f32>)
 
 // -----
 
-// A composite with a pointer component may alias with a dummy
+// A composite with a pointer component does not alias with a dummy
 // argument of composite type with a pointer component:
 // module m
 //   type t
@@ -63,7 +63,7 @@ func.func private @_QPtest2(!fir.ref<f32>)
 // end module m
 
 // CHECK-LABEL: Testing : "_QMmPtest"
-// CHECK: a#0 <-> func.region0#0: MayAlias
+// CHECK: a#0 <-> func.region0#0: NoAlias
 
 fir.global @_QMmEa : !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}> {
   %0 = fir.undefined !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>
@@ -88,7 +88,7 @@ func.func private @_QPtest2(!fir.ref<f32>)
 // -----
 
 // Two dummy arguments of composite type with a pointer component
-// may alias each other:
+// do not alias each other:
 // module m
 //   type t
 //      real, pointer :: pointer_component
@@ -103,7 +103,7 @@ func.func private @_QPtest2(!fir.ref<f32>)
 // end module m
 
 // CHECK-LABEL: Testing : "_QMmPtest"
-// CHECK: func.region0#0 <-> func.region0#1: MayAlias
+// CHECK: func.region0#0 <-> func.region0#1: NoAlias
 
 func.func @_QMmPtest(%arg0: !fir.ref<!fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>> {fir.bindc_name = "a"}, %arg1: !fir.ref<!fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>> {fir.bindc_name = "b"}, %arg2: !fir.ref<f32> {fir.bindc_name = "x", fir.target}) attributes {test.ptr = "func"} {
   %0 = fir.field_index pointer_component, !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>
@@ -137,8 +137,7 @@ func.func private @_QPtest2(!fir.ref<f32>)
 // end module m
 
 // CHECK-LABEL: Testing : "_QMmPtest"
-// FIXME: MayAlias must be NoAlias
-// CHECK: func.region0#0 <-> func.region0#1: MayAlias
+// CHECK: func.region0#0 <-> func.region0#1: NoAlias
 
 func.func @_QMmPtest(%arg0: !fir.ref<!fir.type<_QMmTt{allocatable_component:!fir.box<!fir.heap<f32>>}>> {fir.bindc_name = "a"}, %arg1: !fir.ref<!fir.type<_QMmTt{allocatable_component:!fir.box<!fir.heap<f32>>}>> {fir.bindc_name = "b"}) attributes {test.ptr = "func"} {
   %0 = fir.field_index allocatable_component, !fir.type<_QMmTt{allocatable_component:!fir.box<!fir.heap<f32>>}>
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
index df24a6d32aa59..67ea4d20caa60 100644
--- a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
@@ -17,13 +17,13 @@
 // end module
 
 // CHECK-LABEL: Testing : "_QMmPfoo"
-// TODO: x and y are non pointer, non target argument and therefore do not alias.
-// CHECK-DAG: x#0 <-> y#0: MayAlias
+// x and y are non pointer, non target argument and therefore do not alias.
+// CHECK-DAG: x#0 <-> y#0: NoAlias
 
-// TODO: y is not a pointer object and therefore does not alias with the x%next component. 
-//       Also assigning x to y would not modify x.next
-// CHECK-DAG: y#0 <-> xnext1#0: MayAlias
-// CHECK-DAG: y#0 <-> xnext2#0: MayAlias
+// y is not a pointer object and therefore does not alias with the x%next
+// component.  Also assigning x to y would not modify x.next
+// CHECK-DAG: y#0 <-> xnext1#0: NoAlias
+// CHECK-DAG: y#0 <-> xnext2#0: NoAlias
 
 // We need to catch the fact that assigning y to x will modify xnext. 
 // The only side-effect between the 2 loads of x.next is the assignment to x, 

>From 9f6d2a7add52dcd3fe51d7376be91b1f3e9b7646 Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Thu, 30 May 2024 15:09:54 -0400
Subject: [PATCH 2/3] WIP: Fix case of ptr dummy arg vs. ptr component

---
 .../lib/Optimizer/Analysis/AliasAnalysis.cpp  | 41 ++++++++++++++++++-
 .../AliasAnalysis/alias-analysis-3.fir        |  4 +-
 2 files changed, 41 insertions(+), 4 deletions(-)

diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 571b78dc64aa5..9d6640bf181ec 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -145,7 +145,7 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
         ((lhsSrc.approximateSource && !lhsSrc.isData() && !rhsSrc.approximateSource) ||
          (rhsSrc.approximateSource && !rhsSrc.isData() && !lhsSrc.approximateSource))) {
       LLVM_DEBUG(llvm::dbgs()
-                 << "  aliasing between composite and pointer component\n");
+                 << "  aliasing between composite and non-data component\n");
       return AliasResult::MayAlias;
     }
 
@@ -185,13 +185,50 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
     src2->attributes.set(Attribute::Target);
   }
 
-  // Dummy TARGET/POINTER argument may alias with a global TARGET/POINTER.
+  // Two TARGET/POINTERs may alias.
   if (src1->isTargetOrPointer() && src2->isTargetOrPointer() &&
       src1->isData() == src2->isData()) {
     LLVM_DEBUG(llvm::dbgs() << "  aliasing because of target or pointer\n");
     return AliasResult::MayAlias;
   }
 
+  // A pointer dummy arg may alias a global composite with a pointer component.
+  //
+  // module m
+  //   type t
+  //      real, pointer :: p
+  //   end type
+  //   type(t) :: a
+  //   type(t) :: b
+  // contains
+  //   subroutine test(p)
+  //     real, pointer :: p
+  //     p = 42
+  //     a = b
+  //     print *, p
+  //   end subroutine
+  // end module
+  // program
+  //   use m
+  //   real, target :: x1 = 1
+  //   real, target :: x2 = 2
+  //   a%p => x1
+  //   b%p => x2
+  //   call test(a%p)
+  // end
+  //
+  // The dummy argument p is an alias for a%p, even for the purposes of pointer
+  // association during the assignment a = b.  Thus, the program should print 2.
+  if (src1->kind == SourceKind::Global &&
+      src1->isRecordWithPointerComponent() &&
+      src2->kind == SourceKind::Argument &&
+      src2->attributes.test(Attribute::Pointer)) {
+    LLVM_DEBUG(llvm::dbgs()
+               << "  aliasing because of pointer arg and global composite with "
+               << "pointer component\n");
+    return AliasResult::MayAlias;
+  }
+
   return AliasResult::NoAlias;
 }
 
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir
index 2bf326f2d2af2..eab438576c2bc 100644
--- a/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir
@@ -16,9 +16,9 @@
 //   end subroutine test
 // end module m
 
-// A composite with a pointer component does not alias with a dummy pointer
+// A composite with a pointer component may alias with a dummy pointer
 // CHECK-LABEL: Testing : "_QMmPtest
-// CHECK: a#0 <-> func.region0#0: NoAlias
+// CHECK: a#0 <-> func.region0#0: MayAlias
 
 // a's box cannot alias with raw reference to f32 (x)
 // CHECK: a#0 <-> func.region0#1: NoAlias

>From 78fc160208cb3bd0d1d2a1be418de50ba1ef2a63 Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Wed, 5 Jun 2024 12:10:19 -0400
Subject: [PATCH 3/3] Fix logic in various ways, and add some tests

For the static case (when source values are the same):
- As discussed in this PR with Renaud Kauffmann, rely on types and
  `!isData` instead of `approximateSource`, which doesn't necessarily
  indicate a component.
- Improve comments to document effect on allocatables and some
  remaining limitations.

For the dynamic case:
- For the composite, as discussed in the RFC, don't restrict it to a
  global.  It might be an argument.  Just exclude locals, which cannot
  be aliased by a pointer dummy arg.
- For the pointer, make sure it's the address of the pointer
  (`!isData`) not the address in the pointer.
- For the pointer, make sure it's a dummy argument not a component of
  a composite dummy argument.

For both cases, for the composite, call `isRecordWithPointerComponent`
on the value (`lhs` or `rhs`) not on its source.  Otherwise, we can
mistake a separate component for a composite.

Fix `getSource` to record pointer attributes for `DesignateOp`.
Otherwise, we miss MayAlias for pointer components vs. other pointers.
Before the changes in this PR, `isRecordWithPointerComponent` returned
true on the source of a pointer component, and that was enough to
return MayAlias for it vs. another pointer.

Extend tests, including coverage of all fixes above.
---
 .../flang/Optimizer/Analysis/AliasAnalysis.h  |   9 +-
 .../lib/Optimizer/Analysis/AliasAnalysis.cpp  |  81 +++++---
 .../AliasAnalysis/alias-analysis-9.fir        | 180 +++++++++++++++++-
 3 files changed, 238 insertions(+), 32 deletions(-)

diff --git a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
index 40fd1705115a0..a3356052a31dd 100644
--- a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
+++ b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
@@ -142,17 +142,16 @@ struct AliasAnalysis {
     /// Return true, if Target or Pointer attribute is set.
     bool isTargetOrPointer() const;
 
-    /// Return true, if the memory source's `valueType` is a reference type
-    /// to an object of derived type that contains a component with POINTER
-    /// attribute.
-    bool isRecordWithPointerComponent() const;
-
     bool isDummyArgument() const;
     bool isData() const;
     bool isBoxData() const;
 
     mlir::Type getType() const;
 
+    /// Return true, if `ty` is a reference type to an object of derived type
+    /// that contains a component with POINTER attribute.
+    static bool isRecordWithPointerComponent(mlir::Type ty);
+
     /// Return true, if `ty` is a reference type to a boxed
     /// POINTER object or a raw fir::PointerType.
     static bool isPointerReference(mlir::Type ty);
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 9d6640bf181ec..9a489c558950b 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -70,6 +70,14 @@ void AliasAnalysis::Source::print(llvm::raw_ostream &os) const {
   attributes.Dump(os, EnumToString);
 }
 
+bool AliasAnalysis::Source::isRecordWithPointerComponent(mlir::Type ty) {
+  auto eleTy = fir::dyn_cast_ptrEleTy(ty);
+  if (!eleTy)
+    return false;
+  // TO DO: Look for pointer components
+  return mlir::isa<fir::RecordType>(eleTy);
+}
+
 bool AliasAnalysis::Source::isPointerReference(mlir::Type ty) {
   auto eleTy = fir::dyn_cast_ptrEleTy(ty);
   if (!eleTy)
@@ -96,14 +104,6 @@ bool AliasAnalysis::Source::isBoxData() const {
          origin.isData;
 }
 
-bool AliasAnalysis::Source::isRecordWithPointerComponent() const {
-  auto eleTy = fir::dyn_cast_ptrEleTy(valueType);
-  if (!eleTy)
-    return false;
-  // TO DO: Look for pointer components
-  return mlir::isa<fir::RecordType>(eleTy);
-}
-
 AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
   auto lhsSrc = getSource(lhs);
   auto rhsSrc = getSource(rhs);
@@ -126,14 +126,6 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
     // either source is approximate.  Approximate sources are for parts of the
     // origin, but we don't have info here on which parts and whether they
     // overlap, so we normally return MayAlias in that case.
-    //
-    // There is an exceptional case: The origins might compare unequal because
-    // only one has !isData().  If that source is approximate and the other is
-    // not, then the former is the source for the address *of* a pointer
-    // component in a composite, and the latter is for the address of that
-    // composite.  As for the address of any composite vs. the address of one of
-    // its components, a store to one can affect a load from the other, so the
-    // result is MayAlias.
     if (lhsSrc.origin == rhsSrc.origin) {
       LLVM_DEBUG(llvm::dbgs()
                  << "  aliasing because same source kind and origin\n");
@@ -141,11 +133,32 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
         return AliasResult::MayAlias;
       return AliasResult::MustAlias;
     }
+    // If one value is the address of a composite, and if the other value is the
+    // address of a pointer/allocatable component of that composite, their
+    // origins compare unequal because the latter has !isData().  As for the
+    // address of any component vs. the address of the composite, a store to one
+    // can affect a load from the other, so the result should be MayAlias.  To
+    // catch this case, we conservatively return MayAlias when one value is the
+    // address of a composite, the other value is non-data, and they have the
+    // same origin value.
+    //
+    // TODO: That logic does not check that the latter is actually a component
+    // of the former, so it can return MayAlias when unnecessary.  For example,
+    // they might both be addresses of components of a larger composite.
+    //
+    // FIXME: Actually, we should generalize from
+    // Source::isRecordWithPointerComponent to any composite because a component
+    // with !isData() is not always a pointer.  However,
+    // Source::isRecordWithPointerComponent currently doesn't actually check for
+    // pointer components, so it's fine for now.
     if (lhsSrc.origin.u == rhsSrc.origin.u &&
-        ((lhsSrc.approximateSource && !lhsSrc.isData() && !rhsSrc.approximateSource) ||
-         (rhsSrc.approximateSource && !rhsSrc.isData() && !lhsSrc.approximateSource))) {
+        ((Source::isRecordWithPointerComponent(lhs.getType()) &&
+          !rhsSrc.isData()) ||
+         (Source::isRecordWithPointerComponent(rhs.getType()) &&
+          !lhsSrc.isData()))) {
       LLVM_DEBUG(llvm::dbgs()
-                 << "  aliasing between composite and non-data component\n");
+                 << "  aliasing between composite and non-data component with "
+                 << "same source kind and origin value\n");
       return AliasResult::MayAlias;
     }
 
@@ -157,12 +170,17 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
   }
 
   Source *src1, *src2;
+  Value *val1, *val2;
   if (lhsSrc.kind < rhsSrc.kind) {
     src1 = &lhsSrc;
     src2 = &rhsSrc;
+    val1 = &lhs;
+    val2 = &rhs;
   } else {
     src1 = &rhsSrc;
     src2 = &lhsSrc;
+    val1 = &rhs;
+    val2 = &lhs;
   }
 
   if (src1->kind == SourceKind::Argument &&
@@ -192,7 +210,10 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
     return AliasResult::MayAlias;
   }
 
-  // A pointer dummy arg may alias a global composite with a pointer component.
+  // A pointer dummy arg (but not a pointer component of a dummy arg) may alias
+  // a pointer component and thus the associated composite.  That composite
+  // might be a global or another dummy arg.  This is an example of the global
+  // composite case:
   //
   // module m
   //   type t
@@ -219,13 +240,19 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
   //
   // The dummy argument p is an alias for a%p, even for the purposes of pointer
   // association during the assignment a = b.  Thus, the program should print 2.
-  if (src1->kind == SourceKind::Global &&
-      src1->isRecordWithPointerComponent() &&
-      src2->kind == SourceKind::Argument &&
-      src2->attributes.test(Attribute::Pointer)) {
+  if ((Source::isRecordWithPointerComponent(val1->getType()) &&
+       src1->kind != SourceKind::Allocate &&
+       src2->kind == SourceKind::Argument &&
+       src2->attributes.test(Attribute::Pointer) && !src2->isData() &&
+       !Source::isRecordWithPointerComponent(src2->valueType)) ||
+      (Source::isRecordWithPointerComponent(val2->getType()) &&
+       src2->kind != SourceKind::Allocate &&
+       src1->kind == SourceKind::Argument &&
+       src1->attributes.test(Attribute::Pointer) && !src1->isData() &&
+       !Source::isRecordWithPointerComponent(src1->valueType))) {
     LLVM_DEBUG(llvm::dbgs()
-               << "  aliasing because of pointer arg and global composite with "
-               << "pointer component\n");
+               << "  aliasing between pointer arg and composite with pointer "
+               << "component\n");
     return AliasResult::MayAlias;
   }
 
@@ -395,6 +422,8 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) {
           defOp = v.getDefiningOp();
         })
         .Case<hlfir::DesignateOp>([&](auto op) {
+          auto varIf = llvm::cast<fir::FortranVariableOpInterface>(defOp);
+          attributes |= getAttrsFromVariable(varIf);
           // Track further through the memory indexed into
           // => if the source arrays/structures don't alias then nor do the
           //    results of hlfir.designate
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
index 67ea4d20caa60..f5a2903181d0f 100644
--- a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
@@ -1,5 +1,13 @@
-// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' 2>&1 | FileCheck %s
+// RUN: fir-opt -debug %s -split-input-file \
+// RUN:   -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' \
+// RUN:   2>&1 | FileCheck -match-full-lines %s
 
+// FIXME: Extend much of this to check that it also works after
+// convert-hlfir-to-fir, where component access is via fir.coordinate_of instead
+// of hlfir.designate.
+
+// FIXME: What about renaming this test to ptr-component.fir?  What about
+// merging with alias-analysis-3.fir as it has the same focus?
 
 // module m
 // type t
@@ -57,3 +65,173 @@ func.func @_QMmPfoo(%arg0: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir
   hlfir.assign %15 to %3#0 : i32, !fir.ref<i32>
   return
 }
+
+// -----
+
+// The address of a composite aliases the address of any component, including an
+// allocatable component.  Like the address of a pointer, the address of an
+// allocatable is considered non-data, so AliasAnalysis has special handling to
+// detect the aliasing.
+
+// module m
+//   type t
+//     integer, allocatable :: p
+//   end type
+//   type(t) :: x
+// contains
+//   subroutine test()
+//     ! access x%p
+//   end subroutine
+// end module
+
+// CHECK-LABEL: Testing : "_QMmPtest"
+// CHECK-DAG: x#0 <-> x%p#0: MayAlias
+
+func.func @_QMmPtest() {
+  %0 = fir.address_of(@_QMmEx) : !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.heap<i32>>}>>
+  %1:2 = hlfir.declare %0 {test.ptr="x", uniq_name = "_QMmEx"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.heap<i32>>}>>) -> (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.heap<i32>>}>>, !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.heap<i32>>}>>)
+  %2 = hlfir.designate %1#0{"p"}   {test.ptr="x%p", fortran_attrs = #fir.var_attrs<allocatable>} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.heap<i32>>}>>) -> !fir.ref<!fir.box<!fir.heap<i32>>>
+  return
+}
+
+// -----
+
+// Nested composites.
+
+// module m
+//   type t1
+//     integer, pointer :: p
+//   end type
+//   type t2
+//     type(t1) :: x
+//     integer, pointer :: p
+//     integer :: i
+//   end type
+// contains
+//   subroutine test()
+//     type(t2) :: x
+//   end subroutine
+// end module
+
+// CHECK-LABEL: Testing : "_QMmPtest"
+
+// The addresses of a composite and its pointer component alias even if the
+// composite is nested within another composite.
+// CHECK-DAG: x#0 <-> x%p#0: MayAlias
+// CHECK-DAG: x%x#0 <-> x%x%p#0: MayAlias
+
+// The addresses of different components of the same composite do not alias.
+//
+// TODO: Thus, all results below should be NoAlias.  However, AliasAnalysis
+// normally does not recognize when two values (x%x vs. x%i) are distinct
+// components of the same composite (x) as opposed to being potentially
+// overlapping parts of something, so it returns MayAlias.  There is special
+// handling for a pointer component (x%p) that does recognize it as separate
+// from other components (x%i).  But it does not yet distinguish the composite
+// (x) from a component (x%x) that is also a composite with a pointer component
+// (x%x%p).  Thus, because x and x%p can alias, it assumes x%x and x%p can too.
+// CHECK-DAG: x%x#0 <-> x%i#0: MayAlias
+// CHECK-DAG: x%p#0 <-> x%i#0: NoAlias
+// CHECK-DAG: x%x#0 <-> x%p#0: MayAlias
+
+func.func @_QMmPtest() {
+  %0 = fir.alloca !fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>,p:!fir.box<!fir.ptr<i32>>,i:i32}> {bindc_name = "x", uniq_name = "_QMmFtestEx"}
+  %1:2 = hlfir.declare %0 {test.ptr="x", uniq_name = "_QMmFtestEx"} : (!fir.ref<!fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>,p:!fir.box<!fir.ptr<i32>>,i:i32}>>) -> (!fir.ref<!fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>,p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.ref<!fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>,p:!fir.box<!fir.ptr<i32>>,i:i32}>>)
+  %2 = hlfir.designate %1#0{"x"}  {test.ptr="x%x"}  : (!fir.ref<!fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>,p:!fir.box<!fir.ptr<i32>>,i:i32}>>) -> !fir.ref<!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>>
+  %3 = hlfir.designate %1#0{"p"}   {test.ptr="x%p", fortran_attrs = #fir.var_attrs<pointer>} : (!fir.ref<!fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>,p:!fir.box<!fir.ptr<i32>>,i:i32}>>) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
+  %4 = hlfir.designate %1#0{"i"} {test.ptr="x%i"}  : (!fir.ref<!fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>,p:!fir.box<!fir.ptr<i32>>,i:i32}>>) -> !fir.ref<i32>
+  %5 = hlfir.designate %2{"p"}   {test.ptr="x%x%p", fortran_attrs = #fir.var_attrs<pointer>} : (!fir.ref<!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>>) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
+  return
+}
+
+// -----
+
+// Pointer that might dynamically be a component.
+
+// The address of a pointer dummy arg (argp) might alias the address of a
+// pointer component (x%p) and thus the address of the associated composite (x),
+// but it does not alias the addresses of other components (x%i) of the
+// composite.  Moreover, the address *in* argp does not alias any of those.
+// Finally, an allocatable dummy arg (arga) should not be mistaken for a pointer
+// dummy arg and cannot have such aliasing.
+
+// module m
+//   type t
+//     integer, pointer :: p
+//     integer i
+//   end type
+//   type(t) :: glob
+// contains
+//   subroutine test(argp, arga, arg)
+//     integer, pointer :: argp
+//     integer, allocatable :: arga
+//     type(t) :: arg
+//     type(t) :: loc
+//   end subroutine
+// end module
+
+// CHECK-LABEL: Testing : "_QMmPtest"
+
+// Check when composite is a dummy arg.
+// CHECK-DAG: argp#0 <-> arg#0: MayAlias
+// CHECK-DAG: argp#0 <-> arg%p#0: MayAlias
+// CHECK-DAG: argp#0 <-> arg%i#0: NoAlias
+// CHECK-DAG: argp.tgt#0 <-> arg#0: NoAlias
+// CHECK-DAG: argp.tgt#0 <-> arg%p#0: NoAlias
+// CHECK-DAG: argp.tgt#0 <-> arg%i#0: NoAlias
+// CHECK-DAG: arga#0 <-> arg#0: NoAlias
+// CHECK-DAG: arga#0 <-> arg%p#0: NoAlias
+
+// Check when composite is a global.
+// CHECK-DAG: argp#0 <-> glob#0: MayAlias
+// CHECK-DAG: argp#0 <-> glob%p#0: MayAlias
+// CHECK-DAG: argp#0 <-> glob%i#0: NoAlias
+// CHECK-DAG: argp.tgt#0 <-> glob#0: NoAlias
+// CHECK-DAG: argp.tgt#0 <-> glob%p#0: NoAlias
+// CHECK-DAG: argp.tgt#0 <-> glob%i#0: NoAlias
+// CHECK-DAG: arga#0 <-> glob#0: NoAlias
+// CHECK-DAG: arga#0 <-> glob%p#0: NoAlias
+
+// Check when composite is a local and thus cannot alias a dummy arg.
+//
+// TODO: The argp vs. loc%p case should be NoAlias.  However, AliasAnalysis
+// currently indiscriminately treats all pointers as aliasing.  That makes sense
+// for the addresses within the pointers but not necessarily for the addresses
+// of the pointers here.
+//
+// CHECK-DAG: argp#0 <-> loc#0: NoAlias
+// CHECK-DAG: argp#0 <-> loc%p#0: MayAlias
+// CHECK-DAG: argp#0 <-> loc%i#0: NoAlias
+// CHECK-DAG: argp.tgt#0 <-> loc#0: NoAlias
+// CHECK-DAG: argp.tgt#0 <-> loc%p#0: NoAlias
+// CHECK-DAG: argp.tgt#0 <-> loc%i#0: NoAlias
+// CHECK-DAG: arga#0 <-> loc#0: NoAlias
+// CHECK-DAG: arga#0 <-> loc%p#0: NoAlias
+
+fir.global @_QMmEglob : !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>
+
+func.func @_QMmPtest(%arg0: !fir.ref<!fir.box<!fir.ptr<i32>>> {fir.bindc_name = "argp"}, %arg1: !fir.ref<!fir.box<!fir.heap<i32>>> {fir.bindc_name = "arga"}, %arg2: !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>> {fir.bindc_name = "arg"}) {
+  %0 = fir.dummy_scope : !fir.dscope
+
+  %1:2 = hlfir.declare %arg0 dummy_scope %0 {test.ptr="argp", fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QMmFtestEargp"} : (!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.dscope) -> (!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.ptr<i32>>>)
+  %2 = fir.load %1#0 : !fir.ref<!fir.box<!fir.ptr<i32>>>
+  %3 = fir.box_addr %2 {test.ptr="argp.tgt"} : (!fir.box<!fir.ptr<i32>>) -> !fir.ptr<i32>
+
+  %4:2 = hlfir.declare %arg1 dummy_scope %0 {test.ptr="arga", fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QMmFtestEarga"} : (!fir.ref<!fir.box<!fir.heap<i32>>>, !fir.dscope) -> (!fir.ref<!fir.box<!fir.heap<i32>>>, !fir.ref<!fir.box<!fir.heap<i32>>>)
+
+  %5:2 = hlfir.declare %arg2 dummy_scope %0 {test.ptr="arg", uniq_name = "_QMmFtestEarg"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.dscope) -> (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>)
+  %6 = hlfir.designate %5#0{"p"}   {test.ptr="arg%p", fortran_attrs = #fir.var_attrs<pointer>} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
+  %7 = hlfir.designate %5#0{"i"} {test.ptr="arg%i"}  : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>) -> !fir.ref<i32>
+
+  %8 = fir.address_of(@_QMmEglob) : !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>
+  %9:2 = hlfir.declare %8 {test.ptr="glob", uniq_name = "_QMmEglob"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>) -> (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>)
+  %10 = hlfir.designate %9#0{"p"}   {test.ptr="glob%p", fortran_attrs = #fir.var_attrs<pointer>} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
+  %11 = hlfir.designate %9#0{"i"} {test.ptr="glob%i"}  : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>) -> !fir.ref<i32>
+
+  %12 = fir.alloca !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}> {bindc_name = "loc", uniq_name = "_QMmFtestEloc"}
+  %13:2 = hlfir.declare %12 {test.ptr="loc", uniq_name = "_QMmFtestEloc"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>) -> (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>)
+  %14 = hlfir.designate %13#0{"p"}   {test.ptr="loc%p", fortran_attrs = #fir.var_attrs<pointer>} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
+  %15 = hlfir.designate %13#0{"i"} {test.ptr="loc%i"}  : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>) -> !fir.ref<i32>
+
+  return
+}



More information about the flang-commits mailing list