diff options
author | Nikita Popov <npopov@redhat.com> | 2025-03-19 15:44:52 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-03-19 15:44:52 +0100 |
commit | 38e8dff84ba1e2cf0260712d21dd429d74471d08 (patch) | |
tree | b8c687e292b8e1313e36a18379f4190ea309042b /llvm/lib/Analysis/BasicAliasAnalysis.cpp | |
parent | 67a01131a8d70fcd06c6bd9cea30a8a6262c8c94 (diff) | |
download | llvm-38e8dff84ba1e2cf0260712d21dd429d74471d08.zip llvm-38e8dff84ba1e2cf0260712d21dd429d74471d08.tar.gz llvm-38e8dff84ba1e2cf0260712d21dd429d74471d08.tar.bz2 |
[AA][BasicAA] Move more call logic to BasicAA (#131144)
Currently, the handling for calls is split between AA and BasicAA in an
awkward way. BasicAA does argument alias 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.
Diffstat (limited to 'llvm/lib/Analysis/BasicAliasAnalysis.cpp')
-rw-r--r-- | llvm/lib/Analysis/BasicAliasAnalysis.cpp | 104 |
1 files changed, 48 insertions, 56 deletions
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp index b41c00a..4d1a95a 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 |