From d32ff8617580d043218eee082799fb674cb74ef6 Mon Sep 17 00:00:00 2001 From: "Joel E. Denny" Date: Tue, 26 Nov 2024 15:31:10 -0500 Subject: [PATCH 01/10] [flang] AliasAnalysis: Handle fir.load on fir.alloca 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 ``` --- .../lib/Optimizer/Analysis/AliasAnalysis.cpp | 63 ++- .../AliasAnalysis/alias-analysis-2.fir | 13 +- .../AliasAnalysis/load-ptr-alloca.fir | 412 ++++++++++++++++++ 3 files changed, 465 insertions(+), 23 deletions(-) create mode 100644 flang/test/Analysis/AliasAnalysis/load-ptr-alloca.fir diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp index 2b24791d6c7c5..284b3dadbcef9 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(defOp) - .Case([&](fir::ConvertOp op) { v = op.getValue(); }) - .Case( - [&](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(defOp) + .Case([&](fir::ConvertOp op) { v = op.getValue(); }) + .Case([&](auto op) { + v = op.getMemref(); + auto varIf = llvm::cast(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::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(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([&](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([&](auto op) { @@ -554,10 +579,12 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, .Case([&](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()) { + def.template getDefiningOp() || + def.template getDefiningOp()) { 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 d03348efd2a68..e2cdf8c8ddeb6 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 0000000000000..56c5313d397a5 --- /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 {fir.bindc_name = "n"}) { + %0 = fir.alloca !fir.box> {bindc_name = "alloc", uniq_name = "_QFtestEalloc"} + %1:2 = hlfir.declare %0 {test.ptr="alloc", fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEalloc"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + %2:2 = hlfir.declare %arg0 {uniq_name = "_QFtestEn"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %3 = fir.alloca !fir.box> {bindc_name = "p0", uniq_name = "_QFtestEp0"} + %4:2 = hlfir.declare %3 {test.ptr="p0", fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEp0"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + %5 = fir.alloca !fir.box> {bindc_name = "p1", uniq_name = "_QFtestEp1"} + %6:2 = hlfir.declare %5 {test.ptr="p1", fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEp1"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + %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, uniq_name = "_QFtestEt"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %9 = fir.alloca !fir.box> {bindc_name = "t_alloc", fir.target, uniq_name = "_QFtestEt_alloc"} + %10:2 = hlfir.declare %9 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEt_alloc"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + %11 = fir.alloca f32 {bindc_name = "v", uniq_name = "_QFtestEv"} + %12:2 = hlfir.declare %11 {test.ptr="v", uniq_name = "_QFtestEv"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %13 = fir.load %2#0 : !fir.ref + %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, %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.shape<1>) -> (!fir.box>, !fir.ref>) + %21 = fir.load %2#0 : !fir.ref + %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, %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, uniq_name = "_QFtestEt_arr"} : (!fir.ref>, !fir.shape<1>) -> (!fir.box>, !fir.ref>) + %29 = fir.load %4#0 : !fir.ref>> + %30 = fir.box_addr %29 {test.ptr="p0.tgt"} : (!fir.box>) -> !fir.ptr + %31 = fir.load %6#0 : !fir.ref>> + %32 = fir.box_addr %31 {test.ptr="p1.tgt"} : (!fir.box>) -> !fir.ptr + %c1 = arith.constant 1 : index + %33 = hlfir.designate %20#0 (%c1) {test.ptr="arr(1)"} : (!fir.box>, index) -> !fir.ref + %c1_1 = arith.constant 1 : index + %34 = hlfir.designate %28#0 (%c1_1) {test.ptr="t_arr(1)"} : (!fir.box>, index) -> !fir.ref + %35 = fir.load %1#0 : !fir.ref>> + %36 = fir.box_addr %35 {test.ptr="alloc.tgt"} : (!fir.box>) -> !fir.heap + %37 = fir.load %10#0 : !fir.ref>> + %38 = fir.box_addr %37 {test.ptr="t_alloc.tgt"} : (!fir.box>) -> !fir.heap + return +} + +func.func @_QPtest.fir(%arg0: !fir.ref {fir.bindc_name = "n"}) { + %0 = fir.alloca !fir.box> {bindc_name = "alloc", uniq_name = "_QFtestEalloc"} + %1 = fir.declare %0 {test.ptr = "alloc.fir", fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEalloc"} : (!fir.ref>>) -> !fir.ref>> + %2 = fir.declare %arg0 {uniq_name = "_QFtestEn"} : (!fir.ref) -> !fir.ref + %3 = fir.alloca !fir.box> {bindc_name = "p0", uniq_name = "_QFtestEp0"} + %4 = fir.declare %3 {test.ptr = "p0.fir", fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEp0"} : (!fir.ref>>) -> !fir.ref>> + %5 = fir.alloca !fir.box> {bindc_name = "p1", uniq_name = "_QFtestEp1"} + %6 = fir.declare %5 {test.ptr = "p1.fir", fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEp1"} : (!fir.ref>>) -> !fir.ref>> + %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, uniq_name = "_QFtestEt"} : (!fir.ref) -> !fir.ref + %9 = fir.alloca !fir.box> {bindc_name = "t_alloc", fir.target, uniq_name = "_QFtestEt_alloc"} + %10 = fir.declare %9 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEt_alloc"} : (!fir.ref>>) -> !fir.ref>> + %11 = fir.alloca f32 {bindc_name = "v", uniq_name = "_QFtestEv"} + %12 = fir.declare %11 {test.ptr = "v.fir", uniq_name = "_QFtestEv"} : (!fir.ref) -> !fir.ref + %13 = fir.load %2 : !fir.ref + %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, %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.shape<1>) -> !fir.ref> + %21 = fir.embox %20(%19) : (!fir.ref>, !fir.shape<1>) -> !fir.box> + %22 = fir.load %2 : !fir.ref + %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, %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, uniq_name = "_QFtestEt_arr"} : (!fir.ref>, !fir.shape<1>) -> !fir.ref> + %30 = fir.embox %29(%28) : (!fir.ref>, !fir.shape<1>) -> !fir.box> + %31 = fir.load %4 : !fir.ref>> + %32 = fir.box_addr %31 {test.ptr = "p0.tgt.fir"} : (!fir.box>) -> !fir.ptr + %33 = fir.load %6 : !fir.ref>> + %34 = fir.box_addr %33 {test.ptr = "p1.tgt.fir"} : (!fir.box>) -> !fir.ptr + %c1 = arith.constant 1 : index + %35 = fir.array_coor %20(%19) %c1 {test.ptr="arr(1).fir"} : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref + %c1_1 = arith.constant 1 : index + %36 = fir.array_coor %29(%28) %c1_1 {test.ptr="t_arr(1).fir"} : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref + %37 = fir.load %1 : !fir.ref>> + %38 = fir.box_addr %37 {test.ptr = "alloc.tgt.fir"} : (!fir.box>) -> !fir.heap + %39 = fir.load %10 : !fir.ref>> + %40 = fir.box_addr %39 {test.ptr = "t_alloc.tgt.fir"} : (!fir.box>) -> !fir.heap + 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.bindc_name = "p1"}, %arg1: !fir.box> {fir.bindc_name = "arr"}, %arg2: !fir.box> {fir.bindc_name = "t_arr", fir.target}, %arg3: !fir.ref>> {fir.bindc_name = "alloc"}, %arg4: !fir.ref>> {fir.bindc_name = "t_alloc", fir.target}, %arg5: !fir.ref {fir.bindc_name = "t", fir.target}, %arg6: !fir.ref {fir.bindc_name = "v"}) { + %0 = fir.dummy_scope : !fir.dscope + %1:2 = hlfir.declare %arg3 dummy_scope %0 {test.ptr="alloc", fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEalloc"} : (!fir.ref>>, !fir.dscope) -> (!fir.ref>>, !fir.ref>>) + %2:2 = hlfir.declare %arg1 dummy_scope %0 {uniq_name = "_QFtestEarr"} : (!fir.box>, !fir.dscope) -> (!fir.box>, !fir.box>) + %3 = fir.alloca !fir.box> {bindc_name = "p0", uniq_name = "_QFtestEp0"} + %4:2 = hlfir.declare %3 {test.ptr="p0", fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEp0"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + %5:2 = hlfir.declare %arg0 dummy_scope %0 {test.ptr="p1", fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEp1"} : (!fir.ref>>, !fir.dscope) -> (!fir.ref>>, !fir.ref>>) + %6:2 = hlfir.declare %arg5 dummy_scope %0 {test.ptr="t", fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEt"} : (!fir.ref, !fir.dscope) -> (!fir.ref, !fir.ref) + %7:2 = hlfir.declare %arg4 dummy_scope %0 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEt_alloc"} : (!fir.ref>>, !fir.dscope) -> (!fir.ref>>, !fir.ref>>) + %8:2 = hlfir.declare %arg2 dummy_scope %0 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEt_arr"} : (!fir.box>, !fir.dscope) -> (!fir.box>, !fir.box>) + %9:2 = hlfir.declare %arg6 dummy_scope %0 {test.ptr="v", uniq_name = "_QFtestEv"} : (!fir.ref, !fir.dscope) -> (!fir.ref, !fir.ref) + %10 = fir.load %4#0 : !fir.ref>> + %11 = fir.box_addr %10 {test.ptr="p0.tgt"} : (!fir.box>) -> !fir.ptr + %12 = fir.load %5#0 : !fir.ref>> + %13 = fir.box_addr %12 {test.ptr="p1.tgt"} : (!fir.box>) -> !fir.ptr + %c1 = arith.constant 1 : index + %14 = hlfir.designate %2#0 (%c1) {test.ptr="arr(1)"} : (!fir.box>, index) -> !fir.ref + %c1_0 = arith.constant 1 : index + %15 = hlfir.designate %8#0 (%c1_0) {test.ptr="t_arr(1)"} : (!fir.box>, index) -> !fir.ref + %16 = fir.load %1#0 : !fir.ref>> + %17 = fir.box_addr %16 {test.ptr="alloc.tgt"} : (!fir.box>) -> !fir.heap + %18 = fir.load %7#0 : !fir.ref>> + %19 = fir.box_addr %18 {test.ptr="t_alloc.tgt"} : (!fir.box>) -> !fir.heap + return +} + +func.func @_QPtest.fir(%arg0: !fir.ref>> {fir.bindc_name = "p1"}, %arg1: !fir.box> {fir.bindc_name = "arr"}, %arg2: !fir.box> {fir.bindc_name = "t_arr", fir.target}, %arg3: !fir.ref>> {fir.bindc_name = "alloc"}, %arg4: !fir.ref>> {fir.bindc_name = "t_alloc", fir.target}, %arg5: !fir.ref {fir.bindc_name = "t", fir.target}, %arg6: !fir.ref {fir.bindc_name = "v"}) { + %0 = fir.dummy_scope : !fir.dscope + %1 = fir.declare %arg3 dummy_scope %0 {test.ptr = "alloc.fir", fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEalloc"} : (!fir.ref>>, !fir.dscope) -> !fir.ref>> + %2 = fir.declare %arg1 dummy_scope %0 {uniq_name = "_QFtestEarr"} : (!fir.box>, !fir.dscope) -> !fir.box> + %3 = fir.rebox %2 : (!fir.box>) -> !fir.box> + %4 = fir.alloca !fir.box> {bindc_name = "p0", uniq_name = "_QFtestEp0"} + %5 = fir.declare %4 {test.ptr = "p0.fir", fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEp0"} : (!fir.ref>>) -> !fir.ref>> + %6 = fir.declare %arg0 dummy_scope %0 {test.ptr = "p1.fir", fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEp1"} : (!fir.ref>>, !fir.dscope) -> !fir.ref>> + %7 = fir.declare %arg5 dummy_scope %0 {test.ptr = "t.fir", fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEt"} : (!fir.ref, !fir.dscope) -> !fir.ref + %8 = fir.declare %arg4 dummy_scope %0 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEt_alloc"} : (!fir.ref>>, !fir.dscope) -> !fir.ref>> + %9 = fir.declare %arg2 dummy_scope %0 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEt_arr"} : (!fir.box>, !fir.dscope) -> !fir.box> + %10 = fir.rebox %9 : (!fir.box>) -> !fir.box> + %11 = fir.declare %arg6 dummy_scope %0 {test.ptr = "v.fir", uniq_name = "_QFtestEv"} : (!fir.ref, !fir.dscope) -> !fir.ref + %12 = fir.load %5 : !fir.ref>> + %13 = fir.box_addr %12 {test.ptr = "p0.tgt.fir"} : (!fir.box>) -> !fir.ptr + %14 = fir.load %6 : !fir.ref>> + %15 = fir.box_addr %14 {test.ptr = "p1.tgt.fir"} : (!fir.box>) -> !fir.ptr + %c1 = arith.constant 1 : index + %16 = fir.array_coor %3 %c1 {test.ptr="arr(1).fir"} : (!fir.box>, index) -> !fir.ref + %c1_0 = arith.constant 1 : index + %17 = fir.array_coor %10 %c1_0 {test.ptr="t_arr(1).fir"} : (!fir.box>, index) -> !fir.ref + %18 = fir.load %1 : !fir.ref>> + %19 = fir.box_addr %18 {test.ptr = "alloc.tgt.fir"} : (!fir.box>) -> !fir.heap + %20 = fir.load %8 : !fir.ref>> + %21 = fir.box_addr %20 {test.ptr = "t_alloc.tgt.fir"} : (!fir.box>) -> !fir.heap + return +} + +// ----- + +// Repeat above test except compare the local pointer against globals. + +// module m +// real, pointer :: p1 +// real :: arr(2) +// real, target :: t_arr(2) +// real, allocatable :: alloc +// real, allocatable, target :: t_alloc +// real, target :: t +// real :: v +// contains +// subroutine test() +// real, pointer :: p0 +// end subroutine test +// end module m + +// CHECK-LABEL: Testing : "_QMmPtest" + +// 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 @_QMmPtest() { + %0 = fir.address_of(@_QMmEalloc) : !fir.ref>> + %1:2 = hlfir.declare %0 {test.ptr="alloc", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEalloc"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + %2 = fir.address_of(@_QMmEarr) : !fir.ref> + %c2 = arith.constant 2 : index + %3 = fir.shape %c2 : (index) -> !fir.shape<1> + %4:2 = hlfir.declare %2(%3) {uniq_name = "_QMmEarr"} : (!fir.ref>, !fir.shape<1>) -> (!fir.ref>, !fir.ref>) + %5 = fir.address_of(@_QMmEp1) : !fir.ref>> + %6:2 = hlfir.declare %5 {test.ptr="p1", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEp1"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + %7 = fir.address_of(@_QMmEt) : !fir.ref + %8:2 = hlfir.declare %7 {test.ptr="t", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEt"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %9 = fir.address_of(@_QMmEt_alloc) : !fir.ref>> + %10:2 = hlfir.declare %9 {fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEt_alloc"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + %11 = fir.address_of(@_QMmEt_arr) : !fir.ref> + %c2_0 = arith.constant 2 : index + %12 = fir.shape %c2_0 : (index) -> !fir.shape<1> + %13:2 = hlfir.declare %11(%12) {fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEt_arr"} : (!fir.ref>, !fir.shape<1>) -> (!fir.ref>, !fir.ref>) + %14 = fir.address_of(@_QMmEv) : !fir.ref + %15:2 = hlfir.declare %14 {test.ptr="v", uniq_name = "_QMmEv"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %16 = fir.alloca !fir.box> {bindc_name = "p0", uniq_name = "_QMmFtestEp0"} + %17:2 = hlfir.declare %16 {test.ptr="p0", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmFtestEp0"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + %18 = fir.load %17#0 : !fir.ref>> + %19 = fir.box_addr %18 {test.ptr="p0.tgt"} : (!fir.box>) -> !fir.ptr + %20 = fir.load %6#0 : !fir.ref>> + %21 = fir.box_addr %20 {test.ptr="p1.tgt"} : (!fir.box>) -> !fir.ptr + %c1 = arith.constant 1 : index + %22 = hlfir.designate %4#0 (%c1) {test.ptr="arr(1)"} : (!fir.ref>, index) -> !fir.ref + %c1_1 = arith.constant 1 : index + %23 = hlfir.designate %13#0 (%c1_1) {test.ptr="t_arr(1)"} : (!fir.ref>, index) -> !fir.ref + %24 = fir.load %1#0 : !fir.ref>> + %25 = fir.box_addr %24 {test.ptr="alloc.tgt"} : (!fir.box>) -> !fir.heap + %26 = fir.load %10#0 : !fir.ref>> + %27 = fir.box_addr %26 {test.ptr="t_alloc.tgt"} : (!fir.box>) -> !fir.heap + return +} + +func.func @_QMmPtest.fir() { + %0 = fir.address_of(@_QMmEalloc) : !fir.ref>> + %1 = fir.declare %0 {test.ptr = "alloc.fir", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEalloc"} : (!fir.ref>>) -> !fir.ref>> + %2 = fir.address_of(@_QMmEarr) : !fir.ref> + %c2 = arith.constant 2 : index + %3 = fir.shape %c2 : (index) -> !fir.shape<1> + %4 = fir.declare %2(%3) {uniq_name = "_QMmEarr"} : (!fir.ref>, !fir.shape<1>) -> !fir.ref> + %5 = fir.address_of(@_QMmEp1) : !fir.ref>> + %6 = fir.declare %5 {test.ptr = "p1.fir", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEp1"} : (!fir.ref>>) -> !fir.ref>> + %7 = fir.address_of(@_QMmEt) : !fir.ref + %8 = fir.declare %7 {test.ptr = "t.fir", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEt"} : (!fir.ref) -> !fir.ref + %9 = fir.address_of(@_QMmEt_alloc) : !fir.ref>> + %10 = fir.declare %9 {fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEt_alloc"} : (!fir.ref>>) -> !fir.ref>> + %11 = fir.address_of(@_QMmEt_arr) : !fir.ref> + %c2_0 = arith.constant 2 : index + %12 = fir.shape %c2_0 : (index) -> !fir.shape<1> + %13 = fir.declare %11(%12) {fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEt_arr"} : (!fir.ref>, !fir.shape<1>) -> !fir.ref> + %14 = fir.address_of(@_QMmEv) : !fir.ref + %15 = fir.declare %14 {test.ptr = "v.fir", uniq_name = "_QMmEv"} : (!fir.ref) -> !fir.ref + %16 = fir.alloca !fir.box> {bindc_name = "p0", uniq_name = "_QMmFtestEp0"} + %17 = fir.declare %16 {test.ptr = "p0.fir", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmFtestEp0"} : (!fir.ref>>) -> !fir.ref>> + %18 = fir.load %17 : !fir.ref>> + %19 = fir.box_addr %18 {test.ptr = "p0.tgt.fir"} : (!fir.box>) -> !fir.ptr + %20 = fir.load %6 : !fir.ref>> + %21 = fir.box_addr %20 {test.ptr = "p1.tgt.fir"} : (!fir.box>) -> !fir.ptr + %c1 = arith.constant 1 : index + %22 = fir.array_coor %4(%3) %c1 {test.ptr="arr(1).fir"} : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref + %c1_1 = arith.constant 1 : index + %23 = fir.array_coor %13(%12) %c1_1 {test.ptr="t_arr(1).fir"} : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref + %24 = fir.load %1 : !fir.ref>> + %25 = fir.box_addr %24 {test.ptr = "alloc.tgt.fir"} : (!fir.box>) -> !fir.heap + %26 = fir.load %10 : !fir.ref>> + %27 = fir.box_addr %26 {test.ptr = "t_alloc.tgt.fir"} : (!fir.box>) -> !fir.heap + return +} From 869c7563267ed592023f17b381962b30d66b5744 Mon Sep 17 00:00:00 2001 From: "Joel E. Denny" Date: Tue, 26 Nov 2024 18:03:31 -0500 Subject: [PATCH 02/10] Move getOriginalDef back to where it was, as requested --- .../lib/Optimizer/Analysis/AliasAnalysis.cpp | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp index 284b3dadbcef9..61d8fb4ca23ee 100644 --- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp +++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp @@ -31,6 +31,34 @@ using namespace mlir; // AliasAnalysis: alias //===----------------------------------------------------------------------===// +namespace fir { + static AliasAnalysis::Source::Attributes + getAttrsFromVariable(fir::FortranVariableOpInterface var); +} + +/// 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(defOp) + .Case([&](fir::ConvertOp op) { v = op.getValue(); }) + .Case([&](auto op) { + v = op.getMemref(); + auto varIf = llvm::cast(defOp); + attributes |= fir::getAttrsFromVariable(varIf); + isCapturedInInternalProcedure |= + varIf.isCapturedInInternalProcedure(); + }) + .Default([&](auto op) { breakFromLoop = true; }); + } + return v; +} + namespace fir { void AliasAnalysis::Source::print(llvm::raw_ostream &os) const { @@ -482,29 +510,6 @@ 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(defOp) - .Case([&](fir::ConvertOp op) { v = op.getValue(); }) - .Case([&](auto op) { - v = op.getMemref(); - auto varIf = llvm::cast(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(); From e1fcee00c716eca85721d363c7f29e73df3a09f3 Mon Sep 17 00:00:00 2001 From: "Joel E. Denny" Date: Tue, 26 Nov 2024 18:11:01 -0500 Subject: [PATCH 03/10] Apply clang-format --- 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 61d8fb4ca23ee..05ca52767022e 100644 --- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp +++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp @@ -32,8 +32,8 @@ using namespace mlir; //===----------------------------------------------------------------------===// namespace fir { - static AliasAnalysis::Source::Attributes - getAttrsFromVariable(fir::FortranVariableOpInterface var); +static AliasAnalysis::Source::Attributes +getAttrsFromVariable(fir::FortranVariableOpInterface var); } /// Temporary function to skip through all the no op operations From 7d8e13ea3ee84ecc15092b32e33e40382e4960f5 Mon Sep 17 00:00:00 2001 From: "Joel E. Denny" Date: Tue, 7 Jan 2025 15:15:39 -0500 Subject: [PATCH 04/10] Show the effect of C2+C3 As discussed at , it breaks a test, marked as T1. --- flang/lib/Optimizer/Analysis/AliasAnalysis.cpp | 11 ++++++++--- .../test/Analysis/AliasAnalysis/alias-analysis-2.fir | 6 +++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp index dcadccf9747ee..eb4a6bdb295b6 100644 --- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp +++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp @@ -543,6 +543,7 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, bool isBoxRef{fir::isa_ref_type(v.getType()) && mlir::isa(fir::unwrapRefType(v.getType()))}; bool followingData = !isBoxRef; + // C1 // "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 @@ -551,9 +552,9 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, // (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(defOp) && - isPointerReference(v.getType())) - followingData = false; + //if (mlir::isa_and_nonnull(defOp) && + // isPointerReference(v.getType())) + // followingData = false; mlir::SymbolRefAttr global; Source::Attributes attributes; mlir::Operation *instantiationPoint{nullptr}; @@ -567,6 +568,7 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, .Case([&](auto op) { // Unique memory allocation. type = SourceKind::Allocate; + // C2 // 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 @@ -583,6 +585,9 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, .Case([&](auto op) { v = op->getOperand(0); defOp = v.getDefiningOp(); + // C3 + if (fir::isPointerType(v.getType())) + attributes.set(Attribute::Pointer); if (mlir::isa(v.getType())) followBoxData = true; }) diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir index e2cdf8c8ddeb6..435796276ae28 100644 --- a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir @@ -1,12 +1,12 @@ // Use --mlir-disable-threading so that the AA queries are serialized // as well as its diagnostic output. -// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s +// RUN: fir-opt -debug %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s // CHECK-LABEL: Testing : "_QFPtest" // p1.addr and p2.addr result from 2 different allocas // They cannot physically alias -// CHECK-DAG: p1.addr#0 <-> p2.addr#0: NoAlias +// T1: CHECK-DAG: p1.addr#0 <-> p2.addr#0: NoAlias // 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 @@ -31,7 +31,7 @@ // All arguments are either pointers or targets // 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 +// T2: 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: NoAlias From f440d4d6d0db130c59d50c6d5ff545023a3dffa3 Mon Sep 17 00:00:00 2001 From: "Joel E. Denny" Date: Tue, 21 Jan 2025 15:45:09 -0500 Subject: [PATCH 05/10] Show an issue with C3 alone Add a failing test marked as T3. --- flang/lib/Optimizer/Analysis/AliasAnalysis.cpp | 4 ++-- flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp index eb4a6bdb295b6..f49f84859b5cb 100644 --- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp +++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp @@ -573,8 +573,8 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, // 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); + //if (isPointerReference(ty)) + // attributes.set(Attribute::Pointer); breakFromLoop = true; }) .Case([&](auto op) { diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir index 435796276ae28..aebdc4ec330a2 100644 --- a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir @@ -1,6 +1,6 @@ // Use --mlir-disable-threading so that the AA queries are serialized // as well as its diagnostic output. -// RUN: fir-opt -debug %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s +// RUN: fir-opt -debug %s -o /dev/null -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s // CHECK-LABEL: Testing : "_QFPtest" @@ -50,6 +50,10 @@ // CHECK-DAG: arg2.load#0 <-> arg2.addr#0: MustAlias // CHECK-DAG: boxp1.addr#0 <-> arg2.addr#0: MayAlias +// The address in a pointer cannot alias the address of a pointer, even when the +// pointer has no box. +// T3: CHECK-DAG: p1.addr#0 <-> p1.tgt#0: NoAlias + func.func @_QFPtest(%arg0: !fir.ref {fir.bindc_name = "v1", fir.target}, %arg1: !fir.ref {fir.bindc_name = "v2", fir.target}, %arg2: !fir.ref>> ) attributes {test.ptr = "func"} { %1 = fir.alloca !fir.ptr {test.ptr = "p1.addr"} @@ -66,7 +70,7 @@ func.func @_QFPtest(%arg0: !fir.ref {fir.bindc_name = "v1", fir.target}, %a fir.store %6 to %4 : !fir.ref> %0 = fir.alloca !fir.box> {bindc_name = "p1", uniq_name = "_QFtestEp1"} - %7 = fir.load %1 : !fir.ref> + %7 = fir.load %1 {test.ptr="p1.tgt"} : !fir.ref> %8 = fir.embox %7 : (!fir.ptr) -> !fir.box> fir.store %8 to %0 : !fir.ref>> From b6cd9936e6bf56a527ff69ee092b67281ac374f9 Mon Sep 17 00:00:00 2001 From: "Joel E. Denny" Date: Tue, 21 Jan 2025 16:14:46 -0500 Subject: [PATCH 06/10] Further demonstrate issue with C3 alone Add another failing test marked as T4. --- flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir index aebdc4ec330a2..98688a8c073db 100644 --- a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir @@ -54,6 +54,10 @@ // pointer has no box. // T3: CHECK-DAG: p1.addr#0 <-> p1.tgt#0: NoAlias +// The addresses stored in two different pointers can alias, even if one has no +// box. In this program, they happen to be the same address. +// T4: CHECK-DAG: p1.tgt#0 <-> boxp1.addr#0: MayAlias + func.func @_QFPtest(%arg0: !fir.ref {fir.bindc_name = "v1", fir.target}, %arg1: !fir.ref {fir.bindc_name = "v2", fir.target}, %arg2: !fir.ref>> ) attributes {test.ptr = "func"} { %1 = fir.alloca !fir.ptr {test.ptr = "p1.addr"} From 4439620f7073359734b0b1c36736baf56585a5e0 Mon Sep 17 00:00:00 2001 From: "Joel E. Denny" Date: Wed, 29 Jan 2025 15:03:08 -0500 Subject: [PATCH 07/10] Restore C1+C2 so all tests pass again --- flang/lib/Optimizer/Analysis/AliasAnalysis.cpp | 14 +++++++------- .../Analysis/AliasAnalysis/alias-analysis-2.fir | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp index f49f84859b5cb..62127428d318c 100644 --- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp +++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp @@ -552,9 +552,9 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, // (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(defOp) && - // isPointerReference(v.getType())) - // followingData = false; + if (mlir::isa_and_nonnull(defOp) && + isPointerReference(v.getType())) + followingData = false; mlir::SymbolRefAttr global; Source::Attributes attributes; mlir::Operation *instantiationPoint{nullptr}; @@ -573,8 +573,8 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, // 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); + if (isPointerReference(ty)) + attributes.set(Attribute::Pointer); breakFromLoop = true; }) .Case([&](auto op) { @@ -586,8 +586,8 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, v = op->getOperand(0); defOp = v.getDefiningOp(); // C3 - if (fir::isPointerType(v.getType())) - attributes.set(Attribute::Pointer); + //if (fir::isPointerType(v.getType())) + // attributes.set(Attribute::Pointer); if (mlir::isa(v.getType())) followBoxData = true; }) diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir index 98688a8c073db..3f5350511bf0e 100644 --- a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir @@ -1,6 +1,6 @@ // Use --mlir-disable-threading so that the AA queries are serialized // as well as its diagnostic output. -// RUN: fir-opt -debug %s -o /dev/null -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s +// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s // CHECK-LABEL: Testing : "_QFPtest" From 1ce92161e1bc1032fa9e28165686ccdf639f214c Mon Sep 17 00:00:00 2001 From: "Joel E. Denny" Date: Mon, 3 Feb 2025 15:08:49 -0500 Subject: [PATCH 08/10] Remove C1+C2, restore C3, add test fixmes for regressions --- .../lib/Optimizer/Analysis/AliasAnalysis.cpp | 24 ++----------------- .../AliasAnalysis/alias-analysis-2.fir | 13 +++++----- 2 files changed, 9 insertions(+), 28 deletions(-) diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp index 62127428d318c..c17900f9d2e6c 100644 --- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp +++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp @@ -543,18 +543,6 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, bool isBoxRef{fir::isa_ref_type(v.getType()) && mlir::isa(fir::unwrapRefType(v.getType()))}; bool followingData = !isBoxRef; - // C1 - // "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(defOp) && - isPointerReference(v.getType())) - followingData = false; mlir::SymbolRefAttr global; Source::Attributes attributes; mlir::Operation *instantiationPoint{nullptr}; @@ -568,13 +556,6 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, .Case([&](auto op) { // Unique memory allocation. type = SourceKind::Allocate; - // C2 - // 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([&](auto op) { @@ -585,9 +566,8 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, .Case([&](auto op) { v = op->getOperand(0); defOp = v.getDefiningOp(); - // C3 - //if (fir::isPointerType(v.getType())) - // attributes.set(Attribute::Pointer); + if (fir::isPointerType(v.getType())) + attributes.set(Attribute::Pointer); if (mlir::isa(v.getType())) followBoxData = true; }) diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir index 3f5350511bf0e..757cd06ad627e 100644 --- a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir @@ -6,7 +6,7 @@ // p1.addr and p2.addr result from 2 different allocas // They cannot physically alias -// T1: CHECK-DAG: p1.addr#0 <-> p2.addr#0: NoAlias +// CHECK-DAG: p1.addr#0 <-> p2.addr#0: NoAlias // 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 @@ -31,7 +31,7 @@ // All arguments are either pointers or targets // 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. -// T2: CHECK-DAG: boxp1.addr#0 <-> func.region0#0: MayAlias +// 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: NoAlias @@ -51,12 +51,13 @@ // CHECK-DAG: boxp1.addr#0 <-> arg2.addr#0: MayAlias // The address in a pointer cannot alias the address of a pointer, even when the -// pointer has no box. -// T3: CHECK-DAG: p1.addr#0 <-> p1.tgt#0: NoAlias +// pointer has no box. FIXME: This should be NoAlias. +// CHECK-DAG: p1.addr#0 <-> p1.tgt#0: MustAlias // The addresses stored in two different pointers can alias, even if one has no -// box. In this program, they happen to be the same address. -// T4: CHECK-DAG: p1.tgt#0 <-> boxp1.addr#0: MayAlias +// box. In this program, they happen to be the same address. FIXME: This +// should be MayAlias. +// CHECK-DAG: p1.tgt#0 <-> boxp1.addr#0: NoAlias func.func @_QFPtest(%arg0: !fir.ref {fir.bindc_name = "v1", fir.target}, %arg1: !fir.ref {fir.bindc_name = "v2", fir.target}, %arg2: !fir.ref>> ) attributes {test.ptr = "func"} { From 12d903f8758416f20db2b817cd4a917cab22c4b7 Mon Sep 17 00:00:00 2001 From: "Joel E. Denny" Date: Fri, 7 Feb 2025 14:02:53 -0500 Subject: [PATCH 09/10] Revert "Remove C1+C2, restore C3, add test fixmes for regressions" This reverts commit 1ce92161e1bc1032fa9e28165686ccdf639f214c. --- .../lib/Optimizer/Analysis/AliasAnalysis.cpp | 24 +++++++++++++++++-- .../AliasAnalysis/alias-analysis-2.fir | 13 +++++----- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp index c17900f9d2e6c..62127428d318c 100644 --- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp +++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp @@ -543,6 +543,18 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, bool isBoxRef{fir::isa_ref_type(v.getType()) && mlir::isa(fir::unwrapRefType(v.getType()))}; bool followingData = !isBoxRef; + // C1 + // "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(defOp) && + isPointerReference(v.getType())) + followingData = false; mlir::SymbolRefAttr global; Source::Attributes attributes; mlir::Operation *instantiationPoint{nullptr}; @@ -556,6 +568,13 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, .Case([&](auto op) { // Unique memory allocation. type = SourceKind::Allocate; + // C2 + // 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([&](auto op) { @@ -566,8 +585,9 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, .Case([&](auto op) { v = op->getOperand(0); defOp = v.getDefiningOp(); - if (fir::isPointerType(v.getType())) - attributes.set(Attribute::Pointer); + // C3 + //if (fir::isPointerType(v.getType())) + // attributes.set(Attribute::Pointer); if (mlir::isa(v.getType())) followBoxData = true; }) diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir index 757cd06ad627e..3f5350511bf0e 100644 --- a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir @@ -6,7 +6,7 @@ // p1.addr and p2.addr result from 2 different allocas // They cannot physically alias -// CHECK-DAG: p1.addr#0 <-> p2.addr#0: NoAlias +// T1: CHECK-DAG: p1.addr#0 <-> p2.addr#0: NoAlias // 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 @@ -31,7 +31,7 @@ // All arguments are either pointers or targets // 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 +// T2: 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: NoAlias @@ -51,13 +51,12 @@ // CHECK-DAG: boxp1.addr#0 <-> arg2.addr#0: MayAlias // The address in a pointer cannot alias the address of a pointer, even when the -// pointer has no box. FIXME: This should be NoAlias. -// CHECK-DAG: p1.addr#0 <-> p1.tgt#0: MustAlias +// pointer has no box. +// T3: CHECK-DAG: p1.addr#0 <-> p1.tgt#0: NoAlias // The addresses stored in two different pointers can alias, even if one has no -// box. In this program, they happen to be the same address. FIXME: This -// should be MayAlias. -// CHECK-DAG: p1.tgt#0 <-> boxp1.addr#0: NoAlias +// box. In this program, they happen to be the same address. +// T4: CHECK-DAG: p1.tgt#0 <-> boxp1.addr#0: MayAlias func.func @_QFPtest(%arg0: !fir.ref {fir.bindc_name = "v1", fir.target}, %arg1: !fir.ref {fir.bindc_name = "v2", fir.target}, %arg2: !fir.ref>> ) attributes {test.ptr = "func"} { From 98e8b9932ab5cefeeda659f81c40b7cbc3987502 Mon Sep 17 00:00:00 2001 From: "Joel E. Denny" Date: Wed, 12 Feb 2025 14:34:05 -0500 Subject: [PATCH 10/10] Clean up alias-analysis-2.fir comments 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. --- flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir index c7616a3c99e83..b4c757f2d0b4e 100644 --- a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir @@ -6,7 +6,7 @@ // p1.addr and p2.addr result from 2 different allocas // They cannot physically alias -// T1: CHECK-DAG: p1.addr#0 <-> p2.addr#0: NoAlias +// CHECK-DAG: p1.addr#0 <-> p2.addr#0: NoAlias // 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 @@ -31,7 +31,7 @@ // All arguments are either pointers or targets // 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. -// T2: CHECK-DAG: boxp1.addr#0 <-> func.region0#0: MayAlias +// 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: NoAlias @@ -57,7 +57,7 @@ // The addresses stored in two different pointers can alias, even if one has no // box. In this program, they happen to be the same address. -// T4: +// T4 from . // CHECK-DAG: p1.tgt#0 <-> boxp1.addr#0: MayAlias func.func @_QFPtest(%arg0: !fir.ref {fir.bindc_name = "v1", fir.target}, %arg1: !fir.ref {fir.bindc_name = "v2", fir.target}, %arg2: !fir.ref>> ) attributes {test.ptr = "func"} {