[llvm] [CaptureTracking] Compute results with and without return capture (PR #195857)
via llvm-commits
llvm-commits at lists.llvm.org
Tue May 5 06:47:53 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-analysis
Author: Nikita Popov (nikic)
<details>
<summary>Changes</summary>
Currently PointerMayBeCaptured/FindEarliestEscape accept a ReturnCaptures argument to determine whether returning the pointer should be considered a capture. If you want to do a capture check for both cases, you have to invoke the capture tracking API twice.
This PR instead changes the low level APIs to return a pair of capture components, one where returns are considered non-capturing, and one where they are considered capturing.
This is for use by https://github.com/llvm/llvm-project/pull/193939, where AA wants both our usual captures-before (ignoring returns) reasoning, and captures-anywhere (including returns). This will allow us to do this in one (cached) query.
---
Full diff: https://github.com/llvm/llvm-project/pull/195857.diff
5 Files Affected:
- (modified) llvm/include/llvm/Analysis/CaptureTracking.h (+13-6)
- (modified) llvm/lib/Analysis/BasicAliasAnalysis.cpp (+9-7)
- (modified) llvm/lib/Analysis/CaptureTracking.cpp (+37-36)
- (modified) llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp (+4-4)
- (modified) llvm/lib/Transforms/Utils/SimplifyCFG.cpp (+2-2)
``````````diff
diff --git a/llvm/include/llvm/Analysis/CaptureTracking.h b/llvm/include/llvm/Analysis/CaptureTracking.h
index e652bc5a0a5a6..ca9d1c53de3ef 100644
--- a/llvm/include/llvm/Analysis/CaptureTracking.h
+++ b/llvm/include/llvm/Analysis/CaptureTracking.h
@@ -48,6 +48,14 @@ namespace llvm {
LLVM_ABI bool PointerMayBeCaptured(const Value *V, bool ReturnCaptures,
unsigned MaxUsesToExplore = 0);
+ /// Result of a PointerMayBeCaptured query, which includes the captured
+ /// components for both the case where return is considered a capture, and
+ /// where it isn't.
+ struct CaptureResult {
+ CaptureComponents WithoutRet;
+ CaptureComponents WithRet;
+ };
+
/// Return which components of the pointer may be captured. Only consider
/// components that are part of \p Mask. Once \p StopFn on the accumulated
/// components returns true, the traversal is aborted early. By default, this
@@ -55,8 +63,8 @@ namespace llvm {
/// This function only considers captures of the passed value via its def-use
/// chain, without considering captures of values it may be based on, or
/// implicit captures such as for external globals.
- LLVM_ABI CaptureComponents PointerMayBeCaptured(
- const Value *V, bool ReturnCaptures, CaptureComponents Mask,
+ LLVM_ABI CaptureResult PointerMayBeCaptured(
+ const Value *V, CaptureComponents Mask,
function_ref<bool(CaptureComponents)> StopFn = capturesAnything,
unsigned MaxUsesToExplore = 0);
@@ -106,10 +114,9 @@ namespace llvm {
// not in a cycle.
//
// Only consider components that are part of \p Mask.
- LLVM_ABI std::pair<Instruction *, CaptureComponents>
- FindEarliestCapture(const Value *V, Function &F, bool ReturnCaptures,
- const DominatorTree &DT, CaptureComponents Mask,
- unsigned MaxUsesToExplore = 0);
+ LLVM_ABI std::pair<Instruction *, CaptureResult>
+ FindEarliestCapture(const Value *V, Function &F, const DominatorTree &DT,
+ CaptureComponents Mask, unsigned MaxUsesToExplore = 0);
/// Capture information for a specific Use.
struct UseCaptureInfo {
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index 8172cf29d890b..65ffa34eeb923 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -208,9 +208,11 @@ CaptureComponents SimpleCaptureAnalysis::getCapturesBefore(const Value *Object,
if (!Inserted)
return CacheIt->second;
- CaptureComponents Ret = PointerMayBeCaptured(
- Object, /*ReturnCaptures=*/false, CaptureComponents::Provenance,
- [](CaptureComponents CC) { return capturesFullProvenance(CC); });
+ CaptureComponents Ret =
+ PointerMayBeCaptured(
+ Object, CaptureComponents::Provenance,
+ [](CaptureComponents CC) { return capturesFullProvenance(CC); })
+ .WithoutRet;
CacheIt->second = Ret;
return Ret;
}
@@ -234,13 +236,13 @@ EarliestEscapeAnalysis::getCapturesBefore(const Value *Object,
auto Iter = EarliestEscapes.try_emplace(Object);
if (Iter.second) {
- std::pair<Instruction *, CaptureComponents> EarliestCapture =
- FindEarliestCapture(Object, *DT.getRoot()->getParent(),
- /*ReturnCaptures=*/false, DT,
+ std::pair<Instruction *, CaptureResult> EarliestCapture =
+ FindEarliestCapture(Object, *DT.getRoot()->getParent(), DT,
CaptureComponents::Provenance);
if (EarliestCapture.first)
Inst2Obj[EarliestCapture.first].push_back(Object);
- Iter.first->second = EarliestCapture;
+ Iter.first->second = {EarliestCapture.first,
+ EarliestCapture.second.WithoutRet};
}
auto IsNotCapturedBefore = [&]() {
diff --git a/llvm/lib/Analysis/CaptureTracking.cpp b/llvm/lib/Analysis/CaptureTracking.cpp
index 22229d9c26b3b..c34fde8d11704 100644
--- a/llvm/lib/Analysis/CaptureTracking.cpp
+++ b/llvm/lib/Analysis/CaptureTracking.cpp
@@ -57,32 +57,32 @@ bool CaptureTracker::shouldExplore(const Use *U) { return true; }
namespace {
struct SimpleCaptureTracker : public CaptureTracker {
- explicit SimpleCaptureTracker(bool ReturnCaptures, CaptureComponents Mask,
+ explicit SimpleCaptureTracker(CaptureComponents Mask,
function_ref<bool(CaptureComponents)> StopFn)
- : ReturnCaptures(ReturnCaptures), Mask(Mask), StopFn(StopFn) {}
+ : Mask(Mask), StopFn(StopFn) {}
void tooManyUses() override {
LLVM_DEBUG(dbgs() << "Captured due to too many uses\n");
CC = Mask;
+ CCWithRet = Mask;
}
Action captured(const Use *U, UseCaptureInfo CI) override {
- if (isa<ReturnInst>(U->getUser()) && !ReturnCaptures)
- return ContinueIgnoringReturn;
-
if (capturesNothing(CI.UseCC & Mask))
return Continue;
LLVM_DEBUG(dbgs() << "Captured by: " << *U->getUser() << "\n");
- CC |= CI.UseCC & Mask;
+ CCWithRet |= CI.UseCC & Mask;
+ if (!isa<ReturnInst>(U->getUser()))
+ CC |= CI.UseCC & Mask;
return StopFn(CC) ? Stop : Continue;
}
- bool ReturnCaptures;
CaptureComponents Mask;
function_ref<bool(CaptureComponents)> StopFn;
CaptureComponents CC = CaptureComponents::None;
+ CaptureComponents CCWithRet = CaptureComponents::None;
};
/// Only find pointer captures which happen before the given instruction. Uses
@@ -155,26 +155,26 @@ struct CapturesBefore : public CaptureTracker {
// escape are not in a cycle.
struct EarliestCaptures : public CaptureTracker {
- EarliestCaptures(bool ReturnCaptures, Function &F, const DominatorTree &DT,
- CaptureComponents Mask)
- : DT(DT), ReturnCaptures(ReturnCaptures), F(F), Mask(Mask) {}
+ EarliestCaptures(Function &F, const DominatorTree &DT, CaptureComponents Mask)
+ : DT(DT), F(F), Mask(Mask) {}
void tooManyUses() override {
CC = Mask;
+ CCWithRet = Mask;
EarliestCapture = &*F.getEntryBlock().begin();
}
Action captured(const Use *U, UseCaptureInfo CI) override {
Instruction *I = cast<Instruction>(U->getUser());
- if (isa<ReturnInst>(I) && !ReturnCaptures)
- return ContinueIgnoringReturn;
-
if (capturesAnything(CI.UseCC & Mask)) {
- if (!EarliestCapture)
- EarliestCapture = I;
- else
- EarliestCapture = DT.findNearestCommonDominator(EarliestCapture, I);
- CC |= CI.UseCC & Mask;
+ CCWithRet |= CI.UseCC & Mask;
+ if (!isa<ReturnInst>(I)) {
+ if (!EarliestCapture)
+ EarliestCapture = I;
+ else
+ EarliestCapture = DT.findNearestCommonDominator(EarliestCapture, I);
+ CC |= CI.UseCC & Mask;
+ }
}
// Continue analysis, as we need to see all potential captures.
@@ -182,24 +182,25 @@ struct EarliestCaptures : public CaptureTracker {
}
const DominatorTree &DT;
- bool ReturnCaptures;
Function &F;
CaptureComponents Mask;
Instruction *EarliestCapture = nullptr;
CaptureComponents CC = CaptureComponents::None;
+ CaptureComponents CCWithRet = CaptureComponents::None;
};
} // namespace
-CaptureComponents llvm::PointerMayBeCaptured(
- const Value *V, bool ReturnCaptures, CaptureComponents Mask,
- function_ref<bool(CaptureComponents)> StopFn, unsigned MaxUsesToExplore) {
+CaptureResult
+llvm::PointerMayBeCaptured(const Value *V, CaptureComponents Mask,
+ function_ref<bool(CaptureComponents)> StopFn,
+ unsigned MaxUsesToExplore) {
assert(!isa<GlobalValue>(V) &&
"It doesn't make sense to ask whether a global is captured.");
LLVM_DEBUG(dbgs() << "Captured?: " << *V << " = ");
- SimpleCaptureTracker SCT(ReturnCaptures, Mask, StopFn);
+ SimpleCaptureTracker SCT(Mask, StopFn);
PointerMayBeCaptured(V, &SCT, MaxUsesToExplore);
if (capturesAnything(SCT.CC))
++NumCaptured;
@@ -207,14 +208,14 @@ CaptureComponents llvm::PointerMayBeCaptured(
++NumNotCaptured;
LLVM_DEBUG(dbgs() << "not captured\n");
}
- return SCT.CC;
+ return {SCT.CC, SCT.CCWithRet};
}
bool llvm::PointerMayBeCaptured(const Value *V, bool ReturnCaptures,
unsigned MaxUsesToExplore) {
- return capturesAnything(
- PointerMayBeCaptured(V, ReturnCaptures, CaptureComponents::All,
- capturesAnything, MaxUsesToExplore));
+ CaptureResult Res = PointerMayBeCaptured(V, CaptureComponents::All,
+ capturesAnything, MaxUsesToExplore);
+ return capturesAnything(ReturnCaptures ? Res.WithRet : Res.WithoutRet);
}
CaptureComponents llvm::PointerMayBeCapturedBefore(
@@ -225,9 +226,10 @@ CaptureComponents llvm::PointerMayBeCapturedBefore(
assert(!isa<GlobalValue>(V) &&
"It doesn't make sense to ask whether a global is captured.");
- if (!DT)
- return PointerMayBeCaptured(V, ReturnCaptures, Mask, StopFn,
- MaxUsesToExplore);
+ if (!DT) {
+ CaptureResult Res = PointerMayBeCaptured(V, Mask, StopFn, MaxUsesToExplore);
+ return ReturnCaptures ? Res.WithRet : Res.WithoutRet;
+ }
CapturesBefore CB(ReturnCaptures, I, DT, IncludeI, LI, Mask, StopFn);
PointerMayBeCaptured(V, &CB, MaxUsesToExplore);
@@ -248,20 +250,19 @@ bool llvm::PointerMayBeCapturedBefore(const Value *V, bool ReturnCaptures,
capturesAnything, LI, MaxUsesToExplore));
}
-std::pair<Instruction *, CaptureComponents>
-llvm::FindEarliestCapture(const Value *V, Function &F, bool ReturnCaptures,
- const DominatorTree &DT, CaptureComponents Mask,
- unsigned MaxUsesToExplore) {
+std::pair<Instruction *, CaptureResult>
+llvm::FindEarliestCapture(const Value *V, Function &F, const DominatorTree &DT,
+ CaptureComponents Mask, unsigned MaxUsesToExplore) {
assert(!isa<GlobalValue>(V) &&
"It doesn't make sense to ask whether a global is captured.");
- EarliestCaptures CB(ReturnCaptures, F, DT, Mask);
+ EarliestCaptures CB(F, DT, Mask);
PointerMayBeCaptured(V, &CB, MaxUsesToExplore);
if (capturesAnything(CB.CC))
++NumCapturedBefore;
else
++NumNotCapturedBefore;
- return {CB.EarliestCapture, CB.CC};
+ return {CB.EarliestCapture, {CB.CC, CB.CCWithRet}};
}
UseCaptureInfo llvm::DetermineUseCaptureKind(const Use &U, const Value *Base) {
diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
index 6704fd0ef86e0..056f844b3805a 100644
--- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
@@ -1396,8 +1396,8 @@ bool DSEState::isInvisibleToCallerAfterRet(const Value *V, const Value *Ptr,
}
auto I = InvisibleToCallerAfterRet.insert({V, false});
if (I.second && isInvisibleToCallerOnUnwind(V) && isNoAliasCall(V))
- I.first->second = capturesNothing(PointerMayBeCaptured(
- V, /*ReturnCaptures=*/true, CaptureComponents::Provenance));
+ I.first->second = capturesNothing(
+ PointerMayBeCaptured(V, CaptureComponents::Provenance).WithRet);
return I.first->second;
}
@@ -1414,8 +1414,8 @@ bool DSEState::isInvisibleToCallerOnUnwind(const Value *V) {
// with the killing MemoryDef. But we refrain from doing so for now to
// limit compile-time and this does not cause any changes to the number
// of stores removed on a large test set in practice.
- I.first->second = capturesAnything(PointerMayBeCaptured(
- V, /*ReturnCaptures=*/false, CaptureComponents::Provenance));
+ I.first->second = capturesAnything(
+ PointerMayBeCaptured(V, CaptureComponents::Provenance).WithoutRet);
return !I.first->second;
}
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 5bfd06affdb1f..3a98e86038904 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -3070,8 +3070,8 @@ static Value *isSafeToSpeculateStore(Instruction *I, BasicBlock *BrBB,
bool ExplicitlyDereferenceableOnly;
if (isWritableObject(Obj, ExplicitlyDereferenceableOnly) &&
capturesNothing(
- PointerMayBeCaptured(Obj, /*ReturnCaptures=*/false,
- CaptureComponents::Provenance)) &&
+ PointerMayBeCaptured(Obj, CaptureComponents::Provenance)
+ .WithoutRet) &&
(!ExplicitlyDereferenceableOnly ||
isDereferenceablePointer(StorePtr, StoreTy,
LI->getDataLayout()))) {
``````````
</details>
https://github.com/llvm/llvm-project/pull/195857
More information about the llvm-commits
mailing list