Skip to content

Commit bc7fafb

Browse files
authored
[AA] Take read-only provenance captures into account (#143097)
Update the AA CaptureAnalysis providers to return CaptureComponents, so we can distinguish between full provenance and read-only provenance captures. Use this to restrict "other" memory effects on call from ModRef to Ref. Ideally we would also apply the same reasoning for escape sources, but the current API cannot actually convey the necessary information (we can only say NoAlias or MayAlias, not MayAlias but only via Ref).
1 parent 2ecbfc0 commit bc7fafb

File tree

7 files changed

+89
-72
lines changed

7 files changed

+89
-72
lines changed

llvm/include/llvm/Analysis/AliasAnalysis.h

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -149,23 +149,24 @@ LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, AliasResult AR);
149149
struct LLVM_ABI CaptureAnalysis {
150150
virtual ~CaptureAnalysis() = 0;
151151

152-
/// Check whether Object is not captured before instruction I. If OrAt is
153-
/// true, captures by instruction I itself are also considered.
152+
/// Return how Object may be captured before instruction I, considering only
153+
/// provenance captures. If OrAt is true, captures by instruction I itself
154+
/// are also considered.
154155
///
155156
/// If I is nullptr, then captures at any point will be considered.
156-
virtual bool isNotCapturedBefore(const Value *Object, const Instruction *I,
157-
bool OrAt) = 0;
157+
virtual CaptureComponents
158+
getCapturesBefore(const Value *Object, const Instruction *I, bool OrAt) = 0;
158159
};
159160

160161
/// Context-free CaptureAnalysis provider, which computes and caches whether an
161162
/// object is captured in the function at all, but does not distinguish whether
162163
/// it was captured before or after the context instruction.
163164
class LLVM_ABI SimpleCaptureAnalysis final : public CaptureAnalysis {
164-
SmallDenseMap<const Value *, bool, 8> IsCapturedCache;
165+
SmallDenseMap<const Value *, CaptureComponents, 8> IsCapturedCache;
165166

166167
public:
167-
bool isNotCapturedBefore(const Value *Object, const Instruction *I,
168-
bool OrAt) override;
168+
CaptureComponents getCapturesBefore(const Value *Object, const Instruction *I,
169+
bool OrAt) override;
169170
};
170171

171172
/// Context-sensitive CaptureAnalysis provider, which computes and caches the
@@ -176,10 +177,12 @@ class LLVM_ABI EarliestEscapeAnalysis final : public CaptureAnalysis {
176177
const LoopInfo *LI;
177178

178179
/// Map from identified local object to an instruction before which it does
179-
/// not escape, or nullptr if it never escapes. The "earliest" instruction
180-
/// may be a conservative approximation, e.g. the first instruction in the
181-
/// function is always a legal choice.
182-
DenseMap<const Value *, Instruction *> EarliestEscapes;
180+
/// not escape (or nullptr if it never escapes) and the possible components
181+
/// that may be captured (by any instruction, not necessarily the earliest
182+
/// one). The "earliest" instruction may be a conservative approximation,
183+
/// e.g. the first instruction in the function is always a legal choice.
184+
DenseMap<const Value *, std::pair<Instruction *, CaptureComponents>>
185+
EarliestEscapes;
183186

184187
/// Reverse map from instruction to the objects it is the earliest escape for.
185188
/// This is used for cache invalidation purposes.
@@ -189,8 +192,8 @@ class LLVM_ABI EarliestEscapeAnalysis final : public CaptureAnalysis {
189192
EarliestEscapeAnalysis(DominatorTree &DT, const LoopInfo *LI = nullptr)
190193
: DT(DT), LI(LI) {}
191194

192-
bool isNotCapturedBefore(const Value *Object, const Instruction *I,
193-
bool OrAt) override;
195+
CaptureComponents getCapturesBefore(const Value *Object, const Instruction *I,
196+
bool OrAt) override;
194197

195198
void removeInstruction(Instruction *I);
196199
};

llvm/include/llvm/Analysis/CaptureTracking.h

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -95,21 +95,21 @@ namespace llvm {
9595
function_ref<bool(CaptureComponents)> StopFn = capturesAnything,
9696
const LoopInfo *LI = nullptr, unsigned MaxUsesToExplore = 0);
9797

98-
// Returns the 'earliest' instruction that captures \p V in \F. An instruction
99-
// A is considered earlier than instruction B, if A dominates B. If 2 escapes
100-
// do not dominate each other, the terminator of the common dominator is
101-
// chosen. If not all uses can be analyzed, the earliest escape is set to
102-
// the first instruction in the function entry block. If \p V does not escape,
103-
// nullptr is returned. Note that the caller of the function has to ensure
104-
// that the instruction the result value is compared against is not in a
105-
// cycle.
98+
// Returns the 'earliest' instruction that captures \p V in \F, and which
99+
// components may be captured (by any use, not necessarily the earliest one).
100+
// An instruction A is considered earlier than instruction B, if A dominates
101+
// B. If 2 escapes do not dominate each other, the terminator of the common
102+
// dominator is chosen. If not all uses can be analyzed, the earliest escape
103+
// is set to the first instruction in the function entry block. If \p V does
104+
// not escape, nullptr is returned. Note that the caller of the function has
105+
// to ensure that the instruction the result value is compared against is
106+
// not in a cycle.
106107
//
107108
// Only consider components that are part of \p Mask.
108-
LLVM_ABI Instruction *FindEarliestCapture(const Value *V, Function &F,
109-
bool ReturnCaptures,
110-
const DominatorTree &DT,
111-
CaptureComponents Mask,
112-
unsigned MaxUsesToExplore = 0);
109+
LLVM_ABI std::pair<Instruction *, CaptureComponents>
110+
FindEarliestCapture(const Value *V, Function &F, bool ReturnCaptures,
111+
const DominatorTree &DT, CaptureComponents Mask,
112+
unsigned MaxUsesToExplore = 0);
113113

114114
/// Capture information for a specific Use.
115115
struct UseCaptureInfo {

llvm/lib/Analysis/BasicAliasAnalysis.cpp

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -192,18 +192,20 @@ static bool areBothVScale(const Value *V1, const Value *V2) {
192192

193193
CaptureAnalysis::~CaptureAnalysis() = default;
194194

195-
bool SimpleCaptureAnalysis::isNotCapturedBefore(const Value *Object,
196-
const Instruction *I,
197-
bool OrAt) {
195+
CaptureComponents SimpleCaptureAnalysis::getCapturesBefore(const Value *Object,
196+
const Instruction *I,
197+
bool OrAt) {
198198
if (!isIdentifiedFunctionLocal(Object))
199-
return false;
199+
return CaptureComponents::Provenance;
200200

201-
auto [CacheIt, Inserted] = IsCapturedCache.insert({Object, false});
201+
auto [CacheIt, Inserted] =
202+
IsCapturedCache.insert({Object, CaptureComponents::Provenance});
202203
if (!Inserted)
203204
return CacheIt->second;
204205

205-
bool Ret = !capturesAnything(PointerMayBeCaptured(
206-
Object, /*ReturnCaptures=*/false, CaptureComponents::Provenance));
206+
CaptureComponents Ret = PointerMayBeCaptured(
207+
Object, /*ReturnCaptures=*/false, CaptureComponents::Provenance,
208+
[](CaptureComponents CC) { return capturesFullProvenance(CC); });
207209
CacheIt->second = Ret;
208210
return Ret;
209211
}
@@ -216,37 +218,44 @@ static bool isNotInCycle(const Instruction *I, const DominatorTree *DT,
216218
!isPotentiallyReachableFromMany(Succs, BB, nullptr, DT, LI);
217219
}
218220

219-
bool EarliestEscapeAnalysis::isNotCapturedBefore(const Value *Object,
220-
const Instruction *I,
221-
bool OrAt) {
221+
CaptureComponents
222+
EarliestEscapeAnalysis::getCapturesBefore(const Value *Object,
223+
const Instruction *I, bool OrAt) {
222224
if (!isIdentifiedFunctionLocal(Object))
223-
return false;
225+
return CaptureComponents::Provenance;
224226

225227
auto Iter = EarliestEscapes.try_emplace(Object);
226228
if (Iter.second) {
227-
Instruction *EarliestCapture = FindEarliestCapture(
228-
Object, *const_cast<Function *>(DT.getRoot()->getParent()),
229-
/*ReturnCaptures=*/false, DT, CaptureComponents::Provenance);
230-
if (EarliestCapture)
231-
Inst2Obj[EarliestCapture].push_back(Object);
229+
std::pair<Instruction *, CaptureComponents> EarliestCapture =
230+
FindEarliestCapture(
231+
Object, *const_cast<Function *>(DT.getRoot()->getParent()),
232+
/*ReturnCaptures=*/false, DT, CaptureComponents::Provenance);
233+
if (EarliestCapture.first)
234+
Inst2Obj[EarliestCapture.first].push_back(Object);
232235
Iter.first->second = EarliestCapture;
233236
}
234237

235-
// No capturing instruction.
236-
if (!Iter.first->second)
237-
return true;
238-
239-
// No context instruction means any use is capturing.
240-
if (!I)
241-
return false;
238+
auto IsNotCapturedBefore = [&]() {
239+
// No capturing instruction.
240+
Instruction *CaptureInst = Iter.first->second.first;
241+
if (!CaptureInst)
242+
return true;
242243

243-
if (I == Iter.first->second) {
244-
if (OrAt)
244+
// No context instruction means any use is capturing.
245+
if (!I)
245246
return false;
246-
return isNotInCycle(I, &DT, LI);
247-
}
248247

249-
return !isPotentiallyReachable(Iter.first->second, I, nullptr, &DT, LI);
248+
if (I == CaptureInst) {
249+
if (OrAt)
250+
return false;
251+
return isNotInCycle(I, &DT, LI);
252+
}
253+
254+
return !isPotentiallyReachable(CaptureInst, I, nullptr, &DT, LI);
255+
};
256+
if (IsNotCapturedBefore())
257+
return CaptureComponents::None;
258+
return Iter.first->second.second;
250259
}
251260

252261
void EarliestEscapeAnalysis::removeInstruction(Instruction *I) {
@@ -946,9 +955,14 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call,
946955
// As an exception, ignore allocas, as setjmp is not required to preserve
947956
// non-volatile stores for them.
948957
if (isModOrRefSet(OtherMR) && !isa<Constant>(Object) && Call != Object &&
949-
AAQI.CA->isNotCapturedBefore(Object, Call, /*OrAt=*/false) &&
950-
(isa<AllocaInst>(Object) || !Call->hasFnAttr(Attribute::ReturnsTwice)))
951-
OtherMR = ModRefInfo::NoModRef;
958+
(isa<AllocaInst>(Object) || !Call->hasFnAttr(Attribute::ReturnsTwice))) {
959+
CaptureComponents CC =
960+
AAQI.CA->getCapturesBefore(Object, Call, /*OrAt=*/false);
961+
if (capturesNothing(CC))
962+
OtherMR = ModRefInfo::NoModRef;
963+
else if (capturesReadProvenanceOnly(CC))
964+
OtherMR = ModRefInfo::Ref;
965+
}
952966

953967
// Refine the modref info for argument memory. We only bother to do this
954968
// if ArgMR is not a subset of OtherMR, otherwise this won't have an impact
@@ -1614,11 +1628,13 @@ AliasResult BasicAAResult::aliasCheck(const Value *V1, LocationSize V1Size,
16141628
// temporary store the nocapture argument's value in a temporary memory
16151629
// location if that memory location doesn't escape. Or it may pass a
16161630
// nocapture value to other functions as long as they don't capture it.
1617-
if (isEscapeSource(O1) && AAQI.CA->isNotCapturedBefore(
1618-
O2, dyn_cast<Instruction>(O1), /*OrAt*/ true))
1631+
if (isEscapeSource(O1) &&
1632+
capturesNothing(AAQI.CA->getCapturesBefore(
1633+
O2, dyn_cast<Instruction>(O1), /*OrAt*/ true)))
16191634
return AliasResult::NoAlias;
1620-
if (isEscapeSource(O2) && AAQI.CA->isNotCapturedBefore(
1621-
O1, dyn_cast<Instruction>(O2), /*OrAt*/ true))
1635+
if (isEscapeSource(O2) &&
1636+
capturesNothing(AAQI.CA->getCapturesBefore(
1637+
O1, dyn_cast<Instruction>(O2), /*OrAt*/ true)))
16221638
return AliasResult::NoAlias;
16231639
}
16241640

llvm/lib/Analysis/CaptureTracking.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,10 @@ bool llvm::PointerMayBeCapturedBefore(const Value *V, bool ReturnCaptures,
249249
capturesAnything, LI, MaxUsesToExplore));
250250
}
251251

252-
Instruction *llvm::FindEarliestCapture(const Value *V, Function &F,
253-
bool ReturnCaptures,
254-
const DominatorTree &DT,
255-
CaptureComponents Mask,
256-
unsigned MaxUsesToExplore) {
252+
std::pair<Instruction *, CaptureComponents>
253+
llvm::FindEarliestCapture(const Value *V, Function &F, bool ReturnCaptures,
254+
const DominatorTree &DT, CaptureComponents Mask,
255+
unsigned MaxUsesToExplore) {
257256
assert(!isa<GlobalValue>(V) &&
258257
"It doesn't make sense to ask whether a global is captured.");
259258

@@ -263,7 +262,7 @@ Instruction *llvm::FindEarliestCapture(const Value *V, Function &F,
263262
++NumCapturedBefore;
264263
else
265264
++NumNotCapturedBefore;
266-
return CB.EarliestCapture;
265+
return {CB.EarliestCapture, CB.CC};
267266
}
268267

269268
UseCaptureInfo llvm::DetermineUseCaptureKind(const Use &U, const Value *Base) {

llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2345,7 +2345,8 @@ bool isFuncLocalAndNotCaptured(Value *Arg, const CallBase *CB,
23452345
EarliestEscapeAnalysis &EA) {
23462346
const Value *UnderlyingObj = getUnderlyingObject(Arg);
23472347
return isIdentifiedFunctionLocal(UnderlyingObj) &&
2348-
EA.isNotCapturedBefore(UnderlyingObj, CB, /*OrAt*/ true);
2348+
capturesNothing(
2349+
EA.getCapturesBefore(UnderlyingObj, CB, /*OrAt*/ true));
23492350
}
23502351

23512352
SmallVector<MemoryLocation, 1>

llvm/test/Analysis/BasicAA/captures.ll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ define void @address_capture() {
1717

1818
; CHECK-LABEL: read_only_capture
1919
; 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.
20+
; CHECK: Just Ref: Ptr: i32* %a <-> %p = call ptr @get_ptr()
2221
define void @read_only_capture() {
2322
%a = alloca i32
2423
call void @capture(ptr captures(address, read_provenance) %a)

llvm/test/Transforms/GVN/captures.ll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ define i32 @read_provenance_capture() {
4343
; CHECK-NEXT: call void @capture(ptr captures(address, read_provenance) [[A]])
4444
; CHECK-NEXT: store i32 1, ptr [[A]], align 4
4545
; CHECK-NEXT: call void @unknown_call()
46-
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[A]], align 4
47-
; CHECK-NEXT: ret i32 [[V]]
46+
; CHECK-NEXT: ret i32 1
4847
;
4948
%a = alloca i32
5049
call void @capture(ptr captures(address, read_provenance) %a)

0 commit comments

Comments
 (0)