[flang-commits] [flang] [flang] AliasAnalysis: Fix pointer component logic (PR #94242)
Joel E. Denny via flang-commits
flang-commits at lists.llvm.org
Mon Oct 14 11:20:25 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 01/15] 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 9d0d706a85c5e1..571b78dc64aa5f 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 91829a461dc72a..2bf326f2d2af2f 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 df24a6d32aa596..67ea4d20caa60f 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 02/15] 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 571b78dc64aa5f..9d6640bf181ecf 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 2bf326f2d2af2f..eab438576c2bcf 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 03/15] 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 40fd1705115a06..a3356052a31dda 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 9d6640bf181ecf..9a489c558950b7 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 67ea4d20caa60f..f5a2903181d0fe 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
+}
>From 492efb3c5670dc9cd9513d7ce7691cd4654358c6 Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Tue, 11 Jun 2024 18:05:44 -0400
Subject: [PATCH 04/15] Check x%next vs. y%next
---
flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
index f5a2903181d0fe..a5380bde9d1081 100644
--- a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
@@ -43,6 +43,12 @@
// we are currently not comparing operands involved in offset computations
// CHECK-DAG: xnext1#0 <-> xnext2#0: MayAlias
+// TODO: This 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.
+// CHECK-DAG: xnext1#0 <-> ynext#0: MayAlias
+
func.func @_QMmPfoo(%arg0: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>> {fir.bindc_name = "x"}, %arg1: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>> {fir.bindc_name = "y"}) {
%0 = fir.alloca i32 {bindc_name = "i1", uniq_name = "_QMmFfooEi1"}
%1:2 = hlfir.declare %0 {uniq_name = "_QMmFfooEi1"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
@@ -63,6 +69,7 @@ func.func @_QMmPfoo(%arg0: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir
%14 = hlfir.designate %13{"i"} : (!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>) -> !fir.ref<i32>
%15 = fir.load %14 : !fir.ref<i32>
hlfir.assign %15 to %3#0 : i32, !fir.ref<i32>
+ %16 = hlfir.designate %5#0{"next"} {fortran_attrs = #fir.var_attrs<pointer>, test.ptr = "ynext"} : (!fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>) -> !fir.ref<!fir.box<!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>>>
return
}
>From ad0438c9d03efb24fd327ef70b8b4b1a60f17637 Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Thu, 13 Jun 2024 17:51:54 -0400
Subject: [PATCH 05/15] Drop accidental -debug from fir-opt in test
However, FileCheck directives then fail. fir-opt's stdout (MLIR
output) and stderr (diagnostics) are jumbled together, and somehow
-debug changes the jumble sufficiently for FileCheck directives to
pass. We don't actually need stdout, so send it to /dev/null. That
makes the output easier to read anyway when debugging.
---
flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
index 5753301f975c5d..0042d015b060bb 100644
--- a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
@@ -1,4 +1,4 @@
-// RUN: fir-opt -debug %s -split-input-file \
+// RUN: fir-opt %s -split-input-file -o /dev/null \
// RUN: -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' \
// RUN: 2>&1 | FileCheck -match-full-lines %s
>From 1e5fdd49f4c8558f5181b3bdb2d38002e0b34630 Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Thu, 13 Jun 2024 17:55:35 -0400
Subject: [PATCH 06/15] Relocate some functions as requested by reviewer
---
.../flang/Optimizer/Analysis/AliasAnalysis.h | 17 +++++-----
.../lib/Optimizer/Analysis/AliasAnalysis.cpp | 31 +++++++++----------
2 files changed, 23 insertions(+), 25 deletions(-)
diff --git a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
index 4963d39c52bdda..9a70b7fbfad2b6 100644
--- a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
+++ b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
@@ -158,14 +158,6 @@ struct AliasAnalysis {
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);
};
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
@@ -182,6 +174,15 @@ struct AliasAnalysis {
/// will stop at [hl]fir.declare if it represents a dummy
/// argument declaration (i.e. it has the dummy_scope operand).
Source getSource(mlir::Value, bool getInstantiationPoint = false);
+
+private:
+ /// 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);
};
inline bool operator==(const AliasAnalysis::Source::SourceOrigin &lhs,
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 87389a848b9bf8..3ebe65d515e195 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -60,7 +60,7 @@ void AliasAnalysis::Source::print(llvm::raw_ostream &os) const {
attributes.Dump(os, EnumToString);
}
-bool AliasAnalysis::Source::isRecordWithPointerComponent(mlir::Type ty) {
+bool AliasAnalysis::isRecordWithPointerComponent(mlir::Type ty) {
auto eleTy = fir::dyn_cast_ptrEleTy(ty);
if (!eleTy)
return false;
@@ -68,7 +68,7 @@ bool AliasAnalysis::Source::isRecordWithPointerComponent(mlir::Type ty) {
return mlir::isa<fir::RecordType>(eleTy);
}
-bool AliasAnalysis::Source::isPointerReference(mlir::Type ty) {
+bool AliasAnalysis::isPointerReference(mlir::Type ty) {
auto eleTy = fir::dyn_cast_ptrEleTy(ty);
if (!eleTy)
return false;
@@ -139,16 +139,13 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
// 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.
+ // FIXME: Actually, we should generalize from 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 &&
- ((Source::isRecordWithPointerComponent(lhs.getType()) &&
- !rhsSrc.isData()) ||
- (Source::isRecordWithPointerComponent(rhs.getType()) &&
- !lhsSrc.isData()))) {
+ ((isRecordWithPointerComponent(lhs.getType()) && !rhsSrc.isData()) ||
+ (isRecordWithPointerComponent(rhs.getType()) && !lhsSrc.isData()))) {
LLVM_DEBUG(llvm::dbgs()
<< " aliasing between composite and non-data component with "
<< "same source kind and origin value\n");
@@ -233,16 +230,16 @@ 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 ((Source::isRecordWithPointerComponent(val1->getType()) &&
+ if ((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()) &&
+ !isRecordWithPointerComponent(src2->valueType)) ||
+ (isRecordWithPointerComponent(val2->getType()) &&
src2->kind != SourceKind::Allocate &&
src1->kind == SourceKind::Argument &&
src1->attributes.test(Attribute::Pointer) && !src1->isData() &&
- !Source::isRecordWithPointerComponent(src1->valueType))) {
+ !isRecordWithPointerComponent(src1->valueType))) {
LLVM_DEBUG(llvm::dbgs()
<< " aliasing between pointer arg and composite with pointer "
<< "component\n");
@@ -382,7 +379,7 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
// TODO: Take followBoxData into account when setting the pointer
// attribute
- if (Source::isPointerReference(ty))
+ if (isPointerReference(ty))
attributes.set(Attribute::Pointer);
global = llvm::cast<fir::AddrOfOp>(op).getSymbol();
breakFromLoop = true;
@@ -461,7 +458,7 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
if (fir::valueHasFirAttribute(v, fir::getTargetAttrName()))
attributes.set(Attribute::Target);
- if (Source::isPointerReference(ty))
+ if (isPointerReference(ty))
attributes.set(Attribute::Pointer);
}
>From 3e48239f9a5854693cec0e0d5ef011415c0fa6f7 Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Wed, 19 Jun 2024 10:50:24 -0400
Subject: [PATCH 07/15] Value -> mlir::Value
---
flang/lib/Optimizer/Analysis/AliasAnalysis.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 3ebe65d515e195..c67dae29ffb032 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -94,7 +94,7 @@ bool AliasAnalysis::Source::isBoxData() const {
origin.isData;
}
-AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
+AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
// TODO: alias() has to be aware of the function scopes.
// After MLIR inlining, the current implementation may
// not recognize non-aliasing entities.
@@ -160,7 +160,7 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
}
Source *src1, *src2;
- Value *val1, *val2;
+ mlir::Value *val1, *val2;
if (lhsSrc.kind < rhsSrc.kind) {
src1 = &lhsSrc;
src2 = &rhsSrc;
>From bec039937832bfebdadb8d8afe9ebf0377ceace3 Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Wed, 19 Jun 2024 18:35:18 -0400
Subject: [PATCH 08/15] Fix and test HostAssoc case
As discussed in review.
---
.../lib/Optimizer/Analysis/AliasAnalysis.cpp | 19 +++-
.../AliasAnalysis/alias-analysis-9.fir | 105 ++++++++++++++++++
2 files changed, 122 insertions(+), 2 deletions(-)
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index c67dae29ffb032..2330b816acf651 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -111,6 +111,7 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
// it aliases with everything
if (lhsSrc.kind >= SourceKind::Indirect ||
rhsSrc.kind >= SourceKind::Indirect) {
+ LLVM_DEBUG(llvm::dbgs() << " aliasing because of indirect access\n");
return AliasResult::MayAlias;
}
@@ -230,14 +231,28 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::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.
+ //
+ // The same is true when p is HostAssoc. For example, we might replace the
+ // test subroutine above with:
+ //
+ // subroutine test(p)
+ // real, pointer :: p
+ // call internal()
+ // contains
+ // subroutine internal()
+ // p = 42
+ // a = b
+ // print *, p
+ // end subroutine
+ // end subroutine
if ((isRecordWithPointerComponent(val1->getType()) &&
src1->kind != SourceKind::Allocate &&
- src2->kind == SourceKind::Argument &&
+ src2->kind != SourceKind::Allocate && src2->kind != SourceKind::Global &&
src2->attributes.test(Attribute::Pointer) && !src2->isData() &&
!isRecordWithPointerComponent(src2->valueType)) ||
(isRecordWithPointerComponent(val2->getType()) &&
src2->kind != SourceKind::Allocate &&
- src1->kind == SourceKind::Argument &&
+ src1->kind != SourceKind::Allocate && src1->kind != SourceKind::Global &&
src1->attributes.test(Attribute::Pointer) && !src1->isData() &&
!isRecordWithPointerComponent(src1->valueType))) {
LLVM_DEBUG(llvm::dbgs()
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
index 0042d015b060bb..254ce3ff75dc00 100644
--- a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
@@ -293,3 +293,108 @@ func.func @_QMmPtest(%arg0: !fir.ref<!fir.box<!fir.ptr<i32>>> {fir.bindc_name =
return
}
+
+// -----
+
+// Same as previous case but argp and arga are HostAssoc.
+
+// module m
+// type t
+// integer, pointer :: p
+// integer i
+// end type
+// type(t) :: glob
+// contains
+// subroutine parent(argp, arga)
+// integer, pointer :: argp
+// integer, allocatable :: arga
+// type(t) :: arg
+// call test(arg)
+// contains
+// subroutine test(arg)
+// type(t) :: arg
+// type(t) :: loc
+// end subroutine
+// end subroutine
+// end module
+
+// CHECK-LABEL: Testing : "_QMmFparentPtest"
+
+// 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
+// TODO: Shouldn't these three be NoAlias? However, argp.tgt is currently
+// handled as Indirect.
+// CHECK-DAG: argp.tgt#0 <-> arg#0: MayAlias
+// CHECK-DAG: argp.tgt#0 <-> arg%p#0: MayAlias
+// CHECK-DAG: argp.tgt#0 <-> arg%i#0: MayAlias
+// CHECK-DAG: arga#0 <-> arg#0: NoAlias
+// TODO: Shouldn't this be NoAlias? However, arga is treated like a target
+// because it's HostAssoc and arg is Argument, and arg%p is treated like a
+// pointer.
+// CHECK-DAG: arga#0 <-> arg%p#0: MayAlias
+
+// 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
+// TODO: Shouldn't these three be NoAlias? However, argp.tgt is currently
+// handled as Indirect.
+// CHECK-DAG: argp.tgt#0 <-> glob#0: MayAlias
+// CHECK-DAG: argp.tgt#0 <-> glob%p#0: MayAlias
+// CHECK-DAG: argp.tgt#0 <-> glob%i#0: MayAlias
+// 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
+// TODO: Shouldn't these three be NoAlias? However, argp.tgt is currently
+// handled as Indirect.
+// CHECK-DAG: argp.tgt#0 <-> loc#0: MayAlias
+// CHECK-DAG: argp.tgt#0 <-> loc%p#0: MayAlias
+// CHECK-DAG: argp.tgt#0 <-> loc%i#0: MayAlias
+// 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 private @_QMmFparentPtest(%arg0: !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>> {fir.bindc_name = "arg"}, %arg1: !fir.ref<tuple<!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.heap<i32>>>>> {fir.host_assoc}) attributes {fir.host_symbol = @_QMmPparent, llvm.linkage = #llvm.linkage<internal>} {
+ %0 = fir.dummy_scope : !fir.dscope
+
+ %c0_i32 = arith.constant 0 : i32
+ %1 = fir.coordinate_of %arg1, %c0_i32 : (!fir.ref<tuple<!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.heap<i32>>>>>, i32) -> !fir.llvm_ptr<!fir.ref<!fir.box<!fir.ptr<i32>>>>
+ %2 = fir.load %1 : !fir.llvm_ptr<!fir.ref<!fir.box<!fir.ptr<i32>>>>
+ %3:2 = hlfir.declare %2 {test.ptr="argp", fortran_attrs = #fir.var_attrs<pointer, host_assoc>, uniq_name = "_QMmFparentEargp"} : (!fir.ref<!fir.box<!fir.ptr<i32>>>) -> (!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.ptr<i32>>>)
+ %4 = fir.load %3#0 : !fir.ref<!fir.box<!fir.ptr<i32>>>
+ %5 = fir.box_addr %4 {test.ptr="argp.tgt"} : (!fir.box<!fir.ptr<i32>>) -> !fir.ptr<i32>
+
+ %c1_i32 = arith.constant 1 : i32
+ %10 = fir.coordinate_of %arg1, %c1_i32 : (!fir.ref<tuple<!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.heap<i32>>>>>, i32) -> !fir.llvm_ptr<!fir.ref<!fir.box<!fir.heap<i32>>>>
+ %11 = fir.load %10 : !fir.llvm_ptr<!fir.ref<!fir.box<!fir.heap<i32>>>>
+ %12:2 = hlfir.declare %11 {test.ptr="arga", fortran_attrs = #fir.var_attrs<allocatable, host_assoc>, uniq_name = "_QMmFparentEarga"} : (!fir.ref<!fir.box<!fir.heap<i32>>>) -> (!fir.ref<!fir.box<!fir.heap<i32>>>, !fir.ref<!fir.box<!fir.heap<i32>>>)
+
+ %20 = fir.address_of(@_QMmEglob) : !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>
+ %21:2 = hlfir.declare %20 {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}>>)
+ %22 = hlfir.designate %21#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>>>
+ %23 = hlfir.designate %21#0{"i"} {test.ptr="glob%i"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>) -> !fir.ref<i32>
+
+ %30:2 = hlfir.declare %arg0 dummy_scope %0 {test.ptr="arg", uniq_name = "_QMmFparentFtestEarg"} : (!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}>>)
+ %31 = hlfir.designate %30#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>>>
+ %32 = hlfir.designate %30#0{"i"} {test.ptr="arg%i"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>) -> !fir.ref<i32>
+
+ %40 = fir.alloca !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}> {bindc_name = "loc", uniq_name = "_QMmFparentFtestEloc"}
+ %41:2 = hlfir.declare %40 {test.ptr="loc", uniq_name = "_QMmFparentFtestEloc"} : (!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}>>)
+ %42 = hlfir.designate %41#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>>>
+ %43 = hlfir.designate %41#0{"i"} {test.ptr="loc%i"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>) -> !fir.ref<i32>
+
+ return
+}
>From 776a17dbaaae16507baafdd5d0019c2a13db8150 Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Thu, 20 Jun 2024 11:55:49 -0400
Subject: [PATCH 09/15] Fix and test after convert-hlfir-to-fir
In particular, after hlfir.designate becomes fir.coordinate_of.
---
.../lib/Optimizer/Analysis/AliasAnalysis.cpp | 2 +
.../AliasAnalysis/alias-analysis-9.fir | 283 +++++++++++++++++-
2 files changed, 269 insertions(+), 16 deletions(-)
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 2330b816acf651..30d66e9a799e82 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -353,6 +353,8 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
followBoxData = true;
})
.Case<fir::ArrayCoorOp, fir::CoordinateOp>([&](auto op) {
+ if (isPointerReference(ty))
+ attributes.set(Attribute::Pointer);
v = op->getOperand(0);
defOp = v.getDefiningOp();
if (mlir::isa<fir::BaseBoxType>(v.getType()))
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
index 254ce3ff75dc00..ca496d91a1cedd 100644
--- a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
@@ -1,11 +1,14 @@
-// RUN: fir-opt %s -split-input-file -o /dev/null \
+// Check AliasAnalysis for pointer components.
+//
+// Throughout this test, The ".fir" suffix on symbols indicates a version of the
+// MLIR after convert-hlfir-to-fir. A key difference is that component access
+// is via fir.coordinate_of instead of hlfir.designate. 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
-// 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?
@@ -27,27 +30,34 @@
// CHECK-LABEL: Testing : "_QMmPfoo"
// x and y are non pointer, non target argument and therefore do not alias.
// CHECK-DAG: x#0 <-> y#0: NoAlias
+// CHECK-DAG: x.fir#0 <-> y.fir#0: NoAlias
// 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
+// CHECK-DAG: y.fir#0 <-> xnext1.fir#0: NoAlias
+// CHECK-DAG: y.fir#0 <-> xnext2.fir#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,
// therefore x needs to alias with x.next to prevent the loads from being merged.
// CHECK-DAG: x#0 <-> xnext1#0: MayAlias
// CHECK-DAG: x#0 <-> xnext2#0: MayAlias
+// CHECK-DAG: x.fir#0 <-> xnext1.fir#0: MayAlias
+// CHECK-DAG: x.fir#0 <-> xnext2.fir#0: MayAlias
// TODO: xnext1#0 <-> xnext2#0 are the same and therefore MustAlias but
// we are currently not comparing operands involved in offset computations
// CHECK-DAG: xnext1#0 <-> xnext2#0: MayAlias
+// CHECK-DAG: xnext1.fir#0 <-> xnext2.fir#0: MayAlias
-// TODO: This should be NoAlias. However, AliasAnalysis currently
+// TODO: These 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.
// CHECK-DAG: xnext1#0 <-> ynext#0: MayAlias
+// CHECK-DAG: xnext1.fir#0 <-> ynext.fir#0: MayAlias
func.func @_QMmPfoo(%arg0: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>> {fir.bindc_name = "x"}, %arg1: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>> {fir.bindc_name = "y"}) {
%0 = fir.alloca i32 {bindc_name = "i1", uniq_name = "_QMmFfooEi1"}
@@ -73,11 +83,51 @@ func.func @_QMmPfoo(%arg0: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir
return
}
+func.func @_QMmPfoo.fir(%arg0: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>> {fir.bindc_name = "x"}, %arg1: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>> {fir.bindc_name = "y"}) {
+ %0 = fir.alloca !fir.box<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>
+ %1 = fir.alloca i32 {bindc_name = "i1", uniq_name = "_QMmFfooEi1"}
+ %2 = fir.declare %1 {uniq_name = "_QMmFfooEi1"} : (!fir.ref<i32>) -> !fir.ref<i32>
+ %3 = fir.alloca i32 {bindc_name = "i2", uniq_name = "_QMmFfooEi2"}
+ %4 = fir.declare %3 {uniq_name = "_QMmFfooEi2"} : (!fir.ref<i32>) -> !fir.ref<i32>
+ %5 = fir.declare %arg0 {test.ptr = "x.fir", uniq_name = "_QMmFfooEx"} : (!fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>) -> !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>
+ %6 = fir.declare %arg1 {test.ptr = "y.fir", uniq_name = "_QMmFfooEy"} : (!fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>) -> !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>
+ %7 = fir.field_index next, !fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>
+ %8 = fir.coordinate_of %5, %7 {test.ptr="xnext1.fir"} : (!fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>>>
+ %9 = fir.load %8 : !fir.ref<!fir.box<!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>>>
+ %10 = fir.box_addr %9 : (!fir.box<!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>>) -> !fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>
+ %11 = fir.field_index i, !fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>
+ %12 = fir.coordinate_of %10, %11 : (!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>, !fir.field) -> !fir.ref<i32>
+ %13 = fir.load %12 : !fir.ref<i32>
+ fir.store %13 to %2 : !fir.ref<i32>
+ %14 = fir.embox %5 : (!fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>) -> !fir.box<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>
+ %15 = fir.embox %6 : (!fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>) -> !fir.box<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>
+ fir.store %14 to %0 : !fir.ref<!fir.box<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>>
+ %16 = fir.address_of(@_QQclX746D70332E66697200) : !fir.ref<!fir.char<1,9>>
+ %c9 = arith.constant 9 : index
+ %c14_i32 = arith.constant 14 : i32
+ %17 = fir.convert %0 : (!fir.ref<!fir.box<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>>) -> !fir.ref<!fir.box<none>>
+ %18 = fir.convert %15 : (!fir.box<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>) -> !fir.box<none>
+ %19 = fir.convert %16 : (!fir.ref<!fir.char<1,9>>) -> !fir.ref<i8>
+ %20 = fir.call @_FortranAAssign(%17, %18, %19, %c14_i32) : (!fir.ref<!fir.box<none>>, !fir.box<none>, !fir.ref<i8>, i32) -> none
+ %21 = fir.field_index next, !fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>
+ %22 = fir.coordinate_of %5, %21 {test.ptr="xnext2.fir"}: (!fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>>>
+ %23 = fir.load %22 : !fir.ref<!fir.box<!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>>>
+ %24 = fir.box_addr %23 : (!fir.box<!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>>) -> !fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>
+ %25 = fir.field_index i, !fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>
+ %26 = fir.coordinate_of %24, %25 : (!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>, !fir.field) -> !fir.ref<i32>
+ %27 = fir.load %26 : !fir.ref<i32>
+ fir.store %27 to %4 : !fir.ref<i32>
+ %28 = fir.field_index next, !fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>
+ %29 = fir.coordinate_of %6, %28 {test.ptr="ynext.fir"} : (!fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>>>
+ return
+}
+
// -----
// Same test as above focusing on aliasing between x%next and y%next data
// CHECK-LABEL: Testing : "_QMmPfoo2"
// CHECK-DAG: xnext#0 <-> ynext#0: MayAlias
+// CHECK-DAG: xnext.fir#0 <-> ynext.fir#0: MayAlias
func.func @_QMmPfoo2(%arg0: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>> {fir.bindc_name = "x"}, %arg1: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>> {fir.bindc_name = "y"}) {
%4:2 = hlfir.declare %arg0 {uniq_name = "_QMmFfooEx"} : (!fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>) -> (!fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>, !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>)
@@ -92,6 +142,20 @@ func.func @_QMmPfoo2(%arg0: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fi
return
}
+func.func @_QMmPfoo2.fir(%arg0: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>> {fir.bindc_name = "x"}, %arg1: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>> {fir.bindc_name = "y"}) {
+ %0 = fir.declare %arg0 {uniq_name = "_QMmFfooEx"} : (!fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>) -> !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>
+ %1 = fir.declare %arg1 {uniq_name = "_QMmFfooEy"} : (!fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>) -> !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>
+ %2 = fir.field_index next, !fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>
+ %3 = fir.coordinate_of %0, %2 : (!fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>>>
+ %4 = fir.load %3 : !fir.ref<!fir.box<!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>>>
+ %5 = fir.box_addr %4 {test.ptr = "xnext.fir"} : (!fir.box<!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>>) -> !fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>
+ %6 = fir.field_index next, !fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>
+ %7 = fir.coordinate_of %1, %6 : (!fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>>>
+ %8 = fir.load %7 : !fir.ref<!fir.box<!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>>>
+ %9 = fir.box_addr %8 {test.ptr = "ynext.fir"} : (!fir.box<!fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>>) -> !fir.ptr<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>>
+ return
+}
+
// -----
// module m
@@ -107,6 +171,7 @@ func.func @_QMmPfoo2(%arg0: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fi
// CHECK-LABEL: Testing : "_QMmPfoo3"
// CHECK-DAG: yarray#0 <-> xarray#0: MayAlias
+// CHECK-DAG: yarray.fir#0 <-> xarray.fir#0: MayAlias
func.func @_QMmPfoo3(%arg0: !fir.ref<!fir.type<_QMmTta{array:!fir.box<!fir.ptr<!fir.array<?xi32>>>}>> {fir.bindc_name = "x"}, %arg1: !fir.ref<!fir.type<_QMmTta{array:!fir.box<!fir.ptr<!fir.array<?xi32>>>}>> {fir.bindc_name = "y"}) {
%0:2 = hlfir.declare %arg0 {uniq_name = "_QMmFfooEx"} : (!fir.ref<!fir.type<_QMmTta{array:!fir.box<!fir.ptr<!fir.array<?xi32>>>}>>) -> (!fir.ref<!fir.type<_QMmTta{array:!fir.box<!fir.ptr<!fir.array<?xi32>>>}>>, !fir.ref<!fir.type<_QMmTta{array:!fir.box<!fir.ptr<!fir.array<?xi32>>>}>>)
@@ -124,6 +189,30 @@ func.func @_QMmPfoo3(%arg0: !fir.ref<!fir.type<_QMmTta{array:!fir.box<!fir.ptr<!
return
}
+func.func @_QMmPfoo3.fir(%arg0: !fir.ref<!fir.type<_QMmTta{array:!fir.box<!fir.ptr<!fir.array<?xi32>>>}>> {fir.bindc_name = "x"}, %arg1: !fir.ref<!fir.type<_QMmTta{array:!fir.box<!fir.ptr<!fir.array<?xi32>>>}>> {fir.bindc_name = "y"}) {
+ %0 = fir.declare %arg0 {uniq_name = "_QMmFfooEx"} : (!fir.ref<!fir.type<_QMmTta{array:!fir.box<!fir.ptr<!fir.array<?xi32>>>}>>) -> !fir.ref<!fir.type<_QMmTta{array:!fir.box<!fir.ptr<!fir.array<?xi32>>>}>>
+ %1 = fir.declare %arg1 {uniq_name = "_QMmFfooEy"} : (!fir.ref<!fir.type<_QMmTta{array:!fir.box<!fir.ptr<!fir.array<?xi32>>>}>>) -> !fir.ref<!fir.type<_QMmTta{array:!fir.box<!fir.ptr<!fir.array<?xi32>>>}>>
+ %2 = fir.field_index array, !fir.type<_QMmTta{array:!fir.box<!fir.ptr<!fir.array<?xi32>>>}>
+ %3 = fir.coordinate_of %1, %2 : (!fir.ref<!fir.type<_QMmTta{array:!fir.box<!fir.ptr<!fir.array<?xi32>>>}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+ %4 = fir.load %3 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+ %c1 = arith.constant 1 : index
+ %c0 = arith.constant 0 : index
+ %5:3 = fir.box_dims %4, %c0 : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, index) -> (index, index, index)
+ %6 = fir.shift %5#0 : (index) -> !fir.shift<1>
+ %7 = fir.array_coor %4(%6) %c1 {test.ptr="yarray.fir"} : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, !fir.shift<1>, index) -> !fir.ref<i32>
+ %8 = fir.load %7 : !fir.ref<i32>
+ %9 = fir.field_index array, !fir.type<_QMmTta{array:!fir.box<!fir.ptr<!fir.array<?xi32>>>}>
+ %10 = fir.coordinate_of %0, %9 : (!fir.ref<!fir.type<_QMmTta{array:!fir.box<!fir.ptr<!fir.array<?xi32>>>}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+ %11 = fir.load %10 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+ %c1_0 = arith.constant 1 : index
+ %c0_1 = arith.constant 0 : index
+ %12:3 = fir.box_dims %11, %c0_1 : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, index) -> (index, index, index)
+ %13 = fir.shift %12#0 : (index) -> !fir.shift<1>
+ %14 = fir.array_coor %11(%13) %c1_0 {test.ptr="xarray.fir"} : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, !fir.shift<1>, index) -> !fir.ref<i32>
+ fir.store %8 to %14 : !fir.ref<i32>
+ return
+}
+
// -----
// The address of a composite aliases the address of any component, including an
@@ -144,6 +233,7 @@ func.func @_QMmPfoo3(%arg0: !fir.ref<!fir.type<_QMmTta{array:!fir.box<!fir.ptr<!
// CHECK-LABEL: Testing : "_QMmPtest"
// CHECK-DAG: x#0 <-> x%p#0: MayAlias
+// CHECK-DAG: x.fir#0 <-> x%p.fir#0: MayAlias
func.func @_QMmPtest() {
%0 = fir.address_of(@_QMmEx) : !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.heap<i32>>}>>
@@ -152,6 +242,14 @@ func.func @_QMmPtest() {
return
}
+func.func @_QMmPtest.fir() {
+ %0 = fir.address_of(@_QMmEx) : !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.heap<i32>>}>>
+ %1 = fir.declare %0 {test.ptr = "x.fir", 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>>}>>
+ %2 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box<!fir.heap<i32>>}>
+ %3 = fir.coordinate_of %1, %2 {test.ptr="x%p.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.heap<i32>>}>>, !fir.field) -> !fir.ref<!fir.box<!fir.heap<i32>>>
+ return
+}
+
// -----
// Nested composites.
@@ -177,6 +275,8 @@ func.func @_QMmPtest() {
// composite is nested within another composite.
// CHECK-DAG: x#0 <-> x%p#0: MayAlias
// CHECK-DAG: x%x#0 <-> x%x%p#0: MayAlias
+// CHECK-DAG: x.fir#0 <-> x%p.fir#0: MayAlias
+// CHECK-DAG: x%x.fir#0 <-> x%x%p.fir#0: MayAlias
// The addresses of different components of the same composite do not alias.
//
@@ -191,6 +291,9 @@ func.func @_QMmPtest() {
// 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
+// CHECK-DAG: x%x.fir#0 <-> x%i.fir#0: MayAlias
+// CHECK-DAG: x%p.fir#0 <-> x%i.fir#0: NoAlias
+// CHECK-DAG: x%x.fir#0 <-> x%p.fir#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"}
@@ -202,6 +305,20 @@ func.func @_QMmPtest() {
return
}
+func.func @_QMmPtest.fir() {
+ %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 = fir.declare %0 {test.ptr = "x.fir", 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}>>
+ %2 = fir.field_index x, !fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>,p:!fir.box<!fir.ptr<i32>>,i:i32}>
+ %3 = fir.coordinate_of %1, %2 {test.ptr="x%x.fir"} : (!fir.ref<!fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>,p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.field) -> !fir.ref<!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>>
+ %4 = fir.field_index p, !fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>,p:!fir.box<!fir.ptr<i32>>,i:i32}>
+ %5 = fir.coordinate_of %1, %4 {test.ptr="x%p.fir"} : (!fir.ref<!fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>,p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
+ %6 = fir.field_index i, !fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>,p:!fir.box<!fir.ptr<i32>>,i:i32}>
+ %7 = fir.coordinate_of %1, %6 {test.ptr="x%i.fir"} : (!fir.ref<!fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>,p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.field) -> !fir.ref<i32>
+ %8 = fir.field_index p, !fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>
+ %9 = fir.coordinate_of %3, %8 {test.ptr="x%x%p.fir"} : (!fir.ref<!fir.type<_QMmTt1{p:!fir.box<!fir.ptr<i32>>}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
+ return
+}
+
// -----
// Pointer that might dynamically be a component.
@@ -231,28 +348,50 @@ func.func @_QMmPtest() {
// 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.fir#0 <-> arg.fir#0: MayAlias
+// CHECK-DAG: argp.fir#0 <-> arg%p.fir#0: MayAlias
+// CHECK-DAG: argp.fir#0 <-> arg%i.fir#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: argp.tgt.fir#0 <-> arg.fir#0: NoAlias
+// CHECK-DAG: argp.tgt.fir#0 <-> arg%p.fir#0: NoAlias
+// CHECK-DAG: argp.tgt.fir#0 <-> arg%i.fir#0: NoAlias
+//
// CHECK-DAG: arga#0 <-> arg#0: NoAlias
// CHECK-DAG: arga#0 <-> arg%p#0: NoAlias
+// CHECK-DAG: arga.fir#0 <-> arg.fir#0: NoAlias
+// CHECK-DAG: arga.fir#0 <-> arg%p.fir#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.fir#0 <-> glob.fir#0: MayAlias
+// CHECK-DAG: argp.fir#0 <-> glob%p.fir#0: MayAlias
+// CHECK-DAG: argp.fir#0 <-> glob%i.fir#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: argp.tgt.fir#0 <-> glob.fir#0: NoAlias
+// CHECK-DAG: argp.tgt.fir#0 <-> glob%p.fir#0: NoAlias
+// CHECK-DAG: argp.tgt.fir#0 <-> glob%i.fir#0: NoAlias
+//
// CHECK-DAG: arga#0 <-> glob#0: NoAlias
// CHECK-DAG: arga#0 <-> glob%p#0: NoAlias
+// CHECK-DAG: arga.fir#0 <-> glob.fir#0: NoAlias
+// CHECK-DAG: arga.fir#0 <-> glob%p.fir#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
+// TODO: The argp vs. loc%p cases 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.
@@ -260,11 +399,21 @@ func.func @_QMmPtest() {
// 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.fir#0 <-> loc.fir#0: NoAlias
+// CHECK-DAG: argp.fir#0 <-> loc%p.fir#0: MayAlias
+// CHECK-DAG: argp.fir#0 <-> loc%i.fir#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: argp.tgt.fir#0 <-> loc.fir#0: NoAlias
+// CHECK-DAG: argp.tgt.fir#0 <-> loc%p.fir#0: NoAlias
+// CHECK-DAG: argp.tgt.fir#0 <-> loc%i.fir#0: NoAlias
+//
// CHECK-DAG: arga#0 <-> loc#0: NoAlias
// CHECK-DAG: arga#0 <-> loc%p#0: NoAlias
+// CHECK-DAG: arga.fir#0 <-> loc.fir#0: NoAlias
+// CHECK-DAG: arga.fir#0 <-> loc%p.fir#0: NoAlias
fir.global @_QMmEglob : !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>
@@ -294,6 +443,38 @@ func.func @_QMmPtest(%arg0: !fir.ref<!fir.box<!fir.ptr<i32>>> {fir.bindc_name =
return
}
+func.func @_QMmPtest.fir(%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 = fir.declare %arg0 dummy_scope %0 {fortran_attrs = #fir.var_attrs<pointer>, test.ptr = "argp.fir", uniq_name = "_QMmFtestEargp"} : (!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.dscope) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
+ %2 = fir.load %1 : !fir.ref<!fir.box<!fir.ptr<i32>>>
+ %3 = fir.box_addr %2 {test.ptr = "argp.tgt.fir"} : (!fir.box<!fir.ptr<i32>>) -> !fir.ptr<i32>
+
+ %4 = fir.declare %arg1 dummy_scope %0 {fortran_attrs = #fir.var_attrs<allocatable>, test.ptr = "arga.fir", uniq_name = "_QMmFtestEarga"} : (!fir.ref<!fir.box<!fir.heap<i32>>>, !fir.dscope) -> !fir.ref<!fir.box<!fir.heap<i32>>>
+
+ %5 = fir.declare %arg2 dummy_scope %0 {test.ptr = "arg.fir", 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}>>
+ %6 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>
+ %7 = fir.coordinate_of %5, %6 {test.ptr="arg%p.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
+ %8 = fir.field_index i, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>
+ %9 = fir.coordinate_of %5, %8 {test.ptr="arg%i.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.field) -> !fir.ref<i32>
+
+ %10 = fir.address_of(@_QMmEglob) : !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>
+ %11 = fir.declare %10 {test.ptr = "glob.fir", 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}>>
+ %12 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>
+ %13 = fir.coordinate_of %11, %12 {test.ptr="glob%p.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
+ %14 = fir.field_index i, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>
+ %15 = fir.coordinate_of %11, %14 {test.ptr="glob%i.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.field) -> !fir.ref<i32>
+
+ %16 = fir.alloca !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}> {bindc_name = "loc", uniq_name = "_QMmFtestEloc"}
+ %17 = fir.declare %16 {test.ptr = "loc.fir", 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}>>
+ %18 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>
+ %19 = fir.coordinate_of %17, %18 {test.ptr="loc%p.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
+ %20 = fir.field_index i, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>
+ %21 = fir.coordinate_of %17, %20 {test.ptr="loc%i.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.field) -> !fir.ref<i32>
+
+ return
+}
+
// -----
// Same as previous case but argp and arga are HostAssoc.
@@ -321,31 +502,53 @@ func.func @_QMmPtest(%arg0: !fir.ref<!fir.box<!fir.ptr<i32>>> {fir.bindc_name =
// CHECK-LABEL: Testing : "_QMmFparentPtest"
// 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
-// TODO: Shouldn't these three be NoAlias? However, argp.tgt is currently
-// handled as Indirect.
+// CHECK-DAG: argp.fir#0 <-> arg.fir#0: MayAlias
+// CHECK-DAG: argp.fir#0 <-> arg%p.fir#0: MayAlias
+// CHECK-DAG: argp.fir#0 <-> arg%i.fir#0: NoAlias
+//
+// TODO: Shouldn't these be NoAlias? However, argp.tgt is currently handled as
+// Indirect.
// CHECK-DAG: argp.tgt#0 <-> arg#0: MayAlias
// CHECK-DAG: argp.tgt#0 <-> arg%p#0: MayAlias
// CHECK-DAG: argp.tgt#0 <-> arg%i#0: MayAlias
+// CHECK-DAG: argp.tgt.fir#0 <-> arg.fir#0: MayAlias
+// CHECK-DAG: argp.tgt.fir#0 <-> arg%p.fir#0: MayAlias
+// CHECK-DAG: argp.tgt.fir#0 <-> arg%i.fir#0: MayAlias
+//
+// TODO: Shouldn't the arga vs. arg%p cases be NoAlias? However, arga is
+// treated like a target because it's HostAssoc and arg is Argument, and arg%p
+// is treated like a pointer.
// CHECK-DAG: arga#0 <-> arg#0: NoAlias
-// TODO: Shouldn't this be NoAlias? However, arga is treated like a target
-// because it's HostAssoc and arg is Argument, and arg%p is treated like a
-// pointer.
// CHECK-DAG: arga#0 <-> arg%p#0: MayAlias
+// CHECK-DAG: arga.fir#0 <-> arg.fir#0: NoAlias
+// CHECK-DAG: arga.fir#0 <-> arg%p.fir#0: MayAlias
// 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
-// TODO: Shouldn't these three be NoAlias? However, argp.tgt is currently
-// handled as Indirect.
+// CHECK-DAG: argp.fir#0 <-> glob.fir#0: MayAlias
+// CHECK-DAG: argp.fir#0 <-> glob%p.fir#0: MayAlias
+// CHECK-DAG: argp.fir#0 <-> glob%i.fir#0: NoAlias
+//
+// TODO: Shouldn't these be NoAlias? However, argp.tgt is currently handled as
+// Indirect.
// CHECK-DAG: argp.tgt#0 <-> glob#0: MayAlias
// CHECK-DAG: argp.tgt#0 <-> glob%p#0: MayAlias
// CHECK-DAG: argp.tgt#0 <-> glob%i#0: MayAlias
+// CHECK-DAG: argp.tgt.fir#0 <-> glob.fir#0: MayAlias
+// CHECK-DAG: argp.tgt.fir#0 <-> glob%p.fir#0: MayAlias
+// CHECK-DAG: argp.tgt.fir#0 <-> glob%i.fir#0: MayAlias
+//
// CHECK-DAG: arga#0 <-> glob#0: NoAlias
// CHECK-DAG: arga#0 <-> glob%p#0: NoAlias
+// CHECK-DAG: arga.fir#0 <-> glob.fir#0: NoAlias
+// CHECK-DAG: arga.fir#0 <-> glob%p.fir#0: NoAlias
// Check when composite is a local and thus cannot alias a dummy arg.
//
@@ -357,13 +560,23 @@ func.func @_QMmPtest(%arg0: !fir.ref<!fir.box<!fir.ptr<i32>>> {fir.bindc_name =
// CHECK-DAG: argp#0 <-> loc#0: NoAlias
// CHECK-DAG: argp#0 <-> loc%p#0: MayAlias
// CHECK-DAG: argp#0 <-> loc%i#0: NoAlias
-// TODO: Shouldn't these three be NoAlias? However, argp.tgt is currently
-// handled as Indirect.
+// CHECK-DAG: argp.fir#0 <-> loc.fir#0: NoAlias
+// CHECK-DAG: argp.fir#0 <-> loc%p.fir#0: MayAlias
+// CHECK-DAG: argp.fir#0 <-> loc%i.fir#0: NoAlias
+//
+// TODO: Shouldn't these be NoAlias? However, argp.tgt is currently handled as
+// Indirect.
// CHECK-DAG: argp.tgt#0 <-> loc#0: MayAlias
// CHECK-DAG: argp.tgt#0 <-> loc%p#0: MayAlias
// CHECK-DAG: argp.tgt#0 <-> loc%i#0: MayAlias
+// CHECK-DAG: argp.tgt.fir#0 <-> loc.fir#0: MayAlias
+// CHECK-DAG: argp.tgt.fir#0 <-> loc%p.fir#0: MayAlias
+// CHECK-DAG: argp.tgt.fir#0 <-> loc%i.fir#0: MayAlias
+//
// CHECK-DAG: arga#0 <-> loc#0: NoAlias
// CHECK-DAG: arga#0 <-> loc%p#0: NoAlias
+// CHECK-DAG: arga.fir#0 <-> loc.fir#0: NoAlias
+// CHECK-DAG: arga.fir#0 <-> loc%p.fir#0: NoAlias
fir.global @_QMmEglob : !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>
@@ -398,3 +611,41 @@ func.func private @_QMmFparentPtest(%arg0: !fir.ref<!fir.type<_QMmTt{p:!fir.box<
return
}
+
+func.func private @_QMmFparentPtest.fir(%arg0: !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>> {fir.bindc_name = "arg"}, %arg1: !fir.ref<tuple<!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.heap<i32>>>>> {fir.host_assoc}) attributes {fir.host_symbol = @_QMmPparent, llvm.linkage = #llvm.linkage<internal>} {
+ %0 = fir.dummy_scope : !fir.dscope
+
+ %c0_i32 = arith.constant 0 : i32
+ %1 = fir.coordinate_of %arg1, %c0_i32 : (!fir.ref<tuple<!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.heap<i32>>>>>, i32) -> !fir.llvm_ptr<!fir.ref<!fir.box<!fir.ptr<i32>>>>
+ %2 = fir.load %1 : !fir.llvm_ptr<!fir.ref<!fir.box<!fir.ptr<i32>>>>
+ %3 = fir.declare %2 {fortran_attrs = #fir.var_attrs<pointer, host_assoc>, test.ptr = "argp.fir", uniq_name = "_QMmFparentEargp"} : (!fir.ref<!fir.box<!fir.ptr<i32>>>) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
+ %4 = fir.load %3 : !fir.ref<!fir.box<!fir.ptr<i32>>>
+ %5 = fir.box_addr %4 {test.ptr = "argp.tgt.fir"} : (!fir.box<!fir.ptr<i32>>) -> !fir.ptr<i32>
+
+ %c1_i32 = arith.constant 1 : i32
+ %6 = fir.coordinate_of %arg1, %c1_i32 : (!fir.ref<tuple<!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.heap<i32>>>>>, i32) -> !fir.llvm_ptr<!fir.ref<!fir.box<!fir.heap<i32>>>>
+ %7 = fir.load %6 : !fir.llvm_ptr<!fir.ref<!fir.box<!fir.heap<i32>>>>
+ %8 = fir.declare %7 {fortran_attrs = #fir.var_attrs<allocatable, host_assoc>, test.ptr = "arga.fir", uniq_name = "_QMmFparentEarga"} : (!fir.ref<!fir.box<!fir.heap<i32>>>) -> !fir.ref<!fir.box<!fir.heap<i32>>>
+
+ %9 = fir.address_of(@_QMmEglob) : !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>
+ %10 = fir.declare %9 {test.ptr = "glob.fir", 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}>>
+ %11 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>
+ %12 = fir.coordinate_of %10, %11 {test.ptr="glob%p.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
+ %13 = fir.field_index i, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>
+ %14 = fir.coordinate_of %10, %13 {test.ptr="glob%i.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.field) -> !fir.ref<i32>
+
+ %15 = fir.declare %arg0 dummy_scope %0 {test.ptr = "arg.fir", uniq_name = "_QMmFparentFtestEarg"} : (!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}>>
+ %16 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>
+ %17 = fir.coordinate_of %15, %16 {test.ptr="arg%p.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
+ %18 = fir.field_index i, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>
+ %19 = fir.coordinate_of %15, %18 {test.ptr="arg%i.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.field) -> !fir.ref<i32>
+
+ %20 = fir.alloca !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}> {bindc_name = "loc", uniq_name = "_QMmFparentFtestEloc"}
+ %21 = fir.declare %20 {test.ptr = "loc.fir", uniq_name = "_QMmFparentFtestEloc"} : (!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}>>
+ %22 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>
+ %23 = fir.coordinate_of %21, %22 {test.ptr="loc%p.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
+ %24 = fir.field_index i, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>
+ %25 = fir.coordinate_of %21, %24 {test.ptr="loc%i.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<i32>>,i:i32}>>, !fir.field) -> !fir.ref<i32>
+
+ return
+}
>From 4a73682a01d398baf8ed136ad33c27e6e4cffe70 Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Fri, 12 Jul 2024 13:23:44 -0400
Subject: [PATCH 10/15] Encapsulate repeated logic into functions
---
.../flang/Optimizer/Analysis/AliasAnalysis.h | 11 +++++
.../lib/Optimizer/Analysis/AliasAnalysis.cpp | 46 +++++++++++++------
2 files changed, 44 insertions(+), 13 deletions(-)
diff --git a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
index 9a70b7fbfad2b6..5cad6768fafe19 100644
--- a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
+++ b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
@@ -157,6 +157,17 @@ struct AliasAnalysis {
bool isData() const;
bool isBoxData() const;
+ /// Check conditions related to dummy argument aliasing.
+ ///
+ /// For all uses, a result of false can prevent MayAlias from being
+ /// reported, so the list of cases where false is returned is conservative.
+ /// @{
+ bool aliasesLikeDummyArg() const;
+ bool aliasesLikePtrDummyArg() const;
+ bool canBeActualArg() const;
+ bool canBeActualArgWithPtr(const mlir::Value *val) const;
+ /// @}
+
mlir::Type getType() const;
};
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 30d66e9a799e82..a4b34bec66b8fd 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -94,6 +94,34 @@ bool AliasAnalysis::Source::isBoxData() const {
origin.isData;
}
+bool AliasAnalysis::Source::aliasesLikeDummyArg() const {
+ return kind != SourceKind::Allocate && kind != SourceKind::Global;
+}
+
+bool AliasAnalysis::Source::aliasesLikePtrDummyArg() const {
+ // Must alias like dummy arg (or HostAssoc).
+ if (!aliasesLikeDummyArg())
+ return false;
+ // Must be address of the dummy arg not of a dummy arg component.
+ if (isRecordWithPointerComponent(valueType))
+ return false;
+ // Must be address *of* (not *in*) a pointer.
+ return attributes.test(Attribute::Pointer) && !isData();
+}
+
+bool AliasAnalysis::Source::canBeActualArg() const {
+ return kind != SourceKind::Allocate;
+}
+
+bool AliasAnalysis::Source::canBeActualArgWithPtr(
+ const mlir::Value *val) const {
+ // Must not be local.
+ if (!canBeActualArg())
+ return false;
+ // Must be address of a composite with a pointer component.
+ return isRecordWithPointerComponent(val->getType());
+}
+
AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
// TODO: alias() has to be aware of the function scopes.
// After MLIR inlining, the current implementation may
@@ -220,7 +248,7 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
// print *, p
// end subroutine
// end module
- // program
+ // program main
// use m
// real, target :: x1 = 1
// real, target :: x2 = 2
@@ -245,19 +273,11 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
// print *, p
// end subroutine
// end subroutine
- if ((isRecordWithPointerComponent(val1->getType()) &&
- src1->kind != SourceKind::Allocate &&
- src2->kind != SourceKind::Allocate && src2->kind != SourceKind::Global &&
- src2->attributes.test(Attribute::Pointer) && !src2->isData() &&
- !isRecordWithPointerComponent(src2->valueType)) ||
- (isRecordWithPointerComponent(val2->getType()) &&
- src2->kind != SourceKind::Allocate &&
- src1->kind != SourceKind::Allocate && src1->kind != SourceKind::Global &&
- src1->attributes.test(Attribute::Pointer) && !src1->isData() &&
- !isRecordWithPointerComponent(src1->valueType))) {
+ if ((src1->aliasesLikePtrDummyArg() && src2->canBeActualArgWithPtr(val2)) ||
+ (src2->aliasesLikePtrDummyArg() && src1->canBeActualArgWithPtr(val1))) {
LLVM_DEBUG(llvm::dbgs()
- << " aliasing between pointer arg and composite with pointer "
- << "component\n");
+ << " aliasing between pointer dummy arg and composite with "
+ << "pointer component\n");
return AliasResult::MayAlias;
}
>From ee248f5236ec4914cf8708167fd74f4b48c2733f Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Fri, 12 Jul 2024 13:34:55 -0400
Subject: [PATCH 11/15] Fix todos for handling addresses of pointers
The general `if` calling `isTargetOrPointer` was handling addresses
*of* pointers similarly to addresses *in* pointers: it assumed they
may alias each other. While that's true in the case of dummy args,
it's not true generally, as revealed by some of the test suite todos
fixed by this commit.
---
.../lib/Optimizer/Analysis/AliasAnalysis.cpp | 25 +++++++++-----
.../AliasAnalysis/alias-analysis-9.fir | 33 +++++--------------
2 files changed, 24 insertions(+), 34 deletions(-)
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index a4b34bec66b8fd..72cc0bc8372a75 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -118,8 +118,13 @@ bool AliasAnalysis::Source::canBeActualArgWithPtr(
// Must not be local.
if (!canBeActualArg())
return false;
- // Must be address of a composite with a pointer component.
- return isRecordWithPointerComponent(val->getType());
+ // Can be address *of* (not *in*) a pointer.
+ if (attributes.test(Attribute::Pointer) && !isData())
+ return true;
+ // Can be address of a composite with a pointer component.
+ if (isRecordWithPointerComponent(val->getType()))
+ return true;
+ return false;
}
AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
@@ -222,16 +227,18 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
src2->attributes.set(Attribute::Target);
}
- // Two TARGET/POINTERs may alias.
+ // Two TARGET/POINTERs may alias. The logic here focuses on data. Handling
+ // of non-data is included below.
if (src1->isTargetOrPointer() && src2->isTargetOrPointer() &&
- src1->isData() == src2->isData()) {
+ src1->isData() && src2->isData()) {
LLVM_DEBUG(llvm::dbgs() << " aliasing because of target or pointer\n");
return AliasResult::MayAlias;
}
- // 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
+ // The address of a pointer dummy arg (but not a pointer component of a dummy
+ // arg) may alias the address of either (1) a non-local pointer or (2) thus a
+ // non-local composite with a pointer component. A non-local might be a
+ // global or another dummy arg. The following is an example of the global
// composite case:
//
// module m
@@ -276,8 +283,8 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
if ((src1->aliasesLikePtrDummyArg() && src2->canBeActualArgWithPtr(val2)) ||
(src2->aliasesLikePtrDummyArg() && src1->canBeActualArgWithPtr(val1))) {
LLVM_DEBUG(llvm::dbgs()
- << " aliasing between pointer dummy arg and composite with "
- << "pointer component\n");
+ << " aliasing between pointer dummy arg and either pointer or "
+ << "composite with pointer component\n");
return AliasResult::MayAlias;
}
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
index ca496d91a1cedd..91d016a7df574c 100644
--- a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
@@ -52,12 +52,8 @@
// CHECK-DAG: xnext1#0 <-> xnext2#0: MayAlias
// CHECK-DAG: xnext1.fir#0 <-> xnext2.fir#0: MayAlias
-// TODO: These 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.
-// CHECK-DAG: xnext1#0 <-> ynext#0: MayAlias
-// CHECK-DAG: xnext1.fir#0 <-> ynext.fir#0: MayAlias
+// CHECK-DAG: xnext1#0 <-> ynext#0: NoAlias
+// CHECK-DAG: xnext1.fir#0 <-> ynext.fir#0: NoAlias
func.func @_QMmPfoo(%arg0: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>> {fir.bindc_name = "x"}, %arg1: !fir.ref<!fir.type<_QMmTt{next:!fir.box<!fir.ptr<!fir.type<_QMmTt>>>,i:i32}>> {fir.bindc_name = "y"}) {
%0 = fir.alloca i32 {bindc_name = "i1", uniq_name = "_QMmFfooEi1"}
@@ -391,16 +387,11 @@ func.func @_QMmPtest.fir() {
// Check when composite is a local and thus cannot alias a dummy arg.
//
-// TODO: The argp vs. loc%p cases 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%p#0: NoAlias
// CHECK-DAG: argp#0 <-> loc%i#0: NoAlias
// CHECK-DAG: argp.fir#0 <-> loc.fir#0: NoAlias
-// CHECK-DAG: argp.fir#0 <-> loc%p.fir#0: MayAlias
+// CHECK-DAG: argp.fir#0 <-> loc%p.fir#0: NoAlias
// CHECK-DAG: argp.fir#0 <-> loc%i.fir#0: NoAlias
//
// CHECK-DAG: argp.tgt#0 <-> loc#0: NoAlias
@@ -519,13 +510,10 @@ func.func @_QMmPtest.fir(%arg0: !fir.ref<!fir.box<!fir.ptr<i32>>> {fir.bindc_nam
// CHECK-DAG: argp.tgt.fir#0 <-> arg%p.fir#0: MayAlias
// CHECK-DAG: argp.tgt.fir#0 <-> arg%i.fir#0: MayAlias
//
-// TODO: Shouldn't the arga vs. arg%p cases be NoAlias? However, arga is
-// treated like a target because it's HostAssoc and arg is Argument, and arg%p
-// is treated like a pointer.
// CHECK-DAG: arga#0 <-> arg#0: NoAlias
-// CHECK-DAG: arga#0 <-> arg%p#0: MayAlias
+// CHECK-DAG: arga#0 <-> arg%p#0: NoAlias
// CHECK-DAG: arga.fir#0 <-> arg.fir#0: NoAlias
-// CHECK-DAG: arga.fir#0 <-> arg%p.fir#0: MayAlias
+// CHECK-DAG: arga.fir#0 <-> arg%p.fir#0: NoAlias
// Check when composite is a global.
//
@@ -552,16 +540,11 @@ func.func @_QMmPtest.fir(%arg0: !fir.ref<!fir.box<!fir.ptr<i32>>> {fir.bindc_nam
// 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%p#0: NoAlias
// CHECK-DAG: argp#0 <-> loc%i#0: NoAlias
// CHECK-DAG: argp.fir#0 <-> loc.fir#0: NoAlias
-// CHECK-DAG: argp.fir#0 <-> loc%p.fir#0: MayAlias
+// CHECK-DAG: argp.fir#0 <-> loc%p.fir#0: NoAlias
// CHECK-DAG: argp.fir#0 <-> loc%i.fir#0: NoAlias
//
// TODO: Shouldn't these be NoAlias? However, argp.tgt is currently handled as
>From a61be3a0d7202da4ccd9a3b38dc68dc3f119c2ce Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Fri, 12 Jul 2024 18:36:44 -0400
Subject: [PATCH 12/15] Fix handling of dummy arg with target attribute
That is, the address of a dummy arg may alias the address of a global
or another dummy arg when both have target attributes. If either is a
composite, components may alias as well.
However, this PR broke handling of that case in two previous changes:
1. Initial commits in this PR replaced the conservative handling of
pointer components. As a result, they stopped reporting MayAlias
for, e.g., the address of a pointer component of a dummy arg
composite with a target attribute vs. the address of another
composite with a target attribute. This problem was discussed in
the PR:
<https://github.com/llvm/llvm-project/pull/94242#discussion_r1645883337>
2. The previous commit in this PR changed the conservative
targets/pointers handling not to handle the case where both values
have `!isData()`. As a result, it stopped reporting MayAlias for,
e.g., the address of an allocatable dummy arg with a target
attribute vs. another allocatable with a target attribute.
This commit fixes such cases.
---
.../lib/Optimizer/Analysis/AliasAnalysis.cpp | 22 ++
.../AliasAnalysis/alias-analysis-9.fir | 208 ++++++++++++++++++
2 files changed, 230 insertions(+)
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 72cc0bc8372a75..328aa3e16c86b8 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -235,6 +235,28 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
return AliasResult::MayAlias;
}
+ // Aliasing for dummy arg with target attribute.
+ //
+ // The address of a dummy arg (or HostAssoc) may alias the address of a
+ // non-local (global or another dummy arg) when both have target attributes.
+ // If either is a composite, addresses of components may alias as well.
+ //
+ // The previous "if" calling isTargetOrPointer casts a very wide net and so
+ // reports MayAlias for many such cases that would otherwise be reported here.
+ // It specifically skips such cases where one or both values have !isData()
+ // (e.g., address *of* pointer/allocatable component vs. address of
+ // composite), so this "if" catches those cases.
+ if (src1->attributes.test(Attribute::Target) &&
+ src2->attributes.test(Attribute::Target) &&
+ ((src1->aliasesLikeDummyArg() && src2->canBeActualArg()) ||
+ (src2->aliasesLikeDummyArg() && src1->canBeActualArg()))) {
+ LLVM_DEBUG(llvm::dbgs()
+ << " aliasing between targets where one is a dummy arg\n");
+ return AliasResult::MayAlias;
+ }
+
+ // Aliasing for dummy arg that is a pointer.
+ //
// The address of a pointer dummy arg (but not a pointer component of a dummy
// arg) may alias the address of either (1) a non-local pointer or (2) thus a
// non-local composite with a pointer component. A non-local might be a
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
index 91d016a7df574c..a4cee49c2a1933 100644
--- a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
@@ -632,3 +632,211 @@ func.func private @_QMmFparentPtest.fir(%arg0: !fir.ref<!fir.type<_QMmTt{p:!fir.
return
}
+
+// -----
+
+// Dummy arg with target attribute and pointer component.
+
+// The address of a dummy arg (arg0) may alias the address of a global (glob0)
+// or another dummy arg (arg1) when both have target attributes. If either is a
+// composite, the addresses of components (whether data like r, or non-data like
+// p and a) may also alias the composite or the same component. However, target
+// attributes do not permit two globals (glob0 and glob1) to alias.
+
+// module m
+// type t
+// real, pointer :: p
+// real, allocatable :: a
+// real :: r
+// end type
+// type(t), target :: glob0
+// type(t), target :: glob1
+// contains
+// subroutine test(arg0, arg1)
+// type(t), target :: arg0
+// type(t), target :: arg1
+// end subroutine
+// end module
+
+// TODO: All glob0 vs. glob1 cases can be NoAlias. However, AliasAnalysis
+// currently indiscriminately treats all targets that are data (addresses of
+// glob[01] and glob[01]%r but not glob[01]%p and glob[01]%a) as aliasing.
+
+// Check composite vs. composite.
+//
+// CHECK-DAG: arg0#0 <-> arg1#0: MayAlias
+// CHECK-DAG: arg0.fir#0 <-> arg1.fir#0: MayAlias
+//
+// CHECK-DAG: arg0#0 <-> glob0#0: MayAlias
+// CHECK-DAG: arg0.fir#0 <-> glob0.fir#0: MayAlias
+//
+// CHECK-DAG: glob0#0 <-> glob1#0: MayAlias
+// CHECK-DAG: glob0.fir#0 <-> glob1.fir#0: MayAlias
+
+// Check component vs. composite.
+//
+// CHECK-DAG: arg0%p#0 <-> arg1#0: MayAlias
+// CHECK-DAG: arg0%a#0 <-> arg1#0: MayAlias
+// CHECK-DAG: arg0%r#0 <-> arg1#0: MayAlias
+// CHECK-DAG: arg0%p.fir#0 <-> arg1.fir#0: MayAlias
+// CHECK-DAG: arg0%a.fir#0 <-> arg1.fir#0: MayAlias
+// CHECK-DAG: arg0%r.fir#0 <-> arg1.fir#0: MayAlias
+//
+// CHECK-DAG: arg0%p#0 <-> glob0#0: MayAlias
+// CHECK-DAG: arg0%a#0 <-> glob0#0: MayAlias
+// CHECK-DAG: arg0%r#0 <-> glob0#0: MayAlias
+// CHECK-DAG: arg0%p.fir#0 <-> glob0.fir#0: MayAlias
+// CHECK-DAG: arg0%a.fir#0 <-> glob0.fir#0: MayAlias
+// CHECK-DAG: arg0%r.fir#0 <-> glob0.fir#0: MayAlias
+//
+// CHECK-DAG: glob0%p#0 <-> glob1#0: NoAlias
+// CHECK-DAG: glob0%a#0 <-> glob1#0: NoAlias
+// CHECK-DAG: glob0%r#0 <-> glob1#0: MayAlias
+// CHECK-DAG: glob0%p.fir#0 <-> glob1.fir#0: NoAlias
+// CHECK-DAG: glob0%a.fir#0 <-> glob1.fir#0: NoAlias
+// CHECK-DAG: glob0%r.fir#0 <-> glob1.fir#0: MayAlias
+
+// Check composite vs. component.
+//
+// CHECK-DAG: arg0#0 <-> arg1%p#0: MayAlias
+// CHECK-DAG: arg0#0 <-> arg1%a#0: MayAlias
+// CHECK-DAG: arg0#0 <-> arg1%r#0: MayAlias
+// CHECK-DAG: arg0.fir#0 <-> arg1%p.fir#0: MayAlias
+// CHECK-DAG: arg0.fir#0 <-> arg1%a.fir#0: MayAlias
+// CHECK-DAG: arg0.fir#0 <-> arg1%r.fir#0: MayAlias
+//
+// CHECK-DAG: arg0#0 <-> glob0%p#0: MayAlias
+// CHECK-DAG: arg0#0 <-> glob0%a#0: MayAlias
+// CHECK-DAG: arg0#0 <-> glob0%r#0: MayAlias
+// CHECK-DAG: arg0.fir#0 <-> glob0%p.fir#0: MayAlias
+// CHECK-DAG: arg0.fir#0 <-> glob0%a.fir#0: MayAlias
+// CHECK-DAG: arg0.fir#0 <-> glob0%r.fir#0: MayAlias
+//
+// CHECK-DAG: glob0#0 <-> glob1%p#0: NoAlias
+// CHECK-DAG: glob0#0 <-> glob1%a#0: NoAlias
+// CHECK-DAG: glob0#0 <-> glob1%r#0: MayAlias
+// CHECK-DAG: glob0.fir#0 <-> glob1%p.fir#0: NoAlias
+// CHECK-DAG: glob0.fir#0 <-> glob1%a.fir#0: NoAlias
+// CHECK-DAG: glob0.fir#0 <-> glob1%r.fir#0: MayAlias
+
+// Check component vs. component.
+//
+// CHECK-DAG: arg0%p#0 <-> arg1%p#0: MayAlias
+// CHECK-DAG: arg0%a#0 <-> arg1%a#0: MayAlias
+// CHECK-DAG: arg0%r#0 <-> arg1%r#0: MayAlias
+// CHECK-DAG: arg0%p.fir#0 <-> arg1%p.fir#0: MayAlias
+// CHECK-DAG: arg0%a.fir#0 <-> arg1%a.fir#0: MayAlias
+// CHECK-DAG: arg0%r.fir#0 <-> arg1%r.fir#0: MayAlias
+//
+// CHECK-DAG: arg0%p.fir#0 <-> glob0%p.fir#0: MayAlias
+// CHECK-DAG: arg0%a.fir#0 <-> glob0%a.fir#0: MayAlias
+// CHECK-DAG: arg0%r.fir#0 <-> glob0%r.fir#0: MayAlias
+//
+// CHECK-DAG: glob0%p.fir#0 <-> glob1%p.fir#0: NoAlias
+// CHECK-DAG: glob0%a.fir#0 <-> glob1%a.fir#0: NoAlias
+// CHECK-DAG: glob0%r.fir#0 <-> glob1%r.fir#0: MayAlias
+
+func.func @_QMmPtest(%arg0: !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>> {fir.bindc_name = "arg0", fir.target}, %arg1: !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>> {fir.bindc_name = "arg1", fir.target}) {
+ %0 = fir.dummy_scope : !fir.dscope
+
+ %10:2 = hlfir.declare %arg0 dummy_scope %0 {test.ptr="arg0", fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QMmFtestEarg0"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.dscope) -> (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>)
+ %11 = hlfir.designate %10#0{"p"} {test.ptr="arg0%p", fortran_attrs = #fir.var_attrs<pointer>} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+ %12 = hlfir.designate %10#0{"a"} {test.ptr="arg0%a", fortran_attrs = #fir.var_attrs<allocatable>} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>) -> !fir.ref<!fir.box<!fir.heap<f32>>>
+ %13 = hlfir.designate %10#0{"r"} {test.ptr="arg0%r"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>) -> !fir.ref<f32>
+
+ %20:2 = hlfir.declare %arg1 dummy_scope %0 {test.ptr="arg1", fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QMmFtestEarg1"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.dscope) -> (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>)
+ %21 = hlfir.designate %20#0{"p"} {test.ptr="arg1%p", fortran_attrs = #fir.var_attrs<pointer>} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+ %22 = hlfir.designate %20#0{"a"} {test.ptr="arg1%a", fortran_attrs = #fir.var_attrs<allocatable>} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>) -> !fir.ref<!fir.box<!fir.heap<f32>>>
+ %23 = hlfir.designate %20#0{"r"} {test.ptr="arg1%r"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>) -> !fir.ref<f32>
+
+ %30 = fir.address_of(@_QMmEglob0) : !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>
+ %31:2 = hlfir.declare %30 {test.ptr="glob0", fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QMmEglob0"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>) -> (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>)
+ %32 = hlfir.designate %31#0{"p"} {test.ptr="glob0%p", fortran_attrs = #fir.var_attrs<pointer>} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+ %33 = hlfir.designate %31#0{"a"} {test.ptr="glob0%a", fortran_attrs = #fir.var_attrs<allocatable>} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>) -> !fir.ref<!fir.box<!fir.heap<f32>>>
+ %34 = hlfir.designate %31#0{"r"} {test.ptr="glob0%r"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>) -> !fir.ref<f32>
+
+ %40 = fir.address_of(@_QMmEglob1) : !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>
+ %41:2 = hlfir.declare %40 {test.ptr="glob1", fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QMmEglob1"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>) -> (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>)
+ %42 = hlfir.designate %41#0{"p"} {test.ptr="glob1%p", fortran_attrs = #fir.var_attrs<pointer>} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+ %43 = hlfir.designate %41#0{"a"} {test.ptr="glob1%a", fortran_attrs = #fir.var_attrs<allocatable>} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>) -> !fir.ref<!fir.box<!fir.heap<f32>>>
+ %44 = hlfir.designate %41#0{"r"} {test.ptr="glob1%r"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>) -> !fir.ref<f32>
+
+ return
+}
+
+func.func @_QMmPtest.fir(%arg0: !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>> {fir.bindc_name = "arg0", fir.target}, %arg1: !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>> {fir.bindc_name = "arg1", fir.target}) {
+ %0 = fir.dummy_scope : !fir.dscope
+
+ %1 = fir.declare %arg0 dummy_scope %0 {test.ptr="arg0.fir", fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QMmFtestEarg0"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.dscope) -> !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>
+ %2 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>
+ %3 = fir.coordinate_of %1, %2 {test.ptr="arg0%p.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+ %4 = fir.field_index a, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>
+ %5 = fir.coordinate_of %1, %4 {test.ptr="arg0%a.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.heap<f32>>>
+ %6 = fir.field_index r, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>
+ %7 = fir.coordinate_of %1, %6 {test.ptr="arg0%r.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.field) -> !fir.ref<f32>
+
+ %8 = fir.declare %arg1 dummy_scope %0 {test.ptr="arg1.fir", fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QMmFtestEarg1"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.dscope) -> !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>
+ %9 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>
+ %10 = fir.coordinate_of %8, %9 {test.ptr="arg1%p.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+ %11 = fir.field_index a, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>
+ %12 = fir.coordinate_of %8, %11 {test.ptr="arg1%a.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.heap<f32>>>
+ %13 = fir.field_index r, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>
+ %14 = fir.coordinate_of %8, %13 {test.ptr="arg1%r.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.field) -> !fir.ref<f32>
+
+ %15 = fir.address_of(@_QMmEglob0) : !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>
+ %16 = fir.declare %15 {test.ptr="glob0.fir", fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QMmEglob0"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>) -> !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>
+ %17 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>
+ %18 = fir.coordinate_of %16, %17 {test.ptr="glob0%p.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+ %19 = fir.field_index a, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>
+ %20 = fir.coordinate_of %16, %19 {test.ptr="glob0%a.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.heap<f32>>>
+ %21 = fir.field_index r, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>
+ %22 = fir.coordinate_of %16, %21 {test.ptr="glob0%r.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.field) -> !fir.ref<f32>
+
+ %23 = fir.address_of(@_QMmEglob1) : !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>
+ %24 = fir.declare %23 {test.ptr="glob1.fir", fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QMmEglob1"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>) -> !fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>
+ %25 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>
+ %26 = fir.coordinate_of %24, %25 {test.ptr="glob1%p.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+ %27 = fir.field_index a, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>
+ %28 = fir.coordinate_of %24, %27 {test.ptr="glob1%a.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.field) -> !fir.ref<!fir.box<!fir.heap<f32>>>
+ %29 = fir.field_index r, !fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>
+ %30 = fir.coordinate_of %24, %29 {test.ptr="glob1%r.fir"} : (!fir.ref<!fir.type<_QMmTt{p:!fir.box<!fir.ptr<f32>>,a:!fir.box<!fir.heap<f32>>,r:f32}>>, !fir.field) -> !fir.ref<f32>
+
+ return
+}
+
+// -----
+
+// Allocatable dummy arg with target attribute.
+
+// This case is like the previous one except that the non-data is the address
+// of the dummy arg itself rather than of a pointer component of the dummy arg.
+// The goal is to check that logic introduced into AliasAnalysis to handle the
+// pointer component case doesn't break this related one.
+
+// module m
+// real, allocatable, target :: glob0
+// real, allocatable, target :: glob1
+// contains
+// subroutine test(arg0, arg1)
+// real, allocatable, target :: arg0
+// real, allocatable, target :: arg1
+// end subroutine
+// end module
+
+// CHECK-DAG: arg0#0 <-> arg1#0: MayAlias
+// CHECK-DAG: arg0#0 <-> glob0#0: MayAlias
+// CHECK-DAG: glob0#0 <-> glob1#0: NoAlias
+
+func.func @_QMmPtest(%arg0: !fir.ref<!fir.box<!fir.heap<f32>>> {fir.bindc_name = "arg0", fir.target}, %arg1: !fir.ref<!fir.box<!fir.heap<f32>>> {fir.bindc_name = "arg1", fir.target}) {
+ %0 = fir.dummy_scope : !fir.dscope
+
+ %10:2 = hlfir.declare %arg0 dummy_scope %0 {test.ptr="arg0", fortran_attrs = #fir.var_attrs<allocatable, target>, uniq_name = "_QMmFtestEarg0"} : (!fir.ref<!fir.box<!fir.heap<f32>>>, !fir.dscope) -> (!fir.ref<!fir.box<!fir.heap<f32>>>, !fir.ref<!fir.box<!fir.heap<f32>>>)
+ %11:2 = hlfir.declare %arg1 dummy_scope %0 {test.ptr="arg1", fortran_attrs = #fir.var_attrs<allocatable, target>, uniq_name = "_QMmFtestEarg1"} : (!fir.ref<!fir.box<!fir.heap<f32>>>, !fir.dscope) -> (!fir.ref<!fir.box<!fir.heap<f32>>>, !fir.ref<!fir.box<!fir.heap<f32>>>)
+
+ %20 = fir.address_of(@_QMmEglob0) : !fir.ref<!fir.box<!fir.heap<f32>>>
+ %21:2 = hlfir.declare %20 {test.ptr="glob0", fortran_attrs = #fir.var_attrs<allocatable, target>, uniq_name = "_QMmEglob0"} : (!fir.ref<!fir.box<!fir.heap<f32>>>) -> (!fir.ref<!fir.box<!fir.heap<f32>>>, !fir.ref<!fir.box<!fir.heap<f32>>>)
+ %22 = fir.address_of(@_QMmEglob1) : !fir.ref<!fir.box<!fir.heap<f32>>>
+ %23:2 = hlfir.declare %22 {test.ptr="glob1", fortran_attrs = #fir.var_attrs<allocatable, target>, uniq_name = "_QMmEglob1"} : (!fir.ref<!fir.box<!fir.heap<f32>>>) -> (!fir.ref<!fir.box<!fir.heap<f32>>>, !fir.ref<!fir.box<!fir.heap<f32>>>)
+
+ return
+}
>From ea40a25120ceb2e4e7d2c22458404059b8b0dfa6 Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Fri, 12 Jul 2024 18:38:51 -0400
Subject: [PATCH 13/15] Give test more meaningful name
---
.../{alias-analysis-9.fir => ptr-component.fir} | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
rename flang/test/Analysis/AliasAnalysis/{alias-analysis-9.fir => ptr-component.fir} (99%)
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir b/flang/test/Analysis/AliasAnalysis/ptr-component.fir
similarity index 99%
rename from flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
rename to flang/test/Analysis/AliasAnalysis/ptr-component.fir
index a4cee49c2a1933..7dbf53b14fd52f 100644
--- a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir
+++ b/flang/test/Analysis/AliasAnalysis/ptr-component.fir
@@ -4,14 +4,13 @@
// MLIR after convert-hlfir-to-fir. A key difference is that component access
// is via fir.coordinate_of instead of hlfir.designate. We would like alias
// analysis results to be the same in both versions.
+//
+// TODO: What about merging with alias-analysis-3.fir as it has the same focus?
// 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
-// 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
// type(t), pointer :: next
>From 553cf7491c209658236231029a79c058472e2018 Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Thu, 5 Sep 2024 15:04:34 -0400
Subject: [PATCH 14/15] Remove todo about merging tests
---
flang/test/Analysis/AliasAnalysis/ptr-component.fir | 2 --
1 file changed, 2 deletions(-)
diff --git a/flang/test/Analysis/AliasAnalysis/ptr-component.fir b/flang/test/Analysis/AliasAnalysis/ptr-component.fir
index 7dbf53b14fd52f..279143a5814606 100644
--- a/flang/test/Analysis/AliasAnalysis/ptr-component.fir
+++ b/flang/test/Analysis/AliasAnalysis/ptr-component.fir
@@ -4,8 +4,6 @@
// MLIR after convert-hlfir-to-fir. A key difference is that component access
// is via fir.coordinate_of instead of hlfir.designate. We would like alias
// analysis results to be the same in both versions.
-//
-// TODO: What about merging with alias-analysis-3.fir as it has the same focus?
// RUN: fir-opt %s -split-input-file -o /dev/null --mlir-disable-threading \
// RUN: -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' \
>From 942a625931afef8403981ec9587d60e6367f9d2b Mon Sep 17 00:00:00 2001
From: "Joel E. Denny" <jdenny.ornl at gmail.com>
Date: Mon, 14 Oct 2024 14:19:27 -0400
Subject: [PATCH 15/15] Improve some function names and comments
---
.../flang/Optimizer/Analysis/AliasAnalysis.h | 21 +++++++++++++-----
.../lib/Optimizer/Analysis/AliasAnalysis.cpp | 22 ++++++++++---------
2 files changed, 27 insertions(+), 16 deletions(-)
diff --git a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
index 6f9203b4fd0fc5..97c64dc3419524 100644
--- a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
+++ b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
@@ -157,16 +157,25 @@ struct AliasAnalysis {
bool isData() const;
bool isBoxData() const;
+ /// @name Dummy Argument Aliasing
+ ///
/// Check conditions related to dummy argument aliasing.
///
/// For all uses, a result of false can prevent MayAlias from being
/// reported, so the list of cases where false is returned is conservative.
- /// @{
- bool aliasesLikeDummyArg() const;
- bool aliasesLikePtrDummyArg() const;
- bool canBeActualArg() const;
- bool canBeActualArgWithPtr(const mlir::Value *val) const;
- /// @}
+
+ ///@{
+ /// The address of a (possibly host associated) dummy argument of the
+ /// current function?
+ bool mayBeDummyArgOrHostAssoc() const;
+ /// \c mayBeDummyArgOrHostAssoc and the address of a pointer?
+ bool mayBePtrDummyArgOrHostAssoc() const;
+ /// The address of an actual argument of the current function?
+ bool mayBeActualArg() const;
+ /// \c mayBeActualArg and the address of either a pointer or a composite
+ /// with a pointer component?
+ bool mayBeActualArgWithPtr(const mlir::Value *val) const;
+ ///@}
mlir::Type getType() const;
};
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index d642e0094dc116..241803c3938a68 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -96,13 +96,13 @@ bool AliasAnalysis::Source::isBoxData() const {
origin.isData;
}
-bool AliasAnalysis::Source::aliasesLikeDummyArg() const {
+bool AliasAnalysis::Source::mayBeDummyArgOrHostAssoc() const {
return kind != SourceKind::Allocate && kind != SourceKind::Global;
}
-bool AliasAnalysis::Source::aliasesLikePtrDummyArg() const {
+bool AliasAnalysis::Source::mayBePtrDummyArgOrHostAssoc() const {
// Must alias like dummy arg (or HostAssoc).
- if (!aliasesLikeDummyArg())
+ if (!mayBeDummyArgOrHostAssoc())
return false;
// Must be address of the dummy arg not of a dummy arg component.
if (isRecordWithPointerComponent(valueType))
@@ -111,14 +111,14 @@ bool AliasAnalysis::Source::aliasesLikePtrDummyArg() const {
return attributes.test(Attribute::Pointer) && !isData();
}
-bool AliasAnalysis::Source::canBeActualArg() const {
+bool AliasAnalysis::Source::mayBeActualArg() const {
return kind != SourceKind::Allocate;
}
-bool AliasAnalysis::Source::canBeActualArgWithPtr(
+bool AliasAnalysis::Source::mayBeActualArgWithPtr(
const mlir::Value *val) const {
// Must not be local.
- if (!canBeActualArg())
+ if (!mayBeActualArg())
return false;
// Can be address *of* (not *in*) a pointer.
if (attributes.test(Attribute::Pointer) && !isData())
@@ -250,8 +250,8 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
// composite), so this "if" catches those cases.
if (src1->attributes.test(Attribute::Target) &&
src2->attributes.test(Attribute::Target) &&
- ((src1->aliasesLikeDummyArg() && src2->canBeActualArg()) ||
- (src2->aliasesLikeDummyArg() && src1->canBeActualArg()))) {
+ ((src1->mayBeDummyArgOrHostAssoc() && src2->mayBeActualArg()) ||
+ (src2->mayBeDummyArgOrHostAssoc() && src1->mayBeActualArg()))) {
LLVM_DEBUG(llvm::dbgs()
<< " aliasing between targets where one is a dummy arg\n");
return AliasResult::MayAlias;
@@ -304,8 +304,10 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
// print *, p
// end subroutine
// end subroutine
- if ((src1->aliasesLikePtrDummyArg() && src2->canBeActualArgWithPtr(val2)) ||
- (src2->aliasesLikePtrDummyArg() && src1->canBeActualArgWithPtr(val1))) {
+ if ((src1->mayBePtrDummyArgOrHostAssoc() &&
+ src2->mayBeActualArgWithPtr(val2)) ||
+ (src2->mayBePtrDummyArgOrHostAssoc() &&
+ src1->mayBeActualArgWithPtr(val1))) {
LLVM_DEBUG(llvm::dbgs()
<< " aliasing between pointer dummy arg and either pointer or "
<< "composite with pointer component\n");
More information about the flang-commits
mailing list