[AliasAnalysis] Introduce getModRefInfoMask() as a generalization of pointsToConstantMemory().

The pointsToConstantMemory() method returns true only if the memory pointed to
by the memory location is globally invariant. However, the LLVM memory model
also has the semantic notion of *locally-invariant*: memory that is known to be
invariant for the life of the SSA value representing that pointer. The most
common example of this is a pointer argument that is marked readonly noalias,
which the Rust compiler frequently emits.

It'd be desirable for LLVM to treat locally-invariant memory the same way as
globally-invariant memory when it's safe to do so. This patch implements that,
by introducing the concept of a *ModRefInfo mask*. A ModRefInfo mask is a bound
on the Mod/Ref behavior of an instruction that writes to a memory location,
based on the knowledge that the memory is globally-constant memory (in which
case the mask is NoModRef) or locally-constant memory (in which case the mask
is Ref). ModRefInfo values for an instruction can be combined with the
ModRefInfo mask by simply using the & operator. Where appropriate, this patch
has modified uses of pointsToConstantMemory() to instead examine the mask.

The most notable optimization change I noticed with this patch is that now
redundant loads from readonly noalias pointers can be eliminated across calls,
even when the pointer is captured. Internally, before this patch,
AliasAnalysis was assigning Ref to reads from constant memory; now AA can
assign NoModRef, which is a tighter bound.

Differential Revision: https://reviews.llvm.org/D136659
This commit is contained in:
Patrick Walton 2022-10-24 15:31:35 -07:00
parent 7af01fe42f
commit 01859da84b
21 changed files with 197 additions and 167 deletions

View File

@ -161,14 +161,24 @@ Other useful ``AliasAnalysis`` methods
Several other tidbits of information are often collected by various alias Several other tidbits of information are often collected by various alias
analysis implementations and can be put to good use by various clients. analysis implementations and can be put to good use by various clients.
The ``pointsToConstantMemory`` method The ``getModRefInfoMask`` method
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``pointsToConstantMemory`` method returns true if and only if the analysis The ``getModRefInfoMask`` method returns a bound on Mod/Ref information for
can prove that the pointer only points to unchanging memory locations the supplied pointer, based on knowledge about whether the pointer points to
(functions, constant global variables, and the null pointer). This information globally-constant memory (for which it returns ``NoModRef``) or
can be used to refine mod/ref information: it is impossible for an unchanging locally-invariant memory (for which it returns ``Ref``). Globally-constant
memory location to be modified. memory includes functions, constant global variables, and the null pointer.
Locally-invariant memory is memory that we know is invariant for the lifetime
of its SSA value, but not necessarily for the life of the program: for example,
the memory pointed to by ``readonly`` ``noalias`` parameters is known-invariant
for the duration of the corresponding function call. Given Mod/Ref information
``MRI`` for a memory location ``Loc``, ``MRI`` can be refined with a statement
like ``MRI &= AA.getModRefInfoMask(Loc);``. Another useful idiom is
``isModSet(AA.getModRefInfoMask(Loc))``; this checks to see if the given
location can be modified at all. For convenience, there is also a method
``pointsToConstantMemory(Loc)``; this is synonymous with
``isNoModRef(AA.getModRefInfoMask(Loc))``.
.. _never access memory or only read memory: .. _never access memory or only read memory:

View File

@ -359,7 +359,9 @@ public:
/// Checks whether the given location points to constant memory, or if /// Checks whether the given location points to constant memory, or if
/// \p OrLocal is true whether it points to a local alloca. /// \p OrLocal is true whether it points to a local alloca.
bool pointsToConstantMemory(const MemoryLocation &Loc, bool OrLocal = false); bool pointsToConstantMemory(const MemoryLocation &Loc, bool OrLocal = false) {
return isNoModRef(getModRefInfoMask(Loc, OrLocal));
}
/// A convenience wrapper around the primary \c pointsToConstantMemory /// A convenience wrapper around the primary \c pointsToConstantMemory
/// interface. /// interface.
@ -372,6 +374,22 @@ public:
/// \name Simple mod/ref information /// \name Simple mod/ref information
/// @{ /// @{
/// Returns a bitmask that should be unconditionally applied to the ModRef
/// info of a memory location. This allows us to eliminate Mod and/or Ref
/// from the ModRef info based on the knowledge that the memory location
/// points to constant and/or locally-invariant memory.
///
/// If IgnoreLocals is true, then this method returns NoModRef for memory
/// that points to a local alloca.
ModRefInfo getModRefInfoMask(const MemoryLocation &Loc,
bool IgnoreLocals = false);
/// A convenience wrapper around the primary \c getModRefInfoMask
/// interface.
ModRefInfo getModRefInfoMask(const Value *P, bool IgnoreLocals = false) {
return getModRefInfoMask(MemoryLocation::getBeforeOrAfter(P), IgnoreLocals);
}
/// Get the ModRef info associated with a pointer argument of a call. The /// Get the ModRef info associated with a pointer argument of a call. The
/// result's bits are set to indicate the allowed aliasing ModRef kinds. Note /// result's bits are set to indicate the allowed aliasing ModRef kinds. Note
/// that these bits do not necessarily account for the overall behavior of /// that these bits do not necessarily account for the overall behavior of
@ -614,6 +632,8 @@ public:
AAQueryInfo &AAQI); AAQueryInfo &AAQI);
bool pointsToConstantMemory(const MemoryLocation &Loc, AAQueryInfo &AAQI, bool pointsToConstantMemory(const MemoryLocation &Loc, AAQueryInfo &AAQI,
bool OrLocal = false); bool OrLocal = false);
ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
bool IgnoreLocals = false);
ModRefInfo getModRefInfo(Instruction *I, const CallBase *Call2, ModRefInfo getModRefInfo(Instruction *I, const CallBase *Call2,
AAQueryInfo &AAQIP); AAQueryInfo &AAQIP);
ModRefInfo getModRefInfo(const CallBase *Call, const MemoryLocation &Loc, ModRefInfo getModRefInfo(const CallBase *Call, const MemoryLocation &Loc,
@ -681,6 +701,10 @@ public:
bool pointsToConstantMemory(const MemoryLocation &Loc, bool OrLocal = false) { bool pointsToConstantMemory(const MemoryLocation &Loc, bool OrLocal = false) {
return AA.pointsToConstantMemory(Loc, AAQI, OrLocal); return AA.pointsToConstantMemory(Loc, AAQI, OrLocal);
} }
ModRefInfo getModRefInfoMask(const MemoryLocation &Loc,
bool IgnoreLocals = false) {
return AA.getModRefInfoMask(Loc, AAQI, IgnoreLocals);
}
ModRefInfo getModRefInfo(const CallBase *Call, const MemoryLocation &Loc) { ModRefInfo getModRefInfo(const CallBase *Call, const MemoryLocation &Loc) {
return AA.getModRefInfo(Call, Loc, AAQI); return AA.getModRefInfo(Call, Loc, AAQI);
} }
@ -743,16 +767,19 @@ public:
virtual AliasResult alias(const MemoryLocation &LocA, virtual AliasResult alias(const MemoryLocation &LocA,
const MemoryLocation &LocB, AAQueryInfo &AAQI) = 0; const MemoryLocation &LocB, AAQueryInfo &AAQI) = 0;
/// Checks whether the given location points to constant memory, or if
/// \p OrLocal is true whether it points to a local alloca.
virtual bool pointsToConstantMemory(const MemoryLocation &Loc,
AAQueryInfo &AAQI, bool OrLocal) = 0;
/// @} /// @}
//===--------------------------------------------------------------------===// //===--------------------------------------------------------------------===//
/// \name Simple mod/ref information /// \name Simple mod/ref information
/// @{ /// @{
/// Returns a bitmask that should be unconditionally applied to the ModRef
/// info of a memory location. This allows us to eliminate Mod and/or Ref from
/// the ModRef info based on the knowledge that the memory location points to
/// constant and/or locally-invariant memory.
virtual ModRefInfo getModRefInfoMask(const MemoryLocation &Loc,
AAQueryInfo &AAQI,
bool IgnoreLocals) = 0;
/// Get the ModRef info associated with a pointer argument of a callsite. The /// Get the ModRef info associated with a pointer argument of a callsite. The
/// result's bits are set to indicate the allowed aliasing ModRef kinds. Note /// result's bits are set to indicate the allowed aliasing ModRef kinds. Note
/// that these bits do not necessarily account for the overall behavior of /// that these bits do not necessarily account for the overall behavior of
@ -801,9 +828,9 @@ public:
return Result.alias(LocA, LocB, AAQI); return Result.alias(LocA, LocB, AAQI);
} }
bool pointsToConstantMemory(const MemoryLocation &Loc, AAQueryInfo &AAQI, ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
bool OrLocal) override { bool IgnoreLocals) override {
return Result.pointsToConstantMemory(Loc, AAQI, OrLocal); return Result.getModRefInfoMask(Loc, AAQI, IgnoreLocals);
} }
ModRefInfo getArgModRefInfo(const CallBase *Call, unsigned ArgIdx) override { ModRefInfo getArgModRefInfo(const CallBase *Call, unsigned ArgIdx) override {
@ -856,9 +883,9 @@ public:
return AliasResult::MayAlias; return AliasResult::MayAlias;
} }
bool pointsToConstantMemory(const MemoryLocation &Loc, AAQueryInfo &AAQI, ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
bool OrLocal) { bool IgnoreLocals) {
return false; return ModRefInfo::ModRef;
} }
ModRefInfo getArgModRefInfo(const CallBase *Call, unsigned ArgIdx) { ModRefInfo getArgModRefInfo(const CallBase *Call, unsigned ArgIdx) {

View File

@ -75,9 +75,15 @@ public:
ModRefInfo getModRefInfo(const CallBase *Call1, const CallBase *Call2, ModRefInfo getModRefInfo(const CallBase *Call1, const CallBase *Call2,
AAQueryInfo &AAQI); AAQueryInfo &AAQI);
/// Chases pointers until we find a (constant global) or not. /// Returns a bitmask that should be unconditionally applied to the ModRef
bool pointsToConstantMemory(const MemoryLocation &Loc, AAQueryInfo &AAQI, /// info of a memory location. This allows us to eliminate Mod and/or Ref
bool OrLocal); /// from the ModRef info based on the knowledge that the memory location
/// points to constant and/or locally-invariant memory.
///
/// If IgnoreLocals is true, then this method returns NoModRef for memory
/// that points to a local alloca.
ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
bool IgnoreLocals = false);
/// Get the location associated with a pointer argument of a callsite. /// Get the location associated with a pointer argument of a callsite.
ModRefInfo getArgModRefInfo(const CallBase *Call, unsigned ArgIdx); ModRefInfo getArgModRefInfo(const CallBase *Call, unsigned ArgIdx);

View File

@ -52,8 +52,8 @@ public:
AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB, AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB,
AAQueryInfo &AAQI); AAQueryInfo &AAQI);
bool pointsToConstantMemory(const MemoryLocation &Loc, AAQueryInfo &AAQI, ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
bool OrLocal); bool IgnoreLocals);
using AAResultBase::getMemoryEffects; using AAResultBase::getMemoryEffects;
MemoryEffects getMemoryEffects(const Function *F); MemoryEffects getMemoryEffects(const Function *F);

View File

@ -40,8 +40,8 @@ public:
AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB, AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB,
AAQueryInfo &AAQI); AAQueryInfo &AAQI);
bool pointsToConstantMemory(const MemoryLocation &Loc, AAQueryInfo &AAQI, ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
bool OrLocal); bool IgnoreLocals);
MemoryEffects getMemoryEffects(const CallBase *Call, AAQueryInfo &AAQI); MemoryEffects getMemoryEffects(const CallBase *Call, AAQueryInfo &AAQI);
MemoryEffects getMemoryEffects(const Function *F); MemoryEffects getMemoryEffects(const Function *F);
ModRefInfo getModRefInfo(const CallBase *Call, const MemoryLocation &Loc, ModRefInfo getModRefInfo(const CallBase *Call, const MemoryLocation &Loc,

View File

@ -147,19 +147,25 @@ AliasResult AAResults::alias(const MemoryLocation &LocA,
return Result; return Result;
} }
bool AAResults::pointsToConstantMemory(const MemoryLocation &Loc, ModRefInfo AAResults::getModRefInfoMask(const MemoryLocation &Loc,
bool OrLocal) { bool IgnoreLocals) {
SimpleAAQueryInfo AAQIP(*this); SimpleAAQueryInfo AAQIP(*this);
return pointsToConstantMemory(Loc, AAQIP, OrLocal); return getModRefInfoMask(Loc, AAQIP, IgnoreLocals);
} }
bool AAResults::pointsToConstantMemory(const MemoryLocation &Loc, ModRefInfo AAResults::getModRefInfoMask(const MemoryLocation &Loc,
AAQueryInfo &AAQI, bool OrLocal) { AAQueryInfo &AAQI, bool IgnoreLocals) {
for (const auto &AA : AAs) ModRefInfo Result = ModRefInfo::ModRef;
if (AA->pointsToConstantMemory(Loc, AAQI, OrLocal))
return true;
return false; for (const auto &AA : AAs) {
Result &= AA->getModRefInfoMask(Loc, AAQI, IgnoreLocals);
// Early-exit the moment we reach the bottom of the lattice.
if (isNoModRef(Result))
return ModRefInfo::NoModRef;
}
return Result;
} }
ModRefInfo AAResults::getArgModRefInfo(const CallBase *Call, unsigned ArgIdx) { ModRefInfo AAResults::getArgModRefInfo(const CallBase *Call, unsigned ArgIdx) {
@ -253,10 +259,11 @@ ModRefInfo AAResults::getModRefInfo(const CallBase *Call,
Result &= ArgMR | OtherMR; Result &= ArgMR | OtherMR;
// If Loc is a constant memory location, the call definitely could not // 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. // modify the memory location.
if (isModSet(Result) && pointsToConstantMemory(Loc, AAQI, /*OrLocal*/ false)) if (!isNoModRef(Result))
Result &= ModRefInfo::Ref; Result &= getModRefInfoMask(Loc);
return Result; return Result;
} }
@ -510,9 +517,11 @@ ModRefInfo AAResults::getModRefInfo(const StoreInst *S,
if (AR == AliasResult::NoAlias) if (AR == AliasResult::NoAlias)
return ModRefInfo::NoModRef; return ModRefInfo::NoModRef;
// If the pointer is a pointer to constant memory, then it could not have // Examine the ModRef mask. If Mod isn't present, then return NoModRef.
// been modified by this store. // This ensures that if Loc is a constant memory location, we take into
if (pointsToConstantMemory(Loc, AAQI)) // account the fact that the store definitely could not modify the memory
// location.
if (!isModSet(getModRefInfoMask(Loc)))
return ModRefInfo::NoModRef; return ModRefInfo::NoModRef;
} }
@ -529,10 +538,11 @@ ModRefInfo AAResults::getModRefInfo(const FenceInst *S,
ModRefInfo AAResults::getModRefInfo(const FenceInst *S, ModRefInfo AAResults::getModRefInfo(const FenceInst *S,
const MemoryLocation &Loc, const MemoryLocation &Loc,
AAQueryInfo &AAQI) { AAQueryInfo &AAQI) {
// If we know that the location is a constant memory location, the fence // All we know about a fence instruction is what we get from the ModRef
// cannot modify this location. // mask: if Loc is a constant memory location, the fence definitely could
if (Loc.Ptr && pointsToConstantMemory(Loc, AAQI)) // not modify it.
return ModRefInfo::Ref; if (Loc.Ptr)
return getModRefInfoMask(Loc);
return ModRefInfo::ModRef; return ModRefInfo::ModRef;
} }
@ -552,10 +562,9 @@ ModRefInfo AAResults::getModRefInfo(const VAArgInst *V,
if (AR == AliasResult::NoAlias) if (AR == AliasResult::NoAlias)
return ModRefInfo::NoModRef; return ModRefInfo::NoModRef;
// If the pointer is a pointer to constant memory, then it could not have // If the pointer is a pointer to invariant memory, then it could not have
// been modified by this va_arg. // been modified by this va_arg.
if (pointsToConstantMemory(Loc, AAQI)) return getModRefInfoMask(Loc, AAQI);
return ModRefInfo::NoModRef;
} }
// Otherwise, a va_arg reads and writes. // Otherwise, a va_arg reads and writes.
@ -572,10 +581,9 @@ ModRefInfo AAResults::getModRefInfo(const CatchPadInst *CatchPad,
const MemoryLocation &Loc, const MemoryLocation &Loc,
AAQueryInfo &AAQI) { AAQueryInfo &AAQI) {
if (Loc.Ptr) { if (Loc.Ptr) {
// If the pointer is a pointer to constant memory, // If the pointer is a pointer to invariant memory,
// then it could not have been modified by this catchpad. // then it could not have been modified by this catchpad.
if (pointsToConstantMemory(Loc, AAQI)) return getModRefInfoMask(Loc, AAQI);
return ModRefInfo::NoModRef;
} }
// Otherwise, a catchpad reads and writes. // Otherwise, a catchpad reads and writes.
@ -592,10 +600,9 @@ ModRefInfo AAResults::getModRefInfo(const CatchReturnInst *CatchRet,
const MemoryLocation &Loc, const MemoryLocation &Loc,
AAQueryInfo &AAQI) { AAQueryInfo &AAQI) {
if (Loc.Ptr) { if (Loc.Ptr) {
// If the pointer is a pointer to constant memory, // If the pointer is a pointer to invariant memory,
// then it could not have been modified by this catchpad. // then it could not have been modified by this catchpad.
if (pointsToConstantMemory(Loc, AAQI)) return getModRefInfoMask(Loc, AAQI);
return ModRefInfo::NoModRef;
} }
// Otherwise, a catchret reads and writes. // Otherwise, a catchret reads and writes.

View File

@ -676,33 +676,46 @@ BasicAAResult::DecomposeGEPExpression(const Value *V, const DataLayout &DL,
return Decomposed; return Decomposed;
} }
/// Returns whether the given pointer value points to memory that is local to ModRefInfo BasicAAResult::getModRefInfoMask(const MemoryLocation &Loc,
/// the function, with global constants being considered local to all AAQueryInfo &AAQI,
/// functions. bool IgnoreLocals) {
bool BasicAAResult::pointsToConstantMemory(const MemoryLocation &Loc,
AAQueryInfo &AAQI, bool OrLocal) {
assert(Visited.empty() && "Visited must be cleared after use!"); assert(Visited.empty() && "Visited must be cleared after use!");
auto _ = make_scope_exit([&] { Visited.clear(); }); auto _ = make_scope_exit([&] { Visited.clear(); });
unsigned MaxLookup = 8; unsigned MaxLookup = 8;
SmallVector<const Value *, 16> Worklist; SmallVector<const Value *, 16> Worklist;
Worklist.push_back(Loc.Ptr); Worklist.push_back(Loc.Ptr);
ModRefInfo Result = ModRefInfo::NoModRef;
do { do {
const Value *V = getUnderlyingObject(Worklist.pop_back_val()); const Value *V = getUnderlyingObject(Worklist.pop_back_val());
if (!Visited.insert(V).second) if (!Visited.insert(V).second)
continue; continue;
// An alloca instruction defines local memory. // Ignore allocas if we were instructed to do so.
if (OrLocal && isa<AllocaInst>(V)) if (IgnoreLocals && isa<AllocaInst>(V))
continue; continue;
// A global constant counts as local memory for our purposes. // If the location points to memory that is known to be invariant for
// the life of the underlying SSA value, then we can exclude Mod from
// the set of valid memory effects.
//
// An argument that is marked readonly and noalias is known to be
// invariant while that function is executing.
if (const Argument *Arg = dyn_cast<Argument>(V)) {
if (Arg->hasNoAliasAttr() && Arg->onlyReadsMemory()) {
Result |= ModRefInfo::Ref;
continue;
}
}
// A global constant can't be mutated.
if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(V)) { if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(V)) {
// Note: this doesn't require GV to be "ODR" because it isn't legal for a // Note: this doesn't require GV to be "ODR" because it isn't legal for a
// global to be marked constant in some modules and non-constant in // global to be marked constant in some modules and non-constant in
// others. GV may even be a declaration, not a definition. // others. GV may even be a declaration, not a definition.
if (!GV->isConstant()) if (!GV->isConstant())
return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal); return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals);
continue; continue;
} }
@ -718,16 +731,20 @@ bool BasicAAResult::pointsToConstantMemory(const MemoryLocation &Loc,
if (const PHINode *PN = dyn_cast<PHINode>(V)) { if (const PHINode *PN = dyn_cast<PHINode>(V)) {
// Don't bother inspecting phi nodes with many operands. // Don't bother inspecting phi nodes with many operands.
if (PN->getNumIncomingValues() > MaxLookup) if (PN->getNumIncomingValues() > MaxLookup)
return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal); return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals);
append_range(Worklist, PN->incoming_values()); append_range(Worklist, PN->incoming_values());
continue; continue;
} }
// Otherwise be conservative. // Otherwise be conservative.
return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal); return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals);
} while (!Worklist.empty() && --MaxLookup); } while (!Worklist.empty() && --MaxLookup);
return Worklist.empty(); // If we hit the maximum number of instructions to examine, be conservative.
if (!Worklist.empty())
return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals);
return Result;
} }
static bool isIntrinsicCall(const CallBase *Call, Intrinsic::ID IID) { static bool isIntrinsicCall(const CallBase *Call, Intrinsic::ID IID) {

View File

@ -525,7 +525,7 @@ MemDepResult MemoryDependenceResults::getSimplePointerDependencyFrom(
} }
// Stores don't alias loads from read-only memory. // Stores don't alias loads from read-only memory.
if (BatchAA.pointsToConstantMemory(LoadLoc)) if (!isModSet(BatchAA.getModRefInfoMask(LoadLoc)))
continue; continue;
// Stores depend on may/must aliased loads. // Stores depend on may/must aliased loads.

View File

@ -378,9 +378,10 @@ static bool isUseTriviallyOptimizableToLiveOnEntry(AliasAnalysisType &AA,
const Instruction *I) { const Instruction *I) {
// If the memory can't be changed, then loads of the memory can't be // If the memory can't be changed, then loads of the memory can't be
// clobbered. // clobbered.
if (auto *LI = dyn_cast<LoadInst>(I)) if (auto *LI = dyn_cast<LoadInst>(I)) {
return I->hasMetadata(LLVMContext::MD_invariant_load) || return I->hasMetadata(LLVMContext::MD_invariant_load) ||
AA.pointsToConstantMemory(MemoryLocation::get(LI)); !isModSet(AA.getModRefInfoMask(MemoryLocation::get(LI)));
}
return false; return false;
} }

View File

@ -68,28 +68,29 @@ AliasResult ObjCARCAAResult::alias(const MemoryLocation &LocA,
return AliasResult::MayAlias; return AliasResult::MayAlias;
} }
bool ObjCARCAAResult::pointsToConstantMemory(const MemoryLocation &Loc, ModRefInfo ObjCARCAAResult::getModRefInfoMask(const MemoryLocation &Loc,
AAQueryInfo &AAQI, bool OrLocal) { AAQueryInfo &AAQI,
bool IgnoreLocals) {
if (!EnableARCOpts) if (!EnableARCOpts)
return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal); return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals);
// First, strip off no-ops, including ObjC-specific no-ops, and try making // First, strip off no-ops, including ObjC-specific no-ops, and try making
// a precise alias query. // a precise alias query.
const Value *S = GetRCIdentityRoot(Loc.Ptr); const Value *S = GetRCIdentityRoot(Loc.Ptr);
if (AAResultBase::pointsToConstantMemory( if (isNoModRef(AAResultBase::getModRefInfoMask(
MemoryLocation(S, Loc.Size, Loc.AATags), AAQI, OrLocal)) MemoryLocation(S, Loc.Size, Loc.AATags), AAQI, IgnoreLocals)))
return true; return ModRefInfo::NoModRef;
// If that failed, climb to the underlying object, including climbing through // If that failed, climb to the underlying object, including climbing through
// ObjC-specific no-ops, and try making an imprecise alias query. // ObjC-specific no-ops, and try making an imprecise alias query.
const Value *U = GetUnderlyingObjCPtr(S); const Value *U = GetUnderlyingObjCPtr(S);
if (U != S) if (U != S)
return AAResultBase::pointsToConstantMemory( return AAResultBase::getModRefInfoMask(MemoryLocation::getBeforeOrAfter(U),
MemoryLocation::getBeforeOrAfter(U), AAQI, OrLocal); AAQI, IgnoreLocals);
// If that failed, fail. We don't need to chain here, since that's covered // If that failed, fail. We don't need to chain here, since that's covered
// by the earlier precise query. // by the earlier precise query.
return false; return ModRefInfo::ModRef;
} }
MemoryEffects ObjCARCAAResult::getMemoryEffects(const Function *F) { MemoryEffects ObjCARCAAResult::getMemoryEffects(const Function *F) {

View File

@ -385,23 +385,23 @@ AliasResult TypeBasedAAResult::alias(const MemoryLocation &LocA,
return AliasResult::NoAlias; return AliasResult::NoAlias;
} }
bool TypeBasedAAResult::pointsToConstantMemory(const MemoryLocation &Loc, ModRefInfo TypeBasedAAResult::getModRefInfoMask(const MemoryLocation &Loc,
AAQueryInfo &AAQI, AAQueryInfo &AAQI,
bool OrLocal) { bool IgnoreLocals) {
if (!EnableTBAA) if (!EnableTBAA)
return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal); return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals);
const MDNode *M = Loc.AATags.TBAA; const MDNode *M = Loc.AATags.TBAA;
if (!M) if (!M)
return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal); return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals);
// If this is an "immutable" type, we can assume the pointer is pointing // If this is an "immutable" type, we can assume the pointer is pointing
// to constant memory. // to constant memory.
if ((!isStructPathTBAA(M) && TBAANode(M).isTypeImmutable()) || if ((!isStructPathTBAA(M) && TBAANode(M).isTypeImmutable()) ||
(isStructPathTBAA(M) && TBAAStructTagNode(M).isTypeImmutable())) (isStructPathTBAA(M) && TBAAStructTagNode(M).isTypeImmutable()))
return true; return ModRefInfo::NoModRef;
return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal); return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals);
} }
MemoryEffects TypeBasedAAResult::getMemoryEffects(const CallBase *Call, MemoryEffects TypeBasedAAResult::getMemoryEffects(const CallBase *Call,

View File

@ -124,54 +124,19 @@ AliasResult AMDGPUAAResult::alias(const MemoryLocation &LocA,
return AAResultBase::alias(LocA, LocB, AAQI); return AAResultBase::alias(LocA, LocB, AAQI);
} }
bool AMDGPUAAResult::pointsToConstantMemory(const MemoryLocation &Loc, ModRefInfo AMDGPUAAResult::getModRefInfoMask(const MemoryLocation &Loc,
AAQueryInfo &AAQI, bool OrLocal) { AAQueryInfo &AAQI,
bool IgnoreLocals) {
unsigned AS = Loc.Ptr->getType()->getPointerAddressSpace(); unsigned AS = Loc.Ptr->getType()->getPointerAddressSpace();
if (AS == AMDGPUAS::CONSTANT_ADDRESS || if (AS == AMDGPUAS::CONSTANT_ADDRESS ||
AS == AMDGPUAS::CONSTANT_ADDRESS_32BIT) AS == AMDGPUAS::CONSTANT_ADDRESS_32BIT)
return true; return ModRefInfo::NoModRef;
const Value *Base = getUnderlyingObject(Loc.Ptr); const Value *Base = getUnderlyingObject(Loc.Ptr);
AS = Base->getType()->getPointerAddressSpace(); AS = Base->getType()->getPointerAddressSpace();
if (AS == AMDGPUAS::CONSTANT_ADDRESS || if (AS == AMDGPUAS::CONSTANT_ADDRESS ||
AS == AMDGPUAS::CONSTANT_ADDRESS_32BIT) AS == AMDGPUAS::CONSTANT_ADDRESS_32BIT)
return true; return ModRefInfo::NoModRef;
if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(Base)) { return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals);
if (GV->isConstant())
return true;
} else if (const Argument *Arg = dyn_cast<Argument>(Base)) {
const Function *F = Arg->getParent();
// Only assume constant memory for arguments on kernels.
switch (F->getCallingConv()) {
default:
return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal);
case CallingConv::AMDGPU_LS:
case CallingConv::AMDGPU_HS:
case CallingConv::AMDGPU_ES:
case CallingConv::AMDGPU_GS:
case CallingConv::AMDGPU_VS:
case CallingConv::AMDGPU_PS:
case CallingConv::AMDGPU_CS:
case CallingConv::AMDGPU_KERNEL:
case CallingConv::SPIR_KERNEL:
break;
}
unsigned ArgNo = Arg->getArgNo();
/* On an argument, ReadOnly attribute indicates that the function does
not write through this pointer argument, even though it may write
to the memory that the pointer points to.
On an argument, ReadNone attribute indicates that the function does
not dereference that pointer argument, even though it may read or write
the memory that the pointer points to if accessed through other pointers.
*/
if (F->hasParamAttribute(ArgNo, Attribute::NoAlias) &&
(F->hasParamAttribute(ArgNo, Attribute::ReadNone) ||
F->hasParamAttribute(ArgNo, Attribute::ReadOnly))) {
return true;
}
}
return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal);
} }

View File

@ -38,8 +38,8 @@ public:
AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB, AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB,
AAQueryInfo &AAQI); AAQueryInfo &AAQI);
bool pointsToConstantMemory(const MemoryLocation &Loc, AAQueryInfo &AAQI, ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
bool OrLocal); bool IgnoreLocals);
}; };
/// Analysis pass providing a never-invalidated alias analysis result. /// Analysis pass providing a never-invalidated alias analysis result.

View File

@ -140,13 +140,14 @@ static MemoryEffects checkFunctionMemoryAccess(Function &F, bool ThisBody,
ME |= MemoryEffects::argMemOnly(ModRefInfo::ModRef); ME |= MemoryEffects::argMemOnly(ModRefInfo::ModRef);
auto AddLocAccess = [&](const MemoryLocation &Loc, ModRefInfo MR) { auto AddLocAccess = [&](const MemoryLocation &Loc, ModRefInfo MR) {
// Ignore accesses to local memory. // Ignore accesses to known-invariant or local memory.
if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true)) MR &= AAR.getModRefInfoMask(Loc, /*IgnoreLocal=*/true);
if (isNoModRef(MR))
return; return;
const Value *UO = getUnderlyingObject(Loc.Ptr); const Value *UO = getUnderlyingObject(Loc.Ptr);
assert(!isa<AllocaInst>(UO) && assert(!isa<AllocaInst>(UO) &&
"Should have been handled by pointsToConstantMemory()"); "Should have been handled by getModRefInfoMask()");
if (isa<Argument>(UO)) { if (isa<Argument>(UO)) {
ME |= MemoryEffects::argMemOnly(MR); ME |= MemoryEffects::argMemOnly(MR);
return; return;

View File

@ -135,7 +135,7 @@ Instruction *InstCombinerImpl::SimplifyAnyMemTransfer(AnyMemTransferInst *MI) {
// If we have a store to a location which is known constant, we can conclude // If we have a store to a location which is known constant, we can conclude
// that the store must be storing the constant value (else the memory // that the store must be storing the constant value (else the memory
// wouldn't be constant), and this must be a noop. // wouldn't be constant), and this must be a noop.
if (AA->pointsToConstantMemory(MI->getDest())) { if (!isModSet(AA->getModRefInfoMask(MI->getDest()))) {
// Set the size of the copy to 0, it will be deleted on the next iteration. // Set the size of the copy to 0, it will be deleted on the next iteration.
MI->setLength(Constant::getNullValue(MI->getLength()->getType())); MI->setLength(Constant::getNullValue(MI->getLength()->getType()));
return MI; return MI;
@ -252,7 +252,7 @@ Instruction *InstCombinerImpl::SimplifyAnyMemSet(AnyMemSetInst *MI) {
// If we have a store to a location which is known constant, we can conclude // If we have a store to a location which is known constant, we can conclude
// that the store must be storing the constant value (else the memory // that the store must be storing the constant value (else the memory
// wouldn't be constant), and this must be a noop. // wouldn't be constant), and this must be a noop.
if (AA->pointsToConstantMemory(MI->getDest())) { if (!isModSet(AA->getModRefInfoMask(MI->getDest()))) {
// Set the size of the copy to 0, it will be deleted on the next iteration. // Set the size of the copy to 0, it will be deleted on the next iteration.
MI->setLength(Constant::getNullValue(MI->getLength()->getType())); MI->setLength(Constant::getNullValue(MI->getLength()->getType()));
return MI; return MI;

View File

@ -31,20 +31,20 @@ using namespace PatternMatch;
STATISTIC(NumDeadStore, "Number of dead stores eliminated"); STATISTIC(NumDeadStore, "Number of dead stores eliminated");
STATISTIC(NumGlobalCopies, "Number of allocas copied from constant global"); STATISTIC(NumGlobalCopies, "Number of allocas copied from constant global");
/// isOnlyCopiedFromConstantGlobal - Recursively walk the uses of a (derived) /// isOnlyCopiedFromConstantMemory - Recursively walk the uses of a (derived)
/// pointer to an alloca. Ignore any reads of the pointer, return false if we /// pointer to an alloca. Ignore any reads of the pointer, return false if we
/// see any stores or other unknown uses. If we see pointer arithmetic, keep /// see any stores or other unknown uses. If we see pointer arithmetic, keep
/// track of whether it moves the pointer (with IsOffset) but otherwise traverse /// track of whether it moves the pointer (with IsOffset) but otherwise traverse
/// the uses. If we see a memcpy/memmove that targets an unoffseted pointer to /// the uses. If we see a memcpy/memmove that targets an unoffseted pointer to
/// the alloca, and if the source pointer is a pointer to a constant global, we /// the alloca, and if the source pointer is a pointer to a constant memory
/// can optimize this. /// location, we can optimize this.
static bool static bool
isOnlyCopiedFromConstantMemory(AAResults *AA, AllocaInst *V, isOnlyCopiedFromConstantMemory(AAResults *AA, AllocaInst *V,
MemTransferInst *&TheCopy, MemTransferInst *&TheCopy,
SmallVectorImpl<Instruction *> &ToDelete) { SmallVectorImpl<Instruction *> &ToDelete) {
// We track lifetime intrinsics as we encounter them. If we decide to go // We track lifetime intrinsics as we encounter them. If we decide to go
// ahead and replace the value with the global, this lets the caller quickly // ahead and replace the value with the memory location, this lets the caller
// eliminate the markers. // quickly eliminate the markers.
SmallVector<std::pair<Value *, bool>, 35> ValuesToInspect; SmallVector<std::pair<Value *, bool>, 35> ValuesToInspect;
ValuesToInspect.emplace_back(V, false); ValuesToInspect.emplace_back(V, false);
@ -131,8 +131,8 @@ isOnlyCopiedFromConstantMemory(AAResults *AA, AllocaInst *V,
// If the memintrinsic isn't using the alloca as the dest, reject it. // If the memintrinsic isn't using the alloca as the dest, reject it.
if (U.getOperandNo() != 0) return false; if (U.getOperandNo() != 0) return false;
// If the source of the memcpy/move is not a constant global, reject it. // If the source of the memcpy/move is not constant, reject it.
if (!AA->pointsToConstantMemory(MI->getSource())) if (isModSet(AA->getModRefInfoMask(MI->getSource())))
return false; return false;
// Otherwise, the transform is safe. Remember the copy instruction. // Otherwise, the transform is safe. Remember the copy instruction.
@ -142,9 +142,10 @@ isOnlyCopiedFromConstantMemory(AAResults *AA, AllocaInst *V,
return true; return true;
} }
/// isOnlyCopiedFromConstantGlobal - Return true if the specified alloca is only /// isOnlyCopiedFromConstantMemory - Return true if the specified alloca is only
/// modified by a copy from a constant global. If we can prove this, we can /// modified by a copy from a constant memory location. If we can prove this, we
/// replace any uses of the alloca with uses of the global directly. /// can replace any uses of the alloca with uses of the memory location
/// directly.
static MemTransferInst * static MemTransferInst *
isOnlyCopiedFromConstantMemory(AAResults *AA, isOnlyCopiedFromConstantMemory(AAResults *AA,
AllocaInst *AI, AllocaInst *AI,
@ -398,11 +399,11 @@ Instruction *InstCombinerImpl::visitAllocaInst(AllocaInst &AI) {
} }
// Check to see if this allocation is only modified by a memcpy/memmove from // Check to see if this allocation is only modified by a memcpy/memmove from
// a constant whose alignment is equal to or exceeds that of the allocation. // a memory location whose alignment is equal to or exceeds that of the
// If this is the case, we can change all users to use the constant global // allocation. If this is the case, we can change all users to use the
// instead. This is commonly produced by the CFE by constructs like "void // constant memory location instead. This is commonly produced by the CFE by
// foo() { int A[] = {1,2,3,4,5,6,7,8,9...}; }" if 'A' is only subsequently // constructs like "void foo() { int A[] = {1,2,3,4,5,6,7,8,9...}; }" if 'A'
// read. // is only subsequently read.
SmallVector<Instruction *, 4> ToDelete; SmallVector<Instruction *, 4> ToDelete;
if (MemTransferInst *Copy = isOnlyCopiedFromConstantMemory(AA, &AI, ToDelete)) { if (MemTransferInst *Copy = isOnlyCopiedFromConstantMemory(AA, &AI, ToDelete)) {
Value *TheSrc = Copy->getSource(); Value *TheSrc = Copy->getSource();
@ -1378,7 +1379,7 @@ Instruction *InstCombinerImpl::visitStoreInst(StoreInst &SI) {
// If we have a store to a location which is known constant, we can conclude // If we have a store to a location which is known constant, we can conclude
// that the store must be storing the constant value (else the memory // that the store must be storing the constant value (else the memory
// wouldn't be constant), and this must be a noop. // wouldn't be constant), and this must be a noop.
if (AA->pointsToConstantMemory(Ptr)) if (!isModSet(AA->getModRefInfoMask(Ptr)))
return eraseInstFromFunction(SI); return eraseInstFromFunction(SI);
// Do really simple DSE, to catch cases where there are several consecutive // Do really simple DSE, to catch cases where there are several consecutive

View File

@ -1161,7 +1161,7 @@ bool llvm::canSinkOrHoistInst(Instruction &I, AAResults *AA, DominatorTree *DT,
// Loads from constant memory are always safe to move, even if they end up // Loads from constant memory are always safe to move, even if they end up
// in the same alias set as something that ends up being modified. // in the same alias set as something that ends up being modified.
if (AA->pointsToConstantMemory(LI->getOperand(0))) if (!isModSet(AA->getModRefInfoMask(LI->getOperand(0))))
return true; return true;
if (LI->hasMetadata(LLVMContext::MD_invariant_load)) if (LI->hasMetadata(LLVMContext::MD_invariant_load))
return true; return true;

View File

@ -569,7 +569,7 @@ bool LoopPredication::isLoopInvariantValue(const SCEV* S) {
if (const SCEVUnknown *U = dyn_cast<SCEVUnknown>(S)) if (const SCEVUnknown *U = dyn_cast<SCEVUnknown>(S))
if (const auto *LI = dyn_cast<LoadInst>(U->getValue())) if (const auto *LI = dyn_cast<LoadInst>(U->getValue()))
if (LI->isUnordered() && L->hasLoopInvariantOperands(LI)) if (LI->isUnordered() && L->hasLoopInvariantOperands(LI))
if (AA->pointsToConstantMemory(LI->getOperand(0)) || if (!isModSet(AA->getModRefInfoMask(LI->getOperand(0))) ||
LI->hasMetadata(LLVMContext::MD_invariant_load)) LI->hasMetadata(LLVMContext::MD_invariant_load))
return true; return true;
return false; return false;

View File

@ -6,18 +6,16 @@ declare void @dummy()
declare void @foo(ptr) declare void @foo(ptr)
; FIXME: This could be NoModRef
; CHECK-LABEL: Function: basic ; CHECK-LABEL: Function: basic
; CHECK: Just Ref: Ptr: i32* @c <-> call void @dummy() ; CHECK: NoModRef: Ptr: i32* @c <-> call void @dummy()
define void @basic(ptr %p) { define void @basic(ptr %p) {
call void @dummy() call void @dummy()
load i32, ptr @c load i32, ptr @c
ret void ret void
} }
; FIXME: This could be NoModRef
; CHECK-LABEL: Function: recphi ; CHECK-LABEL: Function: recphi
; CHECK: Just Ref: Ptr: i32* %p <-> call void @dummy() ; CHECK: NoModRef: Ptr: i32* %p <-> call void @dummy()
define void @recphi() { define void @recphi() {
entry: entry:
br label %loop br label %loop
@ -34,20 +32,20 @@ exit:
ret void ret void
} }
; Tests that readonly noalias doesn't imply !Mod yet. ; Tests that readonly noalias implies !Mod.
; ;
; CHECK-LABEL: Function: readonly_noalias ; CHECK-LABEL: Function: readonly_noalias
; CHECK: Both ModRef: Ptr: i32* %p <-> call void @foo(ptr %p) ; CHECK: Just Ref: Ptr: i32* %p <-> call void @foo(ptr %p)
define void @readonly_noalias(ptr readonly noalias %p) { define void @readonly_noalias(ptr readonly noalias %p) {
call void @foo(ptr %p) call void @foo(ptr %p)
load i32, ptr %p load i32, ptr %p
ret void ret void
} }
; Tests that readnone noalias doesn't imply !Mod yet. ; Tests that readnone noalias implies !Mod.
; ;
; CHECK-LABEL: Function: readnone_noalias ; CHECK-LABEL: Function: readnone_noalias
; CHECK: Both ModRef: Ptr: i32* %p <-> call void @foo(ptr %p) ; CHECK: Just Ref: Ptr: i32* %p <-> call void @foo(ptr %p)
define void @readnone_noalias(ptr readnone noalias %p) { define void @readnone_noalias(ptr readnone noalias %p) {
call void @foo(ptr %p) call void @foo(ptr %p)
load i32, ptr %p load i32, ptr %p

View File

@ -338,12 +338,10 @@ entry:
ret float %r ret float %r
} }
; Tests that we can't eliminate allocas copied from readonly noalias pointers yet. ; Tests that we can eliminate allocas copied from readonly noalias pointers.
define void @memcpy_from_readonly_noalias(ptr readonly noalias align 8 dereferenceable(124) %arg) { define void @memcpy_from_readonly_noalias(ptr readonly noalias align 8 dereferenceable(124) %arg) {
; CHECK-LABEL: @memcpy_from_readonly_noalias( ; CHECK-LABEL: @memcpy_from_readonly_noalias(
; CHECK-NEXT: [[ALLOCA:%.*]] = alloca [[T:%.*]], align 8 ; CHECK-NEXT: call void @bar(ptr nonnull [[ARG:%.*]]) #[[ATTR3]]
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 8 dereferenceable(124) [[ALLOCA]], ptr noundef nonnull align 8 dereferenceable(124) [[ARG:%.*]], i64 124, i1 false)
; CHECK-NEXT: call void @bar(ptr nonnull [[ALLOCA]]) #[[ATTR3]]
; CHECK-NEXT: ret void ; CHECK-NEXT: ret void
; ;
%alloca = alloca %T, align 8 %alloca = alloca %T, align 8

View File

@ -336,12 +336,10 @@ define void @store_to_constant() {
ret void ret void
} }
; We can't delete stores to readonly noalias pointers yet. ; Delete stores to readonly noalias pointers.
define void @store_to_readonly_noalias(ptr readonly noalias %0) { define void @store_to_readonly_noalias(ptr readonly noalias %0) {
; CHECK-LABEL: @store_to_readonly_noalias( ; CHECK-LABEL: @store_to_readonly_noalias(
; CHECK-NEXT: store i32 3, ptr [[TMP0:%.*]], align 4
; CHECK-NEXT: ret void ; CHECK-NEXT: ret void
;
store i32 3, ptr %0, align 4 store i32 3, ptr %0, align 4
ret void ret void
} }