diff --git a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h index dfcafe88fee1b5..80c52fe01a33c1 100644 --- a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h +++ b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h @@ -36,8 +36,8 @@ struct AliasAnalysis { /// Represents memory allocated outside of a function /// and passed to the function via host association tuple. HostAssoc, - /// Represents direct memory access whose source cannot be further - /// determined + /// Memory address retrieved from a box, perhaps from a global or + /// an argument. Direct, /// Represents memory allocated by unknown means and /// with the memory address defined by a memory reading @@ -93,6 +93,9 @@ struct AliasAnalysis { /// Return the memory source of a value. Source getSource(mlir::Value); + + /// Return true if `v` is a dummy argument. + static bool isDummyArgument(mlir::Value v); }; inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp index e144640081cbf3..aba7e542cc960e 100644 --- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp +++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp @@ -28,14 +28,6 @@ using namespace mlir; // AliasAnalysis: alias //===----------------------------------------------------------------------===// -static bool isDummyArgument(mlir::Value v) { - auto blockArg{v.dyn_cast()}; - if (!blockArg) - return false; - - return blockArg.getOwner()->isEntryBlock(); -} - /// Temporary function to skip through all the no op operations /// TODO: Generalize support of fir.load static mlir::Value getOriginalDef(mlir::Value v) { @@ -88,12 +80,23 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) { auto lhsSrc = getSource(lhs); auto rhsSrc = getSource(rhs); bool approximateSource = lhsSrc.approximateSource || rhsSrc.approximateSource; - LLVM_DEBUG(llvm::dbgs() << "AliasAnalysis::alias\n"; - llvm::dbgs() << " lhs: " << lhs << "\n"; - llvm::dbgs() << " lhsSrc: " << lhsSrc << "\n"; - llvm::dbgs() << " rhs: " << rhs << "\n"; - llvm::dbgs() << " rhsSrc: " << rhsSrc << "\n"; - llvm::dbgs() << "\n";); + LLVM_DEBUG( + llvm::dbgs() << "AliasAnalysis::alias\n"; + llvm::dbgs() << " lhs: " << lhs << "\n"; + llvm::dbgs() << " lhsSrc: " << lhsSrc << "\n"; + llvm::dbgs() << " lhsSrc kind: " << EnumToString(lhsSrc.kind) << "\n"; + llvm::dbgs() << " lhsSrc pointer: " + << lhsSrc.attributes.test(Attribute::Pointer) << "\n"; + llvm::dbgs() << " lhsSrc target: " + << lhsSrc.attributes.test(Attribute::Target) << "\n"; + llvm::dbgs() << " rhs: " << rhs << "\n"; + llvm::dbgs() << " rhsSrc: " << rhsSrc << "\n"; + llvm::dbgs() << " rhsSrc kind: " << EnumToString(rhsSrc.kind) << "\n"; + llvm::dbgs() << " rhsSrc pointer: " + << rhsSrc.attributes.test(Attribute::Pointer) << "\n"; + llvm::dbgs() << " rhsSrc target: " + << rhsSrc.attributes.test(Attribute::Target) << "\n"; + llvm::dbgs() << "\n";); // Indirect case currently not handled. Conservatively assume // it aliases with everything @@ -101,8 +104,10 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) { return AliasResult::MayAlias; } - // SourceKind::Direct is set for the addresses wrapped in a global boxes. - // ie: fir.global @_QMpointersEp : !fir.box> + // SourceKind::Direct is set for the addresses wrapped in a box, perhaps from + // a global or an argument. + // e.g.: fir.global @_QMpointersEp : !fir.box> + // e.g.: %arg0: !fir.ref>> // Though nothing is known about them, they would only alias with targets or // pointers bool directSourceToNonTargetOrPointer = false; @@ -399,19 +404,30 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) { if (!defOp && type == SourceKind::Unknown) // Check if the memory source is coming through a dummy argument. if (isDummyArgument(v)) { - type = SourceKind::Argument; ty = v.getType(); if (fir::valueHasFirAttribute(v, fir::getTargetAttrName())) attributes.set(Attribute::Target); - if (Source::isPointerReference(ty)) attributes.set(Attribute::Pointer); + if (followBoxAddr && fir::isa_ref_type(ty)) + type = SourceKind::Direct; + else + type = SourceKind::Argument; } - if (type == SourceKind::Global || type == SourceKind::Direct) + if (global) return {global, type, ty, attributes, approximateSource}; return {v, type, ty, attributes, approximateSource}; } +bool AliasAnalysis::isDummyArgument(mlir::Value v) { + auto blockArg{v.dyn_cast()}; + if (!blockArg) + return false; + auto *owner{blockArg.getOwner()}; + return owner->isEntryBlock() && + mlir::isa(owner->getParentOp()); +} + } // namespace fir diff --git a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp index 684aa4462915e5..8babd28564dd3d 100644 --- a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp +++ b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp @@ -170,9 +170,22 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op, LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to direct " << name << " at " << *op << "\n"); tag = state.getFuncTree(func).directDataTree.getTag(name); + } else if (fir::AliasAnalysis::isDummyArgument( + source.u.get())) { + std::string name = getFuncArgName(source.u.get()); + if (!name.empty()) { + LLVM_DEBUG(llvm::dbgs().indent(2) + << "Found reference to direct from dummy argument " << name + << " at " << *op << "\n"); + tag = state.getFuncTree(func).directDataTree.getTag(name); + } else { + LLVM_DEBUG(llvm::dbgs().indent(2) + << "WARN: for direct, couldn't find a name for dummy " + << "argument " << *op << "\n"); + } } else { - // SourceKind::Direct is likely to be extended to cases which are not a - // SymbolRefAttr in the future + // SourceKind::Direct is likely to be extended to more cases in the + // future LLVM_DEBUG(llvm::dbgs().indent(2) << "Can't get name for direct " << source << " at " << *op << "\n"); } diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir index 31459ef21d947c..d915ba9d27905a 100644 --- a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir @@ -44,7 +44,7 @@ // pointer arguments // CHECK-DAG: arg2.addr#0 <-> func.region0#0: MayAlias // CHECK-DAG: arg2.addr#0 <-> func.region0#1: MayAlias -// CHECK-DAG: arg2.addr#0 <-> func.region0#2: MustAlias +// CHECK-DAG: arg2.addr#0 <-> func.region0#2: MayAlias // CHECK-DAG: boxp1.addr#0 <-> arg2.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"} { diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-box-addr-load/arg.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-box-addr-load/arg.fir new file mode 100644 index 00000000000000..36eaf3a1576288 --- /dev/null +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-box-addr-load/arg.fir @@ -0,0 +1,94 @@ +// Check aliasing with the address passed via a pointer dummy argument. + +// Use --mlir-disable-threading so that the AA queries are serialized +// as well as its diagnostic output. +// RUN: fir-opt %s \ +// RUN: -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' \ +// RUN: --mlir-disable-threading 2>&1 | FileCheck -match-full-lines %s + +// subroutine test(p0, p1, arr, t_arr, alloc, t_alloc) +// real, pointer :: p0, p1 +// real :: arr(:) +// real, target :: t_arr(:) +// real, allocatable :: alloc +// real, allocatable, target :: t_alloc +// real, target :: t +// real :: v +// v = p0 +// v = p1 +// v = arr(1) +// v = t_arr(1) +// v = alloc +// v = t_alloc +// 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: t.addr#0 <-> p0.tgt_addr#0: MayAlias +// CHECK-DAG: t.addr#0 <-> p1.tgt_addr#0: MayAlias +// CHECK-DAG: v.addr#0 <-> p0.tgt_addr#0: NoAlias +// CHECK-DAG: v.addr#0 <-> p1.tgt_addr#0: NoAlias +// CHECK-DAG: p0.tgt_addr#0 <-> p1.tgt_addr#0: MayAlias + +// Determining whether the address *in* a pointer can alias the address *of* a +// pointer is not yet handled. In the past, when it was the same pointer, that +// relationship was mistakenly determined to be MustAlias. +// CHECK-DAG: p0.tgt_addr#0 <-> func.region0#0: MayAlias +// CHECK-DAG: p0.tgt_addr#0 <-> func.region0#1: MayAlias +// CHECK-DAG: p1.tgt_addr#0 <-> func.region0#0: MayAlias +// CHECK-DAG: p1.tgt_addr#0 <-> func.region0#1: MayAlias + +// For some cases, AliasAnalysis analyzes hlfir.designate like fir.box_addr, so +// make sure it doesn't mistakenly see arr(1).addr as an address that was loaded +// from a pointer and that could alias something. However, t_arr is a target. +// CHECK-DAG: p0.tgt_addr#0 <-> arr(1).addr#0: NoAlias +// CHECK-DAG: p0.tgt_addr#0 <-> t_arr(1).addr#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_addr#0 <-> alloc.tgt_addr#0: NoAlias +// CHECK-DAG: p0.tgt_addr#0 <-> t_alloc.tgt_addr#0: MayAlias + +// The address *in* an allocatable cannot alias the address *of* that +// allocatable. +// CHECK-DAG: alloc.addr#0 <-> alloc.tgt_addr#0: NoAlias + +func.func @_QPtest(%arg0: !fir.ref>> {fir.bindc_name = "p0"}, %arg1: !fir.ref>> {fir.bindc_name = "p1"}, %arg2: !fir.box> {fir.bindc_name = "arr"}, %arg3: !fir.box> {fir.bindc_name = "t_arr", fir.target}, %arg4: !fir.ref>> {fir.bindc_name = "alloc"}, %arg5: !fir.ref>> {fir.bindc_name = "t_alloc", fir.target}) attributes {test.ptr="func"} { + %0:2 = hlfir.declare %arg4 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEalloc", test.ptr="alloc.addr"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + %1:2 = hlfir.declare %arg2 {uniq_name = "_QFtestEarr"} : (!fir.box>) -> (!fir.box>, !fir.box>) + %2:2 = hlfir.declare %arg0 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEp0"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + %3:2 = hlfir.declare %arg1 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEp1"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + %4 = fir.alloca f32 {bindc_name = "t", fir.target, uniq_name = "_QFtestEt"} + %5:2 = hlfir.declare %4 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEt", test.ptr="t.addr"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %6:2 = hlfir.declare %arg5 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEt_alloc"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + %7:2 = hlfir.declare %arg3 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEt_arr"} : (!fir.box>) -> (!fir.box>, !fir.box>) + %8 = fir.alloca f32 {bindc_name = "v", uniq_name = "_QFtestEv"} + %9:2 = hlfir.declare %8 {uniq_name = "_QFtestEv", test.ptr="v.addr"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %10 = fir.load %2#0 : !fir.ref>> + %11 = fir.box_addr %10 {test.ptr="p0.tgt_addr"} : (!fir.box>) -> !fir.ptr + %12 = fir.load %11 : !fir.ptr + hlfir.assign %12 to %9#0 : f32, !fir.ref + %13 = fir.load %3#0 : !fir.ref>> + %14 = fir.box_addr %13 {test.ptr="p1.tgt_addr"} : (!fir.box>) -> !fir.ptr + %15 = fir.load %14 : !fir.ptr + hlfir.assign %15 to %9#0 : f32, !fir.ref + %c1 = arith.constant 1 : index + %16 = hlfir.designate %1#0 (%c1) {test.ptr="arr(1).addr"} : (!fir.box>, index) -> !fir.ref + %17 = fir.load %16 : !fir.ref + hlfir.assign %17 to %9#0 : f32, !fir.ref + %c1_0 = arith.constant 1 : index + %18 = hlfir.designate %7#0 (%c1_0) {test.ptr="t_arr(1).addr"} : (!fir.box>, index) -> !fir.ref + %19 = fir.load %18 : !fir.ref + hlfir.assign %19 to %9#0 : f32, !fir.ref + %20 = fir.load %0#0 : !fir.ref>> + %21 = fir.box_addr %20 {test.ptr="alloc.tgt_addr"} : (!fir.box>) -> !fir.heap + %22 = fir.load %21 : !fir.heap + hlfir.assign %22 to %9#0 : f32, !fir.ref + %23 = fir.load %6#0 : !fir.ref>> + %24 = fir.box_addr %23 {test.ptr="t_alloc.tgt_addr"} : (!fir.box>) -> !fir.heap + %25 = fir.load %24 : !fir.heap + hlfir.assign %25 to %9#0 : f32, !fir.ref + return +} diff --git a/flang/test/Transforms/tbaa2.fir b/flang/test/Transforms/tbaa2.fir index ab39f65cdade70..13b7a876fa3584 100644 --- a/flang/test/Transforms/tbaa2.fir +++ b/flang/test/Transforms/tbaa2.fir @@ -48,14 +48,14 @@ // CHECK: #[[ANY_ACCESS:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[ANY_DATA:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[ANY_GLBL:.+]] = #llvm.tbaa_type_desc}> -// CHECK: #[[ANY_ARG:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[ANY_DIRECT:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[ANY_ARG:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[GLBL_ZSTART:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[GLBL_ZSTOP:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[GLBL_YSTART:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[GLBL_YSTOP:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[GLBL_XSTART:.+]] = #llvm.tbaa_type_desc}> -// CHECK: #[[ARG_LOW:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[ARG_LOW:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[DIRECT_A:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[DIRECT_B:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[ARG_Z:.+]] = #llvm.tbaa_type_desc}>