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 @@ -2486,7 +2486,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, @@ -2502,6 +2503,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. @@ -2535,6 +2537,145 @@ 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 isKnown( + inverseLocation(NO_INACCESSIBLE_MEM | NO_ARGUMENT_MEM, true, true)); + } + + /// Return true if we assume that the underlying value will only access + /// inaccesible memory or argument pointees (see + /// Attribute::InaccessibleOrArgMemOnly). + bool isAssumedInaccessibleOrArgMemOnly() const { + return isAssumed( + inverseLocation(NO_INACCESSIBLE_MEM | NO_ARGUMENT_MEM, true, true)); + } + + /// 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 @@ -135,6 +135,7 @@ PIPE_OPERATOR(AAHeapToStack) PIPE_OPERATOR(AAReachability) PIPE_OPERATOR(AAMemoryBehavior) +PIPE_OPERATOR(AAMemoryLocation) PIPE_OPERATOR(AAValueConstantRange) PIPE_OPERATOR(AAPrivatizablePtr) @@ -382,7 +383,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()) @@ -399,6 +400,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! @@ -730,7 +733,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). @@ -5481,7 +5483,7 @@ State.addKnownBits(NO_READS); break; default: - llvm_unreachable("Unexpcted attribute!"); + llvm_unreachable("Unexpected attribute!"); } } @@ -5508,6 +5510,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. @@ -5612,6 +5617,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})) { @@ -5754,7 +5763,6 @@ STATS_DECLTRACK_CS_ATTR(writeonly) } }; -} // namespace ChangeStatus AAMemoryBehaviorFunction::updateImpl(Attributor &A) { @@ -5949,6 +5957,384 @@ 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_MALLOCED_MEM)) + S += "malloced,"; + 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) -> Value * { + auto *GEP = dyn_cast(V); + while (GEP) { + V = GEP->getPointerOperand(); + GEP = dyn_cast(V); + } + return V; + }; + + auto VisitValueCB = [&](Value &V, AAMemoryLocation::StateType &T, + bool Stripped) -> bool { + assert(!isa(V) && "GEPs should have been stripped."); + 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 them 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 & NO_ARGUMENT_MEM); + 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 { @@ -7450,6 +7836,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); @@ -7861,6 +8250,7 @@ const char AAHeapToStack::ID = 0; const char AAPrivatizablePtr::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 @@ -7961,6 +8351,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 @@ -66,7 +66,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: [[TMP0:%.*]] = load <4 x i64>, <4 x i64>* [[TMP]], align 1 ; CHECK-NEXT: call fastcc void @promote_avx2(<4 x i64>* noalias nocapture nofree nonnull writeonly align 32 dereferenceable(32) [[TMP2]], <4 x i64> [[TMP0]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <4 x i64>, <4 x i64>* [[TMP2]], align 32 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 @@ -29,7 +29,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: [[TMP0:%.*]] = load <8 x i64>, <8 x i64>* [[TMP]], align 1 ; 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> [[TMP0]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <8 x i64>, <8 x i64>* [[TMP2]], align 32 @@ -71,7 +71,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: [[TMP0:%.*]] = load <8 x i64>, <8 x i64>* [[TMP]], align 1 ; 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> [[TMP0]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <8 x i64>, <8 x i64>* [[TMP2]], align 32 @@ -113,7 +113,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: [[TMP0:%.*]] = load <8 x i64>, <8 x i64>* [[TMP]], align 1 ; 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> [[TMP0]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <8 x i64>, <8 x i64>* [[TMP2]], align 32 @@ -155,7 +155,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: [[TMP0:%.*]] = load <8 x i64>, <8 x i64>* [[TMP]], align 1 ; 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> [[TMP0]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <8 x i64>, <8 x i64>* [[TMP2]], align 32 @@ -195,7 +195,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 @@ -234,7 +234,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 @@ -275,7 +275,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: [[TMP0:%.*]] = load <8 x i64>, <8 x i64>* [[TMP]], align 1 ; 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> [[TMP0]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <8 x i64>, <8 x i64>* [[TMP2]], align 32 @@ -317,7 +317,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: [[TMP0:%.*]] = load <8 x i64>, <8 x i64>* [[TMP]], align 1 ; 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> [[TMP0]]) ; CHECK-NEXT: [[TMP4:%.*]] = load <8 x i64>, <8 x i64>* [[TMP2]], align 32 diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/byval.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/byval.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/byval.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/byval.ll @@ -61,15 +61,15 @@ ; CHECK-NEXT: store i32 1, i32* [[TMP1]], align 8 ; CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 ; CHECK-NEXT: store i64 2, i64* [[TMP4]], align 4 -; CHECK-NEXT: [[S_CAST:%.*]] = bitcast %struct.ss* [[S]] to i32* -; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[S_CAST]], align 1 -; CHECK-NEXT: [[S_0_1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 -; CHECK-NEXT: [[TMP1:%.*]] = load i64, i64* [[S_0_1]], align 1 -; CHECK-NEXT: call void @f(i32 [[TMP0]], i64 [[TMP1]]) ; CHECK-NEXT: [[S_CAST1:%.*]] = bitcast %struct.ss* [[S]] to i32* -; CHECK-NEXT: [[TMP2:%.*]] = load i32, i32* [[S_CAST1]], align 1 +; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[S_CAST1]], align 1 ; CHECK-NEXT: [[S_0_12:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 -; CHECK-NEXT: [[TMP3:%.*]] = load i64, i64* [[S_0_12]], align 1 +; CHECK-NEXT: [[TMP1:%.*]] = load i64, i64* [[S_0_12]], align 1 +; CHECK-NEXT: call void @f(i32 [[TMP0]], i64 [[TMP1]]) +; CHECK-NEXT: [[S_CAST:%.*]] = bitcast %struct.ss* [[S]] to i32* +; CHECK-NEXT: [[TMP2:%.*]] = load i32, i32* [[S_CAST]], align 1 +; CHECK-NEXT: [[S_0_1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 +; CHECK-NEXT: [[TMP3:%.*]] = load i64, i64* [[S_0_1]], align 1 ; CHECK-NEXT: call void @g(i32 [[TMP2]], i64 [[TMP3]]) ; CHECK-NEXT: ret i32 0 ; 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,11 +15,6 @@ define void @run() { ; CHECK-LABEL: define {{[^@]+}}@run() ; CHECK-NEXT: entry: -; CHECK-NEXT: [[A_CAST:%.*]] = bitcast %struct.Foo* @a to i32* -; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[A_CAST]], align 1 -; CHECK-NEXT: [[A_0_1:%.*]] = getelementptr [[STRUCT_FOO:%.*]], %struct.Foo* @a, i32 0, i32 1 -; CHECK-NEXT: [[TMP1:%.*]] = load i64, i64* [[A_0_1]], align 1 -; CHECK-NEXT: [[TMP2:%.*]] = call i64 @CaptureAStruct(i32 [[TMP0]], i64 [[TMP1]]) ; CHECK-NEXT: unreachable ; entry: @@ -51,23 +46,6 @@ } define internal i64 @CaptureAStruct(%struct.Foo* byval %a) { -; CHECK-LABEL: define {{[^@]+}}@CaptureAStruct -; CHECK-SAME: (i32 [[TMP0:%.*]], i64 [[TMP1:%.*]]) -; CHECK-NEXT: entry: -; CHECK-NEXT: [[A_PRIV:%.*]] = alloca [[STRUCT_FOO:%.*]] -; CHECK-NEXT: [[A_PRIV_CAST:%.*]] = bitcast %struct.Foo* [[A_PRIV]] to i32* -; CHECK-NEXT: store i32 [[TMP0]], i32* [[A_PRIV_CAST]] -; CHECK-NEXT: [[A_PRIV_0_1:%.*]] = getelementptr [[STRUCT_FOO]], %struct.Foo* [[A_PRIV]], i32 0, i32 1 -; CHECK-NEXT: store i64 [[TMP1]], i64* [[A_PRIV_0_1]] -; 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: [[TMP2:%.*]] = phi %struct.Foo* [ [[A_PRIV]], [[ENTRY]] ], [ [[TMP2]], [[LOOP]] ] -; CHECK-NEXT: store %struct.Foo* [[PHI]], %struct.Foo** [[A_PTR]], align 8 -; CHECK-NEXT: [[GEP]] = getelementptr [[STRUCT_FOO]], %struct.Foo* [[A_PRIV]], 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,8 +1,8 @@ ; 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,MODULE,ALL_BUT_OLD_CGSCCC -; RUN: opt -attributor-cgscc --attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC -; 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,MODULE,ALL_BUT_OLD_CGSCCC -; RUN: opt -passes='attributor-cgscc' --attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC,ALL_BUT_OLD_CGSCCC +; 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,MODULE,ALL_BUT_OLD_CGSCCC +; RUN: opt -attributor-cgscc --attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC +; 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,MODULE,ALL_BUT_OLD_CGSCCC +; RUN: opt -passes='attributor-cgscc' --attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC,ALL_BUT_OLD_CGSCCC ; UTC_ARGS: --turn off ; ALL_BUT_OLD_CGSCCC: @dead_with_blockaddress_users.l = constant [2 x i8*] [i8* inttoptr (i32 1 to i8*), i8* inttoptr (i32 1 to i8*)] @@ -43,7 +43,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 @@ -56,7 +56,7 @@ } ; TEST 1: Only first block is live. -; CHECK: Function Attrs: nofree noreturn nosync nounwind +; CHECK: Function Attrs: argmemonly nofree noreturn nosync nounwind ; MODULE-NEXT: define i32 @first_block_no_return(i32 %a, i32* nocapture nofree nonnull readonly %ptr1, i32* nocapture nofree readnone %ptr2) ; CGSCC-NEXT: define i32 @first_block_no_return(i32 %a, i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) %ptr1, i32* nocapture nofree readnone %ptr2) define i32 @first_block_no_return(i32 %a, i32* nonnull %ptr1, i32* %ptr2) #0 { diff --git a/llvm/test/Transforms/Attributor/memory_locations.ll b/llvm/test/Transforms/Attributor/memory_locations.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/memory_locations.ll @@ -0,0 +1,241 @@ +; 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=6 -S < %s | FileCheck %s --check-prefixes=CHECK,MODULE,OLD_MODULE +; RUN: opt -attributor-cgscc --attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC,OLD_CGSCC +; 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,MODULE,NEW_MODULE +; RUN: opt -passes='attributor-cgscc' --attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC,NEW_CGSCC +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + +; CHECK: Function Attrs: inaccessiblememonly +declare noalias i8* @malloc(i64) inaccessiblememonly + +define dso_local i8* @internal_only(i32 %arg) { +; CHECK: Function Attrs: inaccessiblememonly +; CHECK-LABEL: define {{[^@]+}}@internal_only +; CHECK-SAME: (i32 [[ARG:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[ARG]] to i64 +; CHECK-NEXT: [[CALL:%.*]] = call noalias i8* @malloc(i64 [[CONV]]) +; CHECK-NEXT: ret i8* [[CALL]] +; +entry: + %conv = sext i32 %arg to i64 + %call = call i8* @malloc(i64 %conv) + ret i8* %call +} + +define dso_local i8* @internal_only_rec(i32 %arg) { +; CHECK: Function Attrs: inaccessiblememonly +; CHECK-LABEL: define {{[^@]+}}@internal_only_rec +; CHECK-SAME: (i32 [[ARG:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[REM:%.*]] = srem i32 [[ARG]], 2 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[REM]], 1 +; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[DIV:%.*]] = sdiv i32 [[ARG]], 2 +; CHECK-NEXT: [[CALL:%.*]] = call noalias i8* @internal_only_rec(i32 [[DIV]]) +; CHECK-NEXT: br label [[RETURN:%.*]] +; CHECK: if.end: +; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[ARG]] to i64 +; CHECK-NEXT: [[CALL1:%.*]] = call noalias i8* @malloc(i64 [[CONV]]) +; CHECK-NEXT: br label [[RETURN]] +; CHECK: return: +; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i8* [ [[CALL]], [[IF_THEN]] ], [ [[CALL1]], [[IF_END]] ] +; CHECK-NEXT: ret i8* [[RETVAL_0]] +; +entry: + %rem = srem i32 %arg, 2 + %cmp = icmp eq i32 %rem, 1 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + %div = sdiv i32 %arg, 2 + %call = call i8* @internal_only_rec(i32 %div) + br label %return + +if.end: ; preds = %entry + %conv = sext i32 %arg to i64 + %call1 = call i8* @malloc(i64 %conv) + br label %return + +return: ; preds = %if.end, %if.then + %retval.0 = phi i8* [ %call, %if.then ], [ %call1, %if.end ] + ret i8* %retval.0 +} + +define dso_local i8* @internal_only_rec_static_helper(i32 %arg) { +; CHECK: Function Attrs: inaccessiblememonly +; CHECK-LABEL: define {{[^@]+}}@internal_only_rec_static_helper +; CHECK-SAME: (i32 [[ARG:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CALL:%.*]] = call noalias i8* @internal_only_rec_static(i32 [[ARG]]) +; CHECK-NEXT: ret i8* [[CALL]] +; +entry: + %call = call i8* @internal_only_rec_static(i32 %arg) + ret i8* %call +} + +define internal i8* @internal_only_rec_static(i32 %arg) { +; CHECK: Function Attrs: inaccessiblememonly +; CHECK-LABEL: define {{[^@]+}}@internal_only_rec_static +; CHECK-SAME: (i32 [[ARG:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[REM:%.*]] = srem i32 [[ARG]], 2 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[REM]], 1 +; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[DIV:%.*]] = sdiv i32 [[ARG]], 2 +; CHECK-NEXT: [[CALL:%.*]] = call noalias i8* @internal_only_rec(i32 [[DIV]]) +; CHECK-NEXT: br label [[RETURN:%.*]] +; CHECK: if.end: +; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[ARG]] to i64 +; CHECK-NEXT: [[CALL1:%.*]] = call noalias i8* @malloc(i64 [[CONV]]) +; CHECK-NEXT: br label [[RETURN]] +; CHECK: return: +; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i8* [ [[CALL]], [[IF_THEN]] ], [ [[CALL1]], [[IF_END]] ] +; CHECK-NEXT: ret i8* [[RETVAL_0]] +; +entry: + %rem = srem i32 %arg, 2 + %cmp = icmp eq i32 %rem, 1 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + %div = sdiv i32 %arg, 2 + %call = call i8* @internal_only_rec(i32 %div) + br label %return + +if.end: ; preds = %entry + %conv = sext i32 %arg to i64 + %call1 = call i8* @malloc(i64 %conv) + br label %return + +return: ; preds = %if.end, %if.then + %retval.0 = phi i8* [ %call, %if.then ], [ %call1, %if.end ] + ret i8* %retval.0 +} + +define dso_local i8* @internal_argmem_only_read(i32* %arg) { +; CHECK: Function Attrs: inaccessiblemem_or_argmemonly +; CHECK-LABEL: define {{[^@]+}}@internal_argmem_only_read +; CHECK-SAME: (i32* nocapture nonnull readonly align 4 dereferenceable(4) [[ARG:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP:%.*]] = load i32, i32* [[ARG]], align 4 +; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP]] to i64 +; CHECK-NEXT: [[CALL:%.*]] = call noalias i8* @malloc(i64 [[CONV]]) +; CHECK-NEXT: ret i8* [[CALL]] +; +entry: + %tmp = load i32, i32* %arg, align 4 + %conv = sext i32 %tmp to i64 + %call = call i8* @malloc(i64 %conv) + ret i8* %call +} + +define dso_local i8* @internal_argmem_only_write(i32* %arg) { +; CHECK: Function Attrs: inaccessiblemem_or_argmemonly +; CHECK-LABEL: define {{[^@]+}}@internal_argmem_only_write +; CHECK-SAME: (i32* nocapture nonnull writeonly align 4 dereferenceable(4) [[ARG:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: store i32 10, i32* [[ARG]], align 4 +; CHECK-NEXT: [[CALL:%.*]] = call noalias dereferenceable_or_null(10) i8* @malloc(i64 10) +; CHECK-NEXT: ret i8* [[CALL]] +; +entry: + store i32 10, i32* %arg, align 4 + %call = call dereferenceable_or_null(10) i8* @malloc(i64 10) + ret i8* %call +} + +define dso_local i8* @internal_argmem_only_rec(i32* %arg) { +; CHECK: Function Attrs: inaccessiblemem_or_argmemonly +; MODULE-LABEL: define {{[^@]+}}@internal_argmem_only_rec +; MODULE-SAME: (i32* nocapture align 4 [[ARG:%.*]]) +; MODULE-NEXT: entry: +; MODULE-NEXT: [[CALL:%.*]] = call noalias i8* @internal_argmem_only_rec_1(i32* nocapture align 4 [[ARG]]) +; MODULE-NEXT: ret i8* [[CALL]] +; +; CGSCC-LABEL: define {{[^@]+}}@internal_argmem_only_rec +; CGSCC-SAME: (i32* nocapture nonnull align 4 dereferenceable(4) [[ARG:%.*]]) +; CGSCC-NEXT: entry: +; CGSCC-NEXT: [[CALL:%.*]] = call noalias i8* @internal_argmem_only_rec_1(i32* nocapture nonnull align 4 dereferenceable(4) [[ARG]]) +; CGSCC-NEXT: ret i8* [[CALL]] +; +entry: + %call = call i8* @internal_argmem_only_rec_1(i32* %arg) + ret i8* %call +} + +define internal i8* @internal_argmem_only_rec_1(i32* %arg) { +; CHECK: Function Attrs: inaccessiblemem_or_argmemonly +; CHECK-LABEL: define {{[^@]+}}@internal_argmem_only_rec_1 +; CHECK-SAME: (i32* nocapture nonnull align 4 dereferenceable(4) [[ARG:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP:%.*]] = load i32, i32* [[ARG]], align 4 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP]], 0 +; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: br label [[RETURN:%.*]] +; CHECK: if.end: +; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[ARG]], align 4 +; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 1 +; CHECK-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END3:%.*]] +; CHECK: if.then2: +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, i32* [[ARG]], i64 -1 +; CHECK-NEXT: [[CALL:%.*]] = call noalias i8* @internal_argmem_only_rec_2(i32* nocapture nonnull align 4 dereferenceable(4) [[ADD_PTR]]) +; CHECK-NEXT: br label [[RETURN]] +; CHECK: if.end3: +; CHECK-NEXT: [[TMP2:%.*]] = load i32, i32* [[ARG]], align 4 +; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP2]] to i64 +; CHECK-NEXT: [[CALL4:%.*]] = call noalias i8* @malloc(i64 [[CONV]]) +; CHECK-NEXT: br label [[RETURN]] +; CHECK: return: +; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i8* [ null, [[IF_THEN]] ], [ [[CALL]], [[IF_THEN2]] ], [ [[CALL4]], [[IF_END3]] ] +; CHECK-NEXT: ret i8* [[RETVAL_0]] +; +entry: + %tmp = load i32, i32* %arg, align 4 + %cmp = icmp eq i32 %tmp, 0 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + br label %return + +if.end: ; preds = %entry + %tmp1 = load i32, i32* %arg, align 4 + %cmp1 = icmp eq i32 %tmp1, 1 + br i1 %cmp1, label %if.then2, label %if.end3 + +if.then2: ; preds = %if.end + %add.ptr = getelementptr inbounds i32, i32* %arg, i64 -1 + %call = call i8* @internal_argmem_only_rec_2(i32* nonnull %add.ptr) + br label %return + +if.end3: ; preds = %if.end + %tmp2 = load i32, i32* %arg, align 4 + %conv = sext i32 %tmp2 to i64 + %call4 = call i8* @malloc(i64 %conv) + br label %return + +return: ; preds = %if.end3, %if.then2, %if.then + %retval.0 = phi i8* [ null, %if.then ], [ %call, %if.then2 ], [ %call4, %if.end3 ] + ret i8* %retval.0 +} + +define internal i8* @internal_argmem_only_rec_2(i32* %arg) { +; CHECK: Function Attrs: inaccessiblemem_or_argmemonly +; CHECK-LABEL: define {{[^@]+}}@internal_argmem_only_rec_2 +; CHECK-SAME: (i32* nocapture nonnull align 4 dereferenceable(4) [[ARG:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: store i32 0, i32* [[ARG]], align 4 +; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, i32* [[ARG]], i64 -1 +; CHECK-NEXT: [[CALL:%.*]] = call noalias i8* @internal_argmem_only_rec_1(i32* nocapture nonnull align 4 dereferenceable(4) [[ADD_PTR]]) +; CHECK-NEXT: ret i8* [[CALL]] +; +entry: + store i32 0, i32* %arg, align 4 + %add.ptr = getelementptr inbounds i32, i32* %arg, i64 -1 + %call = call i8* @internal_argmem_only_rec_1(i32* nonnull %add.ptr) + ret i8* %call +} diff --git a/llvm/test/Transforms/Attributor/nofree.ll b/llvm/test/Transforms/Attributor/nofree.ll --- a/llvm/test/Transforms/Attributor/nofree.ll +++ b/llvm/test/Transforms/Attributor/nofree.ll @@ -1,4 +1,4 @@ -; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR +; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=3 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR ; Copied from Transforms/FunctoinAttrs/nofree-attributor.ll target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 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 @@ -297,8 +297,8 @@ ; } ; FIXME: missing willreturn -; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readonly uwtable -; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable +; ATTRIBUTOR_MODULE: Function Attrs: argmemonly nofree noinline nosync nounwind readonly uwtable +; ATTRIBUTOR_CGSCC: 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 @@ -329,8 +329,8 @@ ; return ans; ; } ; FNATTR-NEXT: define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture readonly %2, i32 %3) local_unnamed_addr -; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readonly uwtable -; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable +; ATTRIBUTOR_MODULE: Function Attrs: argmemonly nofree noinline nosync nounwind readonly uwtable +; ATTRIBUTOR_CGSCC: 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 { @@ -366,8 +366,8 @@ ; FIXME: missing willreturn -; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readonly uwtable -; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable +; ATTRIBUTOR_MODULE: Function Attrs: argmemonly nofree noinline nosync nounwind readonly uwtable +; ATTRIBUTOR_CGSCC: 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 {