aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Analysis/CaptureTracking.cpp
diff options
context:
space:
mode:
authorNikita Popov <npopov@redhat.com>2025-02-13 14:55:42 +0100
committerNikita Popov <npopov@redhat.com>2025-02-13 14:56:12 +0100
commit1e64ea9914d3cc839b52e50d2d497600e03c8b6e (patch)
tree578e5d57bd89f8e5980a909a69747d29c216cbde /llvm/lib/Analysis/CaptureTracking.cpp
parent43780f4f9256117f73bc89cde968b9e757734ddc (diff)
downloadllvm-1e64ea9914d3cc839b52e50d2d497600e03c8b6e.zip
llvm-1e64ea9914d3cc839b52e50d2d497600e03c8b6e.tar.gz
llvm-1e64ea9914d3cc839b52e50d2d497600e03c8b6e.tar.bz2
Revert "[CaptureTracking][FunctionAttrs] Add support for CaptureInfo (#125880)"
This reverts commit ee655ca27aad466bcc54f6eba03f7e564940ad5a. A miscompilation has been reported at: https://github.com/llvm/llvm-project/pull/125880#issuecomment-2656632577
Diffstat (limited to 'llvm/lib/Analysis/CaptureTracking.cpp')
-rw-r--r--llvm/lib/Analysis/CaptureTracking.cpp127
1 files changed, 53 insertions, 74 deletions
diff --git a/llvm/lib/Analysis/CaptureTracking.cpp b/llvm/lib/Analysis/CaptureTracking.cpp
index 5120b91..49baf2e 100644
--- a/llvm/lib/Analysis/CaptureTracking.cpp
+++ b/llvm/lib/Analysis/CaptureTracking.cpp
@@ -81,15 +81,14 @@ struct SimpleCaptureTracker : public CaptureTracker {
Captured = true;
}
- Action captured(const Use *U, UseCaptureInfo CI) override {
- // TODO(captures): Use UseCaptureInfo.
+ bool captured(const Use *U) override {
if (isa<ReturnInst>(U->getUser()) && !ReturnCaptures)
- return ContinueIgnoringReturn;
+ return false;
LLVM_DEBUG(dbgs() << "Captured by: " << *U->getUser() << "\n");
Captured = true;
- return Stop;
+ return true;
}
bool ReturnCaptures;
@@ -123,21 +122,19 @@ struct CapturesBefore : public CaptureTracker {
return !isPotentiallyReachable(I, BeforeHere, nullptr, DT, LI);
}
- Action captured(const Use *U, UseCaptureInfo CI) override {
- // TODO(captures): Use UseCaptureInfo.
+ bool captured(const Use *U) override {
Instruction *I = cast<Instruction>(U->getUser());
if (isa<ReturnInst>(I) && !ReturnCaptures)
- return ContinueIgnoringReturn;
+ return false;
// Check isSafeToPrune() here rather than in shouldExplore() to avoid
// an expensive reachability query for every instruction we look at.
// Instead we only do one for actual capturing candidates.
if (isSafeToPrune(I))
- // If the use is not reachable, the instruction result isn't either.
- return ContinueIgnoringReturn;
+ return false;
Captured = true;
- return Stop;
+ return true;
}
const Instruction *BeforeHere;
@@ -169,11 +166,10 @@ struct EarliestCaptures : public CaptureTracker {
EarliestCapture = &*F.getEntryBlock().begin();
}
- Action captured(const Use *U, UseCaptureInfo CI) override {
- // TODO(captures): Use UseCaptureInfo.
+ bool captured(const Use *U) override {
Instruction *I = cast<Instruction>(U->getUser());
if (isa<ReturnInst>(I) && !ReturnCaptures)
- return ContinueIgnoringReturn;
+ return false;
if (!EarliestCapture)
EarliestCapture = I;
@@ -181,10 +177,9 @@ struct EarliestCaptures : public CaptureTracker {
EarliestCapture = DT.findNearestCommonDominator(EarliestCapture, I);
Captured = true;
- // Continue analysis, as we need to see all potential captures. However,
- // we do not need to follow the instruction result, as this use will
- // dominate any captures made through the instruction result..
- return ContinueIgnoringReturn;
+ // Return false to continue analysis; we need to see all potential
+ // captures.
+ return false;
}
Instruction *EarliestCapture = nullptr;
@@ -279,26 +274,25 @@ Instruction *llvm::FindEarliestCapture(const Value *V, Function &F,
return CB.EarliestCapture;
}
-UseCaptureInfo llvm::DetermineUseCaptureKind(
- const Use &U, const Value *Base,
+UseCaptureKind llvm::DetermineUseCaptureKind(
+ const Use &U,
function_ref<bool(Value *, const DataLayout &)> IsDereferenceableOrNull) {
Instruction *I = dyn_cast<Instruction>(U.getUser());
// TODO: Investigate non-instruction uses.
if (!I)
- return CaptureComponents::All;
+ return UseCaptureKind::MAY_CAPTURE;
switch (I->getOpcode()) {
case Instruction::Call:
case Instruction::Invoke: {
- // TODO(captures): Make this more precise.
auto *Call = cast<CallBase>(I);
// Not captured if the callee is readonly, doesn't return a copy through
// its return value and doesn't unwind (a readonly function can leak bits
// by throwing an exception or not depending on the input value).
if (Call->onlyReadsMemory() && Call->doesNotThrow() &&
Call->getType()->isVoidTy())
- return CaptureComponents::None;
+ return UseCaptureKind::NO_CAPTURE;
// The pointer is not captured if returned pointer is not captured.
// NOTE: CaptureTracking users should not assume that only functions
@@ -306,13 +300,13 @@ UseCaptureInfo llvm::DetermineUseCaptureKind(
// getUnderlyingObject in ValueTracking or DecomposeGEPExpression
// in BasicAA also need to know about this property.
if (isIntrinsicReturningPointerAliasingArgumentWithoutCapturing(Call, true))
- return UseCaptureInfo::passthrough();
+ return UseCaptureKind::PASSTHROUGH;
// Volatile operations effectively capture the memory location that they
// load and store to.
if (auto *MI = dyn_cast<MemIntrinsic>(Call))
if (MI->isVolatile())
- return CaptureComponents::All;
+ return UseCaptureKind::MAY_CAPTURE;
// Calling a function pointer does not in itself cause the pointer to
// be captured. This is a subtle point considering that (for example)
@@ -321,27 +315,30 @@ UseCaptureInfo llvm::DetermineUseCaptureKind(
// captured, even though the loaded value might be the pointer itself
// (think of self-referential objects).
if (Call->isCallee(&U))
- return CaptureComponents::None;
+ return UseCaptureKind::NO_CAPTURE;
// Not captured if only passed via 'nocapture' arguments.
assert(Call->isDataOperand(&U) && "Non-callee must be data operand");
- CaptureInfo CI = Call->getCaptureInfo(Call->getDataOperandNo(&U));
- return UseCaptureInfo(CI.getOtherComponents(), CI.getRetComponents());
+ if (!Call->doesNotCapture(Call->getDataOperandNo(&U))) {
+ // The parameter is not marked 'nocapture' - captured.
+ return UseCaptureKind::MAY_CAPTURE;
+ }
+ return UseCaptureKind::NO_CAPTURE;
}
case Instruction::Load:
// Volatile loads make the address observable.
if (cast<LoadInst>(I)->isVolatile())
- return CaptureComponents::All;
- return CaptureComponents::None;
+ return UseCaptureKind::MAY_CAPTURE;
+ return UseCaptureKind::NO_CAPTURE;
case Instruction::VAArg:
// "va-arg" from a pointer does not cause it to be captured.
- return CaptureComponents::None;
+ return UseCaptureKind::NO_CAPTURE;
case Instruction::Store:
// Stored the pointer - conservatively assume it may be captured.
// Volatile stores make the address observable.
if (U.getOperandNo() == 0 || cast<StoreInst>(I)->isVolatile())
- return CaptureComponents::All;
- return CaptureComponents::None;
+ return UseCaptureKind::MAY_CAPTURE;
+ return UseCaptureKind::NO_CAPTURE;
case Instruction::AtomicRMW: {
// atomicrmw conceptually includes both a load and store from
// the same location.
@@ -350,8 +347,8 @@ UseCaptureInfo llvm::DetermineUseCaptureKind(
// Volatile stores make the address observable.
auto *ARMWI = cast<AtomicRMWInst>(I);
if (U.getOperandNo() == 1 || ARMWI->isVolatile())
- return CaptureComponents::All;
- return CaptureComponents::None;
+ return UseCaptureKind::MAY_CAPTURE;
+ return UseCaptureKind::NO_CAPTURE;
}
case Instruction::AtomicCmpXchg: {
// cmpxchg conceptually includes both a load and store from
@@ -361,35 +358,31 @@ UseCaptureInfo llvm::DetermineUseCaptureKind(
// Volatile stores make the address observable.
auto *ACXI = cast<AtomicCmpXchgInst>(I);
if (U.getOperandNo() == 1 || U.getOperandNo() == 2 || ACXI->isVolatile())
- return CaptureComponents::All;
- return CaptureComponents::None;
+ return UseCaptureKind::MAY_CAPTURE;
+ return UseCaptureKind::NO_CAPTURE;
}
case Instruction::GetElementPtr:
// AA does not support pointers of vectors, so GEP vector splats need to
// be considered as captures.
if (I->getType()->isVectorTy())
- return CaptureComponents::All;
- return UseCaptureInfo::passthrough();
+ return UseCaptureKind::MAY_CAPTURE;
+ return UseCaptureKind::PASSTHROUGH;
case Instruction::BitCast:
case Instruction::PHI:
case Instruction::Select:
case Instruction::AddrSpaceCast:
// The original value is not captured via this if the new value isn't.
- return UseCaptureInfo::passthrough();
+ return UseCaptureKind::PASSTHROUGH;
case Instruction::ICmp: {
unsigned Idx = U.getOperandNo();
unsigned OtherIdx = 1 - Idx;
- if (isa<ConstantPointerNull>(I->getOperand(OtherIdx)) &&
- cast<ICmpInst>(I)->isEquality()) {
- // TODO(captures): Remove these special cases once we make use of
- // captures(address_is_null).
-
+ if (auto *CPN = dyn_cast<ConstantPointerNull>(I->getOperand(OtherIdx))) {
// Don't count comparisons of a no-alias return value against null as
// captures. This allows us to ignore comparisons of malloc results
// with null, for example.
- if (U->getType()->getPointerAddressSpace() == 0)
+ if (CPN->getType()->getAddressSpace() == 0)
if (isNoAliasCall(U.get()->stripPointerCasts()))
- return CaptureComponents::None;
+ return UseCaptureKind::NO_CAPTURE;
if (!I->getFunction()->nullPointerIsDefined()) {
auto *O = I->getOperand(Idx)->stripPointerCastsSameRepresentation();
// Comparing a dereferenceable_or_null pointer against null cannot
@@ -397,23 +390,17 @@ UseCaptureInfo llvm::DetermineUseCaptureKind(
// valid (in-bounds) pointer.
const DataLayout &DL = I->getDataLayout();
if (IsDereferenceableOrNull && IsDereferenceableOrNull(O, DL))
- return CaptureComponents::None;
+ return UseCaptureKind::NO_CAPTURE;
}
-
- // Check whether this is a comparison of the base pointer against
- // null.
- if (U.get() == Base)
- return CaptureComponents::AddressIsNull;
}
// Otherwise, be conservative. There are crazy ways to capture pointers
- // using comparisons. However, only the address is captured, not the
- // provenance.
- return CaptureComponents::Address;
+ // using comparisons.
+ return UseCaptureKind::MAY_CAPTURE;
}
default:
// Something else - be conservative and say it is captured.
- return CaptureComponents::All;
+ return UseCaptureKind::MAY_CAPTURE;
}
}
@@ -451,26 +438,18 @@ void llvm::PointerMayBeCaptured(const Value *V, CaptureTracker *Tracker,
};
while (!Worklist.empty()) {
const Use *U = Worklist.pop_back_val();
- UseCaptureInfo CI = DetermineUseCaptureKind(*U, V, IsDereferenceableOrNull);
- if (capturesAnything(CI.UseCC)) {
- switch (Tracker->captured(U, CI)) {
- case CaptureTracker::Stop:
+ switch (DetermineUseCaptureKind(*U, IsDereferenceableOrNull)) {
+ case UseCaptureKind::NO_CAPTURE:
+ continue;
+ case UseCaptureKind::MAY_CAPTURE:
+ if (Tracker->captured(U))
return;
- case CaptureTracker::ContinueIgnoringReturn:
- continue;
- case CaptureTracker::Continue:
- // Fall through to passthrough handling, but only if ResultCC contains
- // additional components that UseCC does not. We assume that a
- // capture at this point will be strictly more constraining than a
- // later capture from following the return value.
- if (capturesNothing(CI.ResultCC & ~CI.UseCC))
- continue;
- break;
- }
+ continue;
+ case UseCaptureKind::PASSTHROUGH:
+ if (!AddUses(U->getUser()))
+ return;
+ continue;
}
- // TODO(captures): We could keep track of ResultCC for the users.
- if (capturesAnything(CI.ResultCC) && !AddUses(U->getUser()))
- return;
}
// All uses examined.