diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -2362,7 +2362,8 @@ static const char ID; }; -/// An abstract interface for all memory related attributes. +/// An abstract interface for memory access kind related attributes +/// (readnone/readonly/writeonly). struct AAMemoryBehavior : public IRAttribute< Attribute::ReadNone, @@ -2378,6 +2379,7 @@ BEST_STATE = NO_ACCESSES, }; + static_assert(BEST_STATE == getBestState(), "Unexpected BEST_STATE value"); /// Return true if we know that the underlying value is not read or accessed /// in its respective scope. @@ -2411,6 +2413,143 @@ static const char ID; }; +/// An abstract interface for all memory location attributes +/// (readnone/argmemonly/inaccessiblememonly/inaccessibleorargmemonly). +struct AAMemoryLocation + : public IRAttribute< + Attribute::ReadNone, + StateWrapper, AbstractAttribute>> { + using MemoryLocationsKind = StateType::base_t; + + AAMemoryLocation(const IRPosition &IRP) : IRAttribute(IRP) {} + + /// Encoding of different locations that could be accessed by a memory + /// access. + enum { + ALL_LOCATIONS = 0, + NO_LOCAL_MEM = 1 << 0, + NO_CONST_MEM = 1 << 1, + NO_GLOBAL_INTERNAL_MEM = 1 << 2, + NO_GLOBAL_EXTERNAL_MEM = 1 << 3, + NO_GLOBAL_MEM = NO_GLOBAL_INTERNAL_MEM | NO_GLOBAL_EXTERNAL_MEM, + NO_ARGUMENT_MEM = 1 << 4, + NO_INACCESSIBLE_MEM = 1 << 5, + NO_MALLOCED_MEM = 1 << 6, + NO_UNKOWN_MEM = 1 << 7, + NO_LOCATIONS = NO_LOCAL_MEM | NO_CONST_MEM | NO_GLOBAL_INTERNAL_MEM | + NO_GLOBAL_EXTERNAL_MEM | NO_ARGUMENT_MEM | + NO_INACCESSIBLE_MEM | NO_MALLOCED_MEM | NO_UNKOWN_MEM, + + // Helper bit to track if we gave up or not. + VALID_STATE = NO_LOCATIONS + 1, + + BEST_STATE = NO_LOCATIONS | VALID_STATE, + }; + static_assert(BEST_STATE == getBestState(), "Unexpected BEST_STATE value"); + + /// Return true if we know that the associated functions has no observable + /// accesses. + bool isKnownReadNone() const { return isKnown(NO_LOCATIONS); } + + /// Return true if we assume that the associated functions has no observable + /// accesses. + bool isAssumedReadNone() const { + return isAssumed(NO_LOCATIONS) | isAssumedStackOnly(); + } + + /// Return true if we know that the associated functions has at most + /// local/stack accesses. + bool isKnowStackOnly() const { + return isKnown(inverseLocation(NO_LOCAL_MEM, true, true)); + } + + /// Return true if we assume that the associated functions has at most + /// local/stack accesses. + bool isAssumedStackOnly() const { + return isAssumed(inverseLocation(NO_LOCAL_MEM, true, true)); + } + + /// Return true if we know that the underlying value will only access + /// inaccesible memory only (see Attribute::InaccessibleMemOnly). + bool isKnownInaccessibleMemOnly() const { + return isKnown(inverseLocation(NO_INACCESSIBLE_MEM, true, true)); + } + + /// Return true if we assume that the underlying value will only access + /// inaccesible memory only (see Attribute::InaccessibleMemOnly). + bool isAssumedInaccessibleMemOnly() const { + return isAssumed(inverseLocation(NO_INACCESSIBLE_MEM, true, true)); + } + + /// Return true if we know that the underlying value will only access + /// argument pointees (see Attribute::ArgMemOnly). + bool isKnownArgMemOnly() const { + return isKnown(inverseLocation(NO_ARGUMENT_MEM, true, true)); + } + + /// Return true if we assume that the underlying value will only access + /// argument pointees (see Attribute::ArgMemOnly). + bool isAssumedArgMemOnly() const { + return isAssumed(inverseLocation(NO_ARGUMENT_MEM, true, true)); + } + + /// Return true if we know that the underlying value will only access + /// inaccesible memory or argument pointees (see + /// Attribute::InaccessibleOrArgMemOnly). + bool isKnownInaccessibleOrArgMemOnly() const { + return isKnownInaccessibleMemOnly() | isKnownArgMemOnly(); + } + + /// Return true if we assume that the underlying value will only access + /// inaccesible memory or argument pointees (see + /// Attribute::InaccessibleOrArgMemOnly). + bool isAssumedInaccessibleOrArgMemOnly() const { + return isAssumedInaccessibleMemOnly() | isAssumedArgMemOnly(); + } + + /// Return true if the underlying value may access memory through arguement + /// pointers of the associated function, if any. + bool mayAccessArgMem() const { return !isAssumed(NO_ARGUMENT_MEM); } + + /// Return true if only the memory locations specififed by \p MLK are assumed + /// to be accessed by the associated function. + bool isAssumedSpecifiedMemOnly(MemoryLocationsKind MLK) const { + return isAssumed(MLK); + } + + /// Return the locations that are assumed to be not accessed by the associated + /// function, if any. + MemoryLocationsKind getAssumedNotAccessedLocation() const { + return getAssumed(); + } + + /// Return the inverse of location \p Loc, thus for NO_XXX the return + /// describes ONLY_XXX. The flags \p AndLocalMem and \p AndConstMem determine + /// if local (=stack) and constant memory are allowed as well. Most of the + /// time we do want them to be included, e.g., argmemonly allows accesses via + /// argument pointers or local or constant memory accesses. + static MemoryLocationsKind + inverseLocation(MemoryLocationsKind Loc, bool AndLocalMem, bool AndConstMem) { + return NO_LOCATIONS & ~(Loc | (AndLocalMem ? NO_LOCAL_MEM : 0) | + (AndConstMem ? NO_CONST_MEM : 0)); + }; + + /// Return the locations encoded by \p MLK as a readable string. + static std::string getMemoryLocationsAsStr(MemoryLocationsKind MLK); + + /// Create an abstract attribute view for the position \p IRP. + static AAMemoryLocation &createForPosition(const IRPosition &IRP, + Attributor &A); + + /// See AbstractState::getAsStr(). + const std::string getAsStr() const override { + return getMemoryLocationsAsStr(getAssumedNotAccessedLocation()); + } + + /// Unique ID (due to the unique address) + static const char ID; +}; + /// An abstract interface for range value analysis. struct AAValueConstantRange : public IntegerRangeState, public AbstractAttribute, diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -129,6 +129,7 @@ PIPE_OPERATOR(AAHeapToStack) PIPE_OPERATOR(AAReachability) PIPE_OPERATOR(AAMemoryBehavior) +PIPE_OPERATOR(AAMemoryLocation) PIPE_OPERATOR(AAValueConstantRange) #undef PIPE_OPERATOR @@ -320,7 +321,7 @@ static bool genericValueTraversal( Attributor &A, IRPosition IRP, const AAType &QueryingAA, StateTy &State, const function_ref &VisitValueCB, - int MaxValues = 8) { + int MaxValues = 8, const function_ref StripCB = nullptr) { const AAIsDead *LivenessAA = nullptr; if (IRP.getAnchorScope()) @@ -337,6 +338,8 @@ int Iteration = 0; do { Value *V = Worklist.pop_back_val(); + if (StripCB) + V = StripCB(V); // Check if we should process the current value. To prevent endless // recursion keep a record of the values we followed! @@ -668,7 +671,6 @@ } } -namespace { /// Helper function to clamp a state \p S of type \p StateType with the /// information in \p R and indicate/return if \p S did change (as-in update is /// required to be run again). @@ -2542,7 +2544,7 @@ if (getArgNo() == (int)i) continue; const Value *ArgOp = ICS.getArgOperand(i); - if (!ArgOp->getType()->isPointerTy()) + if (!ArgOp->getType()->isPtrOrPtrVectorTy()) continue; if (const Function *F = getAnchorScope()) { @@ -4737,7 +4739,7 @@ State.addKnownBits(NO_READS); break; default: - llvm_unreachable("Unexpcted attribute!"); + llvm_unreachable("Unexpected attribute!"); } } @@ -4764,6 +4766,9 @@ /// See AbstractAttribute::manifest(...). ChangeStatus manifest(Attributor &A) override { + if (hasAttr(Attribute::ReadNone, /* IgnoreSubsumingPositions */ true)) + return ChangeStatus::UNCHANGED; + const IRPosition &IRP = getIRPosition(); // Check if we would improve the existing attributes first. @@ -4868,6 +4873,10 @@ } ChangeStatus manifest(Attributor &A) override { + // TODO: Pointer arguments are not supported on vectors of pointers yet. + if (!getAssociatedValue().getType()->isPointerTy()) + return ChangeStatus::UNCHANGED; + // TODO: From readattrs.ll: "inalloca parameters are always // considered written" if (hasAttr({Attribute::InAlloca})) { @@ -5010,7 +5019,6 @@ STATS_DECLTRACK_CS_ATTR(writeonly) } }; -} // namespace ChangeStatus AAMemoryBehaviorFunction::updateImpl(Attributor &A) { @@ -5205,6 +5213,379 @@ if (UserI->mayWriteToMemory()) removeAssumedBits(NO_WRITES); } + +/// -------------------- Memory Locations Attributes --------------------------- +/// Includes read-none, argmemonly, inaccessiblememonly, +/// inaccessiblememorargmemonly +/// ---------------------------------------------------------------------------- + +std::string AAMemoryLocation::getMemoryLocationsAsStr( + AAMemoryLocation::MemoryLocationsKind MLK) { + if (0 == (MLK & AAMemoryLocation::NO_LOCATIONS)) + return "all memory"; + if (MLK == AAMemoryLocation::NO_LOCATIONS) + return "no memory"; + std::string S = "memory:"; + if (0 == (MLK & AAMemoryLocation::NO_LOCAL_MEM)) + S += "stack,"; + if (0 == (MLK & AAMemoryLocation::NO_CONST_MEM)) + S += "constant,"; + if (0 == (MLK & AAMemoryLocation::NO_GLOBAL_INTERNAL_MEM)) + S += "internal global,"; + if (0 == (MLK & AAMemoryLocation::NO_GLOBAL_EXTERNAL_MEM)) + S += "external global,"; + if (0 == (MLK & AAMemoryLocation::NO_ARGUMENT_MEM)) + S += "argument,"; + if (0 == (MLK & AAMemoryLocation::NO_INACCESSIBLE_MEM)) + S += "inaccessible,"; + if (0 == (MLK & AAMemoryLocation::NO_UNKOWN_MEM)) + S += "unknown,"; + S.pop_back(); + return S; +} + +struct AAMemoryLocationImpl : public AAMemoryLocation { + + AAMemoryLocationImpl(const IRPosition &IRP) : AAMemoryLocation(IRP) {} + + /// See AbstractAttribute::initialize(...). + void initialize(Attributor &A) override { + intersectAssumedBits(BEST_STATE); + getKnownStateFromValue(getIRPosition(), getState()); + IRAttribute::initialize(A); + } + + /// Return the memory behavior information encoded in the IR for \p IRP. + static void getKnownStateFromValue(const IRPosition &IRP, + BitIntegerState &State, + bool IgnoreSubsumingPositions = false) { + SmallVector Attrs; + IRP.getAttrs(AttrKinds, Attrs, IgnoreSubsumingPositions); + for (const Attribute &Attr : Attrs) { + switch (Attr.getKindAsEnum()) { + case Attribute::ReadNone: + State.addKnownBits(NO_LOCAL_MEM | NO_CONST_MEM); + break; + case Attribute::InaccessibleMemOnly: + State.addKnownBits(inverseLocation(NO_INACCESSIBLE_MEM, true, true)); + break; + case Attribute::ArgMemOnly: + State.addKnownBits(inverseLocation(NO_ARGUMENT_MEM, true, true)); + break; + case Attribute::InaccessibleMemOrArgMemOnly: + State.addKnownBits( + inverseLocation(NO_INACCESSIBLE_MEM | NO_ARGUMENT_MEM, true, true)); + break; + default: + llvm_unreachable("Unexpected attribute!"); + } + } + } + + /// See AbstractAttribute::getDeducedAttributes(...). + void getDeducedAttributes(LLVMContext &Ctx, + SmallVectorImpl &Attrs) const override { + assert(Attrs.size() == 0); + if (isAssumedReadNone()) { + Attrs.push_back(Attribute::get(Ctx, Attribute::ReadNone)); + } else if (getIRPosition().getPositionKind() == IRPosition::IRP_FUNCTION) { + if (isAssumedInaccessibleMemOnly()) + Attrs.push_back(Attribute::get(Ctx, Attribute::InaccessibleMemOnly)); + else if (isAssumedArgMemOnly()) + Attrs.push_back(Attribute::get(Ctx, Attribute::ArgMemOnly)); + else if (isAssumedInaccessibleOrArgMemOnly()) + Attrs.push_back( + Attribute::get(Ctx, Attribute::InaccessibleMemOrArgMemOnly)); + } + assert(Attrs.size() <= 1); + } + + /// See AbstractAttribute::manifest(...). + ChangeStatus manifest(Attributor &A) override { + const IRPosition &IRP = getIRPosition(); + + // Check if we would improve the existing attributes first. + SmallVector DeducedAttrs; + getDeducedAttributes(IRP.getAnchorValue().getContext(), DeducedAttrs); + if (llvm::all_of(DeducedAttrs, [&](const Attribute &Attr) { + return IRP.hasAttr(Attr.getKindAsEnum(), + /* IgnoreSubsumingPositions */ true); + })) + return ChangeStatus::UNCHANGED; + + // Clear existing attributes. + IRP.removeAttrs(AttrKinds); + if (isAssumedReadNone()) + IRP.removeAttrs(AAMemoryBehaviorImpl::AttrKinds); + + // Use the generic manifest method. + return IRAttribute::manifest(A); + } + +protected: + /// Return the kind(s) of location that may be accessed by \p V. + AAMemoryLocation::MemoryLocationsKind + categorizeAccessedLocations(Attributor &A, Instruction &I, bool &Changed); + + /// Update the state \p State given that \p I is an access to a \p MLK memory + /// location with the access pointer \p Ptr. + static void updateState(AAMemoryLocation::StateType &State, + MemoryLocationsKind MLK, const Instruction &I, + const Value *Ptr, bool &Changed) { + assert(isPowerOf2_32(MLK) && "Expected a single location set!"); + State.removeAssumedBits(MLK); + } + + /// Determine the underlying locations kinds for \p Ptr, e.g., globals or + /// arguments, and update the state and access map accordingly. + void categorizePtrValue(Attributor &A, const Instruction &I, const Value &Ptr, + AAMemoryLocation::StateType &State, bool &Changed); + + /// The set of IR attributes AAMemoryLocation deals with. + static const Attribute::AttrKind AttrKinds[4]; +}; + +const Attribute::AttrKind AAMemoryLocationImpl::AttrKinds[] = { + Attribute::ReadNone, Attribute::InaccessibleMemOnly, Attribute::ArgMemOnly, + Attribute::InaccessibleMemOrArgMemOnly}; + +void AAMemoryLocationImpl::categorizePtrValue( + Attributor &A, const Instruction &I, const Value &Ptr, + AAMemoryLocation::StateType &State, bool &Changed) { + LLVM_DEBUG(dbgs() << "[AAMemoryLocation] Categorize pointer locations for " + << Ptr << " [" + << getMemoryLocationsAsStr(State.getAssumed()) << "]\n"); + + auto StripGEPCB = [](Value *V) { + if (auto *GEP = dyn_cast(V)) + return GEP->getPointerOperand(); + return V; + }; + + auto VisitValueCB = [&](Value &V, AAMemoryLocation::StateType &T, + bool Stripped) -> bool { + if (isa(V)) + return true; + if (auto *Arg = dyn_cast(&V)) { + if (Arg->hasByValAttr()) + updateState(T, NO_LOCAL_MEM, I, &V, Changed); + else + updateState(T, NO_ARGUMENT_MEM, I, &V, Changed); + return true; + } + if (auto *GV = dyn_cast(&V)) { + if (GV->hasLocalLinkage()) + updateState(T, NO_GLOBAL_INTERNAL_MEM, I, &V, Changed); + else + updateState(T, NO_GLOBAL_EXTERNAL_MEM, I, &V, Changed); + return true; + } + if (isa(V)) { + updateState(T, NO_LOCAL_MEM, I, &V, Changed); + return true; + } + if (ImmutableCallSite ICS = ImmutableCallSite(&V)) { + const auto &NoAliasAA = + A.getAAFor(*this, IRPosition::callsite_returned(ICS)); + if (NoAliasAA.isAssumedNoAlias()) { + updateState(T, NO_MALLOCED_MEM, I, &V, Changed); + return true; + } + } + + updateState(T, NO_UNKOWN_MEM, I, &V, Changed); + LLVM_DEBUG(dbgs() << "[AAMemoryLocation] Ptr value cannot be categorized: " + << V << " -> " << getMemoryLocationsAsStr(T.getAssumed()) + << "\n"); + return true; + }; + + if (!genericValueTraversal( + A, IRPosition::value(Ptr), *this, State, VisitValueCB, + /* MaxValues */ 32, StripGEPCB)) { + LLVM_DEBUG( + dbgs() << "[AAMemoryLocation] Pointer locations not categorized\n"); + updateState(State, NO_UNKOWN_MEM, I, nullptr, Changed); + } else { + LLVM_DEBUG( + dbgs() + << "[AAMemoryLocation] Accessed locations with pointer locations: " + << getMemoryLocationsAsStr(State.getAssumed()) << "\n"); + } +} + +AAMemoryLocation::MemoryLocationsKind +AAMemoryLocationImpl::categorizeAccessedLocations(Attributor &A, Instruction &I, + bool &Changed) { + LLVM_DEBUG(dbgs() << "[AAMemoryLocation] Categorize accessed locations for " + << I << "\n"); + + AAMemoryLocation::StateType AccessedLocs; + AccessedLocs.intersectAssumedBits(NO_LOCATIONS); + + if (ImmutableCallSite ICS = ImmutableCallSite(&I)) { + + // First check if we assume any memory is access is visible. + const auto &ICSMemLocationAA = + A.getAAFor(*this, IRPosition::callsite_function(ICS)); + LLVM_DEBUG(dbgs() << "[AAMemoryLocation] Categorize call site: " << I + << " [" << ICSMemLocationAA << "]\n"); + + if (ICSMemLocationAA.isAssumedReadNone()) + return NO_LOCATIONS; + + if (ICSMemLocationAA.isAssumedInaccessibleMemOnly()) { + updateState(AccessedLocs, NO_INACCESSIBLE_MEM, I, nullptr, Changed); + return AccessedLocs.getAssumed(); + } + + uint32_t ICSAssumedNotAccessedLocs = + ICSMemLocationAA.getAssumedNotAccessedLocation(); + + // Set the argmemonly bit as we handle arguments separately below. + uint32_t ICSAssumedNotAccessedLocsNoArgMem = + ICSAssumedNotAccessedLocs | NO_ARGUMENT_MEM; + + for (MemoryLocationsKind CurMLK = 1; CurMLK < NO_LOCATIONS; CurMLK *= 2) { + if (ICSAssumedNotAccessedLocsNoArgMem & CurMLK) + continue; + updateState(AccessedLocs, CurMLK, I, nullptr, Changed); + } + + LLVM_DEBUG( + dbgs() << "[AAMemoryLocation] Accessed state before argument handling: " + << getMemoryLocationsAsStr(AccessedLocs.getAssumed()) << "\n"); + + // Now handle argument memory if it might be accessed. + bool HasArgAccesses = + ICSAssumedNotAccessedLocs != ICSAssumedNotAccessedLocsNoArgMem; + if (HasArgAccesses) { + for (unsigned ArgNo = 0, e = ICS.getNumArgOperands(); ArgNo < e; + ++ArgNo) { + + // Skip non-pointer arguments. + const Value *ArgOp = ICS.getArgOperand(ArgNo); + if (!ArgOp->getType()->isPtrOrPtrVectorTy()) + continue; + + // Skip readnone arguments. + const IRPosition &ArgOpIRP = IRPosition::callsite_argument(ICS, ArgNo); + const auto &ArgOpMemLocationAA = A.getAAFor( + *this, ArgOpIRP, /* TrackDependence */ true, DepClassTy::OPTIONAL); + + if (ArgOpMemLocationAA.isAssumedReadNone()) + continue; + + // Categorize potentially accessed pointer arguments as if there was an + // access instruction with them as pointer. + categorizePtrValue(A, I, *ArgOp, AccessedLocs, Changed); + } + } + + LLVM_DEBUG( + dbgs() << "[AAMemoryLocation] Accessed state after argument handling: " + << getMemoryLocationsAsStr(AccessedLocs.getAssumed()) << "\n"); + + return AccessedLocs.getAssumed(); + } + + if (const Value *Ptr = getPointerOperand(&I, /* AllowVolatile */ true)) { + LLVM_DEBUG( + dbgs() << "[AAMemoryLocation] Categorize memory access with pointer: " + << I << " [" << *Ptr << "]\n"); + categorizePtrValue(A, I, *Ptr, AccessedLocs, Changed); + return AccessedLocs.getAssumed(); + } + + LLVM_DEBUG(dbgs() << "[AAMemoryLocation] Failed to categorize instruction: " + << I << "\n"); + updateState(AccessedLocs, NO_UNKOWN_MEM, I, nullptr, Changed); + return AccessedLocs.getAssumed(); +} + +/// An AA to represent the memory behavior function attributes. +struct AAMemoryLocationFunction final : public AAMemoryLocationImpl { + AAMemoryLocationFunction(const IRPosition &IRP) : AAMemoryLocationImpl(IRP) {} + + /// See AbstractAttribute::updateImpl(Attributor &A). + virtual ChangeStatus updateImpl(Attributor &A) override { + + const auto &MemBehaviorAA = A.getAAFor( + *this, getIRPosition(), /* TrackDependence */ false); + if (MemBehaviorAA.isAssumedReadNone()) { + if (MemBehaviorAA.isKnownReadNone()) + return indicateOptimisticFixpoint(); + assert(isAssumedReadNone() && + "AAMemoryLocation was not read-none but AAMemoryBehavior was!"); + A.recordDependence(MemBehaviorAA, *this, DepClassTy::OPTIONAL); + return ChangeStatus::UNCHANGED; + } + + // The current assumed state used to determine a change. + auto AssumedState = getAssumed(); + bool Changed = false; + + auto CheckRWInst = [&](Instruction &I) { + MemoryLocationsKind MLK = categorizeAccessedLocations(A, I, Changed); + LLVM_DEBUG(dbgs() << "[AAMemoryLocation] Accessed locations for " << I + << ": " << getMemoryLocationsAsStr(MLK) << "\n"); + removeAssumedBits(inverseLocation(MLK, false, false)); + return true; + }; + + if (!A.checkForAllReadWriteInstructions(CheckRWInst, *this)) + return indicatePessimisticFixpoint(); + + Changed |= AssumedState != getAssumed(); + return Changed ? ChangeStatus::CHANGED : ChangeStatus::UNCHANGED; + } + + /// See AbstractAttribute::trackStatistics() + void trackStatistics() const override { + if (isAssumedReadNone()) + STATS_DECLTRACK_FN_ATTR(readnone) + else if (isAssumedArgMemOnly()) + STATS_DECLTRACK_FN_ATTR(argmemonly) + else if (isAssumedInaccessibleMemOnly()) + STATS_DECLTRACK_FN_ATTR(inaccessiblememonly) + else if (isAssumedInaccessibleOrArgMemOnly()) + STATS_DECLTRACK_FN_ATTR(inaccessiblememorargmemonly) + } +}; + +/// AAMemoryLocation attribute for call sites. +struct AAMemoryLocationCallSite final : AAMemoryLocationImpl { + AAMemoryLocationCallSite(const IRPosition &IRP) : AAMemoryLocationImpl(IRP) {} + + /// See AbstractAttribute::initialize(...). + void initialize(Attributor &A) override { + AAMemoryLocationImpl::initialize(A); + Function *F = getAssociatedFunction(); + if (!F || !F->hasExactDefinition()) + indicatePessimisticFixpoint(); + } + + /// See AbstractAttribute::updateImpl(...). + ChangeStatus updateImpl(Attributor &A) override { + // TODO: Once we have call site specific value information we can provide + // call site specific liveness liveness information and then it makes + // sense to specialize attributes for call sites arguments instead of + // redirecting requests to the callee argument. + Function *F = getAssociatedFunction(); + const IRPosition &FnPos = IRPosition::function(*F); + auto &FnAA = A.getAAFor(*this, FnPos); + return clampStateAndIndicateChange( + getState(), + static_cast(FnAA.getState())); + } + + /// See AbstractAttribute::trackStatistics() + void trackStatistics() const override { + if (isAssumedReadNone()) + STATS_DECLTRACK_CS_ATTR(readnone) + } +}; + /// ------------------ Value Constant Range Attribute ------------------------- struct AAValueConstantRangeImpl : AAValueConstantRange { @@ -6612,6 +6993,9 @@ // Every function might be "readnone/readonly/writeonly/...". getOrCreateAAFor(FPos); + // Every function can be "readnone/argmemonly/inaccessiblememonly/...". + getOrCreateAAFor(FPos); + // Every function might be applicable for Heap-To-Stack conversion. if (EnableHeapToStack) getOrCreateAAFor(FPos); @@ -6931,6 +7315,7 @@ const char AAValueSimplify::ID = 0; const char AAHeapToStack::ID = 0; const char AAMemoryBehavior::ID = 0; +const char AAMemoryLocation::ID = 0; const char AAValueConstantRange::ID = 0; // Macro magic to create the static generator function for attributes that @@ -7031,6 +7416,7 @@ CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAWillReturn) CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoReturn) CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAReturnedValues) +CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAMemoryLocation) CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANonNull) CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoAlias) diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/X86/attributes.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/X86/attributes.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/X86/attributes.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/X86/attributes.ll @@ -26,7 +26,7 @@ ; CHECK-NEXT: [[TMP:%.*]] = alloca <4 x i64>, align 32 ; CHECK-NEXT: [[TMP2:%.*]] = alloca <4 x i64>, align 32 ; CHECK-NEXT: [[TMP3:%.*]] = bitcast <4 x i64>* [[TMP]] to i8* -; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull align 32 dereferenceable(32) [[TMP3]], i8 0, i64 32, i1 false) +; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull writeonly align 32 dereferenceable(32) [[TMP3]], i8 0, i64 32, i1 false) ; CHECK-NEXT: call fastcc void @no_promote_avx2(<4 x i64>* noalias nocapture nofree nonnull writeonly align 32 dereferenceable(32) [[TMP2]], <4 x i64>* noalias nocapture nofree nonnull readonly align 32 dereferenceable(32) [[TMP]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <4 x i64>, <4 x i64>* [[TMP2]], align 32 ; CHECK-NEXT: store <4 x i64> [[TMP4]], <4 x i64>* [[ARG]], align 2 @@ -64,7 +64,7 @@ ; CHECK-NEXT: [[TMP:%.*]] = alloca <4 x i64>, align 32 ; CHECK-NEXT: [[TMP2:%.*]] = alloca <4 x i64>, align 32 ; CHECK-NEXT: [[TMP3:%.*]] = bitcast <4 x i64>* [[TMP]] to i8* -; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull align 32 dereferenceable(32) [[TMP3]], i8 0, i64 32, i1 false) +; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull writeonly align 32 dereferenceable(32) [[TMP3]], i8 0, i64 32, i1 false) ; CHECK-NEXT: call fastcc void @promote_avx2(<4 x i64>* noalias nocapture nofree nonnull writeonly align 32 dereferenceable(32) [[TMP2]], <4 x i64>* noalias nocapture nofree nonnull readonly align 32 dereferenceable(32) [[TMP]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <4 x i64>, <4 x i64>* [[TMP2]], align 32 ; CHECK-NEXT: store <4 x i64> [[TMP4]], <4 x i64>* [[ARG]], align 2 diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/X86/min-legal-vector-width.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/X86/min-legal-vector-width.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/X86/min-legal-vector-width.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/X86/min-legal-vector-width.ll @@ -27,7 +27,7 @@ ; CHECK-NEXT: [[TMP:%.*]] = alloca <8 x i64>, align 32 ; CHECK-NEXT: [[TMP2:%.*]] = alloca <8 x i64>, align 32 ; CHECK-NEXT: [[TMP3:%.*]] = bitcast <8 x i64>* [[TMP]] to i8* -; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull align 32 dereferenceable(64) [[TMP3]], i8 0, i64 32, i1 false) +; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull writeonly align 32 dereferenceable(64) [[TMP3]], i8 0, i64 32, i1 false) ; CHECK-NEXT: call fastcc void @callee_avx512_legal512_prefer512_call_avx512_legal512_prefer512(<8 x i64>* noalias nocapture nofree nonnull writeonly align 32 dereferenceable(64) [[TMP2]], <8 x i64>* noalias nocapture nofree nonnull readonly align 32 dereferenceable(64) [[TMP]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <8 x i64>, <8 x i64>* [[TMP2]], align 32 ; CHECK-NEXT: store <8 x i64> [[TMP4]], <8 x i64>* [[ARG]], align 2 @@ -66,7 +66,7 @@ ; CHECK-NEXT: [[TMP:%.*]] = alloca <8 x i64>, align 32 ; CHECK-NEXT: [[TMP2:%.*]] = alloca <8 x i64>, align 32 ; CHECK-NEXT: [[TMP3:%.*]] = bitcast <8 x i64>* [[TMP]] to i8* -; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull align 32 dereferenceable(64) [[TMP3]], i8 0, i64 32, i1 false) +; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull writeonly align 32 dereferenceable(64) [[TMP3]], i8 0, i64 32, i1 false) ; CHECK-NEXT: call fastcc void @callee_avx512_legal512_prefer256_call_avx512_legal512_prefer256(<8 x i64>* noalias nocapture nofree nonnull writeonly align 32 dereferenceable(64) [[TMP2]], <8 x i64>* noalias nocapture nofree nonnull readonly align 32 dereferenceable(64) [[TMP]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <8 x i64>, <8 x i64>* [[TMP2]], align 32 ; CHECK-NEXT: store <8 x i64> [[TMP4]], <8 x i64>* [[ARG]], align 2 @@ -105,7 +105,7 @@ ; CHECK-NEXT: [[TMP:%.*]] = alloca <8 x i64>, align 32 ; CHECK-NEXT: [[TMP2:%.*]] = alloca <8 x i64>, align 32 ; CHECK-NEXT: [[TMP3:%.*]] = bitcast <8 x i64>* [[TMP]] to i8* -; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull align 32 dereferenceable(64) [[TMP3]], i8 0, i64 32, i1 false) +; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull writeonly align 32 dereferenceable(64) [[TMP3]], i8 0, i64 32, i1 false) ; CHECK-NEXT: call fastcc void @callee_avx512_legal512_prefer512_call_avx512_legal512_prefer256(<8 x i64>* noalias nocapture nofree nonnull writeonly align 32 dereferenceable(64) [[TMP2]], <8 x i64>* noalias nocapture nofree nonnull readonly align 32 dereferenceable(64) [[TMP]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <8 x i64>, <8 x i64>* [[TMP2]], align 32 ; CHECK-NEXT: store <8 x i64> [[TMP4]], <8 x i64>* [[ARG]], align 2 @@ -144,7 +144,7 @@ ; CHECK-NEXT: [[TMP:%.*]] = alloca <8 x i64>, align 32 ; CHECK-NEXT: [[TMP2:%.*]] = alloca <8 x i64>, align 32 ; CHECK-NEXT: [[TMP3:%.*]] = bitcast <8 x i64>* [[TMP]] to i8* -; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull align 32 dereferenceable(64) [[TMP3]], i8 0, i64 32, i1 false) +; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull writeonly align 32 dereferenceable(64) [[TMP3]], i8 0, i64 32, i1 false) ; CHECK-NEXT: call fastcc void @callee_avx512_legal512_prefer256_call_avx512_legal512_prefer512(<8 x i64>* noalias nocapture nofree nonnull writeonly align 32 dereferenceable(64) [[TMP2]], <8 x i64>* noalias nocapture nofree nonnull readonly align 32 dereferenceable(64) [[TMP]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <8 x i64>, <8 x i64>* [[TMP2]], align 32 ; CHECK-NEXT: store <8 x i64> [[TMP4]], <8 x i64>* [[ARG]], align 2 @@ -183,7 +183,7 @@ ; CHECK-NEXT: [[TMP:%.*]] = alloca <8 x i64>, align 32 ; CHECK-NEXT: [[TMP2:%.*]] = alloca <8 x i64>, align 32 ; CHECK-NEXT: [[TMP3:%.*]] = bitcast <8 x i64>* [[TMP]] to i8* -; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull align 32 dereferenceable(64) [[TMP3]], i8 0, i64 32, i1 false) +; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull writeonly align 32 dereferenceable(64) [[TMP3]], i8 0, i64 32, i1 false) ; CHECK-NEXT: call fastcc void @callee_avx512_legal256_prefer256_call_avx512_legal512_prefer256(<8 x i64>* noalias nocapture nofree nonnull writeonly align 32 dereferenceable(64) [[TMP2]], <8 x i64>* noalias nocapture nofree nonnull readonly align 32 dereferenceable(64) [[TMP]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <8 x i64>, <8 x i64>* [[TMP2]], align 32 ; CHECK-NEXT: store <8 x i64> [[TMP4]], <8 x i64>* [[ARG]], align 2 @@ -222,7 +222,7 @@ ; CHECK-NEXT: [[TMP:%.*]] = alloca <8 x i64>, align 32 ; CHECK-NEXT: [[TMP2:%.*]] = alloca <8 x i64>, align 32 ; CHECK-NEXT: [[TMP3:%.*]] = bitcast <8 x i64>* [[TMP]] to i8* -; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull align 32 dereferenceable(64) [[TMP3]], i8 0, i64 32, i1 false) +; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull writeonly align 32 dereferenceable(64) [[TMP3]], i8 0, i64 32, i1 false) ; CHECK-NEXT: call fastcc void @callee_avx512_legal512_prefer256_call_avx512_legal256_prefer256(<8 x i64>* noalias nocapture nofree nonnull writeonly align 32 dereferenceable(64) [[TMP2]], <8 x i64>* noalias nocapture nofree nonnull readonly align 32 dereferenceable(64) [[TMP]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <8 x i64>, <8 x i64>* [[TMP2]], align 32 ; CHECK-NEXT: store <8 x i64> [[TMP4]], <8 x i64>* [[ARG]], align 2 @@ -261,7 +261,7 @@ ; CHECK-NEXT: [[TMP:%.*]] = alloca <8 x i64>, align 32 ; CHECK-NEXT: [[TMP2:%.*]] = alloca <8 x i64>, align 32 ; CHECK-NEXT: [[TMP3:%.*]] = bitcast <8 x i64>* [[TMP]] to i8* -; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull align 32 dereferenceable(64) [[TMP3]], i8 0, i64 32, i1 false) +; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull writeonly align 32 dereferenceable(64) [[TMP3]], i8 0, i64 32, i1 false) ; CHECK-NEXT: call fastcc void @callee_avx2_legal256_prefer256_call_avx2_legal512_prefer256(<8 x i64>* noalias nocapture nofree nonnull writeonly align 32 dereferenceable(64) [[TMP2]], <8 x i64>* noalias nocapture nofree nonnull readonly align 32 dereferenceable(64) [[TMP]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <8 x i64>, <8 x i64>* [[TMP2]], align 32 ; CHECK-NEXT: store <8 x i64> [[TMP4]], <8 x i64>* [[ARG]], align 2 @@ -300,7 +300,7 @@ ; CHECK-NEXT: [[TMP:%.*]] = alloca <8 x i64>, align 32 ; CHECK-NEXT: [[TMP2:%.*]] = alloca <8 x i64>, align 32 ; CHECK-NEXT: [[TMP3:%.*]] = bitcast <8 x i64>* [[TMP]] to i8* -; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull align 32 dereferenceable(64) [[TMP3]], i8 0, i64 32, i1 false) +; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull writeonly align 32 dereferenceable(64) [[TMP3]], i8 0, i64 32, i1 false) ; CHECK-NEXT: call fastcc void @callee_avx2_legal512_prefer256_call_avx2_legal256_prefer256(<8 x i64>* noalias nocapture nofree nonnull writeonly align 32 dereferenceable(64) [[TMP2]], <8 x i64>* noalias nocapture nofree nonnull readonly align 32 dereferenceable(64) [[TMP]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <8 x i64>, <8 x i64>* [[TMP2]], align 32 ; CHECK-NEXT: store <8 x i64> [[TMP4]], <8 x i64>* [[ARG]], align 2 diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll @@ -30,7 +30,7 @@ ; CHECK-LABEL: define {{[^@]+}}@caller ; CHECK-SAME: (i32** nocapture readonly [[Y:%.*]], %struct.pair* nocapture nofree readonly [[P:%.*]]) ; CHECK-NEXT: call void @test(i32** nocapture readonly align 8 [[Y]]), !dbg !4 -; CHECK-NEXT: call void @test_byval(), !dbg !5 +; CHECK-NEXT: call void @test_byval() #1, !dbg !5 ; CHECK-NEXT: ret void ; call void @test(i32** %Y), !dbg !1 diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/fp80.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/fp80.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/fp80.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/fp80.ll @@ -15,7 +15,6 @@ define void @run() { ; CHECK-LABEL: define {{[^@]+}}@run() ; CHECK-NEXT: entry: -; CHECK-NEXT: [[TMP0:%.*]] = call i64 @CaptureAStruct(%struct.Foo* nofree nonnull readonly align 8 dereferenceable(16) @a) ; CHECK-NEXT: unreachable ; entry: @@ -47,18 +46,6 @@ } define internal i64 @CaptureAStruct(%struct.Foo* byval %a) { -; CHECK-LABEL: define {{[^@]+}}@CaptureAStruct -; CHECK-SAME: (%struct.Foo* noalias nofree nonnull byval align 8 dereferenceable(16) [[A:%.*]]) -; CHECK-NEXT: entry: -; CHECK-NEXT: [[A_PTR:%.*]] = alloca %struct.Foo* -; CHECK-NEXT: br label [[LOOP:%.*]] -; CHECK: loop: -; CHECK-NEXT: [[PHI:%.*]] = phi %struct.Foo* [ null, [[ENTRY:%.*]] ], [ [[GEP:%.*]], [[LOOP]] ] -; CHECK-NEXT: [[TMP0:%.*]] = phi %struct.Foo* [ [[A]], [[ENTRY]] ], [ [[TMP0]], [[LOOP]] ] -; CHECK-NEXT: store %struct.Foo* [[PHI]], %struct.Foo** [[A_PTR]], align 8 -; CHECK-NEXT: [[GEP]] = getelementptr [[STRUCT_FOO:%.*]], %struct.Foo* [[A]], i64 0 -; CHECK-NEXT: br label [[LOOP]] -; entry: %a_ptr = alloca %struct.Foo* br label %loop diff --git a/llvm/test/Transforms/Attributor/liveness.ll b/llvm/test/Transforms/Attributor/liveness.ll --- a/llvm/test/Transforms/Attributor/liveness.ll +++ b/llvm/test/Transforms/Attributor/liveness.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,OLDPM -; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,NEWPM +; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s --check-prefixes=CHECK,OLDPM +; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s --check-prefixes=CHECK,NEWPM ; UTC_ARGS: --turn off ; CHECK: @dead_with_blockaddress_users.l = constant [2 x i8*] [i8* inttoptr (i32 1 to i8*), i8* inttoptr (i32 1 to i8*)] @@ -41,7 +41,7 @@ br i1 %10, label %3, label %5 } -; CHECK: Function Attrs: nofree norecurse nounwind uwtable willreturn +; CHECK: Function Attrs: argmemonly nofree norecurse nounwind uwtable willreturn define i32 @volatile_load(i32*) norecurse nounwind uwtable { %2 = load volatile i32, i32* %0, align 4 ret i32 %2 @@ -54,7 +54,7 @@ } ; TEST 1: Only first block is live. -; CHECK: Function Attrs: nofree noreturn nosync nounwind +; CHECK: Function Attrs: argmemonly nofree noreturn nosync nounwind ; CHECK-NEXT: define i32 @first_block_no_return(i32 %a, i32* nocapture nofree nonnull readonly %ptr1, i32* nocapture nofree readnone %ptr2) define i32 @first_block_no_return(i32 %a, i32* nonnull %ptr1, i32* %ptr2) #0 { entry: diff --git a/llvm/test/Transforms/Attributor/nonnull.ll b/llvm/test/Transforms/Attributor/nonnull.ll --- a/llvm/test/Transforms/Attributor/nonnull.ll +++ b/llvm/test/Transforms/Attributor/nonnull.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_OPM -; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_NPM +; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_OPM +; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_NPM ; Copied from Transforms/FunctoinAttrs/nonnull.ll target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" diff --git a/llvm/test/Transforms/Attributor/nosync.ll b/llvm/test/Transforms/Attributor/nosync.ll --- a/llvm/test/Transforms/Attributor/nosync.ll +++ b/llvm/test/Transforms/Attributor/nosync.ll @@ -39,7 +39,7 @@ ; return n; ; } -; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind uwtable +; ATTRIBUTOR: Function Attrs: argmemonly nofree norecurse nosync nounwind uwtable ; ATTRIBUTOR-NEXT: define i32 @load_monotonic(i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) %0) define i32 @load_monotonic(i32* nocapture readonly %0) norecurse nounwind uwtable { %2 = load atomic i32, i32* %0 monotonic, align 4 @@ -53,7 +53,7 @@ ; atomic_load_explicit(num, memory_order_relaxed); ; } -; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind uwtable +; ATTRIBUTOR: Function Attrs: argmemonly nofree norecurse nosync nounwind uwtable ; ATTRIBUTOR-NEXT: define void @store_monotonic(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) %0) define void @store_monotonic(i32* nocapture %0) norecurse nounwind uwtable { store atomic i32 10, i32* %0 monotonic, align 4 @@ -67,7 +67,7 @@ ; return n; ; } -; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable +; ATTRIBUTOR: Function Attrs: argmemonly nofree norecurse nounwind uwtable ; ATTRIBUTOR-NOT: nosync ; ATTRIBUTOR-NEXT: define i32 @load_acquire(i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) %0) define i32 @load_acquire(i32* nocapture readonly %0) norecurse nounwind uwtable { @@ -81,7 +81,7 @@ ; atomic_store_explicit(num, 10, memory_order_release); ; } -; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable +; ATTRIBUTOR: Function Attrs: argmemonly nofree norecurse nounwind uwtable ; ATTRIBUTOR-NOT: nosync ; ATTRIBUTOR-NEXT: define void @load_release(i32* nocapture nofree writeonly align 4 %0) define void @load_release(i32* nocapture %0) norecurse nounwind uwtable { @@ -91,7 +91,7 @@ ; TEST 6 - negative volatile, relaxed atomic -; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable +; ATTRIBUTOR: Function Attrs: argmemonly nofree norecurse nounwind uwtable ; ATTRIBUTOR-NOT: nosync ; ATTRIBUTOR-NEXT: define void @load_volatile_release(i32* nocapture nofree writeonly align 4 %0) define void @load_volatile_release(i32* nocapture %0) norecurse nounwind uwtable { @@ -105,7 +105,7 @@ ; *num = 14; ; } -; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable +; ATTRIBUTOR: Function Attrs: argmemonly nofree norecurse nounwind uwtable ; ATTRIBUTOR-NOT: nosync ; ATTRIBUTOR-NEXT: define void @volatile_store(i32* nofree align 4 %0) define void @volatile_store(i32* %0) norecurse nounwind uwtable { @@ -120,7 +120,7 @@ ; return n; ; } -; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable +; ATTRIBUTOR: Function Attrs: argmemonly nofree norecurse nounwind uwtable ; ATTRIBUTOR-NOT: nosync ; ATTRIBUTOR-NEXT: define i32 @volatile_load(i32* nofree align 4 %0) define i32 @volatile_load(i32* %0) norecurse nounwind uwtable { @@ -256,7 +256,7 @@ ; It is odd to add nocapture but a result of the llvm.memcpy nocapture. ; -; ATTRIBUTOR: Function Attrs: nounwind +; ATTRIBUTOR: Function Attrs: argmemonly nounwind ; ATTRIBUTOR-NOT: nosync ; ATTRIBUTOR-NEXT: define i32 @memcpy_volatile(i8* nocapture writeonly %ptr1, i8* nocapture readonly %ptr2) define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2) { @@ -268,7 +268,7 @@ ; It is odd to add nocapture but a result of the llvm.memset nocapture. ; -; ATTRIBUTOR: Function Attrs: nosync +; ATTRIBUTOR: Function Attrs: argmemonly nosync ; ATTRIBUTOR-NEXT: define i32 @memset_non_volatile(i8* nocapture writeonly %ptr1, i8 %val) define i32 @memset_non_volatile(i8* %ptr1, i8 %val) { call void @llvm.memset(i8* %ptr1, i8 %val, i32 8, i1 0) diff --git a/llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll b/llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll --- a/llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll +++ b/llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll @@ -29,7 +29,7 @@ ; target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" -; CHECK: Function Attrs: nofree nosync nounwind +; CHECK: Function Attrs: argmemonly nofree nosync nounwind ; CHECK-NEXT: define i32* @external_ret2_nrw(i32* nofree %n0, i32* nofree %r0, i32* nofree returned %w0) define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: @@ -40,7 +40,7 @@ ret i32* %call3 } -; CHECK: Function Attrs: nofree nosync nounwind +; CHECK: Function Attrs: argmemonly nofree nosync nounwind ; CHECK-NEXT: define internal i32* @internal_ret0_nw(i32* nofree returned %n0, i32* nofree %w0) define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) { entry: @@ -69,7 +69,7 @@ ret i32* %retval.0 } -; CHECK: Function Attrs: nofree nosync nounwind +; CHECK: Function Attrs: argmemonly nofree nosync nounwind ; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* nofree nonnull align 4 dereferenceable(4) %r0, i32* nofree returned %r1, i32* nofree %w0) define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) { entry: @@ -101,7 +101,7 @@ ret i32* %retval.0 } -; CHECK: Function Attrs: nofree norecurse nosync nounwind +; CHECK: Function Attrs: argmemonly nofree norecurse nosync nounwind ; CHECK-NEXT: define i32* @external_sink_ret2_nrw(i32* nofree readnone %n0, i32* nocapture nofree readonly %r0, i32* nofree returned writeonly "no-capture-maybe-returned" %w0) define i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: @@ -120,7 +120,7 @@ ret i32* %w0 } -; CHECK: Function Attrs: nofree nosync nounwind +; CHECK: Function Attrs: argmemonly nofree nosync nounwind ; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* nofree nonnull align 4 dereferenceable(4) %r0, i32* nofree returned %w0) define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) { entry: @@ -146,7 +146,7 @@ ret i32* %retval.0 } -; CHECK: Function Attrs: nofree nosync nounwind +; CHECK: Function Attrs: argmemonly nofree nosync nounwind ; CHECK-NEXT: define i32* @external_source_ret2_nrw(i32* nofree %n0, i32* nofree %r0, i32* nofree returned %w0) define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: @@ -159,7 +159,7 @@ ; for a subset relation. ; ; CHECK-NOT: attributes # -; CHECK: attributes #{{.*}} = { nofree nosync nounwind } -; CHECK: attributes #{{.*}} = { nofree norecurse nosync nounwind } +; CHECK: attributes #{{.*}} = { argmemonly nofree nosync nounwind } +; CHECK: attributes #{{.*}} = { argmemonly nofree norecurse nosync nounwind } ; CHECK: attributes #{{.*}} = { nosync } ; CHECK-NOT: attributes # diff --git a/llvm/test/Transforms/Attributor/returned.ll b/llvm/test/Transforms/Attributor/returned.ll --- a/llvm/test/Transforms/Attributor/returned.ll +++ b/llvm/test/Transforms/Attributor/returned.ll @@ -246,7 +246,7 @@ ; return *a ? a : rt0(a); ; } ; -; BOTH: Function Attrs: nofree noinline norecurse noreturn nosync nounwind readonly uwtable +; BOTH: Function Attrs: argmemonly nofree noinline norecurse noreturn nosync nounwind readonly uwtable ; BOTH-NEXT: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt0(i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) %a) define i32* @rt0(i32* %a) #0 { entry: @@ -263,7 +263,7 @@ ; return *a ? undef : rt1(a); ; } ; -; BOTH: Function Attrs: nofree noinline norecurse noreturn nosync nounwind readonly uwtable +; BOTH: Function Attrs: argmemonly nofree noinline norecurse noreturn nosync nounwind readonly uwtable ; BOTH-NEXT: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt1(i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) %a) define i32* @rt1(i32* %a) #0 { entry: diff --git a/llvm/test/Transforms/Attributor/willreturn.ll b/llvm/test/Transforms/Attributor/willreturn.ll --- a/llvm/test/Transforms/Attributor/willreturn.ll +++ b/llvm/test/Transforms/Attributor/willreturn.ll @@ -288,7 +288,7 @@ ; } ; FIXME: missing willreturn -; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable +; ATTRIBUTOR: Function Attrs: argmemonly nofree noinline norecurse nosync nounwind readonly uwtable ; ATTRIBUTOR-NEXT: define i32 @loop_constant_trip_count(i32* nocapture nofree readonly %0) define i32 @loop_constant_trip_count(i32* nocapture readonly %0) #0 { br label %3 @@ -318,7 +318,7 @@ ; } ; return ans; ; } -; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable +; ATTRIBUTOR: Function Attrs: argmemonly nofree noinline norecurse nosync nounwind readonly uwtable ; ATTRIBUTOR-NOT: willreturn ; ATTRIBUTOR-NEXT: define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture nofree readonly %2, i32 %3) local_unnamed_addr define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture readonly %2, i32 %3) local_unnamed_addr #0 { @@ -354,7 +354,7 @@ ; FIXME: missing willreturn -; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable +; ATTRIBUTOR: Function Attrs: argmemonly nofree noinline norecurse nosync nounwind readonly uwtable ; ATTRIBUTOR-NEXT: define i32 @loop_trip_dec(i32 %0, i32* nocapture nofree readonly %1) local_unnamed_addr define i32 @loop_trip_dec(i32 %0, i32* nocapture readonly %1) local_unnamed_addr #0 {