Skip to content

Commit ba84cfb

Browse files
authored
[BasicAA] Use nuw attribute of GEPs (#98608)
Use the nuw attribute of GEPs to prove that pointers do not alias, in cases matching the following: + + + | BaseOffset | +<nuw> Indices | ---------------->|-------------------->| |-->V2Size | |-------> V1Size LHS RHS If the difference between pointers is Offset +<nuw> Indices 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>= V2Size.
1 parent b432afc commit ba84cfb

File tree

2 files changed

+263
-20
lines changed

2 files changed

+263
-20
lines changed

llvm/lib/Analysis/BasicAliasAnalysis.cpp

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -555,17 +555,17 @@ 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?
559-
// (std::nullopt iff expression doesn't involve any geps)
560-
std::optional<bool> InBounds;
558+
// Nowrap flags common to all GEP operations involved in expression.
559+
GEPNoWrapFlags NWFlags = GEPNoWrapFlags::all();
561560

562561
void dump() const {
563562
print(dbgs());
564563
dbgs() << "\n";
565564
}
566565
void print(raw_ostream &OS) const {
567-
OS << "(DecomposedGEP Base=" << Base->getName()
568-
<< ", Offset=" << Offset
566+
OS << ", inbounds=" << (NWFlags.isInBounds() ? "1" : "0")
567+
<< ", nuw=" << (NWFlags.hasNoUnsignedWrap() ? "1" : "0")
568+
<< "(DecomposedGEP Base=" << Base->getName() << ", Offset=" << Offset
569569
<< ", VarIndices=[";
570570
for (size_t i = 0; i < VarIndices.size(); i++) {
571571
if (i != 0)
@@ -644,12 +644,8 @@ 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+
Decomposed.NWFlags &= GEPOp->getNoWrapFlags();
653649

654650
assert(GEPOp->getSourceElementType()->isSized() && "GEP must be sized");
655651

@@ -1112,6 +1108,13 @@ AliasResult BasicAAResult::aliasGEP(
11121108
if (DecompGEP1.Base == GEP1 && DecompGEP2.Base == V2)
11131109
return AliasResult::MayAlias;
11141110

1111+
// Swap GEP1 and GEP2 if GEP2 has more variable indices.
1112+
if (DecompGEP1.VarIndices.size() < DecompGEP2.VarIndices.size()) {
1113+
std::swap(DecompGEP1, DecompGEP2);
1114+
std::swap(V1Size, V2Size);
1115+
std::swap(UnderlyingV1, UnderlyingV2);
1116+
}
1117+
11151118
// Subtract the GEP2 pointer from the GEP1 pointer to find out their
11161119
// symbolic difference.
11171120
subtractDecomposedGEPs(DecompGEP1, DecompGEP2, AAQI);
@@ -1120,20 +1123,19 @@ AliasResult BasicAAResult::aliasGEP(
11201123
// for the two to alias, then we can assume noalias.
11211124
// TODO: Remove !isScalable() once BasicAA fully support scalable location
11221125
// size
1123-
if (*DecompGEP1.InBounds && DecompGEP1.VarIndices.empty() &&
1126+
1127+
if (DecompGEP1.NWFlags.isInBounds() && DecompGEP1.VarIndices.empty() &&
11241128
V2Size.hasValue() && !V2Size.isScalable() &&
11251129
DecompGEP1.Offset.sge(V2Size.getValue()) &&
11261130
isBaseOfObject(DecompGEP2.Base))
11271131
return AliasResult::NoAlias;
11281132

1129-
if (isa<GEPOperator>(V2)) {
1130-
// Symmetric case to above.
1131-
if (*DecompGEP2.InBounds && DecompGEP1.VarIndices.empty() &&
1132-
V1Size.hasValue() && !V1Size.isScalable() &&
1133-
DecompGEP1.Offset.sle(-V1Size.getValue()) &&
1134-
isBaseOfObject(DecompGEP1.Base))
1135-
return AliasResult::NoAlias;
1136-
}
1133+
// Symmetric case to above.
1134+
if (DecompGEP2.NWFlags.isInBounds() && DecompGEP1.VarIndices.empty() &&
1135+
V1Size.hasValue() && !V1Size.isScalable() &&
1136+
DecompGEP1.Offset.sle(-V1Size.getValue()) &&
1137+
isBaseOfObject(DecompGEP1.Base))
1138+
return AliasResult::NoAlias;
11371139

11381140
// For GEPs with identical offsets, we can preserve the size and AAInfo
11391141
// when performing the alias check on the underlying objects.
@@ -1239,6 +1241,20 @@ AliasResult BasicAAResult::aliasGEP(
12391241
}
12401242
}
12411243

1244+
// If the difference between pointers is Offset +<nuw> Indices then we know
1245+
// that the addition does not wrap the pointer index type (add nuw) and the
1246+
// constant Offset is a lower bound on the distance between the pointers. We
1247+
// can then prove NoAlias via Offset u>= VLeftSize.
1248+
// + + +
1249+
// | BaseOffset | +<nuw> Indices |
1250+
// ---------------->|-------------------->|
1251+
// |-->V2Size | |-------> V1Size
1252+
// LHS RHS
1253+
if (!DecompGEP1.VarIndices.empty() &&
1254+
DecompGEP1.NWFlags.hasNoUnsignedWrap() && V2Size.hasValue() &&
1255+
!V2Size.isScalable() && DecompGEP1.Offset.uge(V2Size.getValue()))
1256+
return AliasResult::NoAlias;
1257+
12421258
// Bail on analysing scalable LocationSize
12431259
if (V1Size.isScalable() || V2Size.isScalable())
12441260
return AliasResult::MayAlias;
@@ -1843,6 +1859,11 @@ bool BasicAAResult::isValueEqualInPotentialCycles(const Value *V,
18431859
void BasicAAResult::subtractDecomposedGEPs(DecomposedGEP &DestGEP,
18441860
const DecomposedGEP &SrcGEP,
18451861
const AAQueryInfo &AAQI) {
1862+
// Drop nuw flag from GEP if subtraction of constant offsets overflows in an
1863+
// unsigned sense.
1864+
if (DestGEP.Offset.ult(SrcGEP.Offset))
1865+
DestGEP.NWFlags = DestGEP.NWFlags.withoutNoUnsignedWrap();
1866+
18461867
DestGEP.Offset -= SrcGEP.Offset;
18471868
for (const VariableGEPIndex &Src : SrcGEP.VarIndices) {
18481869
// Find V in Dest. This is N^2, but pointer indices almost never have more
@@ -1865,6 +1886,11 @@ void BasicAAResult::subtractDecomposedGEPs(DecomposedGEP &DestGEP,
18651886
// If we found it, subtract off Scale V's from the entry in Dest. If it
18661887
// goes to zero, remove the entry.
18671888
if (Dest.Scale != Src.Scale) {
1889+
// Drop nuw flag from GEP if subtraction of V's Scale overflows in an
1890+
// unsigned sense.
1891+
if (Dest.Scale.ult(Src.Scale))
1892+
DestGEP.NWFlags = DestGEP.NWFlags.withoutNoUnsignedWrap();
1893+
18681894
Dest.Scale -= Src.Scale;
18691895
Dest.IsNSW = false;
18701896
} else {
@@ -1879,6 +1905,9 @@ void BasicAAResult::subtractDecomposedGEPs(DecomposedGEP &DestGEP,
18791905
VariableGEPIndex Entry = {Src.Val, Src.Scale, Src.CxtI, Src.IsNSW,
18801906
/* IsNegated */ true};
18811907
DestGEP.VarIndices.push_back(Entry);
1908+
1909+
// Drop nuw flag when we have unconsumed variable indices from SrcGEP.
1910+
DestGEP.NWFlags = DestGEP.NWFlags.withoutNoUnsignedWrap();
18821911
}
18831912
}
18841913
}
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
; RUN: opt < %s -aa-pipeline=basic-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
2+
3+
; CHECK-LABEL: test_no_lower_bound
4+
;
5+
; CHECK-DAG: MayAlias: i32* %a, i32* %b
6+
define void @test_no_lower_bound(ptr %p, i64 %i) {
7+
%a = getelementptr i8, ptr %p, i64 4
8+
%b = getelementptr nuw i8, ptr %p, i64 %i
9+
10+
load i32, ptr %a
11+
load i32, ptr %b
12+
13+
ret void
14+
}
15+
16+
; CHECK-LABEL: test_lower_bound_lt_size
17+
;
18+
; CHECK-DAG: MayAlias: i32* %a, i32* %b
19+
define void @test_lower_bound_lt_size(ptr %p, i64 %i) {
20+
%a = getelementptr i8, ptr %p
21+
%add = getelementptr nuw i8, ptr %p, i64 2
22+
%b = getelementptr nuw i8, ptr %add, i64 %i
23+
24+
load i32, ptr %a
25+
load i32, ptr %b
26+
27+
ret void
28+
}
29+
30+
; CHECK-LABEL: test_lower_bound_ge_size
31+
;
32+
; CHECK-DAG: NoAlias: i32* %a, i32* %b
33+
define void @test_lower_bound_ge_size(ptr %p, i64 %i) {
34+
%a = getelementptr i8, ptr %p
35+
%add = getelementptr nuw i8, ptr %p, i64 4
36+
%b = getelementptr nuw i8, ptr %add, i64 %i
37+
38+
load i32, ptr %a
39+
load i32, ptr %b
40+
41+
ret void
42+
}
43+
44+
; CHECK-LABEL: test_not_all_nuw
45+
;
46+
; If part of the addressing is done with non-nuw GEPs, we can't use properties
47+
; implied by the last GEP with the whole offset. In this case, the calculation
48+
; of %add (%p + 4) could wrap the pointer index type, such that %add +<nuw> %i
49+
; could still alias with %p.
50+
;
51+
; CHECK-DAG: MayAlias: i32* %a, i32* %b
52+
define void @test_not_all_nuw(ptr %p, i64 %i) {
53+
%a = getelementptr i8, ptr %p
54+
%add = getelementptr i8, ptr %p, i64 4
55+
%b = getelementptr nuw i8, ptr %add, i64 %i
56+
57+
load i32, ptr %a
58+
load i32, ptr %b
59+
60+
ret void
61+
}
62+
63+
; CHECK-LABEL: test_multi_step_not_all_nuw
64+
;
65+
; CHECK-DAG: MayAlias: i32* %a, i32* %b
66+
define void @test_multi_step_not_all_nuw(ptr %p, i64 %i, i64 %j, i64 %k) {
67+
%a = getelementptr i8, ptr %p
68+
%add = getelementptr i8, ptr %p, i64 4
69+
%step1 = getelementptr i8, ptr %add, i64 %i
70+
%step2 = getelementptr i8, ptr %step1, i64 %j
71+
%b = getelementptr nuw i8, ptr %step2, i64 %k
72+
73+
load i32, ptr %a
74+
load i32, ptr %b
75+
76+
ret void
77+
}
78+
79+
; CHECK-LABEL: test_multi_step_all_nuw
80+
;
81+
; CHECK-DAG: NoAlias: i32* %a, i32* %b
82+
define void @test_multi_step_all_nuw(ptr %p, i64 %i, i64 %j, i64 %k) {
83+
%a = getelementptr i8, ptr %p
84+
%add = getelementptr nuw i8, ptr %p, i64 4
85+
%step1 = getelementptr nuw i8, ptr %add, i64 %i
86+
%step2 = getelementptr nuw i8, ptr %step1, i64 %j
87+
%b = getelementptr nuw i8, ptr %step2, i64 %k
88+
89+
load i32, ptr %a
90+
load i32, ptr %b
91+
92+
ret void
93+
}
94+
95+
%struct = type { i64, [2 x i32], i64 }
96+
97+
; CHECK-LABEL: test_struct_no_nuw
98+
;
99+
; The array access may alias with the struct elements before and after, because
100+
; we cannot prove that (%arr + %i) does not alias with the base pointer %p.
101+
;
102+
; CHECK-DAG: MayAlias: i32* %arrayidx, i64* %st
103+
; CHECK-DAG: NoAlias: i64* %after, i64* %st
104+
; CHECK-DAG: MayAlias: i64* %after, i32* %arrayidx
105+
106+
define void @test_struct_no_nuw(ptr %st, i64 %i) {
107+
%arr = getelementptr i8, ptr %st, i64 8
108+
%arrayidx = getelementptr [2 x i32], ptr %arr, i64 0, i64 %i
109+
%after = getelementptr i8, ptr %st, i64 16
110+
111+
load i64, ptr %st
112+
load i32, ptr %arrayidx
113+
load i64, ptr %after
114+
115+
ret void
116+
}
117+
118+
; CHECK-LABEL: test_struct_nuw
119+
;
120+
; We can prove that the array access does not alias with struct element before,
121+
; because we can prove that (%arr +<nuw> %i) does not wrap the pointer index
122+
; type (add nuw). The array access may still alias with the struct element
123+
; after, as the add nuw property does not preclude this.
124+
;
125+
; CHECK-DAG: NoAlias: i32* %arrayidx, i64* %st
126+
; CHECK-DAG: NoAlias: i64* %after, i64* %st
127+
; CHECK-DAG: MayAlias: i64* %after, i32* %arrayidx
128+
129+
define void @test_struct_nuw(ptr %st, i64 %i) {
130+
%arr = getelementptr nuw i8, ptr %st, i64 8
131+
%arrayidx = getelementptr nuw [2 x i32], ptr %arr, i64 0, i64 %i
132+
%after = getelementptr nuw i8, ptr %st, i64 16
133+
134+
load i64, ptr %st
135+
load i32, ptr %arrayidx
136+
load i64, ptr %after
137+
138+
ret void
139+
}
140+
141+
; CHECK-LABEL: constant_offset_overflow
142+
;
143+
; If subtraction of constant offsets could overflow in an unsigned sense, we
144+
; cannot prove the lower bound between the GEPs and so they may still alias.
145+
;
146+
; CHECK-DAG: MayAlias: i32* %a, i32* %b
147+
148+
define void @constant_offset_overflow(ptr %p, i64 %i) {
149+
%a = getelementptr i8, ptr %p, i64 -8
150+
%add = getelementptr nuw i8, ptr %p, i64 4
151+
%b = getelementptr nuw i8, ptr %add, i64 %i
152+
153+
load i32, ptr %a
154+
load i32, ptr %b
155+
156+
ret void
157+
}
158+
159+
; CHECK-LABEL: equal_var_idx_noalias
160+
;
161+
; If GEPs have equal variable indices, we can prove NoAlias when the Scale of
162+
; the RHS GEP is greater, as in this scenario the constant lower bound holds.
163+
;
164+
; CHECK-DAG: NoAlias: i32* %a, i32* %b
165+
166+
define void @equal_var_idx_noalias(ptr %p, i64 %i) {
167+
%a = getelementptr i8, ptr %p, i64 %i
168+
169+
%add = getelementptr nuw i8, ptr %p, i64 4
170+
%b = getelementptr nuw i16, ptr %add, i64 %i
171+
172+
load i32, ptr %a
173+
load i32, ptr %b
174+
175+
ret void
176+
}
177+
178+
; CHECK-LABEL: equal_var_idx_alias
179+
;
180+
; If GEPs have equal variable indices, we cannot prove NoAlias when the Scale of
181+
; the RHS GEP is ult Scale of the LHS GEP.
182+
;
183+
; CHECK-DAG: MayAlias: i32* %a, i32* %b
184+
185+
define void @equal_var_idx_alias(ptr %p, i64 %i) {
186+
%a = getelementptr i32, ptr %p, i64 %i
187+
188+
%add = getelementptr nuw i8, ptr %p, i64 4
189+
%b = getelementptr nuw i16, ptr %add, i64 %i
190+
191+
load i32, ptr %b
192+
load i32, ptr %a
193+
194+
ret void
195+
}
196+
197+
; CHECK-LABEL: both_var_idx
198+
;
199+
; If the RHS GEP has unmatched variable indices, we cannot prove a constant
200+
; lower bound between GEPs.
201+
;
202+
; CHECK-DAG: MayAlias: i32* %a, i32* %b
203+
204+
define void @both_var_idx(ptr %p, i64 %i, i64 %j) {
205+
%a = getelementptr i8, ptr %p, i64 %i
206+
207+
%add = getelementptr nuw i8, ptr %p, i64 4
208+
%b = getelementptr nuw i8, ptr %add, i64 %j
209+
210+
load i32, ptr %a
211+
load i32, ptr %b
212+
213+
ret void
214+
}

0 commit comments

Comments
 (0)