diff --git a/llvm/docs/AliasAnalysis.rst b/llvm/docs/AliasAnalysis.rst --- a/llvm/docs/AliasAnalysis.rst +++ b/llvm/docs/AliasAnalysis.rst @@ -166,9 +166,25 @@ 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. +(functions, constant global variables, and the null pointer). The +``getModRefInfoMask`` function uses this 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``). 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. .. _never access memory or only read memory: diff --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h --- a/llvm/include/llvm/Analysis/AliasAnalysis.h +++ b/llvm/include/llvm/Analysis/AliasAnalysis.h @@ -362,7 +362,9 @@ /// 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. @@ -375,6 +377,22 @@ /// \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 @@ -617,6 +635,8 @@ 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, @@ -684,6 +704,10 @@ 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); } @@ -746,16 +770,19 @@ 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 @@ -804,9 +831,9 @@ 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 { @@ -859,9 +886,9 @@ 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) { diff --git a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h --- a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h +++ b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h @@ -75,9 +75,15 @@ 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); diff --git a/llvm/include/llvm/Analysis/ObjCARCAliasAnalysis.h b/llvm/include/llvm/Analysis/ObjCARCAliasAnalysis.h --- a/llvm/include/llvm/Analysis/ObjCARCAliasAnalysis.h +++ b/llvm/include/llvm/Analysis/ObjCARCAliasAnalysis.h @@ -52,8 +52,8 @@ 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); diff --git a/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h b/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h --- a/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h +++ b/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h @@ -40,8 +40,8 @@ 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, diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp --- a/llvm/lib/Analysis/AliasAnalysis.cpp +++ b/llvm/lib/Analysis/AliasAnalysis.cpp @@ -147,19 +147,25 @@ 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) { @@ -253,10 +259,11 @@ 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; } @@ -510,9 +517,11 @@ 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; } @@ -529,11 +538,10 @@ 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; - return ModRefInfo::ModRef; + // 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. + return getModRefInfoMask(Loc); } ModRefInfo AAResults::getModRefInfo(const VAArgInst *V, @@ -552,10 +560,9 @@ 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. @@ -572,10 +579,9 @@ 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. @@ -592,10 +598,9 @@ 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. diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp --- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -681,60 +681,6 @@ 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) { - assert(Visited.empty() && "Visited must be cleared after use!"); - auto _ = make_scope_exit([&]{ Visited.clear(); }); - - unsigned MaxLookup = 8; - SmallVector Worklist; - Worklist.push_back(Loc.Ptr); - do { - const Value *V = getUnderlyingObject(Worklist.pop_back_val()); - if (!Visited.insert(V).second) - continue; - - // An alloca instruction defines local memory. - if (OrLocal && isa(V)) - continue; - - // A global constant counts as local memory for our purposes. - if (const GlobalVariable *GV = dyn_cast(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); - continue; - } - - // If both select values point to local memory, then so does the select. - if (const SelectInst *SI = dyn_cast(V)) { - Worklist.push_back(SI->getTrueValue()); - Worklist.push_back(SI->getFalseValue()); - continue; - } - - // If all values incoming to a phi node point to local memory, then so does - // the phi. - if (const PHINode *PN = dyn_cast(V)) { - // Don't bother inspecting phi nodes with many operands. - if (PN->getNumIncomingValues() > MaxLookup) - return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal); - append_range(Worklist, PN->incoming_values()); - continue; - } - - // Otherwise be conservative. - return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal); - } while (!Worklist.empty() && --MaxLookup); - - return Worklist.empty(); -} - static bool isIntrinsicCall(const CallBase *Call, Intrinsic::ID IID) { const IntrinsicInst *II = dyn_cast(Call); return II && II->getIntrinsicID() == IID; @@ -820,6 +766,80 @@ return false; } +ModRefInfo BasicAAResult::getModRefInfoMask(const MemoryLocation &Loc, + AAQueryInfo &AAQI, + bool IgnoreLocals) { + if (!Loc.Ptr) + return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals); + + assert(Visited.empty() && "Visited must be cleared after use!"); + auto _ = make_scope_exit([&] { Visited.clear(); }); + + unsigned MaxLookup = 8; + SmallVector 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; + + // Ignore allocas if we were instructed to do so. + if (IgnoreLocals && isa(V)) + continue; + + // 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(V)) { + if (Arg->hasNoAliasAttr() && Arg->onlyReadsMemory()) { + Result |= ModRefInfo::Ref; + continue; + } + } + + // A global constant can't be mutated. + if (const GlobalVariable *GV = dyn_cast(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::getModRefInfoMask(Loc, AAQI, IgnoreLocals); + continue; + } + + // If both select values point to local memory, then so does the select. + if (const SelectInst *SI = dyn_cast(V)) { + Worklist.push_back(SI->getTrueValue()); + Worklist.push_back(SI->getFalseValue()); + continue; + } + + // If all values incoming to a phi node point to local memory, then so does + // the phi. + if (const PHINode *PN = dyn_cast(V)) { + // Don't bother inspecting phi nodes with many operands. + if (PN->getNumIncomingValues() > MaxLookup) + return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals); + append_range(Worklist, PN->incoming_values()); + continue; + } + + // Otherwise be conservative. + return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals); + } while (!Worklist.empty() && --MaxLookup); + + // If we hit the maximum number of instructions to examine, be conservative. + if (!Worklist.empty()) + return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals); + + return Result; +} + ModRefInfo BasicAAResult::getArgModRefInfo(const CallBase *Call, unsigned ArgIdx) { // Checking for known builtin intrinsics and target library functions. diff --git a/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp b/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp --- a/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp +++ b/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp @@ -525,7 +525,7 @@ } // 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. diff --git a/llvm/lib/Analysis/MemorySSA.cpp b/llvm/lib/Analysis/MemorySSA.cpp --- a/llvm/lib/Analysis/MemorySSA.cpp +++ b/llvm/lib/Analysis/MemorySSA.cpp @@ -378,9 +378,10 @@ const Instruction *I) { // If the memory can't be changed, then loads of the memory can't be // clobbered. - if (auto *LI = dyn_cast(I)) + if (auto *LI = dyn_cast(I)) { return I->hasMetadata(LLVMContext::MD_invariant_load) || - AA.pointsToConstantMemory(MemoryLocation::get(LI)); + !isModSet(AA.getModRefInfoMask(MemoryLocation::get(LI))); + } return false; } diff --git a/llvm/lib/Analysis/ObjCARCAliasAnalysis.cpp b/llvm/lib/Analysis/ObjCARCAliasAnalysis.cpp --- a/llvm/lib/Analysis/ObjCARCAliasAnalysis.cpp +++ b/llvm/lib/Analysis/ObjCARCAliasAnalysis.cpp @@ -68,28 +68,29 @@ 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) { diff --git a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp --- a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp +++ b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp @@ -385,23 +385,23 @@ 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, diff --git a/llvm/lib/Target/AMDGPU/AMDGPUAliasAnalysis.h b/llvm/lib/Target/AMDGPU/AMDGPUAliasAnalysis.h --- a/llvm/lib/Target/AMDGPU/AMDGPUAliasAnalysis.h +++ b/llvm/lib/Target/AMDGPU/AMDGPUAliasAnalysis.h @@ -38,8 +38,8 @@ 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. diff --git a/llvm/lib/Target/AMDGPU/AMDGPUAliasAnalysis.cpp b/llvm/lib/Target/AMDGPU/AMDGPUAliasAnalysis.cpp --- a/llvm/lib/Target/AMDGPU/AMDGPUAliasAnalysis.cpp +++ b/llvm/lib/Target/AMDGPU/AMDGPUAliasAnalysis.cpp @@ -124,29 +124,33 @@ 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) { + if (!Loc.Ptr) + return AAResultBase::getModRefInfoMask(Loc, AAQI, 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; + return ModRefInfo::NoModRef; if (const GlobalVariable *GV = dyn_cast(Base)) { if (GV->isConstant()) - return true; + return ModRefInfo::NoModRef; } else if (const Argument *Arg = dyn_cast(Base)) { const Function *F = Arg->getParent(); // Only assume constant memory for arguments on kernels. switch (F->getCallingConv()) { default: - return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal); + return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals); case CallingConv::AMDGPU_LS: case CallingConv::AMDGPU_HS: case CallingConv::AMDGPU_ES: @@ -170,8 +174,8 @@ if (F->hasParamAttribute(ArgNo, Attribute::NoAlias) && (F->hasParamAttribute(ArgNo, Attribute::ReadNone) || F->hasParamAttribute(ArgNo, Attribute::ReadOnly))) { - return true; + return ModRefInfo::Ref; } } - return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal); + return AAResultBase::getModRefInfoMask(Loc, AAQI, IgnoreLocals); } diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -140,13 +140,14 @@ 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(UO) && - "Should have been handled by pointsToConstantMemory()"); + "Should have been handled by getModRefInfoMask()"); if (isa(UO)) { ME |= MemoryEffects::argMemOnly(MR); return; diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -135,7 +135,7 @@ // 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; @@ -252,7 +252,7 @@ // 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; diff --git a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp @@ -31,20 +31,20 @@ 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, Value *V, MemTransferInst *&TheCopy, SmallVectorImpl &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, 35> ValuesToInspect; ValuesToInspect.emplace_back(V, false); @@ -128,8 +128,8 @@ // 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. @@ -139,9 +139,10 @@ 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, @@ -395,11 +396,11 @@ } // 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 ToDelete; if (MemTransferInst *Copy = isOnlyCopiedFromConstantMemory(AA, &AI, ToDelete)) { Value *TheSrc = Copy->getSource(); @@ -1372,7 +1373,7 @@ // 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 diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp --- a/llvm/lib/Transforms/Scalar/LICM.cpp +++ b/llvm/lib/Transforms/Scalar/LICM.cpp @@ -1161,7 +1161,7 @@ // 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; diff --git a/llvm/lib/Transforms/Scalar/LoopPredication.cpp b/llvm/lib/Transforms/Scalar/LoopPredication.cpp --- a/llvm/lib/Transforms/Scalar/LoopPredication.cpp +++ b/llvm/lib/Transforms/Scalar/LoopPredication.cpp @@ -569,7 +569,7 @@ if (const SCEVUnknown *U = dyn_cast(S)) if (const auto *LI = dyn_cast(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; diff --git a/llvm/test/Analysis/BasicAA/constant-memory.ll b/llvm/test/Analysis/BasicAA/constant-memory.ll --- a/llvm/test/Analysis/BasicAA/constant-memory.ll +++ b/llvm/test/Analysis/BasicAA/constant-memory.ll @@ -4,18 +4,16 @@ declare void @dummy() -; 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 diff --git a/llvm/test/Analysis/BasicAA/readonly-noalias.ll b/llvm/test/Analysis/BasicAA/readonly-noalias.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Analysis/BasicAA/readonly-noalias.ll @@ -0,0 +1,13 @@ +; Tests that readonly noalias implies !Mod. +; +; RUN: opt < %s -passes=aa-eval -print-all-alias-modref-info 2>&1 | FileCheck %s + +declare void @foo(ptr) + +; CHECK-LABEL: Function: basic +; CHECK: Just Ref: Ptr: i32* %p <-> call void @foo(ptr %p) +define void @basic(ptr readonly noalias %p) { + call void @foo(ptr %p) + load i32, ptr %p + ret void +} diff --git a/llvm/test/Transforms/InstCombine/copied-from-readonly-noalias.ll b/llvm/test/Transforms/InstCombine/copied-from-readonly-noalias.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/copied-from-readonly-noalias.ll @@ -0,0 +1,20 @@ +; Tests that we can eliminate allocas copied from readonly noalias pointers. +; +; RUN: opt -S < %s -passes=instcombine | FileCheck %s + +%struct.BigStruct = type { i32, i32, i32 } + +declare void @bar(ptr readonly noalias noundef nocapture) readonly + +declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) + +define void @foo(ptr nocapture noundef readonly noalias align(4) dereferenceable(12) %arg) { +; CHECK-LABEL: @foo( +; CHECK-NEXT: call void @bar(ptr noalias nocapture noundef nonnull readonly [[ARG:%.*]]) #[[ATTR0:[0-9]+]] +; CHECK-NEXT: ret void +; + %alloca = alloca %struct.BigStruct, align 4 + call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 4 dereferenceable(12) %alloca, ptr noundef nonnull align 4 dereferenceable(12) %arg, i64 12, i1 false) + call void @bar(ptr readonly noalias noundef nonnull nocapture %alloca) readonly + ret void +} diff --git a/llvm/test/Transforms/InstCombine/store.ll b/llvm/test/Transforms/InstCombine/store.ll --- a/llvm/test/Transforms/InstCombine/store.ll +++ b/llvm/test/Transforms/InstCombine/store.ll @@ -336,6 +336,14 @@ ret void } +; Delete stores to readonly noalias pointers. +define void @store_to_readonly_noalias(ptr readonly noalias %0) { +; CHECK-LABEL: @store_to_readonly_noalias( +; CHECK-NEXT: ret void + store i32 3, ptr %0, align 4 + ret void +} + !0 = !{!4, !4, i64 0} !1 = !{!"omnipotent char", !2} !2 = !{!"Simple C/C++ TBAA"}