Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 15 additions & 5 deletions flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,14 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
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
Expand All @@ -101,8 +107,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<!fir.ptr<f32>>
// 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<!fir.ptr<f32>>
// e.g.: %arg0: !fir.ref<!fir.box<!fir.ptr<f32>>>
// Though nothing is known about them, they would only alias with targets or
// pointers
bool directSourceToNonTargetOrPointer = false;
Expand Down Expand Up @@ -399,16 +407,18 @@ 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 && attributes.test(Attribute::Pointer))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks good overall. The test change looks correct.
Why do we need this condition : && attributes.test(Attribute::Pointer) ?
It should be symmetric with the AddrOfOp.

Copy link
Collaborator Author

@jdenny-ornl jdenny-ornl Apr 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this isDummyArg() case: The pointer check is needed to exclude dummy arguments of type !fir.box<!fir.array<...>>. This is in part because followBoxAddr is set when DesignateOp is applied to a !fir.box.

For the AddrOfOp case: I didn't figure out how to get flang to generate a global with that type, even after asking in flang slack #general. I did find that flang/test/Fir/cg-ops.fir has such a global (again, I don't know the corresponding fortran). However, AddrOfOp turns even that into a !fir.ref, so followBoxAddr isn't set at the DesignateOp, so I still don't know how to create the problem for AddrOfOp.

Copy link
Collaborator Author

@jdenny-ornl jdenny-ornl Apr 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the AddrOfOp case: I noticed that flang/test/Analysis/AliasAnalysis/alias-analysis-8.fir expects the relationship between the address in an allocatable global and the address of that allocatable global to be NoAlias.

For the isDummyArg() case: I just checked the same relationship for allocatable arguments and discovered my patch wasn't handling it correctly: it has it as MustAlias. The pointer check we're discussing is too strict. I'll push a patch to this PR shortly that relaxes that to fir::isa_ref_type(ty) to turn it into NoAlias. However, flang/test/Transforms/tbaa2.fir then fails because AddAliasTags cannot yet handle Direct for the allocatable arguments there. My new patch also attempts to extend AddAliasTags accordingly.

I also tried adding fir::isa_ref_type(ty) to the AddrOfOp case, and check-flang still behaved. So, it doesn't improve existing test cases, and I haven't found a !fir.box<!fir.array<...>> case for AddrOfOp (see previous comment), so for now I've abandoned that addition as apparently useless.

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};
Expand Down
2 changes: 1 addition & 1 deletion flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir
Original file line number Diff line number Diff line change
Expand Up @@ -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<f32> {fir.bindc_name = "v1", fir.target}, %arg1: !fir.ref<f32> {fir.bindc_name = "v2", fir.target}, %arg2: !fir.ref<!fir.box<!fir.ptr<f32>>> ) attributes {test.ptr = "func"} {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// 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 %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

func.func @_QPtest(%arg0: !fir.ref<!fir.box<!fir.ptr<f32>>> {fir.bindc_name = "p0"}, %arg1: !fir.ref<!fir.box<!fir.ptr<f32>>> {fir.bindc_name = "p1"}, %arg2: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "arr"}, %arg3: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "t_arr", fir.target}, %arg4: !fir.ref<!fir.box<!fir.heap<f32>>> {fir.bindc_name = "alloc"}, %arg5: !fir.ref<!fir.box<!fir.heap<f32>>> {fir.bindc_name = "t_alloc", fir.target}) attributes {test.ptr="func"} {
%0:2 = hlfir.declare %arg4 {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>>>)
%1:2 = hlfir.declare %arg2 {uniq_name = "_QFtestEarr"} : (!fir.box<!fir.array<?xf32>>) -> (!fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>)
%2:2 = hlfir.declare %arg0 {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>>>)
%3:2 = hlfir.declare %arg1 {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>>>)
%4 = fir.alloca f32 {bindc_name = "t", fir.target, uniq_name = "_QFtestEt"}
%5:2 = hlfir.declare %4 {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFtestEt", test.ptr="t.addr"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
%6:2 = hlfir.declare %arg5 {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>>>)
%7:2 = hlfir.declare %arg3 {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFtestEt_arr"} : (!fir.box<!fir.array<?xf32>>) -> (!fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>)
%8 = fir.alloca f32 {bindc_name = "v", uniq_name = "_QFtestEv"}
%9:2 = hlfir.declare %8 {uniq_name = "_QFtestEv", test.ptr="v.addr"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
%10 = fir.load %2#0 : !fir.ref<!fir.box<!fir.ptr<f32>>>
%11 = fir.box_addr %10 {test.ptr="p0.tgt_addr"} : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
%12 = fir.load %11 : !fir.ptr<f32>
hlfir.assign %12 to %9#0 : f32, !fir.ref<f32>
%13 = fir.load %3#0 : !fir.ref<!fir.box<!fir.ptr<f32>>>
%14 = fir.box_addr %13 {test.ptr="p1.tgt_addr"} : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
%15 = fir.load %14 : !fir.ptr<f32>
hlfir.assign %15 to %9#0 : f32, !fir.ref<f32>
%c1 = arith.constant 1 : index
%16 = hlfir.designate %1#0 (%c1) {test.ptr="arr(1).addr"} : (!fir.box<!fir.array<?xf32>>, index) -> !fir.ref<f32>
%17 = fir.load %16 : !fir.ref<f32>
hlfir.assign %17 to %9#0 : f32, !fir.ref<f32>
%c1_0 = arith.constant 1 : index
%18 = hlfir.designate %7#0 (%c1_0) {test.ptr="t_arr(1).addr"} : (!fir.box<!fir.array<?xf32>>, index) -> !fir.ref<f32>
%19 = fir.load %18 : !fir.ref<f32>
hlfir.assign %19 to %9#0 : f32, !fir.ref<f32>
%20 = fir.load %0#0 : !fir.ref<!fir.box<!fir.heap<f32>>>
%21 = fir.box_addr %20 {test.ptr="alloc.tgt_addr"} : (!fir.box<!fir.heap<f32>>) -> !fir.heap<f32>
%22 = fir.load %21 : !fir.heap<f32>
hlfir.assign %22 to %9#0 : f32, !fir.ref<f32>
%23 = fir.load %6#0 : !fir.ref<!fir.box<!fir.heap<f32>>>
%24 = fir.box_addr %23 {test.ptr="t_alloc.tgt_addr"} : (!fir.box<!fir.heap<f32>>) -> !fir.heap<f32>
%25 = fir.load %24 : !fir.heap<f32>
hlfir.assign %25 to %9#0 : f32, !fir.ref<f32>
return
}