aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Analysis/BasicAliasAnalysis.cpp
diff options
context:
space:
mode:
authorNikita Popov <npopov@redhat.com>2025-06-12 14:13:15 +0200
committerGitHub <noreply@github.com>2025-06-12 14:13:15 +0200
commitbc7fafbeea08bf8cd9a18fa10d3d3bc63f0c45a3 (patch)
tree82a91f05642bfb051c09b98024e4d05f8296946b /llvm/lib/Analysis/BasicAliasAnalysis.cpp
parent2ecbfc0beb42abbbd2c3d28bfd576b38c44a5b46 (diff)
downloadllvm-bc7fafbeea08bf8cd9a18fa10d3d3bc63f0c45a3.zip
llvm-bc7fafbeea08bf8cd9a18fa10d3d3bc63f0c45a3.tar.gz
llvm-bc7fafbeea08bf8cd9a18fa10d3d3bc63f0c45a3.tar.bz2
[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).
Diffstat (limited to 'llvm/lib/Analysis/BasicAliasAnalysis.cpp')
-rw-r--r--llvm/lib/Analysis/BasicAliasAnalysis.cpp86
1 files changed, 51 insertions, 35 deletions
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index f862d69..31611df 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -192,18 +192,20 @@ static bool areBothVScale(const Value *V1, const Value *V2) {
CaptureAnalysis::~CaptureAnalysis() = default;
-bool SimpleCaptureAnalysis::isNotCapturedBefore(const Value *Object,
- const Instruction *I,
- bool OrAt) {
+CaptureComponents SimpleCaptureAnalysis::getCapturesBefore(const Value *Object,
+ const Instruction *I,
+ bool OrAt) {
if (!isIdentifiedFunctionLocal(Object))
- return false;
+ return CaptureComponents::Provenance;
- auto [CacheIt, Inserted] = IsCapturedCache.insert({Object, false});
+ auto [CacheIt, Inserted] =
+ IsCapturedCache.insert({Object, CaptureComponents::Provenance});
if (!Inserted)
return CacheIt->second;
- bool Ret = !capturesAnything(PointerMayBeCaptured(
- Object, /*ReturnCaptures=*/false, CaptureComponents::Provenance));
+ CaptureComponents Ret = PointerMayBeCaptured(
+ Object, /*ReturnCaptures=*/false, CaptureComponents::Provenance,
+ [](CaptureComponents CC) { return capturesFullProvenance(CC); });
CacheIt->second = Ret;
return Ret;
}
@@ -216,37 +218,44 @@ static bool isNotInCycle(const Instruction *I, const DominatorTree *DT,
!isPotentiallyReachableFromMany(Succs, BB, nullptr, DT, LI);
}
-bool EarliestEscapeAnalysis::isNotCapturedBefore(const Value *Object,
- const Instruction *I,
- bool OrAt) {
+CaptureComponents
+EarliestEscapeAnalysis::getCapturesBefore(const Value *Object,
+ const Instruction *I, bool OrAt) {
if (!isIdentifiedFunctionLocal(Object))
- return false;
+ return CaptureComponents::Provenance;
auto Iter = EarliestEscapes.try_emplace(Object);
if (Iter.second) {
- Instruction *EarliestCapture = FindEarliestCapture(
- Object, *const_cast<Function *>(DT.getRoot()->getParent()),
- /*ReturnCaptures=*/false, DT, CaptureComponents::Provenance);
- if (EarliestCapture)
- Inst2Obj[EarliestCapture].push_back(Object);
+ std::pair<Instruction *, CaptureComponents> EarliestCapture =
+ FindEarliestCapture(
+ Object, *const_cast<Function *>(DT.getRoot()->getParent()),
+ /*ReturnCaptures=*/false, DT, CaptureComponents::Provenance);
+ if (EarliestCapture.first)
+ Inst2Obj[EarliestCapture.first].push_back(Object);
Iter.first->second = EarliestCapture;
}
- // No capturing instruction.
- if (!Iter.first->second)
- return true;
-
- // No context instruction means any use is capturing.
- if (!I)
- return false;
+ auto IsNotCapturedBefore = [&]() {
+ // No capturing instruction.
+ Instruction *CaptureInst = Iter.first->second.first;
+ if (!CaptureInst)
+ return true;
- if (I == Iter.first->second) {
- if (OrAt)
+ // No context instruction means any use is capturing.
+ if (!I)
return false;
- return isNotInCycle(I, &DT, LI);
- }
- return !isPotentiallyReachable(Iter.first->second, I, nullptr, &DT, LI);
+ if (I == CaptureInst) {
+ if (OrAt)
+ return false;
+ return isNotInCycle(I, &DT, LI);
+ }
+
+ return !isPotentiallyReachable(CaptureInst, I, nullptr, &DT, LI);
+ };
+ if (IsNotCapturedBefore())
+ return CaptureComponents::None;
+ return Iter.first->second.second;
}
void EarliestEscapeAnalysis::removeInstruction(Instruction *I) {
@@ -946,9 +955,14 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call,
// As an exception, ignore allocas, as setjmp is not required to preserve
// non-volatile stores for them.
if (isModOrRefSet(OtherMR) && !isa<Constant>(Object) && Call != Object &&
- AAQI.CA->isNotCapturedBefore(Object, Call, /*OrAt=*/false) &&
- (isa<AllocaInst>(Object) || !Call->hasFnAttr(Attribute::ReturnsTwice)))
- OtherMR = ModRefInfo::NoModRef;
+ (isa<AllocaInst>(Object) || !Call->hasFnAttr(Attribute::ReturnsTwice))) {
+ CaptureComponents CC =
+ AAQI.CA->getCapturesBefore(Object, Call, /*OrAt=*/false);
+ if (capturesNothing(CC))
+ OtherMR = ModRefInfo::NoModRef;
+ else if (capturesReadProvenanceOnly(CC))
+ OtherMR = ModRefInfo::Ref;
+ }
// Refine the modref info for argument memory. We only bother to do this
// 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,
// temporary store the nocapture argument's value in a temporary memory
// location if that memory location doesn't escape. Or it may pass a
// nocapture value to other functions as long as they don't capture it.
- if (isEscapeSource(O1) && AAQI.CA->isNotCapturedBefore(
- O2, dyn_cast<Instruction>(O1), /*OrAt*/ true))
+ if (isEscapeSource(O1) &&
+ capturesNothing(AAQI.CA->getCapturesBefore(
+ O2, dyn_cast<Instruction>(O1), /*OrAt*/ true)))
return AliasResult::NoAlias;
- if (isEscapeSource(O2) && AAQI.CA->isNotCapturedBefore(
- O1, dyn_cast<Instruction>(O2), /*OrAt*/ true))
+ if (isEscapeSource(O2) &&
+ capturesNothing(AAQI.CA->getCapturesBefore(
+ O1, dyn_cast<Instruction>(O2), /*OrAt*/ true)))
return AliasResult::NoAlias;
}