Index: llvm/include/llvm/IR/Attributes.h =================================================================== --- llvm/include/llvm/IR/Attributes.h +++ llvm/include/llvm/IR/Attributes.h @@ -146,6 +146,8 @@ static Attribute getWithPreallocatedType(LLVMContext &Context, Type *Ty); static Attribute getWithInAllocaType(LLVMContext &Context, Type *Ty); static Attribute getWithUWTableKind(LLVMContext &Context, UWTableKind Kind); + static Attribute getWithModRefBehavior(LLVMContext &Context, + FunctionModRefBehavior FMRB); /// For a typed attribute, return the equivalent attribute with the type /// changed to \p ReplacementTy. @@ -379,6 +381,7 @@ Optional getVScaleRangeMax() const; UWTableKind getUWTableKind() const; AllocFnKind getAllocKind() const; + FunctionModRefBehavior getModRefBehavior() const; std::string getAsString(bool InAttrGrp = false) const; /// Return true if this attribute set belongs to the LLVMContext. Index: llvm/include/llvm/IR/Function.h =================================================================== --- llvm/include/llvm/IR/Function.h +++ llvm/include/llvm/IR/Function.h @@ -491,54 +491,35 @@ void setPresplitCoroutine() { addFnAttr(Attribute::PresplitCoroutine); } void setSplittedCoroutine() { removeFnAttr(Attribute::PresplitCoroutine); } + FunctionModRefBehavior getModRefBehavior() const; + void setModRefBehavior(FunctionModRefBehavior FMRB); + /// Determine if the function does not access memory. - bool doesNotAccessMemory() const { - return hasFnAttribute(Attribute::ReadNone); - } - void setDoesNotAccessMemory() { - addFnAttr(Attribute::ReadNone); - } + bool doesNotAccessMemory() const; + void setDoesNotAccessMemory(); /// Determine if the function does not access or only reads memory. - bool onlyReadsMemory() const { - return doesNotAccessMemory() || hasFnAttribute(Attribute::ReadOnly); - } - void setOnlyReadsMemory() { - addFnAttr(Attribute::ReadOnly); - } + bool onlyReadsMemory() const; + void setOnlyReadsMemory(); /// Determine if the function does not access or only writes memory. - bool onlyWritesMemory() const { - return doesNotAccessMemory() || hasFnAttribute(Attribute::WriteOnly); - } - void setOnlyWritesMemory() { - addFnAttr(Attribute::WriteOnly); - } + bool onlyWritesMemory() const; + void setOnlyWritesMemory(); /// Determine if the call can access memmory only using pointers based /// on its arguments. - bool onlyAccessesArgMemory() const { - return hasFnAttribute(Attribute::ArgMemOnly); - } - void setOnlyAccessesArgMemory() { addFnAttr(Attribute::ArgMemOnly); } + bool onlyAccessesArgMemory() const; + void setOnlyAccessesArgMemory(); /// Determine if the function may only access memory that is /// inaccessible from the IR. - bool onlyAccessesInaccessibleMemory() const { - return hasFnAttribute(Attribute::InaccessibleMemOnly); - } - void setOnlyAccessesInaccessibleMemory() { - addFnAttr(Attribute::InaccessibleMemOnly); - } + bool onlyAccessesInaccessibleMemory() const; + void setOnlyAccessesInaccessibleMemory(); /// Determine if the function may only access memory that is /// either inaccessible from the IR or pointed to by its arguments. - bool onlyAccessesInaccessibleMemOrArgMem() const { - return hasFnAttribute(Attribute::InaccessibleMemOrArgMemOnly); - } - void setOnlyAccessesInaccessibleMemOrArgMem() { - addFnAttr(Attribute::InaccessibleMemOrArgMemOnly); - } + bool onlyAccessesInaccessibleMemOrArgMem() const; + void setOnlyAccessesInaccessibleMemOrArgMem(); /// Determine if the function cannot return. bool doesNotReturn() const { Index: llvm/include/llvm/IR/InstrTypes.h =================================================================== --- llvm/include/llvm/IR/InstrTypes.h +++ llvm/include/llvm/IR/InstrTypes.h @@ -1847,47 +1847,37 @@ /// Return true if the call should not be inlined. bool isNoInline() const { return hasFnAttr(Attribute::NoInline); } void setIsNoInline() { addFnAttr(Attribute::NoInline); } + + FunctionModRefBehavior getModRefBehavior() const; + void setModRefBehavior(FunctionModRefBehavior FMRB); + /// Determine if the call does not access memory. - bool doesNotAccessMemory() const { return hasFnAttr(Attribute::ReadNone); } - void setDoesNotAccessMemory() { addFnAttr(Attribute::ReadNone); } + bool doesNotAccessMemory() const; + void setDoesNotAccessMemory(); /// Determine if the call does not access or only reads memory. - bool onlyReadsMemory() const { - return hasImpliedFnAttr(Attribute::ReadOnly); - } - - void setOnlyReadsMemory() { addFnAttr(Attribute::ReadOnly); } + bool onlyReadsMemory() const; + void setOnlyReadsMemory(); /// Determine if the call does not access or only writes memory. - bool onlyWritesMemory() const { - return hasImpliedFnAttr(Attribute::WriteOnly); - } - void setOnlyWritesMemory() { addFnAttr(Attribute::WriteOnly); } + bool onlyWritesMemory() const; + void setOnlyWritesMemory(); /// Determine if the call can access memmory only using pointers based /// on its arguments. - bool onlyAccessesArgMemory() const { - return hasFnAttr(Attribute::ArgMemOnly); - } - void setOnlyAccessesArgMemory() { addFnAttr(Attribute::ArgMemOnly); } + bool onlyAccessesArgMemory() const; + void setOnlyAccessesArgMemory(); /// Determine if the function may only access memory that is /// inaccessible from the IR. - bool onlyAccessesInaccessibleMemory() const { - return hasFnAttr(Attribute::InaccessibleMemOnly); - } - void setOnlyAccessesInaccessibleMemory() { - addFnAttr(Attribute::InaccessibleMemOnly); - } + bool onlyAccessesInaccessibleMemory() const; + void setOnlyAccessesInaccessibleMemory(); /// Determine if the function may only access memory that is /// either inaccessible from the IR or pointed to by its arguments. - bool onlyAccessesInaccessibleMemOrArgMem() const { - return hasFnAttr(Attribute::InaccessibleMemOrArgMemOnly); - } - void setOnlyAccessesInaccessibleMemOrArgMem() { - addFnAttr(Attribute::InaccessibleMemOrArgMemOnly); - } + bool onlyAccessesInaccessibleMemOrArgMem() const; + void setOnlyAccessesInaccessibleMemOrArgMem(); + /// Determine if the call cannot return. bool doesNotReturn() const { return hasFnAttr(Attribute::NoReturn); } void setDoesNotReturn() { addFnAttr(Attribute::NoReturn); } Index: llvm/lib/Analysis/BasicAliasAnalysis.cpp =================================================================== --- llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -740,30 +740,11 @@ return II && II->getIntrinsicID() == IID; } -static FunctionModRefBehavior getModRefBehaviorFromAttrs(AttributeSet Attrs) { - if (Attrs.hasAttribute(Attribute::ReadNone)) - return FunctionModRefBehavior::none(); - - ModRefInfo MR = ModRefInfo::ModRef; - if (Attrs.hasAttribute(Attribute::ReadOnly)) - MR = ModRefInfo::Ref; - else if (Attrs.hasAttribute(Attribute::WriteOnly)) - MR = ModRefInfo::Mod; - - if (Attrs.hasAttribute(Attribute::ArgMemOnly)) - return FunctionModRefBehavior::argMemOnly(MR); - if (Attrs.hasAttribute(Attribute::InaccessibleMemOnly)) - return FunctionModRefBehavior::inaccessibleMemOnly(MR); - if (Attrs.hasAttribute(Attribute::InaccessibleMemOrArgMemOnly)) - return FunctionModRefBehavior::inaccessibleOrArgMemOnly(MR); - return FunctionModRefBehavior(MR); -} - /// Returns the behavior when calling the given call site. FunctionModRefBehavior BasicAAResult::getModRefBehavior(const CallBase *Call, AAQueryInfo &AAQI) { FunctionModRefBehavior Min = - getModRefBehaviorFromAttrs(Call->getAttributes().getFnAttrs()); + Call->getAttributes().getFnAttrs().getModRefBehavior(); if (const Function *F = dyn_cast(Call->getCalledOperand())) { FunctionModRefBehavior FMRB = AAQI.AAR.getModRefBehavior(F); @@ -791,7 +772,7 @@ FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::ModRef); } - return getModRefBehaviorFromAttrs(F->getAttributes().getFnAttrs()); + return F->getModRefBehavior(); } /// Returns true if this is a writeonly (i.e Mod only) parameter. Index: llvm/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/lib/AsmParser/LLParser.cpp +++ llvm/lib/AsmParser/LLParser.cpp @@ -148,6 +148,44 @@ std::make_pair(I.first, std::make_pair(I.second, LocTy()))); } +// TODO: This is only a temporary upgrade path, drop it once tests are updated. +static void upgradeMemoryAttrs(AttrBuilder &B) { + if (B.contains(Attribute::Memory)) + return; + + if (B.contains(Attribute::ReadNone)) { + B.removeAttribute(Attribute::ReadNone); + B.addMemoryAttr(FunctionModRefBehavior::none()); + return; + } + + ModRefInfo MR = ModRefInfo::ModRef; + if (B.contains(Attribute::ReadOnly)) { + MR = ModRefInfo::Ref; + B.removeAttribute(Attribute::ReadOnly); + } else if (B.contains(Attribute::WriteOnly)) { + MR = ModRefInfo::Mod; + B.removeAttribute(Attribute::WriteOnly); + } + + FunctionModRefBehavior FMRB(MR); + if (B.contains(Attribute::ArgMemOnly)) { + FMRB = FunctionModRefBehavior::argMemOnly(MR); + B.removeAttribute(Attribute::ArgMemOnly); + B.removeAttribute(Attribute::InaccessibleMemOrArgMemOnly); + } else if (B.contains(Attribute::InaccessibleMemOnly)) { + FMRB = FunctionModRefBehavior::inaccessibleMemOnly(MR); + B.removeAttribute(Attribute::InaccessibleMemOnly); + } else if (B.contains(Attribute::InaccessibleMemOrArgMemOnly)) { + FMRB = FunctionModRefBehavior::inaccessibleMemOnly(MR) | + FunctionModRefBehavior::argMemOnly(MR); + B.removeAttribute(Attribute::InaccessibleMemOrArgMemOnly); + } + + if (FMRB != FunctionModRefBehavior::unknown()) + B.addMemoryAttr(FMRB); +} + /// validateEndOfModule - Do final validity and basic correctness checks at the /// end of the module. bool LLParser::validateEndOfModule(bool UpgradeDebugInfo) { @@ -165,6 +203,8 @@ B.merge(R->second); } + upgradeMemoryAttrs(B); + if (Function *Fn = dyn_cast(V)) { AttributeList AS = Fn->getAttributes(); AttrBuilder FnAttrs(M->getContext(), AS.getFnAttrs()); @@ -5834,6 +5874,8 @@ FuncAttrs.removeAttribute(Attribute::Alignment); } + upgradeMemoryAttrs(FuncAttrs); + // Okay, if we got here, the function is syntactically valid. Convert types // and do semantic checks. std::vector ParamTypeList; @@ -6604,6 +6646,8 @@ if (I != E) return error(CallLoc, "not enough parameters specified for call"); + upgradeMemoryAttrs(FnAttrs); + // Finish off the Attribute and check them AttributeList PAL = AttributeList::get(Context, AttributeSet::get(Context, FnAttrs), @@ -7312,6 +7356,8 @@ if (I != E) return error(CallLoc, "not enough parameters specified for call"); + upgradeMemoryAttrs(FnAttrs); + // Finish off the Attribute and check them AttributeList PAL = AttributeList::get(Context, AttributeSet::get(Context, FnAttrs), Index: llvm/lib/IR/AttributeImpl.h =================================================================== --- llvm/lib/IR/AttributeImpl.h +++ llvm/lib/IR/AttributeImpl.h @@ -263,6 +263,7 @@ Optional getVScaleRangeMax() const; UWTableKind getUWTableKind() const; AllocFnKind getAllocKind() const; + FunctionModRefBehavior getModRefBehavior() const; std::string getAsString(bool InAttrGrp) const; Type *getAttributeType(Attribute::AttrKind Kind) const; Index: llvm/lib/IR/Attributes.cpp =================================================================== --- llvm/lib/IR/Attributes.cpp +++ llvm/lib/IR/Attributes.cpp @@ -211,6 +211,11 @@ return get(Context, UWTable, uint64_t(Kind)); } +Attribute Attribute::getWithModRefBehavior(LLVMContext &Context, + FunctionModRefBehavior FMRB) { + return get(Context, Memory, FMRB.toIntValue()); +} + Attribute Attribute::getWithAllocSizeArgs(LLVMContext &Context, unsigned ElemSizeArg, const Optional &NumElemsArg) { @@ -833,6 +838,11 @@ return SetNode ? SetNode->getAllocKind() : AllocFnKind::Unknown; } +FunctionModRefBehavior AttributeSet::getModRefBehavior() const { + return SetNode ? SetNode->getModRefBehavior() + : FunctionModRefBehavior::unknown(); +} + std::string AttributeSet::getAsString(bool InAttrGrp) const { return SetNode ? SetNode->getAsString(InAttrGrp) : ""; } @@ -1011,6 +1021,12 @@ return AllocFnKind::Unknown; } +FunctionModRefBehavior AttributeSetNode::getModRefBehavior() const { + if (auto A = findEnumAttribute(Attribute::Memory)) + return A->getModRefBehavior(); + return FunctionModRefBehavior::unknown(); +} + std::string AttributeSetNode::getAsString(bool InAttrGrp) const { std::string Str; for (iterator I = begin(), E = end(); I != E; ++I) { Index: llvm/lib/IR/Function.cpp =================================================================== --- llvm/lib/IR/Function.cpp +++ llvm/lib/IR/Function.cpp @@ -51,6 +51,7 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Metadata.h" +#include "llvm/IR/ModRef.h" #include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" #include "llvm/IR/SymbolTableListTraits.h" @@ -727,6 +728,70 @@ setPrologueData(Src->getPrologueData()); } +FunctionModRefBehavior Function::getModRefBehavior() const { + return getAttributes().getFnAttrs().getModRefBehavior(); +} +void Function::setModRefBehavior(FunctionModRefBehavior FMRB) { + addFnAttr(Attribute::getWithModRefBehavior(getContext(), FMRB)); +} + +/// Determine if the function does not access memory. +bool Function::doesNotAccessMemory() const { + return getModRefBehavior().doesNotAccessMemory(); +} +void Function::setDoesNotAccessMemory() { + setModRefBehavior(FunctionModRefBehavior::none()); +} + +/// Determine if the function does not access or only reads memory. +bool Function::onlyReadsMemory() const { + return getModRefBehavior().onlyReadsMemory(); +} +void Function::setOnlyReadsMemory() { + setModRefBehavior(getModRefBehavior() & FunctionModRefBehavior::readOnly()); +} + +/// Determine if the function does not access or only writes memory. +bool Function::onlyWritesMemory() const { + return getModRefBehavior().onlyWritesMemory(); +} +void Function::setOnlyWritesMemory() { + setModRefBehavior(getModRefBehavior() & FunctionModRefBehavior::writeOnly()); +} + +/// Determine if the call can access memmory only using pointers based +/// on its arguments. +bool Function::onlyAccessesArgMemory() const { + return getModRefBehavior().onlyAccessesArgPointees(); +} +void Function::setOnlyAccessesArgMemory() { + setModRefBehavior(getModRefBehavior() & + FunctionModRefBehavior::argMemOnly(ModRefInfo::ModRef)); +} + +/// Determine if the function may only access memory that is +/// inaccessible from the IR. +bool Function::onlyAccessesInaccessibleMemory() const { + return getModRefBehavior().onlyAccessesInaccessibleMem(); +} +void Function::setOnlyAccessesInaccessibleMemory() { + setModRefBehavior( + getModRefBehavior() & + FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::ModRef)); +} + +/// Determine if the function may only access memory that is +/// either inaccessible from the IR or pointed to by its arguments. +bool Function::onlyAccessesInaccessibleMemOrArgMem() const { + return getModRefBehavior().onlyAccessesInaccessibleOrArgMem(); +} +void Function::setOnlyAccessesInaccessibleMemOrArgMem() { + FunctionModRefBehavior FMRB = + FunctionModRefBehavior::argMemOnly(ModRefInfo::ModRef) | + FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::ModRef); + setModRefBehavior(getModRefBehavior() & FMRB); +} + /// Table of string intrinsic names indexed by enum value. static const char * const IntrinsicNameTable[] = { "not_intrinsic", Index: llvm/lib/IR/Instructions.cpp =================================================================== --- llvm/lib/IR/Instructions.cpp +++ llvm/lib/IR/Instructions.cpp @@ -30,6 +30,7 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Metadata.h" +#include "llvm/IR/ModRef.h" #include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" #include "llvm/IR/Type.h" @@ -518,6 +519,83 @@ getIntrinsicID() != Intrinsic::assume; } +FunctionModRefBehavior CallBase::getModRefBehavior() const { + FunctionModRefBehavior FMRB = + getAttributes().getFnAttrs().getModRefBehavior(); + if (auto *Fn = dyn_cast(getCalledOperand())) { + FunctionModRefBehavior FnFMRB = Fn->getModRefBehavior(); + if (hasOperandBundles()) { + // TODO: Add a method to get FMRB for operand bundles instead. + if (hasReadingOperandBundles()) + FnFMRB |= FunctionModRefBehavior::readOnly(); + if (hasClobberingOperandBundles()) + FnFMRB |= FunctionModRefBehavior::writeOnly(); + } + FMRB &= FnFMRB; + } + return FMRB; +} +void CallBase::setModRefBehavior(FunctionModRefBehavior FMRB) { + addFnAttr(Attribute::getWithModRefBehavior(getContext(), FMRB)); +} + +/// Determine if the function does not access memory. +bool CallBase::doesNotAccessMemory() const { + return getModRefBehavior().doesNotAccessMemory(); +} +void CallBase::setDoesNotAccessMemory() { + setModRefBehavior(FunctionModRefBehavior::none()); +} + +/// Determine if the function does not access or only reads memory. +bool CallBase::onlyReadsMemory() const { + return getModRefBehavior().onlyReadsMemory(); +} +void CallBase::setOnlyReadsMemory() { + setModRefBehavior(getModRefBehavior() & FunctionModRefBehavior::readOnly()); +} + +/// Determine if the function does not access or only writes memory. +bool CallBase::onlyWritesMemory() const { + return getModRefBehavior().onlyWritesMemory(); +} +void CallBase::setOnlyWritesMemory() { + setModRefBehavior(getModRefBehavior() & FunctionModRefBehavior::writeOnly()); +} + +/// Determine if the call can access memmory only using pointers based +/// on its arguments. +bool CallBase::onlyAccessesArgMemory() const { + return getModRefBehavior().onlyAccessesArgPointees(); +} +void CallBase::setOnlyAccessesArgMemory() { + setModRefBehavior(getModRefBehavior() & + FunctionModRefBehavior::argMemOnly(ModRefInfo::ModRef)); +} + +/// Determine if the function may only access memory that is +/// inaccessible from the IR. +bool CallBase::onlyAccessesInaccessibleMemory() const { + return getModRefBehavior().onlyAccessesInaccessibleMem(); +} +void CallBase::setOnlyAccessesInaccessibleMemory() { + setModRefBehavior( + getModRefBehavior() & + FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::ModRef)); +} + +/// Determine if the function may only access memory that is +/// either inaccessible from the IR or pointed to by its arguments. +bool CallBase::onlyAccessesInaccessibleMemOrArgMem() const { + return getModRefBehavior().onlyAccessesInaccessibleOrArgMem(); +} +void CallBase::setOnlyAccessesInaccessibleMemOrArgMem() { + FunctionModRefBehavior FMRB = + FunctionModRefBehavior::argMemOnly(ModRefInfo::ModRef) | + FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::ModRef); + setModRefBehavior(getModRefBehavior() & FMRB); +} + //===----------------------------------------------------------------------===// // CallInst Implementation //===----------------------------------------------------------------------===// Index: llvm/lib/Transforms/IPO/FunctionAttrs.cpp =================================================================== --- llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -69,10 +69,7 @@ #define DEBUG_TYPE "function-attrs" -STATISTIC(NumArgMemOnly, "Number of functions marked argmemonly"); -STATISTIC(NumReadNone, "Number of functions marked readnone"); -STATISTIC(NumReadOnly, "Number of functions marked readonly"); -STATISTIC(NumWriteOnly, "Number of functions marked writeonly"); +STATISTIC(NumMemoryAttr, "Number of functions with improved memory attribute"); STATISTIC(NumNoCapture, "Number of arguments marked nocapture"); STATISTIC(NumReturned, "Number of arguments marked returned"); STATISTIC(NumReadNoneArg, "Number of arguments marked readnone"); @@ -243,79 +240,14 @@ return; } - ModRefInfo MR = FMRB.getModRef(); - for (Function *F : SCCNodes) { - if (F->doesNotAccessMemory()) - // Already perfect! - continue; - - if (FMRB.doesNotAccessMemory()) { - // For readnone, remove all other memory attributes. - AttributeMask AttrsToRemove; - AttrsToRemove.addAttribute(Attribute::ReadOnly); - AttrsToRemove.addAttribute(Attribute::WriteOnly); - AttrsToRemove.addAttribute(Attribute::ArgMemOnly); - AttrsToRemove.addAttribute(Attribute::InaccessibleMemOnly); - AttrsToRemove.addAttribute(Attribute::InaccessibleMemOrArgMemOnly); - - ++NumReadNone; - F->removeFnAttrs(AttrsToRemove); - F->addFnAttr(Attribute::ReadNone); - Changed.insert(F); - continue; - } - - // Add argmemonly, inaccessiblememonly, or inaccessible_or_argmemonly - // attributes if possible. - AttributeMask AttrsToRemove; - AttrsToRemove.addAttribute(Attribute::ArgMemOnly); - AttrsToRemove.addAttribute(Attribute::InaccessibleMemOnly); - AttrsToRemove.addAttribute(Attribute::InaccessibleMemOrArgMemOnly); - if (FMRB.onlyAccessesArgPointees()) { - if (!F->onlyAccessesArgMemory()) { - NumArgMemOnly++; - F->removeFnAttrs(AttrsToRemove); - F->addFnAttr(Attribute::ArgMemOnly); - Changed.insert(F); - } - } else if (FMRB.onlyAccessesInaccessibleMem()) { - if (!F->onlyAccessesInaccessibleMemory()) { - F->removeFnAttrs(AttrsToRemove); - F->addFnAttr(Attribute::InaccessibleMemOnly); - Changed.insert(F); - } - } else if (FMRB.onlyAccessesInaccessibleOrArgMem() && - !F->onlyAccessesInaccessibleMemOrArgMem()) { - F->removeFnAttrs(AttrsToRemove); - F->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly); + FunctionModRefBehavior OldFMRB = F->getModRefBehavior(); + FunctionModRefBehavior NewFMRB = FMRB & OldFMRB; + if (NewFMRB != OldFMRB) { + ++NumMemoryAttr; + F->addFnAttr(Attribute::getWithModRefBehavior(F->getContext(), NewFMRB)); Changed.insert(F); } - - // The SCC contains functions both writing and reading from memory. We - // cannot add readonly or writeonline attributes. - if (MR == ModRefInfo::ModRef) - continue; - - if (F->onlyReadsMemory() && MR == ModRefInfo::Ref) - continue; - - if (F->onlyWritesMemory() && MR == ModRefInfo::Mod) - continue; - - Changed.insert(F); - - // Add in the new attribute. - if (MR == ModRefInfo::Mod) { - ++NumWriteOnly; - F->removeFnAttr(Attribute::ReadOnly); - F->addFnAttr(Attribute::WriteOnly); - } else { - ++NumReadOnly; - assert(MR == ModRefInfo::Ref); - F->removeFnAttr(Attribute::WriteOnly); - F->addFnAttr(Attribute::ReadOnly); - } } } Index: llvm/lib/Transforms/Scalar/SCCP.cpp =================================================================== --- llvm/lib/Transforms/Scalar/SCCP.cpp +++ llvm/lib/Transforms/Scalar/SCCP.cpp @@ -590,21 +590,28 @@ } } - // If we replaced an argument, the argmemonly and - // inaccessiblemem_or_argmemonly attributes do not hold any longer. Remove - // them from both the function and callsites. + // If we replaced an argument, we may not also access a global (currently + // classified as "other" memory). Update memory attribute to reflect this. if (ReplacedPointerArg) { - AttributeMask AttributesToRemove; - AttributesToRemove.addAttribute(Attribute::ArgMemOnly); - AttributesToRemove.addAttribute(Attribute::InaccessibleMemOrArgMemOnly); - F.removeFnAttrs(AttributesToRemove); - + auto UpdateAttrs = [&](AttributeList AL) { + FunctionModRefBehavior FMRB = AL.getFnAttrs().getModRefBehavior(); + if (FMRB == FunctionModRefBehavior::unknown()) + return AL; + + FMRB |= FunctionModRefBehavior(FunctionModRefBehavior::Other, + ModRefInfo::ModRef); + return AL.addFnAttribute( + F.getContext(), + Attribute::getWithModRefBehavior(F.getContext(), FMRB)); + }; + + F.setAttributes(UpdateAttrs(F.getAttributes())); for (User *U : F.users()) { auto *CB = dyn_cast(U); if (!CB || CB->getCalledFunction() != &F) continue; - CB->removeFnAttrs(AttributesToRemove); + CB->setAttributes(UpdateAttrs(CB->getAttributes())); } } MadeChanges |= ReplacedPointerArg; Index: llvm/test/Analysis/BasicAA/cs-cs.ll =================================================================== --- llvm/test/Analysis/BasicAA/cs-cs.ll +++ llvm/test/Analysis/BasicAA/cs-cs.ll @@ -429,19 +429,19 @@ } -; CHECK: attributes #0 = { argmemonly nocallback nofree nounwind willreturn writeonly } -; CHECK-NEXT: attributes #1 = { argmemonly nocallback nofree nounwind willreturn } -; CHECK-NEXT: attributes #2 = { argmemonly nosync nounwind willreturn } -; CHECK-NEXT: attributes #3 = { noinline nounwind readonly } -; CHECK-NEXT: attributes #4 = { noinline nounwind writeonly } +; CHECK: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) } +; CHECK-NEXT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +; CHECK-NEXT: attributes #2 = { nosync nounwind willreturn memory(argmem: readwrite) } +; CHECK-NEXT: attributes #3 = { noinline nounwind memory(read) } +; CHECK-NEXT: attributes #4 = { noinline nounwind memory(write) } ; CHECK-NEXT: attributes #5 = { nounwind ssp } -; CHECK-NEXT: attributes #6 = { inaccessiblememonly nounwind } -; CHECK-NEXT: attributes #7 = { inaccessiblemem_or_argmemonly nounwind } -; CHECK-NEXT: attributes #8 = { argmemonly nounwind } -; CHECK-NEXT: attributes #9 = { readonly } -; CHECK-NEXT: attributes #10 = { inaccessiblememonly } -; CHECK-NEXT: attributes #11 = { inaccessiblemem_or_argmemonly } -; CHECK-NEXT: attributes #12 = { argmemonly } +; CHECK-NEXT: attributes #6 = { nounwind memory(inaccessiblemem: readwrite) } +; CHECK-NEXT: attributes #7 = { nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) } +; CHECK-NEXT: attributes #8 = { nounwind memory(argmem: readwrite) } +; CHECK-NEXT: attributes #9 = { memory(read) } +; CHECK-NEXT: attributes #10 = { memory(inaccessiblemem: readwrite) } +; CHECK-NEXT: attributes #11 = { memory(argmem: readwrite, inaccessiblemem: readwrite) } +; CHECK-NEXT: attributes #12 = { memory(argmem: readwrite) } attributes #0 = { argmemonly nounwind } attributes #1 = { noinline nounwind readonly } Index: llvm/test/Analysis/BasicAA/intrinsics.ll =================================================================== --- llvm/test/Analysis/BasicAA/intrinsics.ll +++ llvm/test/Analysis/BasicAA/intrinsics.ll @@ -22,6 +22,6 @@ declare <8 x i16> @llvm.masked.load.v8i16.p0v8i16(<8 x i16>*, i32, <8 x i1>, <8 x i16>) nounwind readonly declare void @llvm.masked.store.v8i16.p0v8i16(<8 x i16>, <8 x i16>*, i32, <8 x i1>) nounwind -; CHECK: attributes #0 = { argmemonly nocallback nofree nosync nounwind readonly willreturn } -; CHECK: attributes #1 = { argmemonly nocallback nofree nosync nounwind willreturn writeonly } +; CHECK: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) } +; CHECK: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: write) } ; CHECK: attributes [[ATTR]] = { nounwind } Index: llvm/test/Analysis/BasicAA/pure-const-dce.ll =================================================================== --- llvm/test/Analysis/BasicAA/pure-const-dce.ll +++ llvm/test/Analysis/BasicAA/pure-const-dce.ll @@ -50,5 +50,5 @@ declare i32 @TestNone(i32) -; CHECK: attributes [[READNONE]] = { readnone } -; CHECK: attributes [[READONLY]] = { readonly } +; CHECK: attributes [[READNONE]] = { memory(none) } +; CHECK: attributes [[READONLY]] = { memory(read) } Index: llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll =================================================================== --- llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll +++ llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll @@ -72,14 +72,14 @@ declare void @callee(i32* %p) nounwind declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1) nounwind -; CHECK: attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone willreturn } -; CHECK: attributes #1 = { argmemonly mustprogress nofree norecurse nosync nounwind willreturn writeonly } -; CHECK: attributes #2 = { nofree nounwind readonly } +; CHECK: attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } +; CHECK: attributes #1 = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) } +; CHECK: attributes #2 = { nofree nounwind memory(read, argmem: none) } ; CHECK: attributes #3 = { nounwind } -; CHECK: attributes #4 = { mustprogress nofree nosync nounwind readnone willreturn } -; CHECK: attributes #5 = { argmemonly mustprogress nofree nosync nounwind willreturn } -; CHECK: attributes #6 = { argmemonly mustprogress nofree norecurse nosync nounwind willreturn } -; CHECK: attributes #7 = { argmemonly nocallback nofree nounwind willreturn } +; CHECK: attributes #4 = { mustprogress nofree nosync nounwind willreturn memory(none) } +; CHECK: attributes #5 = { mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite) } +; CHECK: attributes #6 = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) } +; CHECK: attributes #7 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } ; Root note. !0 = !{ } Index: llvm/test/Analysis/TypeBasedAliasAnalysis/intrinsics.ll =================================================================== --- llvm/test/Analysis/TypeBasedAliasAnalysis/intrinsics.ll +++ llvm/test/Analysis/TypeBasedAliasAnalysis/intrinsics.ll @@ -22,8 +22,8 @@ declare <8 x i16> @llvm.masked.load.v8i16.p0v8i16(<8 x i16>*, i32, <8 x i1>, <8 x i16>) nounwind readonly declare void @llvm.masked.store.v8i16.p0v8i16(<8 x i16>, <8 x i16>*, i32, <8 x i1>) nounwind -; CHECK: attributes #0 = { argmemonly nocallback nofree nosync nounwind readonly willreturn } -; CHECK: attributes #1 = { argmemonly nocallback nofree nosync nounwind willreturn writeonly } +; CHECK: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) } +; CHECK: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: write) } ; CHECK: attributes [[NUW]] = { nounwind } !0 = !{!"tbaa root"} Index: llvm/test/Assembler/aarch64-intrinsics-attributes.ll =================================================================== --- llvm/test/Assembler/aarch64-intrinsics-attributes.ll +++ llvm/test/Assembler/aarch64-intrinsics-attributes.ll @@ -23,4 +23,4 @@ declare @llvm.aarch64.sve.dup.nxv4i32(, , i32) ; CHECK: attributes [[NOFREE_NOUNWIND_WILLRETURN]] = { nofree nounwind willreturn } -; CHECK: attributes [[NO_CALLBACK_NOFREE_NOSYNC_NOUNWIND_READNONE_WILLRETURN]] = { nocallback nofree nosync nounwind readnone willreturn } +; CHECK: attributes [[NO_CALLBACK_NOFREE_NOSYNC_NOUNWIND_READNONE_WILLRETURN]] = { nocallback nofree nosync nounwind willreturn memory(none) } Index: llvm/test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll =================================================================== --- llvm/test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll +++ llvm/test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll @@ -45,7 +45,7 @@ declare void @llvm.dbg.value(metadata, metadata, metadata) nounwind readnone ; CHECK: attributes #0 = { nounwind ssp } -; CHECK: attributes #1 = { nocallback nofree nosync nounwind readnone speculatable willreturn } +; CHECK: attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } ; CHECK: attributes #2 = { noinline nounwind ssp } ; CHECK: attributes [[NUW]] = { nounwind } Index: llvm/test/Transforms/FunctionAttrs/argmemonly.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/argmemonly.ll +++ llvm/test/Transforms/FunctionAttrs/argmemonly.ll @@ -4,7 +4,7 @@ @g = global i32 20 define void @test_no_read_or_write() { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CHECK-LABEL: @test_no_read_or_write( ; CHECK-NEXT: entry: ; CHECK-NEXT: ret void @@ -14,7 +14,7 @@ } define i32 @test_only_read_arg(i32* %ptr) { -; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind readonly willreturn +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) ; CHECK-LABEL: @test_only_read_arg( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[L:%.*]] = load i32, i32* [[PTR:%.*]], align 4 @@ -26,7 +26,7 @@ } define i32 @test_only_read_arg_already_has_argmemonly(i32* %ptr) argmemonly { -; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind readonly willreturn +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) ; CHECK-LABEL: @test_only_read_arg_already_has_argmemonly( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[L:%.*]] = load i32, i32* [[PTR:%.*]], align 4 @@ -38,7 +38,6 @@ } define i32 @test_read_global() { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readonly willreturn ; CHECK-LABEL: @test_read_global( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[L:%.*]] = load i32, i32* @g, align 4 @@ -50,7 +49,6 @@ } define i32 @test_read_loaded_ptr(i32** %ptr) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readonly willreturn ; CHECK-LABEL: @test_read_loaded_ptr( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[L:%.*]] = load i32*, i32** [[PTR:%.*]], align 8 @@ -64,7 +62,7 @@ } define void @test_only_write_arg(i32* %ptr) { -; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind willreturn writeonly +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) ; CHECK-LABEL: @test_only_write_arg( ; CHECK-NEXT: entry: ; CHECK-NEXT: store i32 0, i32* [[PTR:%.*]], align 4 @@ -76,7 +74,6 @@ } define void @test_write_global() { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly ; CHECK-LABEL: @test_write_global( ; CHECK-NEXT: entry: ; CHECK-NEXT: store i32 0, i32* @g, align 4 @@ -103,7 +100,7 @@ declare i32 @fn_readnone() readnone define void @test_call_readnone(i32* %ptr) { -; CHECK: Function Attrs: argmemonly writeonly +; CHECK: Function Attrs: memory(argmem: write) ; CHECK-LABEL: @test_call_readnone( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[C:%.*]] = call i32 @fn_readnone() @@ -119,7 +116,7 @@ declare i32 @fn_argmemonly(i32*) argmemonly define i32 @test_call_argmemonly(i32* %ptr) { -; CHECK: Function Attrs: argmemonly +; CHECK: Function Attrs: memory(argmem: readwrite) ; CHECK-LABEL: @test_call_argmemonly( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[C:%.*]] = call i32 @fn_argmemonly(i32* [[PTR:%.*]]) @@ -131,7 +128,7 @@ } define i32 @test_call_fn_where_argmemonly_can_be_inferred(i32* %ptr) { -; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind readonly willreturn +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) ; CHECK-LABEL: @test_call_fn_where_argmemonly_can_be_inferred( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[C:%.*]] = call i32 @test_only_read_arg(i32* [[PTR:%.*]]) @@ -143,7 +140,7 @@ } define void @test_memcpy_argonly(i8* %dst, i8* %src) { -; CHECK: Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn +; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite) ; CHECK-LABEL: @test_memcpy_argonly( ; CHECK-NEXT: entry: ; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[DST:%.*]], i8* [[SRC:%.*]], i64 32, i1 false) @@ -159,7 +156,6 @@ @arr = global [32 x i8] zeroinitializer define void @test_memcpy_src_global(i8* %dst) { -; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn ; CHECK-LABEL: @test_memcpy_src_global( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[BC:%.*]] = bitcast [32 x i8]* @arr to i8* @@ -173,7 +169,6 @@ } define void @test_memcpy_dst_global(i8* %src) { -; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn ; CHECK-LABEL: @test_memcpy_dst_global( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[BC:%.*]] = bitcast [32 x i8]* @arr to i8* @@ -187,7 +182,7 @@ } define i32 @test_read_arg_access_alloca(i32* %ptr) { -; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind readonly willreturn +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) ; CHECK-LABEL: @test_read_arg_access_alloca( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 @@ -207,7 +202,7 @@ declare void @fn_inaccessiblememonly() inaccessiblememonly define void @test_inaccessiblememonly() { -; CHECK: Function Attrs: inaccessiblememonly +; CHECK: Function Attrs: memory(inaccessiblemem: readwrite) ; CHECK-LABEL: @test_inaccessiblememonly( ; CHECK-NEXT: call void @fn_inaccessiblememonly() ; CHECK-NEXT: ret void @@ -217,9 +212,9 @@ } define void @test_inaccessiblememonly_readonly() { -; CHECK: Function Attrs: inaccessiblememonly nofree readonly +; CHECK: Function Attrs: nofree memory(inaccessiblemem: read) ; CHECK-LABEL: @test_inaccessiblememonly_readonly( -; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR15:[0-9]+]] +; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR16:[0-9]+]] ; CHECK-NEXT: ret void ; call void @fn_inaccessiblememonly() readonly @@ -227,10 +222,9 @@ } define void @test_inaccessibleorargmemonly_readonly(i32* %arg) { -; CHECK: Function Attrs: inaccessiblemem_or_argmemonly nofree readonly ; CHECK-LABEL: @test_inaccessibleorargmemonly_readonly( ; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[ARG:%.*]], align 4 -; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR15]] +; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR16]] ; CHECK-NEXT: ret void ; load i32, i32* %arg @@ -239,10 +233,9 @@ } define void @test_inaccessibleorargmemonly_readwrite(i32* %arg) { -; CHECK: Function Attrs: inaccessiblemem_or_argmemonly ; CHECK-LABEL: @test_inaccessibleorargmemonly_readwrite( ; CHECK-NEXT: store i32 0, i32* [[ARG:%.*]], align 4 -; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR15]] +; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR16]] ; CHECK-NEXT: ret void ; store i32 0, i32* %arg Index: llvm/test/Transforms/InstCombine/AArch64/2012-04-23-Neon-Intrinsics.ll =================================================================== --- llvm/test/Transforms/InstCombine/AArch64/2012-04-23-Neon-Intrinsics.ll +++ llvm/test/Transforms/InstCombine/AArch64/2012-04-23-Neon-Intrinsics.ll @@ -65,6 +65,6 @@ declare <4 x i32> @llvm.aarch64.neon.smull.v4i32(<4 x i16>, <4 x i16>) nounwind readnone declare <4 x i32> @llvm.aarch64.neon.umull.v4i32(<4 x i16>, <4 x i16>) nounwind readnone -; CHECK: attributes #0 = { nounwind readnone ssp } -; CHECK: attributes #1 = { nocallback nofree nosync nounwind readnone willreturn } +; CHECK: attributes #0 = { nounwind ssp memory() } +; CHECK: attributes #1 = { nocallback nofree nosync nounwind willreturn memory() } ; CHECK: attributes [[NUW]] = { nounwind } Index: llvm/test/Transforms/InstCombine/stpncpy-1.ll =================================================================== --- llvm/test/Transforms/InstCombine/stpncpy-1.ll +++ llvm/test/Transforms/InstCombine/stpncpy-1.ll @@ -463,6 +463,6 @@ ret void } ;. -; ANY: attributes #[[ATTR0:[0-9]+]] = { argmemonly nocallback nofree nounwind willreturn writeonly } -; ANY: attributes #[[ATTR1:[0-9]+]] = { argmemonly nocallback nofree nounwind willreturn } +; ANY: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: write) } +; ANY: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } ;. Index: llvm/test/Transforms/LICM/scalar-promote.ll =================================================================== --- llvm/test/Transforms/LICM/scalar-promote.ll +++ llvm/test/Transforms/LICM/scalar-promote.ll @@ -600,7 +600,7 @@ } define i8 @test_hoistable_existing_load_sinkable_store_writeonly(ptr dereferenceable(8) %ptr, i8 %start) writeonly { -; CHECK: Function Attrs: writeonly +; CHECK: Function Attrs: memory(write) ; CHECK-LABEL: @test_hoistable_existing_load_sinkable_store_writeonly( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[PTR_PROMOTED:%.*]] = load i8, ptr [[PTR:%.*]], align 1 @@ -641,7 +641,7 @@ ; Test case for PR51248. define void @test_sink_store_only() writeonly { -; CHECK: Function Attrs: writeonly +; CHECK: Function Attrs: memory(write) ; CHECK-LABEL: @test_sink_store_only( ; CHECK-NEXT: entry: ; CHECK-NEXT: br label [[LOOP_HEADER:%.*]] @@ -678,7 +678,7 @@ } define void @test_sink_store_to_local_object_only_loop_must_execute() writeonly { -; CHECK: Function Attrs: writeonly +; CHECK: Function Attrs: memory(write) ; CHECK-LABEL: @test_sink_store_to_local_object_only_loop_must_execute( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = alloca i8, align 1 @@ -719,7 +719,7 @@ ; The store in the loop may not execute, so we need to introduce a load in the ; pre-header. Make sure the writeonly attribute is dropped. define void @test_sink_store_to_local_object_only_loop_may_not_execute(i8 %n) writeonly { -; CHECK: Function Attrs: writeonly +; CHECK: Function Attrs: memory(write) ; CHECK-LABEL: @test_sink_store_to_local_object_only_loop_may_not_execute( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = alloca i8, align 1 @@ -761,7 +761,7 @@ declare dereferenceable(8) noalias ptr @alloc_writeonly() writeonly define void @test_sink_store_to_noalias_call_object_only_loop_may_not_execute1(i8 %n) writeonly { -; CHECK: Function Attrs: writeonly +; CHECK: Function Attrs: memory(write) ; CHECK-LABEL: @test_sink_store_to_noalias_call_object_only_loop_may_not_execute1( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = call noalias dereferenceable(8) ptr @alloc_writeonly() @@ -801,7 +801,7 @@ } define void @test_sink_store_only_no_phi_needed() writeonly { -; CHECK: Function Attrs: writeonly +; CHECK: Function Attrs: memory(write) ; CHECK-LABEL: @test_sink_store_only_no_phi_needed( ; CHECK-NEXT: entry: ; CHECK-NEXT: br label [[LOOP:%.*]] Index: llvm/test/Transforms/LICM/strlen.ll =================================================================== --- llvm/test/Transforms/LICM/strlen.ll +++ llvm/test/Transforms/LICM/strlen.ll @@ -13,7 +13,7 @@ } ; CHECK: declare i64 @strlen(ptr nocapture) #0 -; CHECK: attributes #0 = { argmemonly mustprogress nofree nounwind readonly willreturn } +; CHECK: attributes #0 = { mustprogress nofree nounwind willreturn memory(argmem: read) } declare i64 @strlen(ptr) Index: llvm/test/Transforms/SCCP/ipscp-drop-argmemonly.ll =================================================================== --- llvm/test/Transforms/SCCP/ipscp-drop-argmemonly.ll +++ llvm/test/Transforms/SCCP/ipscp-drop-argmemonly.ll @@ -1,4 +1,4 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals ; RUN: opt -passes=ipsccp -S %s | FileCheck %s ; Test cases to ensure argmemonly/inaccessiblemem_or_argmemonly attributes are @@ -10,8 +10,11 @@ ; Here the pointer argument %arg will be replaced by a constant. We need to ; drop argmemonly. +;. +; CHECK: @[[G:[a-zA-Z0-9_$"\\.-]+]] = internal global i32 0 +;. define internal void @ptrarg.1(i32* %arg, i32 %val) argmemonly nounwind { -; CHECK: Function Attrs: nounwind +; CHECK: Function Attrs: nounwind memory(readwrite, inaccessiblemem: none) ; CHECK-LABEL: @ptrarg.1( ; CHECK-NEXT: store i32 10, i32* @g, align 4 ; CHECK-NEXT: ret void @@ -37,7 +40,7 @@ ; Here only the non-pointer argument %val is replaced, no need ; to drop the argmemonly attribute. define internal void @ptrarg.2(i32* %arg, i32 %val) argmemonly nounwind { -; CHECK: Function Attrs: argmemonly nounwind +; CHECK: Function Attrs: nounwind memory(argmem: readwrite) ; CHECK-LABEL: @ptrarg.2( ; CHECK-NEXT: store i32 10, i32* [[ARG:%.*]], align 4 ; CHECK-NEXT: ret void @@ -59,7 +62,7 @@ ; Here the pointer argument %arg will be replaced by a constant. We need to ; drop inaccessiblemem_or_argmemonly. define internal void @ptrarg.3(i32* %arg, i32 %val) inaccessiblemem_or_argmemonly nounwind { -; CHECK: Function Attrs: nounwind +; CHECK: Function Attrs: nounwind memory(readwrite) ; CHECK-LABEL: @ptrarg.3( ; CHECK-NEXT: store i32 10, i32* @g, align 4 ; CHECK-NEXT: ret void @@ -85,7 +88,7 @@ ; Here only the non-pointer argument %val is replaced, no need ; to drop the inaccessiblemem_or_argmemonly attribute. define internal void @ptrarg.4(i32* %arg, i32 %val) inaccessiblemem_or_argmemonly nounwind { -; CHECK: Function Attrs: inaccessiblemem_or_argmemonly nounwind +; CHECK: Function Attrs: nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) ; CHECK-LABEL: @ptrarg.4( ; CHECK-NEXT: store i32 10, i32* [[ARG:%.*]], align 4 ; CHECK-NEXT: ret void @@ -107,7 +110,7 @@ ; Here the pointer argument %arg will be replaced by a constant. We need to ; drop inaccessiblemem_or_argmemonly. define internal void @ptrarg.5(i32* %arg, i32 %val) argmemonly inaccessiblemem_or_argmemonly nounwind { -; CHECK: Function Attrs: nounwind +; CHECK: Function Attrs: nounwind memory(readwrite, inaccessiblemem: none) ; CHECK-LABEL: @ptrarg.5( ; CHECK-NEXT: store i32 10, i32* @g, align 4 ; CHECK-NEXT: ret void @@ -143,10 +146,10 @@ define i32 @caller.6.cs.attributes(i32 %n) { ; CHECK-LABEL: @caller.6.cs.attributes( ; CHECK-NEXT: store i32 1, i32* @g, align 4 -; CHECK-NEXT: tail call void @ptrarg.5(i32* @g, i32 10) [[NOUNWIND:#[0-9]+]] -; CHECK-NEXT: tail call void @ptrarg.5(i32* @g, i32 10) [[NOUNWIND:#[0-9]+]] -; CHECK-NEXT: tail call void @ptrarg.5(i32* @g, i32 10) [[NOUNWIND:#[0-9]+]] -; CHECK-NEXT: tail call void @ptrarg.5(i32* @g, i32 10) [[NOUNWIND:#[0-9]+]] +; CHECK-NEXT: tail call void @ptrarg.5(i32* @g, i32 10) #[[ATTR0:[0-9]+]] +; CHECK-NEXT: tail call void @ptrarg.5(i32* @g, i32 10) #[[ATTR2:[0-9]+]] +; CHECK-NEXT: tail call void @ptrarg.5(i32* @g, i32 10) #[[ATTR0]] +; CHECK-NEXT: tail call void @ptrarg.5(i32* @g, i32 10) #[[ATTR4:[0-9]+]] ; CHECK-NEXT: [[G_VAL:%.*]] = load i32, i32* @g, align 4 ; CHECK-NEXT: ret i32 [[G_VAL]] ; @@ -159,4 +162,10 @@ ret i32 %g.val } -; CHECK: [[NOUNWIND]] = { nounwind } +;. +; CHECK: attributes #[[ATTR0]] = { nounwind memory(readwrite, inaccessiblemem: none) } +; CHECK: attributes #[[ATTR1:[0-9]+]] = { nounwind memory(argmem: readwrite) } +; CHECK: attributes #[[ATTR2]] = { nounwind memory(readwrite) } +; CHECK: attributes #[[ATTR3:[0-9]+]] = { nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) } +; CHECK: attributes #[[ATTR4]] = { nounwind } +;. Index: llvm/test/Transforms/SCCP/remove-call-inst.ll =================================================================== --- llvm/test/Transforms/SCCP/remove-call-inst.ll +++ llvm/test/Transforms/SCCP/remove-call-inst.ll @@ -36,4 +36,4 @@ } ; CHECK: attributes #0 = { noreturn nounwind } -; CHECK: attributes #1 = { nounwind readnone willreturn } +; CHECK: attributes #1 = { nounwind willreturn memory(none) } Index: llvm/utils/TableGen/IntrinsicEmitter.cpp =================================================================== --- llvm/utils/TableGen/IntrinsicEmitter.cpp +++ llvm/utils/TableGen/IntrinsicEmitter.cpp @@ -776,49 +776,58 @@ case CodeGenIntrinsic::NoMem: if (Intrinsic.hasSideEffects) break; - OS << " Attribute::get(C, Attribute::ReadNone),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::none()),\n"; break; case CodeGenIntrinsic::ReadArgMem: - OS << " Attribute::get(C, Attribute::ReadOnly),\n"; - OS << " Attribute::get(C, Attribute::ArgMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::argMemOnly(ModRefInfo::Ref)),\n"; break; case CodeGenIntrinsic::ReadMem: - OS << " Attribute::get(C, Attribute::ReadOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::readOnly()),\n"; break; case CodeGenIntrinsic::ReadInaccessibleMem: - OS << " Attribute::get(C, Attribute::ReadOnly),\n"; - OS << " Attribute::get(C, Attribute::InaccessibleMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::Ref)),\n"; break; case CodeGenIntrinsic::ReadInaccessibleMemOrArgMem: - OS << " Attribute::get(C, Attribute::ReadOnly),\n"; - OS << " Attribute::get(C, " - << "Attribute::InaccessibleMemOrArgMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::Ref) | " + << "FunctionModRefBehavior::argMemOnly(ModRefInfo::Ref)),\n"; + break; break; case CodeGenIntrinsic::WriteArgMem: - OS << " Attribute::get(C, Attribute::WriteOnly),\n"; - OS << " Attribute::get(C, Attribute::ArgMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::argMemOnly(ModRefInfo::Mod)),\n"; break; case CodeGenIntrinsic::WriteMem: - OS << " Attribute::get(C, Attribute::WriteOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::writeOnly()),\n"; break; case CodeGenIntrinsic::WriteInaccessibleMem: - OS << " Attribute::get(C, Attribute::WriteOnly),\n"; - OS << " Attribute::get(C, Attribute::InaccessibleMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::Mod)),\n"; break; case CodeGenIntrinsic::WriteInaccessibleMemOrArgMem: - OS << " Attribute::get(C, Attribute::WriteOnly),\n"; - OS << " Attribute::get(C, " - << "Attribute::InaccessibleMemOrArgMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::Mod) | " + << "FunctionModRefBehavior::argMemOnly(ModRefInfo::Mod)),\n"; break; case CodeGenIntrinsic::ReadWriteArgMem: - OS << " Attribute::get(C, Attribute::ArgMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::argMemOnly(ModRefInfo::ModRef)),\n"; break; case CodeGenIntrinsic::ReadWriteInaccessibleMem: - OS << " Attribute::get(C, Attribute::InaccessibleMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::inaccessibleMemOnly(" + << "ModRefInfo::ModRef)),\n"; break; case CodeGenIntrinsic::ReadWriteInaccessibleMemOrArgMem: - OS << " Attribute::get(C, " - << "Attribute::InaccessibleMemOrArgMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::inaccessibleMemOnly(" + << "ModRefInfo::ModRef) | FunctionModRefBehavior::argMemOnly(" + << "ModRefInfo::ModRef)),\n"; break; case CodeGenIntrinsic::ReadWriteMem: break; Index: llvm/utils/UpdateTestChecks/common.py =================================================================== --- llvm/utils/UpdateTestChecks/common.py +++ llvm/utils/UpdateTestChecks/common.py @@ -327,7 +327,7 @@ UNUSED_NOTE = 'NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:' OPT_FUNCTION_RE = re.compile( - r'^(\s*;\s*Function\sAttrs:\s(?P[\w\s]+?))?\s*define\s+(?:internal\s+)?[^@]*@(?P[\w.$-]+?)\s*' + r'^(\s*;\s*Function\sAttrs:\s(?P[\w\s():,]+?))?\s*define\s+(?:internal\s+)?[^@]*@(?P[\w.$-]+?)\s*' r'(?P\((\)|(.*?[\w.-]+?)\))[^{]*\{)\n(?P.*?)^\}$', flags=(re.M | re.S))