Skip to content

Commit be98e95

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 2b077ed commit be98e95

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
@@ -552,9 +552,9 @@ struct BasicAAResult::DecomposedGEP {
552552
APInt Offset;
553553
// Scaled variable (non-constant) indices.
554554
SmallVector<VariableGEPIndex, 4> VarIndices;
555-
// Are all operations inbounds GEPs or non-indexing operations?
555+
// Nowrap flags common to all GEP operations involved in expression.
556556
// (std::nullopt iff expression doesn't involve any geps)
557-
std::optional<bool> InBounds;
557+
std::optional<GEPNoWrapFlags> NWFlags;
558558

559559
void dump() const {
560560
print(dbgs());
@@ -641,12 +641,11 @@ BasicAAResult::DecomposeGEPExpression(const Value *V, const DataLayout &DL,
641641
return Decomposed;
642642
}
643643

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

651650
assert(GEPOp->getSourceElementType()->isSized() && "GEP must be sized");
652651

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

11261125
if (isa<GEPOperator>(V2)) {
11271126
// Symmetric case to above.
1128-
if (*DecompGEP2.InBounds && DecompGEP1.VarIndices.empty() &&
1127+
if (DecompGEP2.NWFlags->isInBounds() && DecompGEP1.VarIndices.empty() &&
11291128
V1Size.hasValue() && !V1Size.isScalable() &&
11301129
DecompGEP1.Offset.sle(-V1Size.getValue()) &&
11311130
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)