Skip to content

Commit de89575

Browse files
authored
[CaptureTracking][AA] Only consider provenance captures (llvm#130777)
For the purposes of alias analysis, we should only consider provenance captures, not address captures. To support this, change (or add) CaptureTracking APIs to accept a Mask and StopFn argument. The Mask determines which components we are interested in (for AA that would be Provenance). The StopFn determines when we can abort the walk early. Currently, we want to do this as soon as any of the components in the Mask is captured. The purpose of making this a separate predicate is that in the future we will also want to distinguish between capturing full provenance and read-only provenance. In that case, we can only stop early once full provenance is captured. The earliest escape analysis does not get a StopFn, because it must always inspect all captures.
1 parent 55b480e commit de89575

File tree

7 files changed

+189
-69
lines changed

7 files changed

+189
-69
lines changed

llvm/include/llvm/Analysis/CaptureTracking.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ namespace llvm {
4444
bool PointerMayBeCaptured(const Value *V, bool ReturnCaptures,
4545
unsigned MaxUsesToExplore = 0);
4646

47+
/// Return which components of the pointer may be captured. Only consider
48+
/// components that are part of \p Mask. Once \p StopFn on the accumulated
49+
/// components returns true, the traversal is aborted early. By default, this
50+
/// happens when *any* of the components in \p Mask are captured.
51+
CaptureComponents PointerMayBeCaptured(
52+
const Value *V, bool ReturnCaptures, CaptureComponents Mask,
53+
function_ref<bool(CaptureComponents)> StopFn = capturesAnything,
54+
unsigned MaxUsesToExplore = 0);
55+
4756
/// PointerMayBeCapturedBefore - Return true if this pointer value may be
4857
/// captured by the enclosing function (which is required to exist). If a
4958
/// DominatorTree is provided, only captures which happen before the given
@@ -61,6 +70,17 @@ namespace llvm {
6170
unsigned MaxUsesToExplore = 0,
6271
const LoopInfo *LI = nullptr);
6372

73+
/// Return which components of the pointer may be captured on the path to
74+
/// \p I. Only consider components that are part of \p Mask. Once \p StopFn
75+
/// on the accumulated components returns true, the traversal is aborted
76+
/// early. By default, this happens when *any* of the components in \p Mask
77+
/// are captured.
78+
CaptureComponents PointerMayBeCapturedBefore(
79+
const Value *V, bool ReturnCaptures, const Instruction *I,
80+
const DominatorTree *DT, bool IncludeI, CaptureComponents Mask,
81+
function_ref<bool(CaptureComponents)> StopFn = capturesAnything,
82+
const LoopInfo *LI = nullptr, unsigned MaxUsesToExplore = 0);
83+
6484
// Returns the 'earliest' instruction that captures \p V in \F. An instruction
6585
// A is considered earlier than instruction B, if A dominates B. If 2 escapes
6686
// do not dominate each other, the terminator of the common dominator is
@@ -69,8 +89,11 @@ namespace llvm {
6989
// nullptr is returned. Note that the caller of the function has to ensure
7090
// that the instruction the result value is compared against is not in a
7191
// cycle.
92+
//
93+
// Only consider components that are part of \p Mask.
7294
Instruction *FindEarliestCapture(const Value *V, Function &F,
7395
bool ReturnCaptures, const DominatorTree &DT,
96+
CaptureComponents Mask,
7497
unsigned MaxUsesToExplore = 0);
7598

7699
/// Capture information for a specific Use.

llvm/include/llvm/Support/ModRef.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,10 @@ inline bool capturesFullProvenance(CaptureComponents CC) {
326326
return (CC & CaptureComponents::Provenance) == CaptureComponents::Provenance;
327327
}
328328

329+
inline bool capturesAnyProvenance(CaptureComponents CC) {
330+
return (CC & CaptureComponents::Provenance) != CaptureComponents::None;
331+
}
332+
329333
inline bool capturesAll(CaptureComponents CC) {
330334
return CC == CaptureComponents::All;
331335
}

llvm/lib/Analysis/AliasAnalysis.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -622,8 +622,9 @@ ModRefInfo AAResults::callCapturesBefore(const Instruction *I,
622622
if (!Call || Call == Object)
623623
return ModRefInfo::ModRef;
624624

625-
if (PointerMayBeCapturedBefore(Object, /* ReturnCaptures */ true, I, DT,
626-
/* include Object */ true))
625+
if (capturesAnything(PointerMayBeCapturedBefore(
626+
Object, /* ReturnCaptures */ true, I, DT,
627+
/* include Object */ true, CaptureComponents::Provenance)))
627628
return ModRefInfo::ModRef;
628629

629630
unsigned ArgNo = 0;
@@ -637,10 +638,11 @@ ModRefInfo AAResults::callCapturesBefore(const Instruction *I,
637638
if (!(*CI)->getType()->isPointerTy())
638639
continue;
639640

640-
// Make sure we still check captures(ret: address, provenance) arguments,
641-
// as these wouldn't be treated as a capture at the call-site.
641+
// Make sure we still check captures(ret: address, provenance) and
642+
// captures(address) arguments, as these wouldn't be treated as a capture
643+
// at the call-site.
642644
CaptureInfo Captures = Call->getCaptureInfo(ArgNo);
643-
if (!capturesNothing(Captures.getOtherComponents()))
645+
if (capturesAnyProvenance(Captures.getOtherComponents()))
644646
continue;
645647

646648
AliasResult AR =

llvm/lib/Analysis/BasicAliasAnalysis.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ bool EarliestEscapeAnalysis::isNotCapturedBefore(const Value *Object,
220220
if (Iter.second) {
221221
Instruction *EarliestCapture = FindEarliestCapture(
222222
Object, *const_cast<Function *>(DT.getRoot()->getParent()),
223-
/*ReturnCaptures=*/false, DT);
223+
/*ReturnCaptures=*/false, DT, CaptureComponents::Provenance);
224224
if (EarliestCapture)
225225
Inst2Obj[EarliestCapture].push_back(Object);
226226
Iter.first->second = EarliestCapture;

llvm/lib/Analysis/CaptureTracking.cpp

Lines changed: 81 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -73,28 +73,32 @@ bool CaptureTracker::isDereferenceableOrNull(Value *O, const DataLayout &DL) {
7373

7474
namespace {
7575
struct SimpleCaptureTracker : public CaptureTracker {
76-
explicit SimpleCaptureTracker(bool ReturnCaptures)
77-
: ReturnCaptures(ReturnCaptures) {}
76+
explicit SimpleCaptureTracker(bool ReturnCaptures, CaptureComponents Mask,
77+
function_ref<bool(CaptureComponents)> StopFn)
78+
: ReturnCaptures(ReturnCaptures), Mask(Mask), StopFn(StopFn) {}
7879

7980
void tooManyUses() override {
8081
LLVM_DEBUG(dbgs() << "Captured due to too many uses\n");
81-
Captured = true;
82+
CC = Mask;
8283
}
8384

8485
Action captured(const Use *U, UseCaptureInfo CI) override {
85-
// TODO(captures): Use UseCaptureInfo.
8686
if (isa<ReturnInst>(U->getUser()) && !ReturnCaptures)
8787
return ContinueIgnoringReturn;
8888

89-
LLVM_DEBUG(dbgs() << "Captured by: " << *U->getUser() << "\n");
89+
if (capturesNothing(CI.UseCC & Mask))
90+
return Continue;
9091

91-
Captured = true;
92-
return Stop;
92+
LLVM_DEBUG(dbgs() << "Captured by: " << *U->getUser() << "\n");
93+
CC |= CI.UseCC & Mask;
94+
return StopFn(CC) ? Stop : Continue;
9395
}
9496

9597
bool ReturnCaptures;
98+
CaptureComponents Mask;
99+
function_ref<bool(CaptureComponents)> StopFn;
96100

97-
bool Captured = false;
101+
CaptureComponents CC = CaptureComponents::None;
98102
};
99103

100104
/// Only find pointer captures which happen before the given instruction. Uses
@@ -104,11 +108,13 @@ struct SimpleCaptureTracker : public CaptureTracker {
104108
struct CapturesBefore : public CaptureTracker {
105109

106110
CapturesBefore(bool ReturnCaptures, const Instruction *I,
107-
const DominatorTree *DT, bool IncludeI, const LoopInfo *LI)
111+
const DominatorTree *DT, bool IncludeI, const LoopInfo *LI,
112+
CaptureComponents Mask,
113+
function_ref<bool(CaptureComponents)> StopFn)
108114
: BeforeHere(I), DT(DT), ReturnCaptures(ReturnCaptures),
109-
IncludeI(IncludeI), LI(LI) {}
115+
IncludeI(IncludeI), LI(LI), Mask(Mask), StopFn(StopFn) {}
110116

111-
void tooManyUses() override { Captured = true; }
117+
void tooManyUses() override { CC = Mask; }
112118

113119
bool isSafeToPrune(Instruction *I) {
114120
if (BeforeHere == I)
@@ -124,7 +130,6 @@ struct CapturesBefore : public CaptureTracker {
124130
}
125131

126132
Action captured(const Use *U, UseCaptureInfo CI) override {
127-
// TODO(captures): Use UseCaptureInfo.
128133
Instruction *I = cast<Instruction>(U->getUser());
129134
if (isa<ReturnInst>(I) && !ReturnCaptures)
130135
return ContinueIgnoringReturn;
@@ -136,8 +141,11 @@ struct CapturesBefore : public CaptureTracker {
136141
// If the use is not reachable, the instruction result isn't either.
137142
return ContinueIgnoringReturn;
138143

139-
Captured = true;
140-
return Stop;
144+
if (capturesNothing(CI.UseCC & Mask))
145+
return Continue;
146+
147+
CC |= CI.UseCC & Mask;
148+
return StopFn(CC) ? Stop : Continue;
141149
}
142150

143151
const Instruction *BeforeHere;
@@ -146,9 +154,11 @@ struct CapturesBefore : public CaptureTracker {
146154
bool ReturnCaptures;
147155
bool IncludeI;
148156

149-
bool Captured = false;
157+
CaptureComponents CC = CaptureComponents::None;
150158

151159
const LoopInfo *LI;
160+
CaptureComponents Mask;
161+
function_ref<bool(CaptureComponents)> StopFn;
152162
};
153163

154164
/// Find the 'earliest' instruction before which the pointer is known not to
@@ -161,104 +171,110 @@ struct CapturesBefore : public CaptureTracker {
161171
// escape are not in a cycle.
162172
struct EarliestCaptures : public CaptureTracker {
163173

164-
EarliestCaptures(bool ReturnCaptures, Function &F, const DominatorTree &DT)
165-
: DT(DT), ReturnCaptures(ReturnCaptures), F(F) {}
174+
EarliestCaptures(bool ReturnCaptures, Function &F, const DominatorTree &DT,
175+
CaptureComponents Mask)
176+
: DT(DT), ReturnCaptures(ReturnCaptures), F(F), Mask(Mask) {}
166177

167178
void tooManyUses() override {
168-
Captured = true;
179+
CC = Mask;
169180
EarliestCapture = &*F.getEntryBlock().begin();
170181
}
171182

172183
Action captured(const Use *U, UseCaptureInfo CI) override {
173-
// TODO(captures): Use UseCaptureInfo.
174184
Instruction *I = cast<Instruction>(U->getUser());
175185
if (isa<ReturnInst>(I) && !ReturnCaptures)
176186
return ContinueIgnoringReturn;
177187

178-
if (!EarliestCapture)
179-
EarliestCapture = I;
180-
else
181-
EarliestCapture = DT.findNearestCommonDominator(EarliestCapture, I);
182-
Captured = true;
188+
if (capturesAnything(CI.UseCC & Mask)) {
189+
if (!EarliestCapture)
190+
EarliestCapture = I;
191+
else
192+
EarliestCapture = DT.findNearestCommonDominator(EarliestCapture, I);
193+
CC |= CI.UseCC & Mask;
194+
}
183195

184-
// Continue analysis, as we need to see all potential captures. However,
185-
// we do not need to follow the instruction result, as this use will
186-
// dominate any captures made through the instruction result.
187-
return ContinueIgnoringReturn;
196+
// Continue analysis, as we need to see all potential captures.
197+
return Continue;
188198
}
189199

190-
Instruction *EarliestCapture = nullptr;
191-
192200
const DominatorTree &DT;
193-
194201
bool ReturnCaptures;
195-
196-
bool Captured = false;
197-
198202
Function &F;
203+
CaptureComponents Mask;
204+
205+
Instruction *EarliestCapture = nullptr;
206+
CaptureComponents CC = CaptureComponents::None;
199207
};
200208
} // namespace
201209

202-
/// PointerMayBeCaptured - Return true if this pointer value may be captured
203-
/// by the enclosing function (which is required to exist). This routine can
204-
/// be expensive, so consider caching the results. The boolean ReturnCaptures
205-
/// specifies whether returning the value (or part of it) from the function
206-
/// counts as capturing it or not.
207-
bool llvm::PointerMayBeCaptured(const Value *V, bool ReturnCaptures,
208-
unsigned MaxUsesToExplore) {
210+
CaptureComponents llvm::PointerMayBeCaptured(
211+
const Value *V, bool ReturnCaptures, CaptureComponents Mask,
212+
function_ref<bool(CaptureComponents)> StopFn, unsigned MaxUsesToExplore) {
209213
assert(!isa<GlobalValue>(V) &&
210214
"It doesn't make sense to ask whether a global is captured.");
211215

212216
LLVM_DEBUG(dbgs() << "Captured?: " << *V << " = ");
213217

214-
SimpleCaptureTracker SCT(ReturnCaptures);
218+
SimpleCaptureTracker SCT(ReturnCaptures, Mask, StopFn);
215219
PointerMayBeCaptured(V, &SCT, MaxUsesToExplore);
216-
if (SCT.Captured)
220+
if (capturesAnything(SCT.CC))
217221
++NumCaptured;
218222
else {
219223
++NumNotCaptured;
220224
LLVM_DEBUG(dbgs() << "not captured\n");
221225
}
222-
return SCT.Captured;
226+
return SCT.CC;
223227
}
224228

225-
/// PointerMayBeCapturedBefore - Return true if this pointer value may be
226-
/// captured by the enclosing function (which is required to exist). If a
227-
/// DominatorTree is provided, only captures which happen before the given
228-
/// instruction are considered. This routine can be expensive, so consider
229-
/// caching the results. The boolean ReturnCaptures specifies whether
230-
/// returning the value (or part of it) from the function counts as capturing
231-
/// it or not.
232-
bool llvm::PointerMayBeCapturedBefore(const Value *V, bool ReturnCaptures,
233-
const Instruction *I,
234-
const DominatorTree *DT, bool IncludeI,
235-
unsigned MaxUsesToExplore,
236-
const LoopInfo *LI) {
229+
bool llvm::PointerMayBeCaptured(const Value *V, bool ReturnCaptures,
230+
unsigned MaxUsesToExplore) {
231+
return capturesAnything(
232+
PointerMayBeCaptured(V, ReturnCaptures, CaptureComponents::All,
233+
capturesAnything, MaxUsesToExplore));
234+
}
235+
236+
CaptureComponents llvm::PointerMayBeCapturedBefore(
237+
const Value *V, bool ReturnCaptures, const Instruction *I,
238+
const DominatorTree *DT, bool IncludeI, CaptureComponents Mask,
239+
function_ref<bool(CaptureComponents)> StopFn, const LoopInfo *LI,
240+
unsigned MaxUsesToExplore) {
237241
assert(!isa<GlobalValue>(V) &&
238242
"It doesn't make sense to ask whether a global is captured.");
239243

240244
if (!DT)
241-
return PointerMayBeCaptured(V, ReturnCaptures, MaxUsesToExplore);
245+
return PointerMayBeCaptured(V, ReturnCaptures, Mask, StopFn,
246+
MaxUsesToExplore);
242247

243-
CapturesBefore CB(ReturnCaptures, I, DT, IncludeI, LI);
248+
CapturesBefore CB(ReturnCaptures, I, DT, IncludeI, LI, Mask, StopFn);
244249
PointerMayBeCaptured(V, &CB, MaxUsesToExplore);
245-
if (CB.Captured)
250+
if (capturesAnything(CB.CC))
246251
++NumCapturedBefore;
247252
else
248253
++NumNotCapturedBefore;
249-
return CB.Captured;
254+
return CB.CC;
255+
}
256+
257+
bool llvm::PointerMayBeCapturedBefore(const Value *V, bool ReturnCaptures,
258+
const Instruction *I,
259+
const DominatorTree *DT, bool IncludeI,
260+
unsigned MaxUsesToExplore,
261+
const LoopInfo *LI) {
262+
return capturesAnything(PointerMayBeCapturedBefore(
263+
V, ReturnCaptures, I, DT, IncludeI, CaptureComponents::All,
264+
capturesAnything, LI, MaxUsesToExplore));
250265
}
251266

252267
Instruction *llvm::FindEarliestCapture(const Value *V, Function &F,
253268
bool ReturnCaptures,
254269
const DominatorTree &DT,
270+
CaptureComponents Mask,
255271
unsigned MaxUsesToExplore) {
256272
assert(!isa<GlobalValue>(V) &&
257273
"It doesn't make sense to ask whether a global is captured.");
258274

259-
EarliestCaptures CB(ReturnCaptures, F, DT);
275+
EarliestCaptures CB(ReturnCaptures, F, DT, Mask);
260276
PointerMayBeCaptured(V, &CB, MaxUsesToExplore);
261-
if (CB.Captured)
277+
if (capturesAnything(CB.CC))
262278
++NumCapturedBefore;
263279
else
264280
++NumNotCapturedBefore;
@@ -473,8 +489,10 @@ bool llvm::isNonEscapingLocalObject(
473489
}
474490

475491
// If this is an identified function-local object, check to see if it escapes.
492+
// We only care about provenance here, not address capture.
476493
if (isIdentifiedFunctionLocal(V)) {
477-
auto Ret = !PointerMayBeCaptured(V, /*ReturnCaptures=*/false);
494+
bool Ret = !capturesAnything(PointerMayBeCaptured(
495+
V, /*ReturnCaptures=*/false, CaptureComponents::Provenance));
478496
if (IsCapturedCache)
479497
CacheIt->second = Ret;
480498
return Ret;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
; RUN: opt < %s -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
2+
3+
declare void @capture(ptr)
4+
declare ptr @get_ptr()
5+
6+
; CHECK-LABEL: address_capture
7+
; CHECK: NoAlias: i32* %a, i32* %p
8+
; CHECK: NoModRef: Ptr: i32* %a <-> %p = call ptr @get_ptr()
9+
define void @address_capture() {
10+
%a = alloca i32
11+
call void @capture(ptr captures(address) %a)
12+
%p = call ptr @get_ptr()
13+
store i32 0, ptr %p
14+
load i32, ptr %a
15+
ret void
16+
}
17+
18+
; CHECK-LABEL: read_only_capture
19+
; CHECK: MayAlias: i32* %a, i32* %p
20+
; CHECK: Both ModRef: Ptr: i32* %a <-> %p = call ptr @get_ptr()
21+
; TODO: The ModRef could be just Ref.
22+
define void @read_only_capture() {
23+
%a = alloca i32
24+
call void @capture(ptr captures(address, read_provenance) %a)
25+
%p = call ptr @get_ptr()
26+
store i32 0, ptr %p
27+
load i32, ptr %a
28+
ret void
29+
}
30+
31+
; CHECK-LABEL: address_capture_and_full_capture
32+
; CHECK: MayAlias: i32* %a, i32* %p
33+
; CHECK: Both ModRef: Ptr: i32* %a <-> %p = call ptr @get_ptr()
34+
define void @address_capture_and_full_capture() {
35+
%a = alloca i32
36+
call void @capture(ptr captures(address) %a)
37+
call void @capture(ptr %a)
38+
%p = call ptr @get_ptr()
39+
store i32 0, ptr %p
40+
load i32, ptr %a
41+
ret void
42+
}

0 commit comments

Comments
 (0)