[AA] Model operand bundles more precisely
Based on D130896, we can model operand bundles more precisely. In addition to the baseline ModRefBehavior, a reading/clobbering operand bundle may also read/write all locations. For example, a memcpy with deopt bundle can read any memory, but only write argument memory. This means that getModRefInfo() for memcpy with a pointer that does not alias the arguments results in Ref, rather than ModRef, without the need to implement any special handling. Differential Revision: https://reviews.llvm.org/D130980
This commit is contained in:
parent
cc61c822e0
commit
ab25ea6d35
|
@ -235,35 +235,37 @@ ModRefInfo AAResults::getModRefInfo(const CallBase *Call,
|
|||
|
||||
// Try to refine the mod-ref info further using other API entry points to the
|
||||
// aggregate set of AA results.
|
||||
auto MRB = getModRefBehavior(Call);
|
||||
if (MRB.onlyAccessesInaccessibleMem())
|
||||
|
||||
// We can completely ignore inaccessible memory here, because MemoryLocations
|
||||
// can only reference accessible memory.
|
||||
auto MRB = getModRefBehavior(Call).getWithoutLoc(
|
||||
FunctionModRefBehavior::InaccessibleMem);
|
||||
if (MRB.doesNotAccessMemory())
|
||||
return ModRefInfo::NoModRef;
|
||||
|
||||
// TODO: Exclude inaccessible memory location here.
|
||||
Result &= MRB.getModRef();
|
||||
|
||||
if (MRB.onlyAccessesArgPointees() || MRB.onlyAccessesInaccessibleOrArgMem()) {
|
||||
ModRefInfo ArgMR = MRB.getModRef(FunctionModRefBehavior::ArgMem);
|
||||
ModRefInfo OtherMR =
|
||||
MRB.getWithoutLoc(FunctionModRefBehavior::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;
|
||||
if (MRB.doesAccessArgPointees()) {
|
||||
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);
|
||||
if (ArgAlias != AliasResult::NoAlias)
|
||||
AllArgsMask |= getArgModRefInfo(Call, ArgIdx);
|
||||
}
|
||||
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);
|
||||
if (ArgAlias != AliasResult::NoAlias)
|
||||
AllArgsMask |= getArgModRefInfo(Call, ArgIdx);
|
||||
}
|
||||
// Return NoModRef if no alias found with any argument.
|
||||
if (isNoModRef(AllArgsMask))
|
||||
return ModRefInfo::NoModRef;
|
||||
// Logical & between other AA analyses and argument analysis.
|
||||
Result &= AllArgsMask;
|
||||
ArgMR &= AllArgsMask;
|
||||
}
|
||||
|
||||
Result &= ArgMR | OtherMR;
|
||||
|
||||
// If Loc is a constant memory location, the call definitely could not
|
||||
// modify the memory location.
|
||||
if (isModSet(Result) && pointsToConstantMemory(Loc, AAQI, /*OrLocal*/ false))
|
||||
|
|
|
@ -769,12 +769,16 @@ FunctionModRefBehavior BasicAAResult::getModRefBehavior(const CallBase *Call) {
|
|||
else if (Call->onlyAccessesInaccessibleMemOrArgMem())
|
||||
Min = FunctionModRefBehavior::inaccessibleOrArgMemOnly(MR);
|
||||
|
||||
// If the call has operand bundles then aliasing attributes from the function
|
||||
// it calls do not directly apply to the call. This can be made more precise
|
||||
// in the future.
|
||||
if (!Call->hasOperandBundles())
|
||||
if (const Function *F = Call->getCalledFunction())
|
||||
Min &= getBestAAResults().getModRefBehavior(F);
|
||||
if (const Function *F = Call->getCalledFunction()) {
|
||||
FunctionModRefBehavior FMRB = getBestAAResults().getModRefBehavior(F);
|
||||
// Operand bundles on the call may also read or write memory, in addition
|
||||
// to the behavior of the called function.
|
||||
if (Call->hasReadingOperandBundles())
|
||||
FMRB |= FunctionModRefBehavior::readOnly();
|
||||
if (Call->hasClobberingOperandBundles())
|
||||
FMRB |= FunctionModRefBehavior::writeOnly();
|
||||
Min &= FMRB;
|
||||
}
|
||||
|
||||
return Min;
|
||||
}
|
||||
|
@ -976,26 +980,6 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call,
|
|||
return ModRefInfo::NoModRef;
|
||||
}
|
||||
|
||||
// Ideally, there should be no need to special case for memcpy/memove
|
||||
// intrinsics here since general machinery (based on memory attributes) should
|
||||
// already handle it just fine. Unfortunately, it doesn't due to deficiency in
|
||||
// operand bundles support. At the moment it's not clear if complexity behind
|
||||
// enhancing general mechanism worths it.
|
||||
// TODO: Consider improving operand bundles support in general mechanism.
|
||||
if (auto *Inst = dyn_cast<AnyMemTransferInst>(Call)) {
|
||||
AliasResult SrcAA =
|
||||
getBestAAResults().alias(MemoryLocation::getForSource(Inst), Loc, AAQI);
|
||||
AliasResult DestAA =
|
||||
getBestAAResults().alias(MemoryLocation::getForDest(Inst), Loc, AAQI);
|
||||
// It's also possible for Loc to alias both src and dest, or neither.
|
||||
ModRefInfo rv = ModRefInfo::NoModRef;
|
||||
if (SrcAA != AliasResult::NoAlias || Call->hasReadingOperandBundles())
|
||||
rv |= ModRefInfo::Ref;
|
||||
if (DestAA != AliasResult::NoAlias || Call->hasClobberingOperandBundles())
|
||||
rv |= ModRefInfo::Mod;
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Guard intrinsics are marked as arbitrarily writing so that proper control
|
||||
// dependencies are maintained but they never mods any particular memory
|
||||
// location.
|
||||
|
|
|
@ -33,19 +33,14 @@ define i32 @test2(i32* %P, i32* noalias %P2) {
|
|||
; heap, but they don't write to any location in the heap if the callee does not
|
||||
; deoptimize the caller. This fact, combined with the fact that
|
||||
; @argmemonly_function is, well, an argmemonly function, can be used to conclude
|
||||
; that %P is not written to at the callsite. However LLVM currently cannot
|
||||
; describe the "does not write to non-args, and reads the entire heap" effect on
|
||||
; a callsite.
|
||||
; that %P is not written to at the callsite.
|
||||
|
||||
; CHECK-LABEL: @test2(
|
||||
%v1 = load i32, i32* %P
|
||||
; CHECK: %v1 = load i32, i32* %P
|
||||
call void @argmemonly_function(i32* %P2) [ "deopt"() ]
|
||||
; CHECK: call void @argmemonly_function(
|
||||
%v2 = load i32, i32* %P
|
||||
; CHECK: %v2 = load i32, i32* %P
|
||||
%diff = sub i32 %v1, %v2
|
||||
; CHECK: %diff = sub i32 %v1, %v2
|
||||
ret i32 %diff
|
||||
; CHECK: ret i32 %diff
|
||||
; CHECK: ret i32 0
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue