[llvm] [AA][BasicAA] Move more call logic to BasicAA (PR #131144)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Thu Mar 13 06:38:40 PDT 2025
https://github.com/nikic created https://github.com/llvm/llvm-project/pull/131144
Currently, the handling for calls is split between AA and BasicAA in an awkward way. BasicAA does argument aliasing analysis for non-escaping objects (but without considering MemoryEffects), while AA handles the generic case using MemoryEffects. However, fundamentally, both of these are really trying to do the same thing.
The new merged logic first tries to remove the OtherMR component of the memory effects, which includes accesses to escaped memory. If a function-local object does not escape, OtherMR can be set to NoModRef.
Then we perform the argument scan in basically the same way as AA previously did. However, we also need to look at the operand bundles. To support that, I've adjusted getArgModRefInfo to accept operand bundle arguments.
>From 9bece91d63ab9e4988a7900780c0c05b2605d8e8 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Thu, 13 Mar 2025 12:28:10 +0100
Subject: [PATCH] [AA][BasicAA] Move more call logic to BasicAA
Currently, the handling for calls is split between AA and BasicAA
in an awkward way. BasicAA does argument aliasing analysis for
non-escaping objects (but without considering MemoryEffects), while
AA handles the generic case using MemoryEffects. However,
fundamentally, both of these are really trying to do the same thing.
The new merged logic first tries to remove the OtherMR component
of the memory effects, which includes accesses to escape memory.
If the object does not escape, OtherMR can be set to NoModRef.
Then we perform the argument scan in basically the same way as AA
previously did. However, we also need to look at the operand
bundles. To support that, I've adjusted getArgModRefInfo to accept
operand bundle arguments.
---
llvm/lib/Analysis/AliasAnalysis.cpp | 32 -------
llvm/lib/Analysis/BasicAliasAnalysis.cpp | 104 +++++++++++------------
2 files changed, 48 insertions(+), 88 deletions(-)
diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp
index 5114137da14ae..9573da93a3755 100644
--- a/llvm/lib/Analysis/AliasAnalysis.cpp
+++ b/llvm/lib/Analysis/AliasAnalysis.cpp
@@ -220,38 +220,6 @@ ModRefInfo AAResults::getModRefInfo(const CallBase *Call,
return ModRefInfo::NoModRef;
}
- // Try to refine the mod-ref info further using other API entry points to the
- // aggregate set of AA results.
-
- // We can completely ignore inaccessible memory here, because MemoryLocations
- // can only reference accessible memory.
- auto ME = getMemoryEffects(Call, AAQI)
- .getWithoutLoc(IRMemLocation::InaccessibleMem);
- if (ME.doesNotAccessMemory())
- return ModRefInfo::NoModRef;
-
- ModRefInfo ArgMR = ME.getModRef(IRMemLocation::ArgMem);
- ModRefInfo OtherMR = ME.getWithoutLoc(IRMemLocation::ArgMem).getModRef();
- if ((ArgMR | OtherMR) != OtherMR) {
- // 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
- // on the final result.
- ModRefInfo AllArgsMask = ModRefInfo::NoModRef;
- for (const auto &I : llvm::enumerate(Call->args())) {
- const Value *Arg = I.value();
- if (!Arg->getType()->isPointerTy())
- continue;
- unsigned ArgIdx = I.index();
- MemoryLocation ArgLoc = MemoryLocation::getForArgument(Call, ArgIdx, TLI);
- AliasResult ArgAlias = alias(ArgLoc, Loc, AAQI, Call);
- if (ArgAlias != AliasResult::NoAlias)
- AllArgsMask |= getArgModRefInfo(Call, ArgIdx);
- }
- ArgMR &= AllArgsMask;
- }
-
- Result &= ArgMR | OtherMR;
-
// Apply the ModRef mask. This ensures that if Loc is a constant memory
// location, we take into account the fact that the call definitely could not
// modify the memory location.
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index b41c00ada7ab2..4d1a95a0c4b43 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -847,15 +847,15 @@ MemoryEffects BasicAAResult::getMemoryEffects(const Function *F) {
ModRefInfo BasicAAResult::getArgModRefInfo(const CallBase *Call,
unsigned ArgIdx) {
- if (Call->paramHasAttr(ArgIdx, Attribute::WriteOnly))
+ if (Call->doesNotAccessMemory(ArgIdx))
+ return ModRefInfo::NoModRef;
+
+ if (Call->onlyWritesMemory(ArgIdx))
return ModRefInfo::Mod;
- if (Call->paramHasAttr(ArgIdx, Attribute::ReadOnly))
+ if (Call->onlyReadsMemory(ArgIdx))
return ModRefInfo::Ref;
- if (Call->paramHasAttr(ArgIdx, Attribute::ReadNone))
- return ModRefInfo::NoModRef;
-
return ModRefInfo::ModRef;
}
@@ -921,66 +921,58 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call,
if (!AI->isStaticAlloca() && isIntrinsicCall(Call, Intrinsic::stackrestore))
return ModRefInfo::Mod;
- // A call can access a locally allocated object either because it is passed as
- // an argument to the call, or because it has escaped prior to the call.
- //
- // Make sure the object has not escaped here, and then check that none of the
- // call arguments alias the object below.
+ // We can completely ignore inaccessible memory here, because MemoryLocations
+ // can only reference accessible memory.
+ auto ME = AAQI.AAR.getMemoryEffects(Call, AAQI)
+ .getWithoutLoc(IRMemLocation::InaccessibleMem);
+ if (ME.doesNotAccessMemory())
+ return ModRefInfo::NoModRef;
+
+ ModRefInfo ArgMR = ME.getModRef(IRMemLocation::ArgMem);
+ ModRefInfo OtherMR = ME.getWithoutLoc(IRMemLocation::ArgMem).getModRef();
+
+ // An identified function-local object that does not escape can only be
+ // accessed via call arguments. Reduce OtherMR (which includes accesses to
+ // escaped memory) based on that.
//
// We model calls that can return twice (setjmp) as clobbering non-escaping
// objects, to model any accesses that may occur prior to the second return.
// As an exception, ignore allocas, as setjmp is not required to preserve
// non-volatile stores for them.
- if (!isa<Constant>(Object) && Call != Object &&
- AAQI.CA->isNotCapturedBefore(Object, Call, /*OrAt*/ false) &&
- (isa<AllocaInst>(Object) || !Call->hasFnAttr(Attribute::ReturnsTwice))) {
-
- // Optimistically assume that call doesn't touch Object and check this
- // assumption in the following loop.
- ModRefInfo Result = ModRefInfo::NoModRef;
-
- unsigned OperandNo = 0;
- for (auto CI = Call->data_operands_begin(), CE = Call->data_operands_end();
- CI != CE; ++CI, ++OperandNo) {
- if (!(*CI)->getType()->isPointerTy())
- continue;
-
- // Call doesn't access memory through this operand, so we don't care
- // if it aliases with Object.
- if (Call->doesNotAccessMemory(OperandNo))
- continue;
-
- // If this is a no-capture pointer argument, see if we can tell that it
- // is impossible to alias the pointer we're checking.
- AliasResult AR =
- AAQI.AAR.alias(MemoryLocation::getBeforeOrAfter(*CI),
- MemoryLocation::getBeforeOrAfter(Object), AAQI);
- // Operand doesn't alias 'Object', continue looking for other aliases
- if (AR == AliasResult::NoAlias)
+ 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;
+
+ // 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
+ // on the final result.
+ if ((ArgMR | OtherMR) != OtherMR) {
+ ModRefInfo NewArgMR = ModRefInfo::NoModRef;
+ for (const Use &U : Call->data_ops()) {
+ const Value *Arg = U;
+ if (!Arg->getType()->isPointerTy())
continue;
- // Operand aliases 'Object', but call doesn't modify it. Strengthen
- // initial assumption and keep looking in case if there are more aliases.
- if (Call->onlyReadsMemory(OperandNo)) {
- Result |= ModRefInfo::Ref;
- continue;
- }
- // Operand aliases 'Object' but call only writes into it.
- if (Call->onlyWritesMemory(OperandNo)) {
- Result |= ModRefInfo::Mod;
- continue;
- }
- // This operand aliases 'Object' and call reads and writes into it.
- // Setting ModRef will not yield an early return below, MustAlias is not
- // used further.
- Result = ModRefInfo::ModRef;
- break;
+ unsigned ArgIdx = Call->getDataOperandNo(&U);
+ MemoryLocation ArgLoc =
+ Call->isArgOperand(&U)
+ ? MemoryLocation::getForArgument(Call, ArgIdx, TLI)
+ : MemoryLocation::getBeforeOrAfter(Arg);
+ AliasResult ArgAlias = AAQI.AAR.alias(ArgLoc, Loc, AAQI, Call);
+ if (ArgAlias != AliasResult::NoAlias)
+ NewArgMR |= ArgMR & AAQI.AAR.getArgModRefInfo(Call, ArgIdx);
+
+ // Exit early if we cannot improve over the original ArgMR.
+ if (NewArgMR == ArgMR)
+ break;
}
-
- // Early return if we improved mod ref information
- if (!isModAndRefSet(Result))
- return Result;
+ ArgMR = NewArgMR;
}
+ ModRefInfo Result = ArgMR | OtherMR;
+ if (!isModAndRefSet(Result))
+ return Result;
+
// If the call is malloc/calloc like, we can assume that it doesn't
// modify any IR visible value. This is only valid because we assume these
// routines do not read values visible in the IR. TODO: Consider special
More information about the llvm-commits
mailing list