Skip to content

Commit 9a09c73

Browse files
authored
[BasicAA] Make isNotCapturedBeforeOrAt() check for calls more precise (#69931)
For calls, we are only interested in captures before the call, not captures by the call itself -- arguments that get passed to the call are checked explicitly. In particular, the current implementation is not optimal if the pointer is captured via a readonly argument -- in that case, we know that even if the argument is captured, the call will not modify the argument (at least not via that argument). Make this more precise by renaming to isCapturedBefore() and adding an OrAt argument that allows us to toggle whether to consider captures in the instruction itself or not.
1 parent 68386a7 commit 9a09c73

File tree

3 files changed

+33
-23
lines changed

3 files changed

+33
-23
lines changed

llvm/include/llvm/Analysis/AliasAnalysis.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,11 @@ raw_ostream &operator<<(raw_ostream &OS, AliasResult AR);
152152
/// Virtual base class for providers of capture information.
153153
struct CaptureInfo {
154154
virtual ~CaptureInfo() = 0;
155-
virtual bool isNotCapturedBeforeOrAt(const Value *Object,
156-
const Instruction *I) = 0;
155+
156+
/// Check whether Object is not captured before instruction I. If OrAt is
157+
/// true, captures by instruction I itself are also considered.
158+
virtual bool isNotCapturedBefore(const Value *Object, const Instruction *I,
159+
bool OrAt) = 0;
157160
};
158161

159162
/// Context-free CaptureInfo provider, which computes and caches whether an
@@ -163,8 +166,8 @@ class SimpleCaptureInfo final : public CaptureInfo {
163166
SmallDenseMap<const Value *, bool, 8> IsCapturedCache;
164167

165168
public:
166-
bool isNotCapturedBeforeOrAt(const Value *Object,
167-
const Instruction *I) override;
169+
bool isNotCapturedBefore(const Value *Object, const Instruction *I,
170+
bool OrAt) override;
168171
};
169172

170173
/// Context-sensitive CaptureInfo provider, which computes and caches the
@@ -188,8 +191,8 @@ class EarliestEscapeInfo final : public CaptureInfo {
188191
EarliestEscapeInfo(DominatorTree &DT, const LoopInfo *LI = nullptr)
189192
: DT(DT), LI(LI) {}
190193

191-
bool isNotCapturedBeforeOrAt(const Value *Object,
192-
const Instruction *I) override;
194+
bool isNotCapturedBefore(const Value *Object, const Instruction *I,
195+
bool OrAt) override;
193196

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

llvm/lib/Analysis/BasicAliasAnalysis.cpp

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,21 @@ static bool isObjectSize(const Value *V, TypeSize Size, const DataLayout &DL,
194194

195195
CaptureInfo::~CaptureInfo() = default;
196196

197-
bool SimpleCaptureInfo::isNotCapturedBeforeOrAt(const Value *Object,
198-
const Instruction *I) {
197+
bool SimpleCaptureInfo::isNotCapturedBefore(const Value *Object,
198+
const Instruction *I, bool OrAt) {
199199
return isNonEscapingLocalObject(Object, &IsCapturedCache);
200200
}
201201

202-
bool EarliestEscapeInfo::isNotCapturedBeforeOrAt(const Value *Object,
203-
const Instruction *I) {
202+
static bool isNotInCycle(const Instruction *I, const DominatorTree &DT,
203+
const LoopInfo *LI) {
204+
BasicBlock *BB = const_cast<BasicBlock *>(I->getParent());
205+
SmallVector<BasicBlock *> Succs(successors(BB));
206+
return Succs.empty() ||
207+
!isPotentiallyReachableFromMany(Succs, BB, nullptr, &DT, LI);
208+
}
209+
210+
bool EarliestEscapeInfo::isNotCapturedBefore(const Value *Object,
211+
const Instruction *I, bool OrAt) {
204212
if (!isIdentifiedFunctionLocal(Object))
205213
return false;
206214

@@ -220,8 +228,13 @@ bool EarliestEscapeInfo::isNotCapturedBeforeOrAt(const Value *Object,
220228
if (!Iter.first->second)
221229
return true;
222230

223-
return I != Iter.first->second &&
224-
!isPotentiallyReachable(Iter.first->second, I, nullptr, &DT, LI);
231+
if (I == Iter.first->second) {
232+
if (OrAt)
233+
return false;
234+
return isNotInCycle(I, DT, LI);
235+
}
236+
237+
return !isPotentiallyReachable(Iter.first->second, I, nullptr, &DT, LI);
225238
}
226239

227240
void EarliestEscapeInfo::removeInstruction(Instruction *I) {
@@ -887,7 +900,7 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call,
887900
// Make sure the object has not escaped here, and then check that none of the
888901
// call arguments alias the object below.
889902
if (!isa<Constant>(Object) && Call != Object &&
890-
AAQI.CI->isNotCapturedBeforeOrAt(Object, Call)) {
903+
AAQI.CI->isNotCapturedBefore(Object, Call, /*OrAt*/ false)) {
891904

892905
// Optimistically assume that call doesn't touch Object and check this
893906
// assumption in the following loop.
@@ -1512,10 +1525,10 @@ AliasResult BasicAAResult::aliasCheck(const Value *V1, LocationSize V1Size,
15121525
// location if that memory location doesn't escape. Or it may pass a
15131526
// nocapture value to other functions as long as they don't capture it.
15141527
if (isEscapeSource(O1) &&
1515-
AAQI.CI->isNotCapturedBeforeOrAt(O2, cast<Instruction>(O1)))
1528+
AAQI.CI->isNotCapturedBefore(O2, cast<Instruction>(O1), /*OrAt*/ true))
15161529
return AliasResult::NoAlias;
15171530
if (isEscapeSource(O2) &&
1518-
AAQI.CI->isNotCapturedBeforeOrAt(O1, cast<Instruction>(O2)))
1531+
AAQI.CI->isNotCapturedBefore(O1, cast<Instruction>(O2), /*OrAt*/ true))
15191532
return AliasResult::NoAlias;
15201533
}
15211534

@@ -1708,12 +1721,7 @@ bool BasicAAResult::isValueEqualInPotentialCycles(const Value *V,
17081721
if (!Inst || Inst->getParent()->isEntryBlock())
17091722
return true;
17101723

1711-
// Check whether the instruction is part of a cycle, by checking whether the
1712-
// block can (non-trivially) reach itself.
1713-
BasicBlock *BB = const_cast<BasicBlock *>(Inst->getParent());
1714-
SmallVector<BasicBlock *> Succs(successors(BB));
1715-
return !Succs.empty() &&
1716-
!isPotentiallyReachableFromMany(Succs, BB, nullptr, DT);
1724+
return isNotInCycle(Inst, *DT, /*LI*/ nullptr);
17171725
}
17181726

17191727
/// Computes the symbolic difference between two de-composed GEPs.

llvm/test/Transforms/GVN/captured-before.ll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,7 @@ define i32 @test_capture_readonly() {
8383
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
8484
; CHECK-NEXT: store i32 123, ptr [[A]], align 4
8585
; CHECK-NEXT: call void @capture(ptr readonly [[A]])
86-
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[A]], align 4
87-
; CHECK-NEXT: ret i32 [[V]]
86+
; CHECK-NEXT: ret i32 123
8887
;
8988
%a = alloca i32
9089
store i32 123, ptr %a

0 commit comments

Comments
 (0)