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 @@ -2508,7 +2508,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, @@ -2524,6 +2525,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. @@ -2557,6 +2559,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 @@ -134,6 +134,7 @@ PIPE_OPERATOR(AAHeapToStack) PIPE_OPERATOR(AAReachability) PIPE_OPERATOR(AAMemoryBehavior) +PIPE_OPERATOR(AAMemoryLocation) PIPE_OPERATOR(AAValueConstantRange) PIPE_OPERATOR(AAPrivatizablePtr) @@ -383,7 +384,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()) @@ -400,6 +401,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! @@ -733,7 +736,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). @@ -5562,7 +5564,7 @@ State.addKnownBits(NO_READS); break; default: - llvm_unreachable("Unexpcted attribute!"); + llvm_unreachable("Unexpected attribute!"); } } @@ -5589,6 +5591,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. @@ -5693,6 +5698,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})) { @@ -5835,7 +5844,6 @@ STATS_DECLTRACK_CS_ATTR(writeonly) } }; -} // namespace ChangeStatus AAMemoryBehaviorFunction::updateImpl(Attributor &A) { @@ -6028,6 +6036,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 { @@ -7595,6 +7981,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); @@ -8010,6 +8399,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 @@ -8110,6 +8500,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/lib/Transforms/Utils/CallGraphUpdater.cpp b/llvm/lib/Transforms/Utils/CallGraphUpdater.cpp --- a/llvm/lib/Transforms/Utils/CallGraphUpdater.cpp +++ b/llvm/lib/Transforms/Utils/CallGraphUpdater.cpp @@ -47,16 +47,16 @@ if (LCG && !ReplacedFunctions.count(DeadFn)) { // Taken mostly from the inliner: - FunctionAnalysisManager &FAM = - AM->getResult(*SCC, *LCG) - .getManager(); - LazyCallGraph::Node &N = LCG->get(*DeadFn); auto *DeadSCC = LCG->lookupSCC(N); assert(DeadSCC && DeadSCC->size() == 1 && &DeadSCC->begin()->getFunction() == DeadFn); auto &DeadRC = DeadSCC->getOuterRefSCC(); + FunctionAnalysisManager &FAM = + AM->getResult(*DeadSCC, *LCG) + .getManager(); + FAM.clear(*DeadFn, DeadFn->getName()); AM->clear(*DeadSCC, DeadSCC->getName()); LCG->removeDeadFunction(*DeadFn); 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/attrs.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/attrs.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/attrs.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/attrs.ll @@ -4,7 +4,7 @@ %struct.ss = type { i32, i64 } ; Don't drop 'byval' on %X here. -define internal void @f(%struct.ss* byval %b, i32* byval %X, i32 %i) nounwind { +define internal i32 @f(%struct.ss* byval %b, i32* byval %X, i32 %i) nounwind { ; CHECK-LABEL: define {{[^@]+}}@f ; CHECK-SAME: (i32 [[TMP0:%.*]], i64 [[TMP1:%.*]], i32 [[TMP2:%.*]], i32 [[I:%.*]]) ; CHECK-NEXT: entry: @@ -20,7 +20,9 @@ ; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[TMP1]], 1 ; CHECK-NEXT: store i32 [[TMP2]], i32* [[TMP]], align 8 ; CHECK-NEXT: store i32 0, i32* [[X_PRIV]] -; CHECK-NEXT: ret void +; CHECK-NEXT: [[L:%.*]] = load i32, i32* [[X_PRIV]] +; CHECK-NEXT: [[A:%.*]] = add i32 [[L]], [[TMP2]] +; CHECK-NEXT: ret i32 [[A]] ; entry: @@ -30,7 +32,9 @@ store i32 %tmp2, i32* %tmp, align 4 store i32 %i, i32* %X - ret void + %l = load i32, i32* %X + %a = add i32 %l, %tmp2 + ret i32 %a } ; Also make sure we don't drop the call zeroext attribute. @@ -48,8 +52,8 @@ ; 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: [[TMP2:%.*]] = load i32, i32* [[X]], align 1 -; CHECK-NEXT: call void @f(i32 [[TMP0]], i64 [[TMP1]], i32 [[TMP2]], i32 zeroext 0) -; CHECK-NEXT: ret i32 0 +; CHECK-NEXT: [[C:%.*]] = call i32 @f(i32 [[TMP0]], i64 [[TMP1]], i32 [[TMP2]], i32 zeroext 0) +; CHECK-NEXT: ret i32 [[C]] ; entry: %S = alloca %struct.ss @@ -58,7 +62,7 @@ %tmp4 = getelementptr %struct.ss, %struct.ss* %S, i32 0, i32 1 store i64 2, i64* %tmp4, align 4 - call void @f( %struct.ss* byval %S, i32* byval %X, i32 zeroext 0) + %c = call i32 @f( %struct.ss* byval %S, i32* byval %X, i32 zeroext 0) - ret i32 0 + ret i32 %c } diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/byval-2.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/byval-2.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/byval-2.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/byval-2.ll @@ -4,23 +4,6 @@ %struct.ss = type { i32, i64 } define internal void @f(%struct.ss* byval %b, i32* byval %X) nounwind { -; CHECK-LABEL: define {{[^@]+}}@f -; CHECK-SAME: (i32 [[TMP0:%.*]], i64 [[TMP1:%.*]], i32 [[TMP2:%.*]]) -; CHECK-NEXT: entry: -; CHECK-NEXT: [[X_PRIV:%.*]] = alloca i32 -; CHECK-NEXT: store i32 [[TMP2]], i32* [[X_PRIV]] -; CHECK-NEXT: [[B_PRIV:%.*]] = alloca [[STRUCT_SS:%.*]] -; CHECK-NEXT: [[B_PRIV_CAST:%.*]] = bitcast %struct.ss* [[B_PRIV]] to i32* -; CHECK-NEXT: store i32 [[TMP0]], i32* [[B_PRIV_CAST]] -; CHECK-NEXT: [[B_PRIV_0_1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[B_PRIV]], i32 0, i32 1 -; CHECK-NEXT: store i64 [[TMP1]], i64* [[B_PRIV_0_1]] -; CHECK-NEXT: [[TMP:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[B_PRIV]], i32 0, i32 0 -; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[TMP]], align 8 -; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[TMP1]], 1 -; CHECK-NEXT: store i32 [[TMP2]], i32* [[TMP]], align 8 -; CHECK-NEXT: store i32 0, i32* [[X_PRIV]] -; CHECK-NEXT: ret void -; entry: %tmp = getelementptr %struct.ss, %struct.ss* %b, i32 0, i32 0 %tmp1 = load i32, i32* %tmp, align 4 @@ -40,12 +23,6 @@ ; 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: [[TMP2:%.*]] = load i32, i32* [[X]], align 1 -; CHECK-NEXT: call void @f(i32 [[TMP0]], i64 [[TMP1]], i32 [[TMP2]]) ; CHECK-NEXT: ret i32 0 ; entry: 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 @@ -5,7 +5,7 @@ %struct.ss = type { i32, i64 } -define internal void @f(%struct.ss* byval %b) nounwind { +define internal i32 @f(%struct.ss* byval %b) nounwind { ; CHECK-LABEL: define {{[^@]+}}@f ; CHECK-SAME: (i32 [[TMP0:%.*]], i64 [[TMP1:%.*]]) ; CHECK-NEXT: entry: @@ -18,18 +18,18 @@ ; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[TMP]], align 8 ; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[TMP1]], 1 ; CHECK-NEXT: store i32 [[TMP2]], i32* [[TMP]], align 8 -; CHECK-NEXT: ret void +; CHECK-NEXT: ret i32 [[TMP1]] ; entry: %tmp = getelementptr %struct.ss, %struct.ss* %b, i32 0, i32 0 %tmp1 = load i32, i32* %tmp, align 4 %tmp2 = add i32 %tmp1, 1 store i32 %tmp2, i32* %tmp, align 4 - ret void + ret i32 %tmp1 } -define internal void @g(%struct.ss* byval align 32 %b) nounwind { +define internal i32 @g(%struct.ss* byval align 32 %b) nounwind { ; CHECK-LABEL: define {{[^@]+}}@g ; CHECK-SAME: (i32 [[TMP0:%.*]], i64 [[TMP1:%.*]]) ; CHECK-NEXT: entry: @@ -42,14 +42,14 @@ ; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[TMP]], align 32 ; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[TMP1]], 1 ; CHECK-NEXT: store i32 [[TMP2]], i32* [[TMP]], align 32 -; CHECK-NEXT: ret void +; CHECK-NEXT: ret i32 [[TMP2]] ; entry: %tmp = getelementptr %struct.ss, %struct.ss* %b, i32 0, i32 0 %tmp1 = load i32, i32* %tmp, align 4 %tmp2 = add i32 %tmp1, 1 store i32 %tmp2, i32* %tmp, align 4 - ret void + ret i32 %tmp2 } @@ -61,17 +61,18 @@ ; 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_CAST1:%.*]] = bitcast %struct.ss* [[S]] to i32* -; 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: [[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: [[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: [[TMP3:%.*]] = load i64, i64* [[S_0_1]], align 1 -; CHECK-NEXT: call void @g(i32 [[TMP2]], i64 [[TMP3]]) -; CHECK-NEXT: ret i32 0 +; CHECK-NEXT: [[TMP1:%.*]] = load i64, i64* [[S_0_1]], align 1 +; CHECK-NEXT: [[C0:%.*]] = call i32 @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: [[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: [[C1:%.*]] = call i32 @g(i32 [[TMP2]], i64 [[TMP3]]) +; CHECK-NEXT: [[A:%.*]] = add i32 [[C0]], [[C1]] +; CHECK-NEXT: ret i32 [[A]] ; entry: %S = alloca %struct.ss @@ -79,9 +80,10 @@ store i32 1, i32* %tmp1, align 8 %tmp4 = getelementptr %struct.ss, %struct.ss* %S, i32 0, i32 1 store i64 2, i64* %tmp4, align 4 - call void @f(%struct.ss* byval %S) nounwind - call void @g(%struct.ss* byval %S) nounwind - ret i32 0 + %c0 = call i32 @f(%struct.ss* byval %S) nounwind + %c1 = call i32 @g(%struct.ss* byval %S) nounwind + %a = add i32 %c0, %c1 + ret i32 %a } 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/ArgumentPromotion/live_called_from_dead.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/live_called_from_dead.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/live_called_from_dead.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/live_called_from_dead.ll @@ -1,13 +1,12 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -S -basicaa -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=4 < %s | FileCheck %s --check-prefixes=CHECK,OLDPM,OLDPM_MODULE -; RUN: opt -S -basicaa -attributor-cgscc -attributor-disable=false < %s | FileCheck %s --check-prefixes=CHECK,OLDPM,OLDPM_CGSCC -; RUN: opt -S -passes='attributor' -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=4 < %s | FileCheck %s --check-prefixes=CHECK,NEWPM,NEWPM_MODULE -; RUN: opt -S -passes='attributor-cgscc' -aa-pipeline='basic-aa' -attributor-disable=false < %s | FileCheck %s --check-prefixes=CHECK,NEWPM,NEWPM_CGSCC +; RUN: opt -S -basicaa -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=4 < %s | FileCheck %s --check-prefixes=CHECK +; RUN: opt -S -basicaa -attributor-cgscc -attributor-disable=false < %s | FileCheck %s --check-prefixes=CHECK +; RUN: opt -S -passes='attributor' -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=4 < %s | FileCheck %s --check-prefixes=CHECK +; RUN: opt -S -passes='attributor-cgscc' -aa-pipeline='basic-aa' -attributor-disable=false < %s | FileCheck %s --check-prefixes=CHECK -; OLDPM_MODULE-NOT: @dead -; NEWPM_MODULE-NOT: @dead -; OLDPM_CGSCC-NOT: @dead -; NEWPM_CGSCC-NOT: @dead +; CHECK-NOT: @dead( +; CHECK-NOT: @test( +; CHECK-NOT: @caller( define internal void @dead() { call i32 @test(i32* null, i32* null) @@ -15,33 +14,6 @@ } define internal i32 @test(i32* %X, i32* %Y) { -; OLDPM-LABEL: define {{[^@]+}}@test -; OLDPM-SAME: (i32* noalias nocapture nofree writeonly align 4 [[X:%.*]]) -; OLDPM-NEXT: br i1 true, label [[LIVE:%.*]], label [[DEAD:%.*]] -; OLDPM: live: -; OLDPM-NEXT: store i32 0, i32* [[X]], align 4 -; OLDPM-NEXT: ret i32 undef -; OLDPM: dead: -; OLDPM-NEXT: unreachable -; -; NEWPM_MODULE-LABEL: define {{[^@]+}}@test -; NEWPM_MODULE-SAME: (i32* noalias nocapture nofree writeonly align 4 [[X:%.*]]) -; NEWPM_MODULE-NEXT: br i1 true, label [[LIVE:%.*]], label [[DEAD:%.*]] -; NEWPM_MODULE: live: -; NEWPM_MODULE-NEXT: store i32 0, i32* [[X]], align 4 -; NEWPM_MODULE-NEXT: ret i32 undef -; NEWPM_MODULE: dead: -; NEWPM_MODULE-NEXT: unreachable -; -; NEWPM_CGSCC-LABEL: define {{[^@]+}}@test -; NEWPM_CGSCC-SAME: (i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[X:%.*]]) -; NEWPM_CGSCC-NEXT: br i1 true, label [[LIVE:%.*]], label [[DEAD:%.*]] -; NEWPM_CGSCC: live: -; NEWPM_CGSCC-NEXT: store i32 0, i32* [[X]], align 4 -; NEWPM_CGSCC-NEXT: ret i32 undef -; NEWPM_CGSCC: dead: -; NEWPM_CGSCC-NEXT: unreachable -; br i1 true, label %live, label %dead live: store i32 0, i32* %X @@ -52,27 +24,7 @@ ret i32 1 } -; FIXME: This function should not be writeonly because only stack memory is written. Once we realize that @caller can be deleted. - define internal i32 @caller(i32* %B) { -; OLDPM_MODULE-LABEL: define {{[^@]+}}@caller() -; OLDPM_MODULE-NEXT: [[A:%.*]] = alloca i32 -; OLDPM_MODULE-NEXT: store i32 1, i32* [[A]], align 4 -; OLDPM_MODULE-NEXT: [[C:%.*]] = call i32 @test(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) -; OLDPM_MODULE-NEXT: ret i32 undef -; -; OLDPM_CGSCC-LABEL: define {{[^@]+}}@caller() -; OLDPM_CGSCC-NEXT: [[A:%.*]] = alloca i32 -; OLDPM_CGSCC-NEXT: store i32 1, i32* [[A]], align 4 -; OLDPM_CGSCC-NEXT: [[C:%.*]] = call i32 @test(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) -; OLDPM_CGSCC-NEXT: ret i32 0 -; -; NEWPM-LABEL: define {{[^@]+}}@caller() -; NEWPM-NEXT: [[A:%.*]] = alloca i32 -; NEWPM-NEXT: store i32 1, i32* [[A]], align 4 -; NEWPM-NEXT: [[C:%.*]] = call i32 @test(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) -; NEWPM-NEXT: ret i32 undef -; %A = alloca i32 store i32 1, i32* %A %C = call i32 @test(i32* %A, i32* %B) @@ -83,7 +35,6 @@ ; CHECK-LABEL: define {{[^@]+}}@callercaller() ; CHECK-NEXT: [[B:%.*]] = alloca i32 ; CHECK-NEXT: store i32 2, i32* [[B]], align 4 -; CHECK-NEXT: [[X:%.*]] = call i32 @caller() ; CHECK-NEXT: ret i32 0 ; %B = alloca i32 diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/2009-09-24-byval-ptr.ll b/llvm/test/Transforms/Attributor/IPConstantProp/2009-09-24-byval-ptr.ll --- a/llvm/test/Transforms/Attributor/IPConstantProp/2009-09-24-byval-ptr.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/2009-09-24-byval-ptr.ll @@ -5,22 +5,6 @@ %struct.MYstr = type { i8, i32 } @mystr = internal global %struct.MYstr zeroinitializer ; <%struct.MYstr*> [#uses=3] define internal void @vfu1(%struct.MYstr* byval align 4 %u) nounwind { -; CHECK-LABEL: define {{[^@]+}}@vfu1 -; CHECK-SAME: (i8 [[TMP0:%.*]], i32 [[TMP1:%.*]]) -; CHECK-NEXT: entry: -; CHECK-NEXT: [[U_PRIV:%.*]] = alloca [[STRUCT_MYSTR:%.*]] -; CHECK-NEXT: [[U_PRIV_CAST:%.*]] = bitcast %struct.MYstr* [[U_PRIV]] to i8* -; CHECK-NEXT: store i8 [[TMP0]], i8* [[U_PRIV_CAST]] -; CHECK-NEXT: [[U_PRIV_0_1:%.*]] = getelementptr [[STRUCT_MYSTR]], %struct.MYstr* [[U_PRIV]], i32 0, i32 1 -; CHECK-NEXT: store i32 [[TMP1]], i32* [[U_PRIV_0_1]] -; CHECK-NEXT: [[TMP2:%.*]] = getelementptr [[STRUCT_MYSTR]], %struct.MYstr* [[U_PRIV]], i32 0, i32 1 -; CHECK-NEXT: store i32 99, i32* [[TMP2]], align 4 -; CHECK-NEXT: [[TMP3:%.*]] = getelementptr [[STRUCT_MYSTR]], %struct.MYstr* [[U_PRIV]], i32 0, i32 0 -; CHECK-NEXT: store i8 97, i8* [[TMP3]], align 8 -; CHECK-NEXT: br label [[RETURN:%.*]] -; CHECK: return: -; CHECK-NEXT: ret void -; entry: %0 = getelementptr %struct.MYstr, %struct.MYstr* %u, i32 0, i32 1 ; [#uses=1] store i32 99, i32* %0, align 4 @@ -62,16 +46,11 @@ define i32 @unions() nounwind { ; CHECK-LABEL: define {{[^@]+}}@unions() ; CHECK-NEXT: entry: -; CHECK-NEXT: [[MYSTR_CAST1:%.*]] = bitcast %struct.MYstr* @mystr to i8* -; CHECK-NEXT: [[TMP0:%.*]] = load i8, i8* [[MYSTR_CAST1]], align 1 -; CHECK-NEXT: [[MYSTR_0_12:%.*]] = getelementptr [[STRUCT_MYSTR:%.*]], %struct.MYstr* @mystr, i32 0, i32 1 -; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[MYSTR_0_12]], align 1 -; CHECK-NEXT: call void @vfu1(i8 [[TMP0]], i32 [[TMP1]]) ; CHECK-NEXT: [[MYSTR_CAST:%.*]] = bitcast %struct.MYstr* @mystr to i8* -; CHECK-NEXT: [[TMP2:%.*]] = load i8, i8* [[MYSTR_CAST]], align 1 -; CHECK-NEXT: [[MYSTR_0_1:%.*]] = getelementptr [[STRUCT_MYSTR]], %struct.MYstr* @mystr, i32 0, i32 1 -; CHECK-NEXT: [[TMP3:%.*]] = load i32, i32* [[MYSTR_0_1]], align 1 -; CHECK-NEXT: [[RESULT:%.*]] = call i32 @vfu2(i8 [[TMP2]], i32 [[TMP3]]) +; CHECK-NEXT: [[TMP0:%.*]] = load i8, i8* [[MYSTR_CAST]], align 1 +; CHECK-NEXT: [[MYSTR_0_1:%.*]] = getelementptr [[STRUCT_MYSTR:%.*]], %struct.MYstr* @mystr, i32 0, i32 1 +; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[MYSTR_0_1]], align 1 +; CHECK-NEXT: [[RESULT:%.*]] = call i32 @vfu2(i8 [[TMP0]], i32 [[TMP1]]) ; CHECK-NEXT: ret i32 [[RESULT]] ; entry: @@ -114,16 +93,11 @@ define i32 @unions_v2() nounwind { ; CHECK-LABEL: define {{[^@]+}}@unions_v2() ; CHECK-NEXT: entry: -; CHECK-NEXT: [[MYSTR_CAST1:%.*]] = bitcast %struct.MYstr* @mystr to i8* -; CHECK-NEXT: [[TMP0:%.*]] = load i8, i8* [[MYSTR_CAST1]], align 1 -; CHECK-NEXT: [[MYSTR_0_12:%.*]] = getelementptr [[STRUCT_MYSTR:%.*]], %struct.MYstr* @mystr, i32 0, i32 1 -; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[MYSTR_0_12]], align 1 -; CHECK-NEXT: call void @vfu1(i8 [[TMP0]], i32 [[TMP1]]) ; CHECK-NEXT: [[MYSTR_CAST:%.*]] = bitcast %struct.MYstr* @mystr to i8* -; CHECK-NEXT: [[TMP2:%.*]] = load i8, i8* [[MYSTR_CAST]], align 1 -; CHECK-NEXT: [[MYSTR_0_1:%.*]] = getelementptr [[STRUCT_MYSTR]], %struct.MYstr* @mystr, i32 0, i32 1 -; CHECK-NEXT: [[TMP3:%.*]] = load i32, i32* [[MYSTR_0_1]], align 1 -; CHECK-NEXT: [[RESULT:%.*]] = call i32 @vfu2_v2(i8 [[TMP2]], i32 [[TMP3]]) +; CHECK-NEXT: [[TMP0:%.*]] = load i8, i8* [[MYSTR_CAST]], align 1 +; CHECK-NEXT: [[MYSTR_0_1:%.*]] = getelementptr [[STRUCT_MYSTR:%.*]], %struct.MYstr* @mystr, i32 0, i32 1 +; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[MYSTR_0_1]], align 1 +; CHECK-NEXT: [[RESULT:%.*]] = call i32 @vfu2_v2(i8 [[TMP0]], i32 [[TMP1]]) ; CHECK-NEXT: ret i32 [[RESULT]] ; entry: 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 @@ -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 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/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/range.ll b/llvm/test/Transforms/Attributor/range.ll --- a/llvm/test/Transforms/Attributor/range.ll +++ b/llvm/test/Transforms/Attributor/range.ll @@ -20,12 +20,12 @@ ; ; OLD_PM-LABEL: define {{[^@]+}}@test0-range-check ; OLD_PM-SAME: (i32* nocapture nofree readonly [[P:%.*]]) -; OLD_PM-NEXT: [[A:%.*]] = tail call i32 @test0(i32* nocapture nofree readonly [[P]]) #0, !range !0 +; OLD_PM-NEXT: [[A:%.*]] = tail call i32 @test0(i32* nocapture nofree readonly [[P]]) #{{[0-9]+}}, !range !0 ; OLD_PM-NEXT: ret i32 [[A]] ; ; NEW_PM-LABEL: define {{[^@]+}}@test0-range-check ; NEW_PM-SAME: (i32* nocapture nofree readonly [[P:%.*]]) -; NEW_PM-NEXT: [[A:%.*]] = tail call i32 @test0(i32* nocapture nofree readonly [[P]]) #0, !range !0 +; NEW_PM-NEXT: [[A:%.*]] = tail call i32 @test0(i32* nocapture nofree readonly [[P]]) #{{[0-9]+}}, !range !0 ; NEW_PM-NEXT: ret i32 [[A]] ; ; CGSCC_OLD_PM-LABEL: define {{[^@]+}}@test0-range-check @@ -40,7 +40,7 @@ ; ; MODULE-LABEL: define {{[^@]+}}@test0-range-check ; MODULE-SAME: (i32* nocapture nofree readonly [[P:%.*]]) -; MODULE-NEXT: [[A:%.*]] = tail call i32 @test0(i32* nocapture nofree readonly [[P]]) #0, !range !0 +; MODULE-NEXT: [[A:%.*]] = tail call i32 @test0(i32* nocapture nofree readonly [[P]]) #{{[0-9]+}}, !range !0 ; MODULE-NEXT: ret i32 [[A]] ; CGSCC-LABEL: define {{[^@]+}}@test0-range-check ; CGSCC-SAME: (i32* nocapture nofree nonnull readonly dereferenceable(4) [[P:%.*]]) @@ -65,7 +65,7 @@ define void @test0-icmp-check(i32* %p){ ; OLD_PM-LABEL: define {{[^@]+}}@test0-icmp-check ; OLD_PM-SAME: (i32* nocapture nofree readonly [[P:%.*]]) -; OLD_PM-NEXT: [[RET:%.*]] = tail call i32 @test0(i32* nocapture nofree readonly [[P]]) #0, !range !0 +; OLD_PM-NEXT: [[RET:%.*]] = tail call i32 @test0(i32* nocapture nofree readonly [[P]]) #{{[0-9]+}}, !range !0 ; OLD_PM-NEXT: [[CMP_EQ_2:%.*]] = icmp eq i32 [[RET]], 9 ; OLD_PM-NEXT: [[CMP_EQ_3:%.*]] = icmp eq i32 [[RET]], 8 ; OLD_PM-NEXT: [[CMP_EQ_4:%.*]] = icmp eq i32 [[RET]], 1 @@ -112,7 +112,7 @@ ; ; NEW_PM-LABEL: define {{[^@]+}}@test0-icmp-check ; NEW_PM-SAME: (i32* nocapture nofree readonly [[P:%.*]]) -; NEW_PM-NEXT: [[RET:%.*]] = tail call i32 @test0(i32* nocapture nofree readonly [[P]]) #0, !range !0 +; NEW_PM-NEXT: [[RET:%.*]] = tail call i32 @test0(i32* nocapture nofree readonly [[P]]) #{{[0-9]+}}, !range !0 ; NEW_PM-NEXT: [[CMP_EQ_2:%.*]] = icmp eq i32 [[RET]], 9 ; NEW_PM-NEXT: [[CMP_EQ_3:%.*]] = icmp eq i32 [[RET]], 8 ; NEW_PM-NEXT: [[CMP_EQ_4:%.*]] = icmp eq i32 [[RET]], 1 @@ -293,7 +293,7 @@ ; ; MODULE-LABEL: define {{[^@]+}}@test0-icmp-check ; MODULE-SAME: (i32* nocapture nofree readonly [[P:%.*]]) -; MODULE-NEXT: [[RET:%.*]] = tail call i32 @test0(i32* nocapture nofree readonly [[P]]) #0, !range !0 +; MODULE-NEXT: [[RET:%.*]] = tail call i32 @test0(i32* nocapture nofree readonly [[P]]) #{{[0-9]+}}, !range !0 ; MODULE-NEXT: [[CMP_EQ_2:%.*]] = icmp eq i32 [[RET]], 9 ; MODULE-NEXT: [[CMP_EQ_3:%.*]] = icmp eq i32 [[RET]], 8 ; MODULE-NEXT: [[CMP_EQ_4:%.*]] = icmp eq i32 [[RET]], 1 @@ -505,13 +505,13 @@ define i1 @test1-check(i32* %p) { ; OLD_PM-LABEL: define {{[^@]+}}@test1-check ; OLD_PM-SAME: (i32* nocapture nofree readonly [[P:%.*]]) -; OLD_PM-NEXT: [[RES:%.*]] = tail call i32 @test1(i32* nocapture nofree readonly [[P]]) #0, !range !2 +; OLD_PM-NEXT: [[RES:%.*]] = tail call i32 @test1(i32* nocapture nofree readonly [[P]]) #{{[0-9]+}}, !range !2 ; OLD_PM-NEXT: [[CMP:%.*]] = icmp eq i32 [[RES]], 500 ; OLD_PM-NEXT: ret i1 [[CMP]] ; ; NEW_PM-LABEL: define {{[^@]+}}@test1-check ; NEW_PM-SAME: (i32* nocapture nofree readonly [[P:%.*]]) -; NEW_PM-NEXT: [[RES:%.*]] = tail call i32 @test1(i32* nocapture nofree readonly [[P]]) #0, !range !2 +; NEW_PM-NEXT: [[RES:%.*]] = tail call i32 @test1(i32* nocapture nofree readonly [[P]]) #{{[0-9]+}}, !range !2 ; NEW_PM-NEXT: [[CMP:%.*]] = icmp eq i32 [[RES]], 500 ; NEW_PM-NEXT: ret i1 [[CMP]] ; @@ -529,7 +529,7 @@ ; ; MODULE-LABEL: define {{[^@]+}}@test1-check ; MODULE-SAME: (i32* nocapture nofree readonly [[P:%.*]]) -; MODULE-NEXT: [[RES:%.*]] = tail call i32 @test1(i32* nocapture nofree readonly [[P]]) #0, !range !2 +; MODULE-NEXT: [[RES:%.*]] = tail call i32 @test1(i32* nocapture nofree readonly [[P]]) #{{[0-9]+}}, !range !2 ; MODULE-NEXT: [[CMP:%.*]] = icmp eq i32 [[RES]], 500 ; MODULE-NEXT: ret i1 [[CMP]] ; CGSCC-LABEL: define {{[^@]+}}@test1-check 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 nounwind } ; CHECK-NOT: attributes # diff --git a/llvm/test/Transforms/Attributor/value-simplify.ll b/llvm/test/Transforms/Attributor/value-simplify.ll --- a/llvm/test/Transforms/Attributor/value-simplify.ll +++ b/llvm/test/Transforms/Attributor/value-simplify.ll @@ -243,25 +243,38 @@ @S = external global %struct.X define internal void @test_byval(%struct.X* byval %a) { -; CHECK-LABEL: define {{[^@]+}}@test_byval -; CHECK-SAME: (%struct.X* noalias nocapture nofree nonnull writeonly byval align 8 dereferenceable(8) [[A:%.*]]) -; CHECK-NEXT: [[G0:%.*]] = getelementptr [[STRUCT_X:%.*]], %struct.X* [[A]], i32 0, i32 0 -; CHECK-NEXT: store i8* null, i8** [[G0]], align 8 -; CHECK-NEXT: ret void -; %g0 = getelementptr %struct.X, %struct.X* %a, i32 0, i32 0 store i8* null, i8** %g0 ret void } define void @complicated_args_byval() { ; CHECK-LABEL: define {{[^@]+}}@complicated_args_byval() -; CHECK-NEXT: call void @test_byval(%struct.X* nofree nonnull readonly align 8 dereferenceable(8) @S) ; CHECK-NEXT: ret void ; call void @test_byval(%struct.X* @S) ret void } +define internal i8*@test_byval2(%struct.X* byval %a) { +; CHECK-LABEL: define {{[^@]+}}@test_byval2 +; CHECK-SAME: (%struct.X* noalias nocapture nofree nonnull readonly byval align 8 dereferenceable(8) [[A:%.*]]) +; CHECK-NEXT: [[G0:%.*]] = getelementptr [[STRUCT_X:%.*]], %struct.X* @S, i32 0, i32 0 +; CHECK-NEXT: [[L:%.*]] = load i8*, i8** [[G0]], align 8 +; CHECK-NEXT: ret i8* [[L]] +; + %g0 = getelementptr %struct.X, %struct.X* %a, i32 0, i32 0 + %l = load i8*, i8** %g0 + ret i8* %l +} +define i8* @complicated_args_byval2() { +; CHECK-LABEL: define {{[^@]+}}@complicated_args_byval2() +; CHECK-NEXT: [[C:%.*]] = call i8* @test_byval2(%struct.X* nofree nonnull readonly align 8 dereferenceable(8) @S) +; CHECK-NEXT: ret i8* [[C]] +; + %c = call i8* @test_byval2(%struct.X* @S) + ret i8* %c +} + define void @fixpoint_changed(i32* %p) { ; CHECK-LABEL: define {{[^@]+}}@fixpoint_changed ; CHECK-SAME: (i32* nocapture nofree writeonly [[P:%.*]]) 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 @@ -306,8 +306,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 @@ -338,8 +338,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 { @@ -375,8 +375,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 {