Skip to content

Commit 91def8c

Browse files
committed
[flang] AliasAnalysis: distinguish addr of arg vs. addr in arg
For example, in the following program, the relationship between the address *in* arg (dynamically the address of x's alloca) and the address *of* arg (dynamically the address of p's alloca) is not MustAlias, as determined without this patch: ``` subroutine f() real, pointer :: p real, target :: x p => x call g(p) end subroutine f subroutine g(arg) real, pointer :: arg end subroutine g ``` Generally extend test coverage for HLFIR-based alias analysis for addresses extracted from pointers.
1 parent 13be0d4 commit 91def8c

File tree

4 files changed

+108
-8
lines changed

4 files changed

+108
-8
lines changed

flang/include/flang/Optimizer/Analysis/AliasAnalysis.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ struct AliasAnalysis {
3636
/// Represents memory allocated outside of a function
3737
/// and passed to the function via host association tuple.
3838
HostAssoc,
39-
/// Represents direct memory access whose source cannot be further
40-
/// determined
39+
/// Memory address retrieved from a box, perhaps from a global or
40+
/// an argument.
4141
Direct,
4242
/// Represents memory allocated by unknown means and
4343
/// with the memory address defined by a memory reading

flang/lib/Optimizer/Analysis/AliasAnalysis.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,14 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
9191
LLVM_DEBUG(llvm::dbgs() << "AliasAnalysis::alias\n";
9292
llvm::dbgs() << " lhs: " << lhs << "\n";
9393
llvm::dbgs() << " lhsSrc: " << lhsSrc << "\n";
94+
llvm::dbgs() << " lhsSrc kind: " << EnumToString(lhsSrc.kind) << "\n";
95+
llvm::dbgs() << " lhsSrc pointer: " << lhsSrc.attributes.test(Attribute::Pointer) << "\n";
96+
llvm::dbgs() << " lhsSrc target: " << lhsSrc.attributes.test(Attribute::Target) << "\n";
9497
llvm::dbgs() << " rhs: " << rhs << "\n";
9598
llvm::dbgs() << " rhsSrc: " << rhsSrc << "\n";
99+
llvm::dbgs() << " rhsSrc kind: " << EnumToString(rhsSrc.kind) << "\n";
100+
llvm::dbgs() << " rhsSrc pointer: " << rhsSrc.attributes.test(Attribute::Pointer) << "\n";
101+
llvm::dbgs() << " rhsSrc target: " << rhsSrc.attributes.test(Attribute::Target) << "\n";
96102
llvm::dbgs() << "\n";);
97103

98104
// Indirect case currently not handled. Conservatively assume
@@ -101,8 +107,10 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
101107
return AliasResult::MayAlias;
102108
}
103109

104-
// SourceKind::Direct is set for the addresses wrapped in a global boxes.
105-
// ie: fir.global @_QMpointersEp : !fir.box<!fir.ptr<f32>>
110+
// SourceKind::Direct is set for the addresses wrapped in a box, perhaps from
111+
// a global or an argument.
112+
// e.g.: fir.global @_QMpointersEp : !fir.box<!fir.ptr<f32>>
113+
// e.g.: %arg0: !fir.ref<!fir.box<!fir.ptr<f32>>>
106114
// Though nothing is known about them, they would only alias with targets or
107115
// pointers
108116
bool directSourceToNonTargetOrPointer = false;
@@ -399,16 +407,18 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) {
399407
if (!defOp && type == SourceKind::Unknown)
400408
// Check if the memory source is coming through a dummy argument.
401409
if (isDummyArgument(v)) {
402-
type = SourceKind::Argument;
403410
ty = v.getType();
404411
if (fir::valueHasFirAttribute(v, fir::getTargetAttrName()))
405412
attributes.set(Attribute::Target);
406-
407413
if (Source::isPointerReference(ty))
408414
attributes.set(Attribute::Pointer);
415+
if (followBoxAddr && attributes.test(Attribute::Pointer))
416+
type = SourceKind::Direct;
417+
else
418+
type = SourceKind::Argument;
409419
}
410420

411-
if (type == SourceKind::Global || type == SourceKind::Direct)
421+
if (global)
412422
return {global, type, ty, attributes, approximateSource};
413423

414424
return {v, type, ty, attributes, approximateSource};

flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
// pointer arguments
4545
// CHECK-DAG: arg2.addr#0 <-> func.region0#0: MayAlias
4646
// CHECK-DAG: arg2.addr#0 <-> func.region0#1: MayAlias
47-
// CHECK-DAG: arg2.addr#0 <-> func.region0#2: MustAlias
47+
// CHECK-DAG: arg2.addr#0 <-> func.region0#2: MayAlias
4848
// CHECK-DAG: boxp1.addr#0 <-> arg2.addr#0: MayAlias
4949

5050
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"} {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Check aliasing with the address passed via a pointer dummy argument.
2+
3+
// Use --mlir-disable-threading so that the AA queries are serialized
4+
// as well as its diagnostic output.
5+
// RUN: fir-opt %s \
6+
// RUN: -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' \
7+
// RUN: --mlir-disable-threading 2>&1 | FileCheck %s
8+
9+
// subroutine test(p0, p1, arr, t_arr, alloc, t_alloc)
10+
// real, pointer :: p0, p1
11+
// real :: arr(:)
12+
// real, target :: t_arr(:)
13+
// real, allocatable :: alloc
14+
// real, allocatable, target :: t_alloc
15+
// real, target :: t
16+
// real :: v
17+
// v = p0
18+
// v = p1
19+
// v = arr(1)
20+
// v = t_arr(1)
21+
// v = alloc
22+
// v = t_alloc
23+
// end subroutine test
24+
25+
// CHECK-LABEL: Testing : "_QPtest"
26+
27+
// The address in a pointer can alias the address in another pointer or the
28+
// address of a target but not the address of other variables.
29+
// CHECK-DAG: t.addr#0 <-> p0.tgt_addr#0: MayAlias
30+
// CHECK-DAG: t.addr#0 <-> p1.tgt_addr#0: MayAlias
31+
// CHECK-DAG: v.addr#0 <-> p0.tgt_addr#0: NoAlias
32+
// CHECK-DAG: v.addr#0 <-> p1.tgt_addr#0: NoAlias
33+
// CHECK-DAG: p0.tgt_addr#0 <-> p1.tgt_addr#0: MayAlias
34+
35+
// Determining whether the address *in* a pointer can alias the address *of* a
36+
// pointer is not yet handled. In the past, when it was the same pointer, that
37+
// relationship was mistakenly determined to be MustAlias.
38+
// CHECK-DAG: p0.tgt_addr#0 <-> func.region0#0: MayAlias
39+
// CHECK-DAG: p0.tgt_addr#0 <-> func.region0#1: MayAlias
40+
// CHECK-DAG: p1.tgt_addr#0 <-> func.region0#0: MayAlias
41+
// CHECK-DAG: p1.tgt_addr#0 <-> func.region0#1: MayAlias
42+
43+
// For some cases, AliasAnalysis analyzes hlfir.designate like fir.box_addr, so
44+
// make sure it doesn't mistakenly see arr(1).addr as an address that was loaded
45+
// from a pointer and that could alias something. However, t_arr is a target.
46+
// CHECK-DAG: p0.tgt_addr#0 <-> arr(1).addr#0: NoAlias
47+
// CHECK-DAG: p0.tgt_addr#0 <-> t_arr(1).addr#0: MayAlias
48+
49+
// Like a pointer, an allocatable contains an address, but an allocatable is not
50+
// a pointer and so cannot alias pointers. However, t_alloc is a target.
51+
// CHECK-DAG: p0.tgt_addr#0 <-> alloc.tgt_addr#0: NoAlias
52+
// CHECK-DAG: p0.tgt_addr#0 <-> t_alloc.tgt_addr#0: MayAlias
53+
54+
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"} {
55+
%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>>>)
56+
%1:2 = hlfir.declare %arg2 {uniq_name = "_QFtestEarr"} : (!fir.box<!fir.array<?xf32>>) -> (!fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>)
57+
%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>>>)
58+
%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>>>)
59+
%4 = fir.alloca f32 {bindc_name = "t", fir.target, uniq_name = "_QFtestEt"}
60+
%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>)
61+
%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>>>)
62+
%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>>)
63+
%8 = fir.alloca f32 {bindc_name = "v", uniq_name = "_QFtestEv"}
64+
%9:2 = hlfir.declare %8 {uniq_name = "_QFtestEv", test.ptr="v.addr"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
65+
%10 = fir.load %2#0 : !fir.ref<!fir.box<!fir.ptr<f32>>>
66+
%11 = fir.box_addr %10 {test.ptr="p0.tgt_addr"} : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
67+
%12 = fir.load %11 : !fir.ptr<f32>
68+
hlfir.assign %12 to %9#0 : f32, !fir.ref<f32>
69+
%13 = fir.load %3#0 : !fir.ref<!fir.box<!fir.ptr<f32>>>
70+
%14 = fir.box_addr %13 {test.ptr="p1.tgt_addr"} : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
71+
%15 = fir.load %14 : !fir.ptr<f32>
72+
hlfir.assign %15 to %9#0 : f32, !fir.ref<f32>
73+
%c1 = arith.constant 1 : index
74+
%16 = hlfir.designate %1#0 (%c1) {test.ptr="arr(1).addr"} : (!fir.box<!fir.array<?xf32>>, index) -> !fir.ref<f32>
75+
%17 = fir.load %16 : !fir.ref<f32>
76+
hlfir.assign %17 to %9#0 : f32, !fir.ref<f32>
77+
%c1_0 = arith.constant 1 : index
78+
%18 = hlfir.designate %7#0 (%c1_0) {test.ptr="t_arr(1).addr"} : (!fir.box<!fir.array<?xf32>>, index) -> !fir.ref<f32>
79+
%19 = fir.load %18 : !fir.ref<f32>
80+
hlfir.assign %19 to %9#0 : f32, !fir.ref<f32>
81+
%20 = fir.load %0#0 : !fir.ref<!fir.box<!fir.heap<f32>>>
82+
%21 = fir.box_addr %20 {test.ptr="alloc.tgt_addr"} : (!fir.box<!fir.heap<f32>>) -> !fir.heap<f32>
83+
%22 = fir.load %21 : !fir.heap<f32>
84+
hlfir.assign %22 to %9#0 : f32, !fir.ref<f32>
85+
%23 = fir.load %6#0 : !fir.ref<!fir.box<!fir.heap<f32>>>
86+
%24 = fir.box_addr %23 {test.ptr="t_alloc.tgt_addr"} : (!fir.box<!fir.heap<f32>>) -> !fir.heap<f32>
87+
%25 = fir.load %24 : !fir.heap<f32>
88+
hlfir.assign %25 to %9#0 : f32, !fir.ref<f32>
89+
return
90+
}

0 commit comments

Comments
 (0)