Several other tidbits of information are often collected by various alias
analysis implementations and can be put to good use by various clients.
-The ``pointsToConstantMemory`` method
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The ``pointsToConstantMemory`` method returns true if and only if the analysis
-can prove that the pointer only points to unchanging memory locations
-(functions, constant global variables, and the null pointer). This information
-can be used to refine mod/ref information: it is impossible for an unchanging
-memory location to be modified.
+The ``getModRefInfoMask`` method
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``getModRefInfoMask`` method returns a bound on Mod/Ref information for
+the supplied pointer, based on knowledge about whether the pointer points to
+globally-constant memory (for which it returns ``NoModRef``) or
+locally-invariant memory (for which it returns ``Ref``). Globally-constant
+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:
/// Checks whether the given location points to constant memory, or if
/// \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
/// interface.
/// \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
/// 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
AAQueryInfo &AAQI);
bool pointsToConstantMemory(const MemoryLocation &Loc, AAQueryInfo &AAQI,
bool OrLocal = false);
+ ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
+ bool IgnoreLocals = false);
ModRefInfo getModRefInfo(Instruction *I, const CallBase *Call2,
AAQueryInfo &AAQIP);
ModRefInfo getModRefInfo(const CallBase *Call, const MemoryLocation &Loc,
bool pointsToConstantMemory(const MemoryLocation &Loc, bool OrLocal = false) {
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) {
return AA.getModRefInfo(Call, Loc, AAQI);
}
virtual AliasResult alias(const MemoryLocation &LocA,
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
/// @{
+ /// 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
/// 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
return Result.alias(LocA, LocB, AAQI);
}
- bool pointsToConstantMemory(const MemoryLocation &Loc, AAQueryInfo &AAQI,
- bool OrLocal) override {
- return Result.pointsToConstantMemory(Loc, AAQI, OrLocal);
+ ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
+ bool IgnoreLocals) override {
+ return Result.getModRefInfoMask(Loc, AAQI, IgnoreLocals);
}
ModRefInfo getArgModRefInfo(const CallBase *Call, unsigned ArgIdx) override {
return AliasResult::MayAlias;
}
- bool pointsToConstantMemory(const MemoryLocation &Loc, AAQueryInfo &AAQI,
- bool OrLocal) {
- return false;
+ ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
+ bool IgnoreLocals) {
+ return ModRefInfo::ModRef;
}
ModRefInfo getArgModRefInfo(const CallBase *Call, unsigned ArgIdx) {
ModRefInfo getModRefInfo(const CallBase *Call1, const CallBase *Call2,
AAQueryInfo &AAQI);
- /// Chases pointers until we find a (constant global) or not.
- bool pointsToConstantMemory(const MemoryLocation &Loc, AAQueryInfo &AAQI,
- bool OrLocal);
+ /// 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, AAQueryInfo &AAQI,
+ bool IgnoreLocals = false);
/// Get the location associated with a pointer argument of a callsite.
ModRefInfo getArgModRefInfo(const CallBase *Call, unsigned ArgIdx);
AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB,
AAQueryInfo &AAQI);
- bool pointsToConstantMemory(const MemoryLocation &Loc, AAQueryInfo &AAQI,
- bool OrLocal);
+ ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
+ bool IgnoreLocals);
using AAResultBase::getMemoryEffects;
MemoryEffects getMemoryEffects(const Function *F);
AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB,
AAQueryInfo &AAQI);
- bool pointsToConstantMemory(const MemoryLocation &Loc, AAQueryInfo &AAQI,
- bool OrLocal);
+ ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
+ bool IgnoreLocals);
MemoryEffects getMemoryEffects(const CallBase *Call, AAQueryInfo &AAQI);
MemoryEffects getMemoryEffects(const Function *F);
ModRefInfo getModRefInfo(const CallBase *Call, const MemoryLocation &Loc,
return Result;
}
-bool AAResults::pointsToConstantMemory(const MemoryLocation &Loc,
- bool OrLocal) {
+ModRefInfo AAResults::getModRefInfoMask(const MemoryLocation &Loc,
+ bool IgnoreLocals) {
SimpleAAQueryInfo AAQIP(*this);
- return pointsToConstantMemory(Loc, AAQIP, OrLocal);
+ return getModRefInfoMask(Loc, AAQIP, IgnoreLocals);
}
-bool AAResults::pointsToConstantMemory(const MemoryLocation &Loc,
- AAQueryInfo &AAQI, bool OrLocal) {
- for (const auto &AA : AAs)
- if (AA->pointsToConstantMemory(Loc, AAQI, OrLocal))
- return true;
+ModRefInfo AAResults::getModRefInfoMask(const MemoryLocation &Loc,
+ AAQueryInfo &AAQI, bool IgnoreLocals) {
+ ModRefInfo Result = ModRefInfo::ModRef;
- 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) {
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.
- if (isModSet(Result) && pointsToConstantMemory(Loc, AAQI, /*OrLocal*/ false))
- Result &= ModRefInfo::Ref;
+ if (!isNoModRef(Result))
+ Result &= getModRefInfoMask(Loc);
return Result;
}
if (AR == AliasResult::NoAlias)
return ModRefInfo::NoModRef;
- // If the pointer is a pointer to constant memory, then it could not have
- // been modified by this store.
- if (pointsToConstantMemory(Loc, AAQI))
+ // Examine the ModRef mask. If Mod isn't present, then return NoModRef.
+ // This ensures that if Loc is a constant memory location, we take into
+ // account the fact that the store definitely could not modify the memory
+ // location.
+ if (!isModSet(getModRefInfoMask(Loc)))
return ModRefInfo::NoModRef;
}
ModRefInfo AAResults::getModRefInfo(const FenceInst *S,
const MemoryLocation &Loc,
AAQueryInfo &AAQI) {
- // If we know that the location is a constant memory location, the fence
- // cannot modify this location.
- if (Loc.Ptr && pointsToConstantMemory(Loc, AAQI))
- return ModRefInfo::Ref;
+ // All we know about a fence instruction is what we get from the ModRef
+ // mask: if Loc is a constant memory location, the fence definitely could
+ // not modify it.
+ if (Loc.Ptr)
+ return getModRefInfoMask(Loc);
return ModRefInfo::ModRef;
}
if (AR == AliasResult::NoAlias)
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.
- if (pointsToConstantMemory(Loc, AAQI))
- return ModRefInfo::NoModRef;
+ return getModRefInfoMask(Loc, AAQI);
}
// Otherwise, a va_arg reads and writes.
const MemoryLocation &Loc,
AAQueryInfo &AAQI) {
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.
- if (pointsToConstantMemory(Loc, AAQI))
- return ModRefInfo::NoModRef;
+ return getModRefInfoMask(Loc, AAQI);
}
// Otherwise, a catchpad reads and writes.
const MemoryLocation &Loc,
AAQueryInfo &AAQI) {
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.
- if (pointsToConstantMemory(Loc, AAQI))
- return ModRefInfo::NoModRef;
+ return getModRefInfoMask(Loc, AAQI);
}
// Otherwise, a catchret reads and writes.
return Decomposed;
}
-/// Returns whether the given pointer value points to memory that is local to
-/// the function, with global constants being considered local to all
-/// functions.
-bool BasicAAResult::pointsToConstantMemory(const MemoryLocation &Loc,
- AAQueryInfo &AAQI, bool OrLocal) {
+ModRefInfo BasicAAResult::getModRefInfoMask(const MemoryLocation &Loc,
+ AAQueryInfo &AAQI,
+ bool IgnoreLocals) {
assert(Visited.empty() && "Visited must be cleared after use!");
- auto _ = make_scope_exit([&]{ Visited.clear(); });
+ auto _ = make_scope_exit([&] { Visited.clear(); });
unsigned MaxLookup = 8;
SmallVector<const Value *, 16> Worklist;
Worklist.push_back(Loc.Ptr);
+ ModRefInfo Result = ModRefInfo::NoModRef;
+
do {
const Value *V = getUnderlyingObject(Worklist.pop_back_val());
if (!Visited.insert(V).second)
continue;
- // An alloca instruction defines local memory.
- if (OrLocal && isa<AllocaInst>(V))
+ // Ignore allocas if we were instructed to do so.
+ if (IgnoreLocals && isa<AllocaInst>(V))
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)) {
// 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
// others. GV may even be a declaration, not a definition.
if (!GV->isConstant())
- return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal);
+ return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals);
continue;
}
if (const PHINode *PN = dyn_cast<PHINode>(V)) {
// Don't bother inspecting phi nodes with many operands.
if (PN->getNumIncomingValues() > MaxLookup)
- return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal);
+ return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals);
append_range(Worklist, PN->incoming_values());
continue;
}
// Otherwise be conservative.
- return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal);
+ return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals);
} 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) {
}
// Stores don't alias loads from read-only memory.
- if (BatchAA.pointsToConstantMemory(LoadLoc))
+ if (!isModSet(BatchAA.getModRefInfoMask(LoadLoc)))
continue;
// Stores depend on may/must aliased loads.
const Instruction *I) {
// If the memory can't be changed, then loads of the memory can't be
// clobbered.
- if (auto *LI = dyn_cast<LoadInst>(I))
+ if (auto *LI = dyn_cast<LoadInst>(I)) {
return I->hasMetadata(LLVMContext::MD_invariant_load) ||
- AA.pointsToConstantMemory(MemoryLocation::get(LI));
+ !isModSet(AA.getModRefInfoMask(MemoryLocation::get(LI)));
+ }
return false;
}
return AliasResult::MayAlias;
}
-bool ObjCARCAAResult::pointsToConstantMemory(const MemoryLocation &Loc,
- AAQueryInfo &AAQI, bool OrLocal) {
+ModRefInfo ObjCARCAAResult::getModRefInfoMask(const MemoryLocation &Loc,
+ AAQueryInfo &AAQI,
+ bool IgnoreLocals) {
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
// a precise alias query.
const Value *S = GetRCIdentityRoot(Loc.Ptr);
- if (AAResultBase::pointsToConstantMemory(
- MemoryLocation(S, Loc.Size, Loc.AATags), AAQI, OrLocal))
- return true;
+ if (isNoModRef(AAResultBase::getModRefInfoMask(
+ MemoryLocation(S, Loc.Size, Loc.AATags), AAQI, IgnoreLocals)))
+ return ModRefInfo::NoModRef;
// If that failed, climb to the underlying object, including climbing through
// ObjC-specific no-ops, and try making an imprecise alias query.
const Value *U = GetUnderlyingObjCPtr(S);
if (U != S)
- return AAResultBase::pointsToConstantMemory(
- MemoryLocation::getBeforeOrAfter(U), AAQI, OrLocal);
+ return AAResultBase::getModRefInfoMask(MemoryLocation::getBeforeOrAfter(U),
+ AAQI, IgnoreLocals);
// If that failed, fail. We don't need to chain here, since that's covered
// by the earlier precise query.
- return false;
+ return ModRefInfo::ModRef;
}
MemoryEffects ObjCARCAAResult::getMemoryEffects(const Function *F) {
return AliasResult::NoAlias;
}
-bool TypeBasedAAResult::pointsToConstantMemory(const MemoryLocation &Loc,
- AAQueryInfo &AAQI,
- bool OrLocal) {
+ModRefInfo TypeBasedAAResult::getModRefInfoMask(const MemoryLocation &Loc,
+ AAQueryInfo &AAQI,
+ bool IgnoreLocals) {
if (!EnableTBAA)
- return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal);
+ return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals);
const MDNode *M = Loc.AATags.TBAA;
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
// to constant memory.
if ((!isStructPathTBAA(M) && TBAANode(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,
return AAResultBase::alias(LocA, LocB, AAQI);
}
-bool AMDGPUAAResult::pointsToConstantMemory(const MemoryLocation &Loc,
- AAQueryInfo &AAQI, bool OrLocal) {
+ModRefInfo AMDGPUAAResult::getModRefInfoMask(const MemoryLocation &Loc,
+ AAQueryInfo &AAQI,
+ bool IgnoreLocals) {
unsigned AS = Loc.Ptr->getType()->getPointerAddressSpace();
if (AS == AMDGPUAS::CONSTANT_ADDRESS ||
AS == AMDGPUAS::CONSTANT_ADDRESS_32BIT)
- return true;
+ return ModRefInfo::NoModRef;
const Value *Base = getUnderlyingObject(Loc.Ptr);
AS = Base->getType()->getPointerAddressSpace();
if (AS == AMDGPUAS::CONSTANT_ADDRESS ||
AS == AMDGPUAS::CONSTANT_ADDRESS_32BIT)
- return true;
-
- if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(Base)) {
- 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;
- }
+ return ModRefInfo::NoModRef;
- 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);
+ return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals);
}
AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB,
AAQueryInfo &AAQI);
- bool pointsToConstantMemory(const MemoryLocation &Loc, AAQueryInfo &AAQI,
- bool OrLocal);
+ ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
+ bool IgnoreLocals);
};
/// Analysis pass providing a never-invalidated alias analysis result.
ME |= MemoryEffects::argMemOnly(ModRefInfo::ModRef);
auto AddLocAccess = [&](const MemoryLocation &Loc, ModRefInfo MR) {
- // Ignore accesses to local memory.
- if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true))
+ // Ignore accesses to known-invariant or local memory.
+ MR &= AAR.getModRefInfoMask(Loc, /*IgnoreLocal=*/true);
+ if (isNoModRef(MR))
return;
const Value *UO = getUnderlyingObject(Loc.Ptr);
assert(!isa<AllocaInst>(UO) &&
- "Should have been handled by pointsToConstantMemory()");
+ "Should have been handled by getModRefInfoMask()");
if (isa<Argument>(UO)) {
ME |= MemoryEffects::argMemOnly(MR);
return;
// 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
// 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.
MI->setLength(Constant::getNullValue(MI->getLength()->getType()));
return MI;
// 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
// 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.
MI->setLength(Constant::getNullValue(MI->getLength()->getType()));
return MI;
STATISTIC(NumDeadStore, "Number of dead stores eliminated");
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
/// 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
/// 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
-/// can optimize this.
+/// the alloca, and if the source pointer is a pointer to a constant memory
+/// location, we can optimize this.
static bool
isOnlyCopiedFromConstantMemory(AAResults *AA, AllocaInst *V,
MemTransferInst *&TheCopy,
SmallVectorImpl<Instruction *> &ToDelete) {
// 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
- // eliminate the markers.
+ // ahead and replace the value with the memory location, this lets the caller
+ // quickly eliminate the markers.
SmallVector<std::pair<Value *, bool>, 35> ValuesToInspect;
ValuesToInspect.emplace_back(V, false);
// If the memintrinsic isn't using the alloca as the dest, reject it.
if (U.getOperandNo() != 0) return false;
- // If the source of the memcpy/move is not a constant global, reject it.
- if (!AA->pointsToConstantMemory(MI->getSource()))
+ // If the source of the memcpy/move is not constant, reject it.
+ if (isModSet(AA->getModRefInfoMask(MI->getSource())))
return false;
// Otherwise, the transform is safe. Remember the copy instruction.
return true;
}
-/// isOnlyCopiedFromConstantGlobal - Return true if the specified alloca is only
-/// modified by a copy from a constant global. If we can prove this, we can
-/// replace any uses of the alloca with uses of the global directly.
+/// isOnlyCopiedFromConstantMemory - Return true if the specified alloca is only
+/// modified by a copy from a constant memory location. If we can prove this, we
+/// can replace any uses of the alloca with uses of the memory location
+/// directly.
static MemTransferInst *
isOnlyCopiedFromConstantMemory(AAResults *AA,
AllocaInst *AI,
}
// 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.
- // If this is the case, we can change all users to use the constant global
- // instead. This is commonly produced by the CFE by constructs like "void
- // foo() { int A[] = {1,2,3,4,5,6,7,8,9...}; }" if 'A' is only subsequently
- // read.
+ // a memory location whose alignment is equal to or exceeds that of the
+ // allocation. If this is the case, we can change all users to use the
+ // constant memory location instead. This is commonly produced by the CFE by
+ // constructs like "void foo() { int A[] = {1,2,3,4,5,6,7,8,9...}; }" if 'A'
+ // is only subsequently read.
SmallVector<Instruction *, 4> ToDelete;
if (MemTransferInst *Copy = isOnlyCopiedFromConstantMemory(AA, &AI, ToDelete)) {
Value *TheSrc = Copy->getSource();
// 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
// wouldn't be constant), and this must be a noop.
- if (AA->pointsToConstantMemory(Ptr))
+ if (!isModSet(AA->getModRefInfoMask(Ptr)))
return eraseInstFromFunction(SI);
// Do really simple DSE, to catch cases where there are several consecutive
// 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.
- if (AA->pointsToConstantMemory(LI->getOperand(0)))
+ if (!isModSet(AA->getModRefInfoMask(LI->getOperand(0))))
return true;
if (LI->hasMetadata(LLVMContext::MD_invariant_load))
return true;
if (const SCEVUnknown *U = dyn_cast<SCEVUnknown>(S))
if (const auto *LI = dyn_cast<LoadInst>(U->getValue()))
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))
return true;
return false;
declare void @foo(ptr)
-; FIXME: This could be NoModRef
; 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) {
call void @dummy()
load i32, ptr @c
ret void
}
-; FIXME: This could be NoModRef
; CHECK-LABEL: Function: recphi
-; CHECK: Just Ref: Ptr: i32* %p <-> call void @dummy()
+; CHECK: NoModRef: Ptr: i32* %p <-> call void @dummy()
define void @recphi() {
entry:
br label %loop
ret void
}
-; Tests that readonly noalias doesn't imply !Mod yet.
+; Tests that readonly noalias implies !Mod.
;
; 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) {
call void @foo(ptr %p)
load i32, ptr %p
ret void
}
-; Tests that readnone noalias doesn't imply !Mod yet.
+; Tests that readnone noalias implies !Mod.
;
; 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) {
call void @foo(ptr %p)
load i32, ptr %p
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) {
; CHECK-LABEL: @memcpy_from_readonly_noalias(
-; CHECK-NEXT: [[ALLOCA:%.*]] = alloca [[T:%.*]], align 8
-; 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: call void @bar(ptr nonnull [[ARG:%.*]]) #[[ATTR3]]
; CHECK-NEXT: ret void
;
%alloca = alloca %T, align 8
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) {
; CHECK-LABEL: @store_to_readonly_noalias(
-; CHECK-NEXT: store i32 3, ptr [[TMP0:%.*]], align 4
; CHECK-NEXT: ret void
-;
store i32 3, ptr %0, align 4
ret void
}