-
Notifications
You must be signed in to change notification settings - Fork 13k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[flang] AliasAnalysis: Handle fir.load on fir.alloca #117785
Conversation
For example, determine that the address in p below cannot alias the address of v: ``` subroutine test() real, pointer :: p real, target :: t real :: v p => t v = p end subroutine test ```
@llvm/pr-subscribers-flang-fir-hlfir Author: Joel E. Denny (jdenny-ornl) ChangesFor example, determine that the address in p below cannot alias the address of v:
Patch is 34.61 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/117785.diff 3 Files Affected:
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 2b24791d6c7c52..284b3dadbcef96 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -31,21 +31,6 @@ using namespace mlir;
// AliasAnalysis: alias
//===----------------------------------------------------------------------===//
-/// Temporary function to skip through all the no op operations
-/// TODO: Generalize support of fir.load
-static mlir::Value getOriginalDef(mlir::Value v) {
- mlir::Operation *defOp;
- bool breakFromLoop = false;
- while (!breakFromLoop && (defOp = v.getDefiningOp())) {
- llvm::TypeSwitch<Operation *>(defOp)
- .Case<fir::ConvertOp>([&](fir::ConvertOp op) { v = op.getValue(); })
- .Case<fir::DeclareOp, hlfir::DeclareOp>(
- [&](auto op) { v = op.getMemref(); })
- .Default([&](auto op) { breakFromLoop = true; });
- }
- return v;
-}
-
namespace fir {
void AliasAnalysis::Source::print(llvm::raw_ostream &os) const {
@@ -497,6 +482,29 @@ static Value getPrivateArg(omp::BlockArgOpenMPOpInterface &argIface,
return privateArg;
}
+/// Temporary function to skip through all the no op operations
+/// TODO: Generalize support of fir.load
+static mlir::Value
+getOriginalDef(mlir::Value v,
+ fir::AliasAnalysis::Source::Attributes &attributes,
+ bool &isCapturedInInternalProcedure) {
+ mlir::Operation *defOp;
+ bool breakFromLoop = false;
+ while (!breakFromLoop && (defOp = v.getDefiningOp())) {
+ llvm::TypeSwitch<Operation *>(defOp)
+ .Case<fir::ConvertOp>([&](fir::ConvertOp op) { v = op.getValue(); })
+ .Case<fir::DeclareOp, hlfir::DeclareOp>([&](auto op) {
+ v = op.getMemref();
+ auto varIf = llvm::cast<fir::FortranVariableOpInterface>(defOp);
+ attributes |= getAttrsFromVariable(varIf);
+ isCapturedInInternalProcedure |=
+ varIf.isCapturedInInternalProcedure();
+ })
+ .Default([&](auto op) { breakFromLoop = true; });
+ }
+ return v;
+}
+
AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
bool getLastInstantiationPoint) {
auto *defOp = v.getDefiningOp();
@@ -509,6 +517,17 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
bool isBoxRef{fir::isa_ref_type(v.getType()) &&
mlir::isa<fir::BaseBoxType>(fir::unwrapRefType(v.getType()))};
bool followingData = !isBoxRef;
+ // "fir.alloca !fir.ptr<...>" returns the address *of* a pointer and is thus
+ // non-data, and yet there's no box. Don't treat it like data, or it will
+ // appear to alias like the address *in* a pointer. TODO: That case occurs in
+ // our test suite (alias-analysis-2.fir), but does flang currently generate
+ // such code? Perhaps we should update docs
+ // (AliasAnalysis::Source::SourceOrigin::isData) and debug output
+ // (AliasAnalysis::Source::print) for isData as the current wording implies
+ // !isData requires a box.
+ if (mlir::isa_and_nonnull<fir::AllocaOp, fir::AllocMemOp>(defOp) &&
+ isPointerReference(v.getType()))
+ followingData = false;
mlir::SymbolRefAttr global;
Source::Attributes attributes;
mlir::Operation *instantiationPoint{nullptr};
@@ -522,6 +541,12 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
.Case<fir::AllocaOp, fir::AllocMemOp>([&](auto op) {
// Unique memory allocation.
type = SourceKind::Allocate;
+ // If there's no DeclareOp, then we need to get the pointer attribute
+ // from the type. TODO: That case occurs in our test suite
+ // (alias-analysis-2.fir), but does flang currently generate such
+ // code?
+ if (isPointerReference(ty))
+ attributes.set(Attribute::Pointer);
breakFromLoop = true;
})
.Case<fir::ConvertOp>([&](auto op) {
@@ -554,10 +579,12 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
.Case<fir::LoadOp>([&](auto op) {
// If the load is from a leaf source, return the leaf. Do not track
// through indirections otherwise.
- // TODO: Add support to fir.alloca and fir.allocmem
- auto def = getOriginalDef(op.getMemref());
+ // TODO: Add support to fir.allocmem.
+ auto def = getOriginalDef(op.getMemref(), attributes,
+ isCapturedInInternalProcedure);
if (isDummyArgument(def) ||
- def.template getDefiningOp<fir::AddrOfOp>()) {
+ def.template getDefiningOp<fir::AddrOfOp>() ||
+ def.template getDefiningOp<fir::AllocaOp>()) {
v = def;
defOp = v.getDefiningOp();
return;
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir
index d03348efd2a68c..e2cdf8c8ddeb66 100644
--- a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir
@@ -8,9 +8,11 @@
// They cannot physically alias
// CHECK-DAG: p1.addr#0 <-> p2.addr#0: NoAlias
-// p1.addr and p2.addr could both be wrapped inside boxes
-// CHECK-DAG: p1.addr#0 <-> boxp1.addr#0: MayAlias
-// CHECK-DAG: p2.addr#0 <-> boxp1.addr#0: MayAlias
+// p1.addr is the address returned by an alloca. It does not have a target
+// attribute, and it is not the address retrieved from a pointer. It cannot
+// alias anything. Likewise for p2.addr.
+// CHECK-DAG: p1.addr#0 <-> boxp1.addr#0: NoAlias
+// CHECK-DAG: p2.addr#0 <-> boxp1.addr#0: NoAlias
// TODO: To really see aliasing, we should be looking at a load of p1.addr
// p1.addr is just a local address holding the address to the data
@@ -27,10 +29,11 @@
// CHECK-DAG: p2.addr#0 <-> func.region0#2: NoAlias
// All arguments are either pointers or targets
-// A pointer in a box may alias with both
+// The address *in* a local pointer may alias the address of a target
+// argument, but it does not alias the address *of* a pointer argument.
// CHECK-DAG: boxp1.addr#0 <-> func.region0#0: MayAlias
// CHECK-DAG: boxp1.addr#0 <-> func.region0#1: MayAlias
-// CHECK-DAG: boxp1.addr#0 <-> func.region0#2: MayAlias
+// CHECK-DAG: boxp1.addr#0 <-> func.region0#2: NoAlias
// A target dummy may alias with another target
// CHECK-DAG: func.region0#0 <-> func.region0#1: MayAlias
diff --git a/flang/test/Analysis/AliasAnalysis/load-ptr-alloca.fir b/flang/test/Analysis/AliasAnalysis/load-ptr-alloca.fir
new file mode 100644
index 00000000000000..56c5313d397a51
--- /dev/null
+++ b/flang/test/Analysis/AliasAnalysis/load-ptr-alloca.fir
@@ -0,0 +1,412 @@
+// Check aliasing with the address *in* (not *of*) a local (fir.alloca) pointer
+// variable.
+//
+// Throughout this test, the ".fir" suffix on symbols indicates a version of the
+// MLIR after convert-hlfir-to-fir. We would like alias analysis results to be
+// the same in both versions.
+
+// RUN: fir-opt %s -split-input-file -o /dev/null --mlir-disable-threading \
+// RUN: -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' \
+// RUN: 2>&1 | FileCheck -match-full-lines %s
+
+// subroutine test(n)
+// integer :: n
+// real, pointer :: p0, p1
+// real :: arr(n)
+// real, target :: t_arr(n)
+// real, allocatable :: alloc
+// real, allocatable, target :: t_alloc
+// real, target :: t
+// real :: v
+// end subroutine test
+
+// CHECK-LABEL: Testing : "_QPtest"
+
+// The address in a pointer can alias the address in another pointer or the
+// address of a target but not the address of other variables.
+// CHECK-DAG: p0.tgt#0 <-> p1.tgt#0: MayAlias
+// CHECK-DAG: t#0 <-> p0.tgt#0: MayAlias
+// CHECK-DAG: t#0 <-> p1.tgt#0: MayAlias
+// CHECK-DAG: v#0 <-> p0.tgt#0: NoAlias
+// CHECK-DAG: v#0 <-> p1.tgt#0: NoAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> p1.tgt.fir#0: MayAlias
+// CHECK-DAG: t.fir#0 <-> p0.tgt.fir#0: MayAlias
+// CHECK-DAG: t.fir#0 <-> p1.tgt.fir#0: MayAlias
+// CHECK-DAG: v.fir#0 <-> p0.tgt.fir#0: NoAlias
+// CHECK-DAG: v.fir#0 <-> p1.tgt.fir#0: NoAlias
+
+// The address in a pointer cannot alias the address of a pointer.
+// CHECK-DAG: p0#0 <-> p0.tgt#0: NoAlias
+// CHECK-DAG: p0#0 <-> p1.tgt#0: NoAlias
+// CHECK-DAG: p1#0 <-> p0.tgt#0: NoAlias
+// CHECK-DAG: p1#0 <-> p1.tgt#0: NoAlias
+// CHECK-DAG: p0.fir#0 <-> p0.tgt.fir#0: NoAlias
+// CHECK-DAG: p0.fir#0 <-> p1.tgt.fir#0: NoAlias
+// CHECK-DAG: p1.fir#0 <-> p0.tgt.fir#0: NoAlias
+// CHECK-DAG: p1.fir#0 <-> p1.tgt.fir#0: NoAlias
+
+// For some cases, AliasAnalysis analyzes hlfir.designate like fir.box_addr, so
+// make sure it doesn't mistakenly see the address of arr(1) as an address that
+// was loaded from a pointer and that could alias something. However, t_arr is
+// a target.
+// CHECK-DAG: p0.tgt#0 <-> arr(1)#0: NoAlias
+// CHECK-DAG: p0.tgt#0 <-> t_arr(1)#0: MayAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> arr(1).fir#0: NoAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> t_arr(1).fir#0: MayAlias
+
+// Like a pointer, an allocatable contains an address, but an allocatable is not
+// a pointer and so cannot alias pointers. However, t_alloc is a target.
+// CHECK-DAG: p0.tgt#0 <-> alloc.tgt#0: NoAlias
+// CHECK-DAG: p0.tgt#0 <-> t_alloc.tgt#0: MayAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> alloc.tgt.fir#0: NoAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> t_alloc.tgt.fir#0: MayAlias
+
+// The address in an allocatable cannot alias the address of that allocatable.
+// CHECK-DAG: alloc#0 <-> alloc.tgt#0: NoAlias
+// CHECK-DAG: alloc.fir#0 <-> alloc.tgt.fir#0: NoAlias
+
+func.func @_QPtest(%arg0: !fir.ref<i32> {fir.bindc_name = "n"}) {
+ %0 = fir.alloca !fir.box<!fir.heap<f32>> {bindc_name = "alloc", uniq_name = "_QFtestEalloc"}
+ %1:2 = hlfir.declare %0 {test.ptr="alloc", fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtestEalloc"} : (!fir.ref<!fir.box<!fir.heap<f32>>>) -> (!fir.ref<!fir.box<!fir.heap<f32>>>, !fir.ref<!fir.box<!fir.heap<f32>>>)
+ %2:2 = hlfir.declare %arg0 {uniq_name = "_QFtestEn"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %3 = fir.alloca !fir.box<!fir.ptr<f32>> {bindc_name = "p0", uniq_name = "_QFtestEp0"}
+ %4:2 = hlfir.declare %3 {test.ptr="p0", fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFtestEp0"} : (!fir.ref<!fir.box<!fir.ptr<f32>>>) -> (!fir.ref<!fir.box<!fir.ptr<f32>>>, !fir.ref<!fir.box<!fir.ptr<f32>>>)
+ %5 = fir.alloca !fir.box<!fir.ptr<f32>> {bindc_name = "p1", uniq_name = "_QFtestEp1"}
+ %6:2 = hlfir.declare %5 {test.ptr="p1", fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFtestEp1"} : (!fir.ref<!fir.box<!fir.ptr<f32>>>) -> (!fir.ref<!fir.box<!fir.ptr<f32>>>, !fir.ref<!fir.box<!fir.ptr<f32>>>)
+ %7 = fir.alloca f32 {bindc_name = "t", fir.target, uniq_name = "_QFtestEt"}
+ %8:2 = hlfir.declare %7 {test.ptr="t", fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFtestEt"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
+ %9 = fir.alloca !fir.box<!fir.heap<f32>> {bindc_name = "t_alloc", fir.target, uniq_name = "_QFtestEt_alloc"}
+ %10:2 = hlfir.declare %9 {fortran_attrs = #fir.var_attrs<allocatable, target>, uniq_name = "_QFtestEt_alloc"} : (!fir.ref<!fir.box<!fir.heap<f32>>>) -> (!fir.ref<!fir.box<!fir.heap<f32>>>, !fir.ref<!fir.box<!fir.heap<f32>>>)
+ %11 = fir.alloca f32 {bindc_name = "v", uniq_name = "_QFtestEv"}
+ %12:2 = hlfir.declare %11 {test.ptr="v", uniq_name = "_QFtestEv"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
+ %13 = fir.load %2#0 : !fir.ref<i32>
+ %14 = fir.convert %13 : (i32) -> i64
+ %15 = fir.convert %14 : (i64) -> index
+ %c0 = arith.constant 0 : index
+ %16 = arith.cmpi sgt, %15, %c0 : index
+ %17 = arith.select %16, %15, %c0 : index
+ %18 = fir.alloca !fir.array<?xf32>, %17 {bindc_name = "arr", uniq_name = "_QFtestEarr"}
+ %19 = fir.shape %17 : (index) -> !fir.shape<1>
+ %20:2 = hlfir.declare %18(%19) {uniq_name = "_QFtestEarr"} : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>) -> (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.array<?xf32>>)
+ %21 = fir.load %2#0 : !fir.ref<i32>
+ %22 = fir.convert %21 : (i32) -> i64
+ %23 = fir.convert %22 : (i64) -> index
+ %c0_0 = arith.constant 0 : index
+ %24 = arith.cmpi sgt, %23, %c0_0 : index
+ %25 = arith.select %24, %23, %c0_0 : index
+ %26 = fir.alloca !fir.array<?xf32>, %25 {bindc_name = "t_arr", fir.target, uniq_name = "_QFtestEt_arr"}
+ %27 = fir.shape %25 : (index) -> !fir.shape<1>
+ %28:2 = hlfir.declare %26(%27) {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFtestEt_arr"} : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>) -> (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.array<?xf32>>)
+ %29 = fir.load %4#0 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+ %30 = fir.box_addr %29 {test.ptr="p0.tgt"} : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
+ %31 = fir.load %6#0 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+ %32 = fir.box_addr %31 {test.ptr="p1.tgt"} : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
+ %c1 = arith.constant 1 : index
+ %33 = hlfir.designate %20#0 (%c1) {test.ptr="arr(1)"} : (!fir.box<!fir.array<?xf32>>, index) -> !fir.ref<f32>
+ %c1_1 = arith.constant 1 : index
+ %34 = hlfir.designate %28#0 (%c1_1) {test.ptr="t_arr(1)"} : (!fir.box<!fir.array<?xf32>>, index) -> !fir.ref<f32>
+ %35 = fir.load %1#0 : !fir.ref<!fir.box<!fir.heap<f32>>>
+ %36 = fir.box_addr %35 {test.ptr="alloc.tgt"} : (!fir.box<!fir.heap<f32>>) -> !fir.heap<f32>
+ %37 = fir.load %10#0 : !fir.ref<!fir.box<!fir.heap<f32>>>
+ %38 = fir.box_addr %37 {test.ptr="t_alloc.tgt"} : (!fir.box<!fir.heap<f32>>) -> !fir.heap<f32>
+ return
+}
+
+func.func @_QPtest.fir(%arg0: !fir.ref<i32> {fir.bindc_name = "n"}) {
+ %0 = fir.alloca !fir.box<!fir.heap<f32>> {bindc_name = "alloc", uniq_name = "_QFtestEalloc"}
+ %1 = fir.declare %0 {test.ptr = "alloc.fir", fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtestEalloc"} : (!fir.ref<!fir.box<!fir.heap<f32>>>) -> !fir.ref<!fir.box<!fir.heap<f32>>>
+ %2 = fir.declare %arg0 {uniq_name = "_QFtestEn"} : (!fir.ref<i32>) -> !fir.ref<i32>
+ %3 = fir.alloca !fir.box<!fir.ptr<f32>> {bindc_name = "p0", uniq_name = "_QFtestEp0"}
+ %4 = fir.declare %3 {test.ptr = "p0.fir", fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFtestEp0"} : (!fir.ref<!fir.box<!fir.ptr<f32>>>) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+ %5 = fir.alloca !fir.box<!fir.ptr<f32>> {bindc_name = "p1", uniq_name = "_QFtestEp1"}
+ %6 = fir.declare %5 {test.ptr = "p1.fir", fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFtestEp1"} : (!fir.ref<!fir.box<!fir.ptr<f32>>>) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+ %7 = fir.alloca f32 {bindc_name = "t", fir.target, uniq_name = "_QFtestEt"}
+ %8 = fir.declare %7 {test.ptr = "t.fir", fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFtestEt"} : (!fir.ref<f32>) -> !fir.ref<f32>
+ %9 = fir.alloca !fir.box<!fir.heap<f32>> {bindc_name = "t_alloc", fir.target, uniq_name = "_QFtestEt_alloc"}
+ %10 = fir.declare %9 {fortran_attrs = #fir.var_attrs<allocatable, target>, uniq_name = "_QFtestEt_alloc"} : (!fir.ref<!fir.box<!fir.heap<f32>>>) -> !fir.ref<!fir.box<!fir.heap<f32>>>
+ %11 = fir.alloca f32 {bindc_name = "v", uniq_name = "_QFtestEv"}
+ %12 = fir.declare %11 {test.ptr = "v.fir", uniq_name = "_QFtestEv"} : (!fir.ref<f32>) -> !fir.ref<f32>
+ %13 = fir.load %2 : !fir.ref<i32>
+ %14 = fir.convert %13 : (i32) -> i64
+ %15 = fir.convert %14 : (i64) -> index
+ %c0 = arith.constant 0 : index
+ %16 = arith.cmpi sgt, %15, %c0 : index
+ %17 = arith.select %16, %15, %c0 : index
+ %18 = fir.alloca !fir.array<?xf32>, %17 {bindc_name = "arr", uniq_name = "_QFtestEarr"}
+ %19 = fir.shape %17 : (index) -> !fir.shape<1>
+ %20 = fir.declare %18(%19) {uniq_name = "_QFtestEarr"} : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.ref<!fir.array<?xf32>>
+ %21 = fir.embox %20(%19) : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.box<!fir.array<?xf32>>
+ %22 = fir.load %2 : !fir.ref<i32>
+ %23 = fir.convert %22 : (i32) -> i64
+ %24 = fir.convert %23 : (i64) -> index
+ %c0_0 = arith.constant 0 : index
+ %25 = arith.cmpi sgt, %24, %c0_0 : index
+ %26 = arith.select %25, %24, %c0_0 : index
+ %27 = fir.alloca !fir.array<?xf32>, %26 {bindc_name = "t_arr", fir.target, uniq_name = "_QFtestEt_arr"}
+ %28 = fir.shape %26 : (index) -> !fir.shape<1>
+ %29 = fir.declare %27(%28) {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFtestEt_arr"} : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.ref<!fir.array<?xf32>>
+ %30 = fir.embox %29(%28) : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.box<!fir.array<?xf32>>
+ %31 = fir.load %4 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+ %32 = fir.box_addr %31 {test.ptr = "p0.tgt.fir"} : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
+ %33 = fir.load %6 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+ %34 = fir.box_addr %33 {test.ptr = "p1.tgt.fir"} : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
+ %c1 = arith.constant 1 : index
+ %35 = fir.array_coor %20(%19) %c1 {test.ptr="arr(1).fir"} : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>, index) -> !fir.ref<f32>
+ %c1_1 = arith.constant 1 : index
+ %36 = fir.array_coor %29(%28) %c1_1 {test.ptr="t_arr(1).fir"} : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>, index) -> !fir.ref<f32>
+ %37 = fir.load %1 : !fir.ref<!fir.box<!fir.heap<f32>>>
+ %38 = fir.box_addr %37 {test.ptr = "alloc.tgt.fir"} : (!fir.box<!fir.heap<f32>>) -> !fir.heap<f32>
+ %39 = fir.load %10 : !fir.ref<!fir.box<!fir.heap<f32>>>
+ %40 = fir.box_addr %39 {test.ptr = "t_alloc.tgt.fir"} : (!fir.box<!fir.heap<f32>>) -> !fir.heap<f32>
+ return
+}
+
+// -----
+
+// Repeat above test except compare the local pointer against dummy args instead
+// of other locals.
+
+// subroutine test(p1, arr, t_arr, alloc, t_alloc, t, v)
+// real, pointer :: p1
+// real :: arr(:)
+// real, target :: t_arr(:)
+// real, allocatable :: alloc
+// real, allocatable, target :: t_alloc
+// real, target :: t
+// real :: v
+// real, pointer :: p0
+// end subroutine test
+
+// CHECK-LABEL: Testing : "_QPtest"
+
+// The address in a pointer can alias the address in another pointer or the
+// address of a target but not the address of other variables.
+// CHECK-DAG: p0.tgt#0 <-> p1.tgt#0: MayAlias
+// CHECK-DAG: t#0 <-> p0.tgt#0: MayAlias
+// CHECK-DAG: t#0 <-> p1.tgt#0: MayAlias
+// CHECK-DAG: v#0 <-> p0.tgt#0: NoAlias
+// CHECK-DAG: v#0 <-> p1.tgt#0: NoAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> p1.tgt.fir#0: MayAlias
+// CHECK-DAG: t.fir#0 <-> p0.tgt.fir#0: MayAlias
+// CHECK-DAG: t.fir#0 <-> p1.tgt.fir#0: MayAlias
+// CHECK-DAG: v.fir#0 <-> p0.tgt.fir#0: NoAlias
+// CHECK-DAG: v.fir#0 <-> p1.tgt.fir#0: NoAlias
+
+// The address in a pointer cannot alias the address of a pointer.
+// CHECK-DAG: p0#0 <-> p0.tgt#0: NoAlias
+// CHECK-DAG: p0#0 <-> p1.tgt#0: NoAlias
+// CHECK-DAG: p1#0 <-> p0.tgt#0: NoAlias
+// CHECK-DAG: p1#0 <-> p1.tgt#0: NoAlias
+// CHECK-DAG: p0.fir#0 <-> p0.tgt.fir#0: NoAlias
+// CHECK-DAG: p0.fir#0 <-> p1.tgt.fir#0: NoAlias
+// CHECK-DAG: p1.fir#0 <-> p0.tgt.fir#0: NoAlias
+// CHECK-DAG: p1.fir#0 <-> p1.tgt.fir#0: NoAlias
+
+// For some cases, AliasAnalysis analyzes hlfir.designate like fir.box_addr, so
+// make sure it doesn't mistakenly see the address of arr(1) as an address that
+// was loaded from a pointer and that could alias something. However, t_arr is
+// a target.
+// CHECK-DAG: p0.tgt#0 <-> arr(1)#0: NoAlias
+// CHECK-DAG: p0.tgt#0 <-> t_arr(1)#0: MayAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> arr(1).fir#0: NoAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> t_arr(1).fir#0: MayAlias
+
+// Like a pointer, an allocatable contains an address, but an allocatable is not
+// a pointer and so cannot alias pointers. However, t_alloc is a target.
+// CHECK-DAG: p0.tgt#0 <-> alloc.tgt#0: NoAlias
+// CHECK-DAG: p0.tgt#0 <-> t_alloc.tgt#0: MayAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> alloc.tgt.fir#0: NoAlias
+// CHECK-DAG: p0.tgt.fir#0 <-> t_alloc.tgt.fir#0: MayAlias
+
+// The address in an allocatable cannot alias the address of that allocatable.
+// CHECK-DAG: alloc#0 <-> alloc.tgt#0: NoAlias
+// CHECK-DAG: alloc.fir#0 <-> alloc.tgt.fir#0: NoAlias
+
+func.func @_QPtest(%arg0: !fir.ref<!fir.box<!fir.ptr<f32>>> {fir.bindc_name = "p1"}, %arg1: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "arr"}, %arg2: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "t_arr", fir.target}, %arg3: !fir.ref<!fir.box<!fir.heap<f32>>> {fir.bindc_name = "a...
[truncated]
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
ping |
I posted comments a while back, as you can see from the date, but they remained in "Pending" status. My bad. |
As discussed at <llvm#117785 (comment)>, it breaks a test, marked as T1.
Add a failing test marked as T3.
Add another failing test marked as T4.
To establish a baseline for new tests mentioned in #117785, adding them here independently.
…25917) To establish a baseline for new tests mentioned in llvm/llvm-project#117785, adding them here independently.
For now, I'm just trying to keep this PR up to date with main so it's easy to reference. I understand it might need to be rewritten after @Renaud-K lands his PRs. |
To establish a baseline for new tests mentioned in llvm#117785, adding them here independently.
Don't introduce T1 and T2 comments as I'm not sure they matter so much going forward. As for T3, make it clear what T4 references.
For example, determine that the address in p below cannot alias the address of v: ``` subroutine test() real, pointer :: p real, target :: t real :: v p => t v = p end subroutine test ```
@jdenny-ornl After this commit, a test which uses Cray pointer fails. Could you take a look? Before this commit (660cdac): $ flang Fortran/0734/0734_0018.f90
$ ./a.out
pass After this commit (eb8ffd6): $ flang Fortran/0734/0734_0018.f90
$ ./a.out
erorr-1
pass |
@kawashima-fj Thanks for the report, and sorry for the trouble. I am able to reproduce the issue on a local test machine, and I will investigate. If I don't find a simple resolution today, I will try to revert the change. Let me know if you feel I should revert sooner. Thanks. |
I don't have experience working with cray pointers or forall loops in fortran. However, what I've read suggests the above test is invalid, and so the result is undefined. Specifically, the test has:
Because Furthermore, on my test system, both gfortran 11.4.0 and 13.2.0 produce the same result for this test as flang does with this PR merged. Let me know if you believe flang's previous behavior for this test is the only correct behavior. |
@jdenny-ornl Thanks for your investigation. I will check with my colleague who is familiar with the specifications. |
@jdenny-ornl I and my colleague confirmed. As you saied, it's an invalid test. I'm sorry to bother you. |
@kawashima-fj No worries. Thanks for confirming. |
For example, determine that the address in p below cannot alias the address of v: ``` subroutine test() real, pointer :: p real, target :: t real :: v p => t v = p end subroutine test ```
For example, determine that the address in p below cannot alias the address of v: ``` subroutine test() real, pointer :: p real, target :: t real :: v p => t v = p end subroutine test ```
For example, determine that the address in p below cannot alias the address of v: ``` subroutine test() real, pointer :: p real, target :: t real :: v p => t v = p end subroutine test ```
For example, determine that the address in p below cannot alias the address of v: