Skip to content

Commit ac8d810

Browse files
committed
[BasicAA] Use nuw attribute of GEPs
Use the nuw attribute of GEPs to prove that pointers do not alias, in cases matching the following: + + + | BaseOffset | +<nuw> Indices | ---------------->|-------------------->| |-->VLeftSize | |------->VRightSize LHS RHS If the difference between pointers is Offset +<nuw> Indices (the variable indices all come from nuw GEPs) then we know that the addition does not wrap the pointer index type (add nuw) and the constant Offset is a lower bound on the distance between the pointers. We can then prove NoAlias via Offset u>= VLeftSize.
1 parent 161e250 commit ac8d810

File tree

2 files changed

+177
-10
lines changed

2 files changed

+177
-10
lines changed

llvm/lib/Analysis/BasicAliasAnalysis.cpp

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -555,9 +555,9 @@ struct BasicAAResult::DecomposedGEP {
555555
APInt Offset;
556556
// Scaled variable (non-constant) indices.
557557
SmallVector<VariableGEPIndex, 4> VarIndices;
558-
// Are all operations inbounds GEPs or non-indexing operations?
558+
// Nowrap flags common to all GEP operations involved in expression.
559559
// (std::nullopt iff expression doesn't involve any geps)
560-
std::optional<bool> InBounds;
560+
std::optional<GEPNoWrapFlags> NWFlags;
561561

562562
void dump() const {
563563
print(dbgs());
@@ -644,12 +644,11 @@ BasicAAResult::DecomposeGEPExpression(const Value *V, const DataLayout &DL,
644644
return Decomposed;
645645
}
646646

647-
// Track whether we've seen at least one in bounds gep, and if so, whether
648-
// all geps parsed were in bounds.
649-
if (Decomposed.InBounds == std::nullopt)
650-
Decomposed.InBounds = GEPOp->isInBounds();
651-
else if (!GEPOp->isInBounds())
652-
Decomposed.InBounds = false;
647+
// Track the common nowrap flags for all GEPs we see.
648+
if (Decomposed.NWFlags == std::nullopt)
649+
Decomposed.NWFlags = GEPOp->getNoWrapFlags();
650+
else
651+
*Decomposed.NWFlags &= GEPOp->getNoWrapFlags();
653652

654653
assert(GEPOp->getSourceElementType()->isSized() && "GEP must be sized");
655654

@@ -1120,15 +1119,15 @@ AliasResult BasicAAResult::aliasGEP(
11201119
// for the two to alias, then we can assume noalias.
11211120
// TODO: Remove !isScalable() once BasicAA fully support scalable location
11221121
// size
1123-
if (*DecompGEP1.InBounds && DecompGEP1.VarIndices.empty() &&
1122+
if (DecompGEP1.NWFlags->isInBounds() && DecompGEP1.VarIndices.empty() &&
11241123
V2Size.hasValue() && !V2Size.isScalable() &&
11251124
DecompGEP1.Offset.sge(V2Size.getValue()) &&
11261125
isBaseOfObject(DecompGEP2.Base))
11271126
return AliasResult::NoAlias;
11281127

11291128
if (isa<GEPOperator>(V2)) {
11301129
// Symmetric case to above.
1131-
if (*DecompGEP2.InBounds && DecompGEP1.VarIndices.empty() &&
1130+
if (DecompGEP2.NWFlags->isInBounds() && DecompGEP1.VarIndices.empty() &&
11321131
V1Size.hasValue() && !V1Size.isScalable() &&
11331132
DecompGEP1.Offset.sle(-V1Size.getValue()) &&
11341133
isBaseOfObject(DecompGEP1.Base))
@@ -1239,6 +1238,32 @@ AliasResult BasicAAResult::aliasGEP(
12391238
}
12401239
}
12411240

1241+
// If the difference between pointers is Offset +<nuw> Indices (the variable
1242+
// indices all come from nuw GEPs) then we know that the addition does not
1243+
// wrap the pointer index type (add nuw) and the constant Offset is a lower
1244+
// bound on the distance between the pointers. We can then prove NoAlias via
1245+
// Offset u>= VLeftSize.
1246+
// + + +
1247+
// | BaseOffset | +<nuw> Indices |
1248+
// ---------------->|-------------------->|
1249+
// |-->VLeftSize | |-------> VRightSize
1250+
// LHS RHS
1251+
if (!DecompGEP1.VarIndices.empty() &&
1252+
llvm::all_of(DecompGEP1.VarIndices, [&](const VariableGEPIndex &V) {
1253+
return V.IsNegated == DecompGEP1.VarIndices.front().IsNegated;
1254+
})) {
1255+
APInt Off = DecompGEP1.Offset;
1256+
bool Swapped = Off.isNegative();
1257+
LocationSize VLeftSize = Swapped ? V1Size : V2Size;
1258+
DecomposedGEP &DecompRight = Swapped ? DecompGEP2 : DecompGEP1;
1259+
1260+
bool IndicesFromRight = DecompGEP1.VarIndices.front().IsNegated == Swapped;
1261+
if (IndicesFromRight && DecompRight.NWFlags->hasNoUnsignedWrap())
1262+
if (!VLeftSize.isScalable() && VLeftSize.hasValue() &&
1263+
Off.abs().uge(VLeftSize.getValue()))
1264+
return AliasResult::NoAlias;
1265+
}
1266+
12421267
// Bail on analysing scalable LocationSize
12431268
if (V1Size.isScalable() || V2Size.isScalable())
12441269
return AliasResult::MayAlias;
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
; RUN: opt < %s -aa-pipeline=basic-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
2+
3+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
4+
5+
; CHECK-LABEL: test_no_lower_bound
6+
;
7+
; CHECK-DAG: MayAlias: i32* %a, i32* %b
8+
define void @test_no_lower_bound(ptr %p, i64 %i) {
9+
%a = getelementptr i8, ptr %p, i64 4
10+
%b = getelementptr nuw i8, ptr %p, i64 %i
11+
12+
load i32, ptr %a
13+
load i32, ptr %b
14+
15+
ret void
16+
}
17+
18+
; CHECK-LABEL: test_lower_bound_lt_size
19+
;
20+
; CHECK-DAG: MayAlias: i32* %a, i32* %b
21+
define void @test_lower_bound_lt_size(ptr %p, i64 %i) {
22+
%a = getelementptr i8, ptr %p
23+
%add = getelementptr nuw i8, ptr %p, i64 2
24+
%b = getelementptr nuw i8, ptr %add, i64 %i
25+
26+
load i32, ptr %a
27+
load i32, ptr %b
28+
29+
ret void
30+
}
31+
32+
; CHECK-LABEL: test_lower_bound_ge_size
33+
;
34+
; CHECK-DAG: NoAlias: i32* %a, i32* %b
35+
define void @test_lower_bound_ge_size(ptr %p, i64 %i) {
36+
%a = getelementptr i8, ptr %p
37+
%add = getelementptr nuw i8, ptr %p, i64 4
38+
%b = getelementptr nuw i8, ptr %add, i64 %i
39+
40+
load i32, ptr %a
41+
load i32, ptr %b
42+
43+
ret void
44+
}
45+
46+
; CHECK-LABEL: test_not_all_nuw
47+
;
48+
; If part of the addressing is done with non-nuw GEPs, we can't use properties
49+
; implied by the last GEP with the whole offset. In this case, the calculation
50+
; of %add (%p + 4) could wrap the pointer index type, such that %add +<nuw> %i
51+
; could still alias with %p.
52+
;
53+
; CHECK-DAG: MayAlias: i32* %a, i32* %b
54+
define void @test_not_all_nuw(ptr %p, i64 %i) {
55+
%a = getelementptr i8, ptr %p
56+
%add = getelementptr i8, ptr %p, i64 4
57+
%b = getelementptr nuw i8, ptr %add, i64 %i
58+
59+
load i32, ptr %a
60+
load i32, ptr %b
61+
62+
ret void
63+
}
64+
65+
; CHECK-LABEL: test_multi_step_not_all_nuw
66+
;
67+
; CHECK-DAG: MayAlias: i32* %a, i32* %b
68+
define void @test_multi_step_not_all_nuw(ptr %p, i64 %i, i64 %j, i64 %k) {
69+
%a = getelementptr i8, ptr %p
70+
%add = getelementptr i8, ptr %p, i64 4
71+
%step1 = getelementptr i8, ptr %add, i64 %i
72+
%step2 = getelementptr i8, ptr %step1, i64 %j
73+
%b = getelementptr nuw i8, ptr %step2, i64 %k
74+
75+
load i32, ptr %a
76+
load i32, ptr %b
77+
78+
ret void
79+
}
80+
81+
; CHECK-LABEL: test_multi_step_all_nuw
82+
;
83+
; CHECK-DAG: NoAlias: i32* %a, i32* %b
84+
define void @test_multi_step_all_nuw(ptr %p, i64 %i, i64 %j, i64 %k) {
85+
%a = getelementptr i8, ptr %p
86+
%add = getelementptr nuw i8, ptr %p, i64 4
87+
%step1 = getelementptr nuw i8, ptr %add, i64 %i
88+
%step2 = getelementptr nuw i8, ptr %step1, i64 %j
89+
%b = getelementptr nuw i8, ptr %step2, i64 %k
90+
91+
load i32, ptr %a
92+
load i32, ptr %b
93+
94+
ret void
95+
}
96+
97+
%struct = type { i64, [2 x i32], i64 }
98+
99+
; CHECK-LABEL: test_struct_no_nuw
100+
;
101+
; The array access may alias with the struct elements before and after, because
102+
; we cannot prove that (%arr + %i) does not alias with the base pointer %p.
103+
;
104+
; CHECK-DAG: MayAlias: i32* %arrayidx, i64* %st
105+
; CHECK-DAG: NoAlias: i64* %after, i64* %st
106+
; CHECK-DAG: MayAlias: i64* %after, i32* %arrayidx
107+
108+
define void @test_struct_no_nuw(ptr %st, i64 %i) {
109+
%arr = getelementptr i8, ptr %st, i64 8
110+
%arrayidx = getelementptr [2 x i32], ptr %arr, i64 0, i64 %i
111+
%after = getelementptr i8, ptr %st, i64 16
112+
113+
load i64, ptr %st
114+
load i32, ptr %arrayidx
115+
load i64, ptr %after
116+
117+
ret void
118+
}
119+
120+
; CHECK-LABEL: test_struct_nuw
121+
;
122+
; We can prove that the array access does not alias with struct element before,
123+
; because we can prove that (%arr +<nuw> %i) does not wrap the pointer index
124+
; type (add nuw). The array access may still alias with the struct element
125+
; after, as the add nuw property does not preclude this.
126+
;
127+
; CHECK-DAG: NoAlias: i32* %arrayidx, i64* %st
128+
; CHECK-DAG: NoAlias: i64* %after, i64* %st
129+
; CHECK-DAG: MayAlias: i64* %after, i32* %arrayidx
130+
131+
define void @test_struct_nuw(ptr %st, i64 %i) {
132+
%arr = getelementptr nuw i8, ptr %st, i64 8
133+
%arrayidx = getelementptr nuw [2 x i32], ptr %arr, i64 0, i64 %i
134+
%after = getelementptr nuw i8, ptr %st, i64 16
135+
136+
load i64, ptr %st
137+
load i32, ptr %arrayidx
138+
load i64, ptr %after
139+
140+
ret void
141+
}
142+

0 commit comments

Comments
 (0)