Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -1949,7 +1949,8 @@ const AbstractAttribute *AA, SmallVectorImpl &Values, AA::ValueScope S, - bool &UsedAssumedInformation); + bool &UsedAssumedInformation, + bool RecurseForSelectAndPHI = true); /// Register \p CB as a simplification callback. /// `Attributor::getAssumedSimplified` will use these callbacks before @@ -2264,22 +2265,15 @@ bool &UsedAssumedInformation, bool CheckPotentiallyDead = false); - /// Check \p Pred on all values potentially returned by \p F. - /// - /// This method will evaluate \p Pred on all values potentially returned by - /// the function associated with \p QueryingAA. The returned values are - /// matched with their respective return instructions. Returns true if \p Pred - /// holds on all of them. - bool checkForAllReturnedValuesAndReturnInsts( - function_ref &)> Pred, - const AbstractAttribute &QueryingAA); - /// Check \p Pred on all values potentially returned by the function /// associated with \p QueryingAA. /// /// This is the context insensitive version of the method above. - bool checkForAllReturnedValues(function_ref Pred, - const AbstractAttribute &QueryingAA); + bool + checkForAllReturnedValues(function_ref Pred, + const AbstractAttribute &QueryingAA, + AA::ValueScope S = AA::ValueScope::Intraprocedural, + bool RecurseForSelectAndPHI = true); /// Check \p Pred on all instructions in \p Fn with an opcode present in /// \p Opcodes. @@ -3385,53 +3379,6 @@ /// Abstract Attribute Classes /// ---------------------------------------------------------------------------- -/// An abstract attribute for the returned values of a function. -struct AAReturnedValues - : public IRAttribute { - AAReturnedValues(const IRPosition &IRP, Attributor &A) : IRAttribute(IRP) {} - - /// Check \p Pred on all returned values. - /// - /// This method will evaluate \p Pred on returned values and return - /// true if (1) all returned values are known, and (2) \p Pred returned true - /// for all returned values. - /// - /// Note: Unlike the Attributor::checkForAllReturnedValuesAndReturnInsts - /// method, this one will not filter dead return instructions. - virtual bool checkForAllReturnedValuesAndReturnInsts( - function_ref &)> Pred) - const = 0; - - using iterator = - MapVector>::iterator; - using const_iterator = - MapVector>::const_iterator; - virtual llvm::iterator_range returned_values() = 0; - virtual llvm::iterator_range returned_values() const = 0; - - virtual size_t getNumReturnValues() const = 0; - - /// Create an abstract attribute view for the position \p IRP. - static AAReturnedValues &createForPosition(const IRPosition &IRP, - Attributor &A); - - /// See AbstractAttribute::getName() - const std::string getName() const override { return "AAReturnedValues"; } - - /// See AbstractAttribute::getIdAddr() - const char *getIdAddr() const override { return &ID; } - - /// This function should return true if the type of the \p AA is - /// AAReturnedValues - static bool classof(const AbstractAttribute *AA) { - return (AA->getIdAddr() == &ID); - } - - /// Unique ID (due to the unique address) - static const char ID; -}; - struct AANoUnwind : public IRAttribute, @@ -5140,10 +5087,9 @@ static const char ID; private: - virtual bool - getAssumedSimplifiedValues(Attributor &A, - SmallVectorImpl &Values, - AA::ValueScope) const = 0; + virtual bool getAssumedSimplifiedValues( + Attributor &A, SmallVectorImpl &Values, + AA::ValueScope, bool RecurseForSelectAndPHI = false) const = 0; friend struct Attributor; }; Index: llvm/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/lib/Transforms/IPO/Attributor.cpp +++ llvm/lib/Transforms/IPO/Attributor.cpp @@ -1378,36 +1378,67 @@ } bool Attributor::getAssumedSimplifiedValues( - const IRPosition &IRP, const AbstractAttribute *AA, + const IRPosition &InitialIRP, const AbstractAttribute *AA, SmallVectorImpl &Values, AA::ValueScope S, - bool &UsedAssumedInformation) { - // First check all callbacks provided by outside AAs. If any of them returns - // a non-null value that is different from the associated value, or - // std::nullopt, we assume it's simplified. - const auto &SimplificationCBs = SimplificationCallbacks.lookup(IRP); - for (const auto &CB : SimplificationCBs) { - std::optional CBResult = CB(IRP, AA, UsedAssumedInformation); - if (!CBResult.has_value()) - continue; - Value *V = *CBResult; - if (!V) - return false; - if ((S & AA::ValueScope::Interprocedural) || - AA::isValidInScope(*V, IRP.getAnchorScope())) - Values.push_back(AA::ValueAndContext{*V, nullptr}); - else - return false; - } - if (!SimplificationCBs.empty()) - return true; + bool &UsedAssumedInformation, bool RecurseForSelectAndPHI) { + SmallPtrSet Seen; + SmallVector Worklist; + Worklist.push_back(InitialIRP); + while (!Worklist.empty()) { + const IRPosition &IRP = Worklist.pop_back_val(); + + // First check all callbacks provided by outside AAs. If any of them returns + // a non-null value that is different from the associated value, or + // std::nullopt, we assume it's simplified. + int NV = Values.size(); + const auto &SimplificationCBs = SimplificationCallbacks.lookup(IRP); + for (const auto &CB : SimplificationCBs) { + std::optional CBResult = CB(IRP, AA, UsedAssumedInformation); + if (!CBResult.has_value()) + continue; + Value *V = *CBResult; + if (!V) + return false; + if ((S & AA::ValueScope::Interprocedural) || + AA::isValidInScope(*V, IRP.getAnchorScope())) + Values.push_back(AA::ValueAndContext{*V, nullptr}); + else + return false; + } + if (SimplificationCBs.empty()) { + // If no high-level/outside simplification occurred, use + // AAPotentialValues. + const auto *PotentialValuesAA = + getOrCreateAAFor(IRP, AA, DepClassTy::OPTIONAL); + if (PotentialValuesAA && PotentialValuesAA->getAssumedSimplifiedValues(*this, Values, S)) { + UsedAssumedInformation |= !PotentialValuesAA->isAtFixpoint(); + } else if (IRP.getPositionKind() != IRPosition::IRP_RETURNED) { + Values.push_back({IRP.getAssociatedValue(), IRP.getCtxI()}); + } else { + // TODO: We could visit all returns and add the operands. + return false; + } + } - // If no high-level/outside simplification occurred, use AAPotentialValues. - const auto *PotentialValuesAA = - getOrCreateAAFor(IRP, AA, DepClassTy::OPTIONAL); - if (!PotentialValuesAA || - !PotentialValuesAA->getAssumedSimplifiedValues(*this, Values, S)) - return false; - UsedAssumedInformation |= !PotentialValuesAA->isAtFixpoint(); + if (!RecurseForSelectAndPHI) + break; + + for (int I = NV, E = Values.size(); I < E; ++I) { + Value *V = Values[I].getValue(); + if (!isa(V) && !isa(V)) + continue; + if (!Seen.insert(V).second) + continue; + // Move the last element to this slot. + Values[I] = Values[E - 1]; + // Eliminate the last slot, adjust the indices. + Values.pop_back(); + --E; + --I; + // Add a new value (select or phi) to the worklist. + Worklist.push_back(IRPosition::value(*V)); + } + } return true; } @@ -1882,49 +1913,26 @@ return EnableCallSiteSpecific; } -bool Attributor::checkForAllReturnedValuesAndReturnInsts( - function_ref &)> Pred, - const AbstractAttribute &QueryingAA) { +bool Attributor::checkForAllReturnedValues(function_ref Pred, + const AbstractAttribute &QueryingAA, + AA::ValueScope S, + bool RecurseForSelectAndPHI) { const IRPosition &IRP = QueryingAA.getIRPosition(); - // Since we need to provide return instructions we have to have an exact - // definition. const Function *AssociatedFunction = IRP.getAssociatedFunction(); if (!AssociatedFunction) return false; - // If this is a call site query we use the call site specific return values - // and liveness information. - // TODO: use the function scope once we have call site AAReturnedValues. - const IRPosition &QueryIRP = IRPosition::function(*AssociatedFunction); - const auto *AARetVal = - getAAFor(QueryingAA, QueryIRP, DepClassTy::REQUIRED); - if (!AARetVal || !AARetVal->getState().isValidState()) - return false; - - return AARetVal->checkForAllReturnedValuesAndReturnInsts(Pred); -} - -bool Attributor::checkForAllReturnedValues( - function_ref Pred, const AbstractAttribute &QueryingAA) { - - const IRPosition &IRP = QueryingAA.getIRPosition(); - const Function *AssociatedFunction = IRP.getAssociatedFunction(); - if (!AssociatedFunction) - return false; - - // TODO: use the function scope once we have call site AAReturnedValues. - const IRPosition &QueryIRP = IRPosition::function( - *AssociatedFunction, QueryingAA.getCallBaseContext()); - const auto *AARetVal = - getAAFor(QueryingAA, QueryIRP, DepClassTy::REQUIRED); - if (!AARetVal || !AARetVal->getState().isValidState()) + bool UsedAssumedInformation; + SmallVector Values; + if (!getAssumedSimplifiedValues( + IRPosition::returned(*AssociatedFunction), &QueryingAA, Values, S, + UsedAssumedInformation, RecurseForSelectAndPHI)) return false; - return AARetVal->checkForAllReturnedValuesAndReturnInsts( - [&](Value &RV, const SmallSetVector &) { - return Pred(RV); - }); + return llvm::all_of(Values, [&](const AA::ValueAndContext &VAC) { + return Pred(*VAC.getValue()); + }); } static bool checkForAllInstructionsImpl( @@ -1968,7 +1976,6 @@ if (!Fn || Fn->isDeclaration()) return false; - // TODO: use the function scope once we have call site AAReturnedValues. const IRPosition &QueryIRP = IRPosition::function(*Fn); const auto *LivenessAA = CheckPotentiallyDead @@ -2007,7 +2014,6 @@ if (!AssociatedFunction) return false; - // TODO: use the function scope once we have call site AAReturnedValues. const IRPosition &QueryIRP = IRPosition::function(*AssociatedFunction); const auto *LivenessAA = getAAFor(QueryingAA, QueryIRP, DepClassTy::NONE); @@ -3312,10 +3318,6 @@ // Return attributes are only appropriate if the return type is non void. Type *ReturnType = F.getReturnType(); if (!ReturnType->isVoidTy()) { - // Argument attribute "returned" --- Create only one per function even - // though it is an argument attribute. - getOrCreateAAFor(FPos); - IRPosition RetPos = IRPosition::returned(F); AttributeSet RetAttrs = Attrs.getRetAttrs(); Index: llvm/lib/Transforms/IPO/AttributorAttributes.cpp =================================================================== --- llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -162,7 +162,6 @@ PIPE_OPERATOR(AANonConvergent) PIPE_OPERATOR(AAWillReturn) PIPE_OPERATOR(AANoReturn) -PIPE_OPERATOR(AAReturnedValues) PIPE_OPERATOR(AANonNull) PIPE_OPERATOR(AAMustProgress) PIPE_OPERATOR(AANoAlias) @@ -377,7 +376,8 @@ /// Clamp the information known for all returned values of a function /// (identified by \p QueryingAA) into \p S. template + Attribute::AttrKind IRAttributeKind = Attribute::None, + bool RecurseForSelectAndPHI = true> static void clampReturnedValueStates( Attributor &A, const AAType &QueryingAA, StateType &S, const IRPosition::CallBaseContext *CBContext = nullptr) { @@ -420,7 +420,9 @@ return T->isValidState(); }; - if (!A.checkForAllReturnedValues(CheckReturnValue, QueryingAA)) + if (!A.checkForAllReturnedValues(CheckReturnValue, QueryingAA, + AA::ValueScope::Intraprocedural, + RecurseForSelectAndPHI)) S.indicatePessimisticFixpoint(); else if (T) S ^= *T; @@ -431,7 +433,8 @@ template + Attribute::AttrKind IRAttributeKind = Attribute::None, + bool RecurseForSelectAndPHI = true> struct AAReturnedFromReturnedValues : public BaseType { AAReturnedFromReturnedValues(const IRPosition &IRP, Attributor &A) : BaseType(IRP, A) {} @@ -439,7 +442,7 @@ /// See AbstractAttribute::updateImpl(...). ChangeStatus updateImpl(Attributor &A) override { StateType S(StateType::getBestState(this->getState())); - clampReturnedValueStates( + clampReturnedValueStates( A, *this, S, PropagateCallBaseContext ? this->getCallBaseContext() : nullptr); // TODO: If we know we visited all returned values, thus no are assumed @@ -2103,252 +2106,6 @@ }; } // namespace -/// --------------------- Function Return Values ------------------------------- - -namespace { -/// "Attribute" that collects all potential returned values and the return -/// instructions that they arise from. -/// -/// If there is a unique returned value R, the manifest method will: -/// - mark R with the "returned" attribute, if R is an argument. -class AAReturnedValuesImpl : public AAReturnedValues, public AbstractState { - - /// Mapping of values potentially returned by the associated function to the - /// return instructions that might return them. - MapVector> ReturnedValues; - - /// State flags - /// - ///{ - bool IsFixed = false; - bool IsValidState = true; - ///} - -public: - AAReturnedValuesImpl(const IRPosition &IRP, Attributor &A) - : AAReturnedValues(IRP, A) {} - - /// See AbstractAttribute::initialize(...). - void initialize(Attributor &A) override { - // Reset the state. - IsFixed = false; - IsValidState = true; - ReturnedValues.clear(); - - Function *F = getAssociatedFunction(); - if (!F || F->isDeclaration() || F->getReturnType()->isVoidTy()) { - indicatePessimisticFixpoint(); - return; - } - - // The map from instruction opcodes to those instructions in the function. - auto &OpcodeInstMap = A.getInfoCache().getOpcodeInstMapForFunction(*F); - - // Look through all arguments, if one is marked as returned we are done. - for (Argument &Arg : F->args()) { - if (Arg.hasReturnedAttr()) { - auto &ReturnInstSet = ReturnedValues[&Arg]; - if (auto *Insts = OpcodeInstMap.lookup(Instruction::Ret)) - for (Instruction *RI : *Insts) - ReturnInstSet.insert(cast(RI)); - - indicateOptimisticFixpoint(); - return; - } - } - } - - /// See AbstractAttribute::manifest(...). - ChangeStatus manifest(Attributor &A) override; - - /// See AbstractAttribute::getState(...). - AbstractState &getState() override { return *this; } - - /// See AbstractAttribute::getState(...). - const AbstractState &getState() const override { return *this; } - - /// See AbstractAttribute::updateImpl(Attributor &A). - ChangeStatus updateImpl(Attributor &A) override; - - llvm::iterator_range returned_values() override { - return llvm::make_range(ReturnedValues.begin(), ReturnedValues.end()); - } - - llvm::iterator_range returned_values() const override { - return llvm::make_range(ReturnedValues.begin(), ReturnedValues.end()); - } - - /// Return the number of potential return values, -1 if unknown. - size_t getNumReturnValues() const override { - return isValidState() ? ReturnedValues.size() : -1; - } - - /// Return an assumed unique return value if a single candidate is found. If - /// there cannot be one, return a nullptr. If it is not clear yet, return - /// std::nullopt. - std::optional getAssumedUniqueReturnValue(Attributor &A) const; - - /// See AbstractState::checkForAllReturnedValues(...). - bool checkForAllReturnedValuesAndReturnInsts( - function_ref &)> Pred) - const override; - - /// Pretty print the attribute similar to the IR representation. - const std::string getAsStr(Attributor *A) const override; - - /// See AbstractState::isAtFixpoint(). - bool isAtFixpoint() const override { return IsFixed; } - - /// See AbstractState::isValidState(). - bool isValidState() const override { return IsValidState; } - - /// See AbstractState::indicateOptimisticFixpoint(...). - ChangeStatus indicateOptimisticFixpoint() override { - IsFixed = true; - return ChangeStatus::UNCHANGED; - } - - ChangeStatus indicatePessimisticFixpoint() override { - IsFixed = true; - IsValidState = false; - return ChangeStatus::CHANGED; - } -}; - -ChangeStatus AAReturnedValuesImpl::manifest(Attributor &A) { - ChangeStatus Changed = ChangeStatus::UNCHANGED; - - // Bookkeeping. - assert(isValidState()); - STATS_DECLTRACK(KnownReturnValues, FunctionReturn, - "Number of function with known return values"); - - // Check if we have an assumed unique return value that we could manifest. - std::optional UniqueRV = getAssumedUniqueReturnValue(A); - - if (!UniqueRV || !*UniqueRV) - return Changed; - - // Bookkeeping. - STATS_DECLTRACK(UniqueReturnValue, FunctionReturn, - "Number of function with unique return"); - // If the assumed unique return value is an argument, annotate it. - if (auto *UniqueRVArg = dyn_cast(*UniqueRV)) { - if (UniqueRVArg->getType()->canLosslesslyBitCastTo( - getAssociatedFunction()->getReturnType())) { - getIRPosition() = IRPosition::argument(*UniqueRVArg); - Changed = IRAttribute::manifest(A); - } - } - return Changed; -} - -const std::string AAReturnedValuesImpl::getAsStr(Attributor *A) const { - return (isAtFixpoint() ? "returns(#" : "may-return(#") + - (isValidState() ? std::to_string(getNumReturnValues()) : "?") + ")"; -} - -std::optional -AAReturnedValuesImpl::getAssumedUniqueReturnValue(Attributor &A) const { - // If checkForAllReturnedValues provides a unique value, ignoring potential - // undef values that can also be present, it is assumed to be the actual - // return value and forwarded to the caller of this method. If there are - // multiple, a nullptr is returned indicating there cannot be a unique - // returned value. - std::optional UniqueRV; - Type *Ty = getAssociatedFunction()->getReturnType(); - - auto Pred = [&](Value &RV) -> bool { - UniqueRV = AA::combineOptionalValuesInAAValueLatice(UniqueRV, &RV, Ty); - return UniqueRV != std::optional(nullptr); - }; - - if (!A.checkForAllReturnedValues(Pred, *this)) - UniqueRV = nullptr; - - return UniqueRV; -} - -bool AAReturnedValuesImpl::checkForAllReturnedValuesAndReturnInsts( - function_ref &)> Pred) - const { - if (!isValidState()) - return false; - - // Check all returned values but ignore call sites as long as we have not - // encountered an overdefined one during an update. - for (const auto &It : ReturnedValues) { - Value *RV = It.first; - if (!Pred(*RV, It.second)) - return false; - } - - return true; -} - -ChangeStatus AAReturnedValuesImpl::updateImpl(Attributor &A) { - ChangeStatus Changed = ChangeStatus::UNCHANGED; - - SmallVector Values; - bool UsedAssumedInformation = false; - auto ReturnInstCB = [&](Instruction &I) { - ReturnInst &Ret = cast(I); - Values.clear(); - if (!A.getAssumedSimplifiedValues(IRPosition::value(*Ret.getReturnValue()), - *this, Values, AA::Intraprocedural, - UsedAssumedInformation)) - Values.push_back({*Ret.getReturnValue(), Ret}); - - for (auto &VAC : Values) { - assert(AA::isValidInScope(*VAC.getValue(), Ret.getFunction()) && - "Assumed returned value should be valid in function scope!"); - if (ReturnedValues[VAC.getValue()].insert(&Ret)) - Changed = ChangeStatus::CHANGED; - } - return true; - }; - - // Discover returned values from all live returned instructions in the - // associated function. - if (!A.checkForAllInstructions(ReturnInstCB, *this, {Instruction::Ret}, - UsedAssumedInformation)) - return indicatePessimisticFixpoint(); - return Changed; -} - -struct AAReturnedValuesFunction final : public AAReturnedValuesImpl { - AAReturnedValuesFunction(const IRPosition &IRP, Attributor &A) - : AAReturnedValuesImpl(IRP, A) {} - - /// See AbstractAttribute::trackStatistics() - void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(returned) } -}; - -/// Returned values information for a call sites. -struct AAReturnedValuesCallSite final : AAReturnedValuesImpl { - AAReturnedValuesCallSite(const IRPosition &IRP, Attributor &A) - : AAReturnedValuesImpl(IRP, A) {} - - /// See AbstractAttribute::initialize(...). - void initialize(Attributor &A) override { - // TODO: Once we have call site specific value information we can provide - // call site specific liveness information and then it makes - // sense to specialize attributes for call sites instead of - // redirecting requests to the callee. - llvm_unreachable("Abstract attributes for returned values are not " - "supported for call sites yet!"); - } - - /// See AbstractAttribute::updateImpl(...). - ChangeStatus updateImpl(Attributor &A) override { - return indicatePessimisticFixpoint(); - } - - /// See AbstractAttribute::trackStatistics() - void trackStatistics() const override {} -}; -} // namespace - /// ------------------------ NoSync Function Attribute ------------------------- bool AANoSync::isAlignedBarrier(const CallBase &CB, bool ExecutedAligned) { @@ -6133,17 +5890,20 @@ // TODO: we could do this in a more sophisticated way inside // AAReturnedValues, e.g., track all values that escape through returns // directly somehow. - auto CheckReturnedArgs = [&](const AAReturnedValues *RVAA) { - if (!RVAA || !RVAA->getState().isValidState()) + auto CheckReturnedArgs = [&](bool &UsedAssumedInformation) { + SmallVector Values; + if (!A.getAssumedSimplifiedValues(IRPosition::returned(*F), this, Values, + AA::ValueScope::Intraprocedural, + UsedAssumedInformation)) return false; bool SeenConstant = false; - for (const auto &It : RVAA->returned_values()) { - if (isa(It.first)) { + for (const AA::ValueAndContext &VAC : Values) { + if (isa(VAC.getValue())) { if (SeenConstant) return false; SeenConstant = true; - } else if (!isa(It.first) || - It.first == getAssociatedArgument()) + } else if (!isa(VAC.getValue()) || + VAC.getValue() == getAssociatedArgument()) return false; } return true; @@ -6153,16 +5913,12 @@ if (AA::hasAssumedIRAttr( A, this, FnPos, DepClassTy::OPTIONAL, IsKnownNoUnwind)) { bool IsVoidTy = F->getReturnType()->isVoidTy(); - const AAReturnedValues *RVAA = - IsVoidTy ? nullptr - : A.getAAFor(*this, FnPos, - - DepClassTy::OPTIONAL); - if (IsVoidTy || CheckReturnedArgs(RVAA)) { + bool UsedAssumedInformation = false; + if (IsVoidTy || CheckReturnedArgs(UsedAssumedInformation)) { T.addKnownBits(NOT_CAPTURED_IN_RET); if (T.isKnown(NOT_CAPTURED_IN_MEM)) return ChangeStatus::UNCHANGED; - if (IsKnownNoUnwind && (IsVoidTy || RVAA->getState().isAtFixpoint())) { + if (IsKnownNoUnwind && (IsVoidTy || !UsedAssumedInformation)) { addKnownBits(NOT_CAPTURED_IN_RET); if (isKnown(NOT_CAPTURED_IN_MEM)) return indicateOptimisticFixpoint(); @@ -6720,27 +6476,7 @@ /// See AbstractAttribute::updateImpl(...). ChangeStatus updateImpl(Attributor &A) override { - auto Before = SimplifiedAssociatedValue; - auto *RetAA = A.getAAFor( - *this, IRPosition::function(*getAssociatedFunction()), - DepClassTy::REQUIRED); - auto PredForReturned = - [&](Value &RetVal, const SmallSetVector &RetInsts) { - bool UsedAssumedInformation = false; - std::optional CSRetVal = - A.translateArgumentToCallSiteContent( - &RetVal, *cast(getCtxI()), *this, - UsedAssumedInformation); - SimplifiedAssociatedValue = AA::combineOptionalValuesInAAValueLatice( - SimplifiedAssociatedValue, CSRetVal, getAssociatedType()); - return SimplifiedAssociatedValue != std::optional(nullptr); - }; - if (!RetAA || - !RetAA->checkForAllReturnedValuesAndReturnInsts(PredForReturned)) - if (!askSimplifiedValueForOtherAAs(A)) return indicatePessimisticFixpoint(); - return Before == SimplifiedAssociatedValue ? ChangeStatus::UNCHANGED - : ChangeStatus ::CHANGED; } void trackStatistics() const override { @@ -9343,7 +9079,10 @@ : Base(IRP, A) {} /// See AbstractAttribute::initialize(...). - void initialize(Attributor &A) override {} + void initialize(Attributor &A) override { + if (!A.isFunctionIPOAmendable(*getAssociatedFunction())) + indicatePessimisticFixpoint(); + } /// See AbstractAttribute::trackStatistics() void trackStatistics() const override { @@ -9826,6 +9565,12 @@ AAPotentialConstantValuesReturned(const IRPosition &IRP, Attributor &A) : Base(IRP, A) {} + void initialize(Attributor &A) override { + if (!A.isFunctionIPOAmendable(*getAssociatedFunction())) + indicatePessimisticFixpoint(); + Base::initialize(A); + } + /// See AbstractAttribute::trackStatistics() void trackStatistics() const override { STATS_DECLTRACK_FNRET_ATTR(potential_values) @@ -10561,9 +10306,12 @@ }; struct AANoFPClassReturned final - : AAReturnedFromReturnedValues { + : AAReturnedFromReturnedValues { AANoFPClassReturned(const IRPosition &IRP, Attributor &A) - : AAReturnedFromReturnedValues(IRP, A) {} + : AAReturnedFromReturnedValues( + IRP, A) {} /// See AbstractAttribute::trackStatistics() void trackStatistics() const override { @@ -10942,9 +10690,9 @@ return nullptr; } - void addValue(Attributor &A, StateType &State, Value &V, - const Instruction *CtxI, AA::ValueScope S, - Function *AnchorScope) const { + virtual void addValue(Attributor &A, StateType &State, Value &V, + const Instruction *CtxI, AA::ValueScope S, + Function *AnchorScope) const { IRPosition ValIRP = IRPosition::value(V); if (auto *CB = dyn_cast_or_null(CtxI)) { @@ -11074,14 +10822,23 @@ return ChangeStatus::UNCHANGED; } - bool getAssumedSimplifiedValues(Attributor &A, - SmallVectorImpl &Values, - AA::ValueScope S) const override { + bool getAssumedSimplifiedValues( + Attributor &A, SmallVectorImpl &Values, + AA::ValueScope S, bool RecurseForSelectAndPHI = false) const override { if (!isValidState()) return false; + bool UsedAssumedInformation = false; for (const auto &It : getAssumedSet()) - if (It.second & S) + if (It.second & S) { + if (RecurseForSelectAndPHI && (isa(It.first.getValue()) || + isa(It.first.getValue()))) { + if (A.getAssumedSimplifiedValues( + IRPosition::inst(*cast(It.first.getValue())), + this, Values, S, UsedAssumedInformation)) + continue; + } Values.push_back(It.first); + } assert(!undefIsContained() && "Undef should be an explicit value!"); return true; } @@ -11095,7 +10852,7 @@ ChangeStatus updateImpl(Attributor &A) override { auto AssumedBefore = getAssumed(); - genericValueTraversal(A); + genericValueTraversal(A, &getAssociatedValue()); return (AssumedBefore == getAssumed()) ? ChangeStatus::UNCHANGED : ChangeStatus::CHANGED; @@ -11407,10 +11164,9 @@ return false; } - void genericValueTraversal(Attributor &A) { + void genericValueTraversal(Attributor &A, Value *InitialV) { SmallMapVector LivenessAAs; - Value *InitialV = &getAssociatedValue(); SmallSet Visited; SmallVector Worklist; Worklist.push_back({{*InitialV, getCtxI()}, AA::AnyScope}); @@ -11568,25 +11324,127 @@ } }; -struct AAPotentialValuesReturned - : AAReturnedFromReturnedValues { - using Base = - AAReturnedFromReturnedValues; +struct AAPotentialValuesReturned : public AAPotentialValuesFloating { + using Base = AAPotentialValuesFloating; AAPotentialValuesReturned(const IRPosition &IRP, Attributor &A) : Base(IRP, A) {} /// See AbstractAttribute::initialize(..). void initialize(Attributor &A) override { - if (A.hasSimplificationCallback(getIRPosition())) + Function *F = getAssociatedFunction(); + if (!F || F->isDeclaration() || F->getReturnType()->isVoidTy()) { indicatePessimisticFixpoint(); - else - AAPotentialValues::initialize(A); + return; + } + + for (Argument &Arg : F->args()) + if (Arg.hasReturnedAttr()) { + addValue(A, getState(), Arg, nullptr, AA::AnyScope, F); + ReturnedArg = &Arg; + break; + } + if (!A.isFunctionIPOAmendable(*F) || + A.hasSimplificationCallback(getIRPosition())) { + if (!ReturnedArg) + indicatePessimisticFixpoint(); + else + indicateOptimisticFixpoint(); + } + } + + /// See AbstractAttribute::updateImpl(...). + ChangeStatus updateImpl(Attributor &A) override { + auto AssumedBefore = getAssumed(); + bool UsedAssumedInformation; + + SmallVector Values; + Function *AnchorScope = getAnchorScope(); + auto HandleReturnedValue = [&](Value &V, Instruction *CtxI, + bool AddValues) { + for (AA::ValueScope S : {AA::Interprocedural, AA::Intraprocedural}) { + Values.clear(); + if (!A.getAssumedSimplifiedValues(IRPosition::value(V), this, Values, S, + UsedAssumedInformation, + /* RecurseForSelectAndPHI */ true)) + return false; + if (!AddValues) + continue; + for (const AA::ValueAndContext &VAC : Values) + addValue(A, getState(), *VAC.getValue(), + VAC.getCtxI() ? VAC.getCtxI() : CtxI, S, AnchorScope); + } + return true; + }; + + if (ReturnedArg) { + HandleReturnedValue(*ReturnedArg, nullptr, true); + } else { + auto RetInstPred = [&](Instruction &RetI) { + bool AddValues = true; + if (isa(RetI.getOperand(0)) || + isa(RetI.getOperand(0))) { + addValue(A, getState(), *RetI.getOperand(0), &RetI, AA::AnyScope, + AnchorScope); + AddValues = false; + } + return HandleReturnedValue(*RetI.getOperand(0), &RetI, AddValues); + }; + + if (!A.checkForAllInstructions(RetInstPred, *this, {Instruction::Ret}, + UsedAssumedInformation, + /* CheckBBLivenessOnly */ true)) + return indicatePessimisticFixpoint(); + } + + return (AssumedBefore == getAssumed()) ? ChangeStatus::UNCHANGED + : ChangeStatus::CHANGED; + } + + void addValue(Attributor &A, StateType &State, Value &V, + const Instruction *CtxI, AA::ValueScope S, + Function *AnchorScope) const override { + Function *F = getAssociatedFunction(); + if (auto *CB = dyn_cast(&V)) + if (CB->getCalledOperand() == F) + return; + Base::addValue(A, State, V, CtxI, S, AnchorScope); } ChangeStatus manifest(Attributor &A) override { - // We queried AAValueSimplify for the returned values so they will be - // replaced if a simplified form was found. Nothing to do here. - return ChangeStatus::UNCHANGED; + if (ReturnedArg) + return ChangeStatus::UNCHANGED; + SmallVector Values; + if (!getAssumedSimplifiedValues(A, Values, AA::ValueScope::Intraprocedural, + /* RecurseForSelectAndPHI */ true)) + return ChangeStatus::UNCHANGED; + Value *NewVal = getSingleValue(A, *this, getIRPosition(), Values); + if (!NewVal) + return ChangeStatus::UNCHANGED; + + ChangeStatus Changed = ChangeStatus::UNCHANGED; + if (auto *Arg = dyn_cast(NewVal)) { + STATS_DECLTRACK(UniqueReturnValue, FunctionReturn, + "Number of function with unique return"); + Changed |= A.manifestAttrs( + IRPosition::argument(*Arg), + {Attribute::get(Arg->getContext(), Attribute::Returned)}); + STATS_DECLTRACK_ARG_ATTR(returned); + } + + auto RetInstPred = [&](Instruction &RetI) { + Value *RetOp = RetI.getOperand(0); + if (isa(RetOp) || RetOp == NewVal) + return true; + if (AA::isValidAtPosition({*NewVal, RetI}, A.getInfoCache())) + if (A.changeUseAfterManifest(RetI.getOperandUse(0), *NewVal)) + Changed = ChangeStatus::CHANGED; + return true; + }; + bool UsedAssumedInformation; + (void)A.checkForAllInstructions(RetInstPred, *this, {Instruction::Ret}, + UsedAssumedInformation, + /* CheckBBLivenessOnly */ true); + return Changed; } ChangeStatus indicatePessimisticFixpoint() override { @@ -11594,9 +11452,11 @@ } /// See AbstractAttribute::trackStatistics() - void trackStatistics() const override { - STATS_DECLTRACK_FNRET_ATTR(potential_values) - } + void trackStatistics() const override{ + STATS_DECLTRACK_FNRET_ATTR(potential_values)} + + /// The argumented with an existing `returned` attribute. + Argument *ReturnedArg = nullptr; }; struct AAPotentialValuesFunction : AAPotentialValuesImpl { @@ -12024,7 +11884,6 @@ }; } // namespace -const char AAReturnedValues::ID = 0; const char AANoUnwind::ID = 0; const char AANoSync::ID = 0; const char AANoFree::ID = 0; @@ -12156,7 +12015,6 @@ CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoRecurse) 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_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AACallEdges) CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAAssumptionInfo) Index: llvm/test/Transforms/Attributor/ArgumentPromotion/musttail.ll =================================================================== --- llvm/test/Transforms/Attributor/ArgumentPromotion/musttail.ll +++ llvm/test/Transforms/Attributor/ArgumentPromotion/musttail.ll @@ -127,7 +127,7 @@ ; TUNIT-NEXT: [[A:%.*]] = load i32, ptr [[A_GEP]], align 4 ; TUNIT-NEXT: [[B:%.*]] = load i32, ptr [[B_GEP]], align 4 ; TUNIT-NEXT: [[V:%.*]] = add i32 [[A]], [[B]] -; TUNIT-NEXT: [[CA:%.*]] = musttail call noundef i32 @bar(ptr undef, i32 [[V]]) #[[ATTR5:[0-9]+]] +; TUNIT-NEXT: [[CA:%.*]] = musttail call i32 @bar(ptr undef, i32 [[V]]) #[[ATTR5:[0-9]+]] ; TUNIT-NEXT: ret i32 [[CA]] ; ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite) @@ -154,8 +154,8 @@ ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) ; TUNIT-LABEL: define {{[^@]+}}@caller2b ; TUNIT-SAME: (ptr nocapture nofree readonly [[G:%.*]]) #[[ATTR3]] { -; TUNIT-NEXT: [[V:%.*]] = call noundef i32 @test2b(ptr nocapture nofree readonly [[G]], i32 undef) #[[ATTR6:[0-9]+]] -; TUNIT-NEXT: ret i32 [[V]] +; TUNIT-NEXT: [[V:%.*]] = call i32 @test2b(ptr nocapture nofree readonly [[G]], i32 undef) #[[ATTR6:[0-9]+]] +; TUNIT-NEXT: ret i32 0 ; ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite) ; CGSCC-LABEL: define {{[^@]+}}@caller2b Index: llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll =================================================================== --- llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll +++ llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll @@ -72,11 +72,11 @@ ; TUNIT: RET: ; TUNIT-NEXT: ret void ; -; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn +; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@caller ; CGSCC-SAME: (i1 noundef [[C:%.*]]) #[[ATTR2:[0-9]+]] personality ptr @__gxx_personality_v0 { ; CGSCC-NEXT: [[Q:%.*]] = alloca i32, align 4 -; CGSCC-NEXT: [[W:%.*]] = call align 4 ptr @incdec(i1 noundef [[C]], ptr nofree noundef nonnull align 4 dereferenceable(4) [[Q]]) #[[ATTR3:[0-9]+]] +; CGSCC-NEXT: [[W:%.*]] = call align 4 ptr @incdec(i1 noundef [[C]], ptr noalias nofree noundef nonnull align 4 dereferenceable(4) [[Q]]) #[[ATTR3:[0-9]+]] ; CGSCC-NEXT: [[S1:%.*]] = call { i32, i32 } @foo(i32 noundef 1, i32 noundef 2) #[[ATTR4:[0-9]+]] ; CGSCC-NEXT: [[X1:%.*]] = extractvalue { i32, i32 } [[S1]], 0 ; CGSCC-NEXT: [[S2:%.*]] = call { i32, i32 } @foo(i32 noundef 3, i32 noundef 4) #[[ATTR5:[0-9]+]] @@ -124,7 +124,7 @@ ;. ; CGSCC: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) } ; CGSCC: attributes #[[ATTR1]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } -; CGSCC: attributes #[[ATTR2]] = { mustprogress nofree nosync nounwind willreturn } +; CGSCC: attributes #[[ATTR2]] = { mustprogress nofree nosync nounwind willreturn memory(none) } ; CGSCC: attributes #[[ATTR3]] = { nofree nounwind willreturn } ; CGSCC: attributes #[[ATTR4]] = { nofree willreturn } ; CGSCC: attributes #[[ATTR5]] = { nofree nounwind willreturn memory(none) } Index: llvm/test/Transforms/Attributor/depgraph.ll =================================================================== --- llvm/test/Transforms/Attributor/depgraph.ll +++ llvm/test/Transforms/Attributor/depgraph.ll @@ -99,26 +99,29 @@ ; GRAPH-NEXT: [AAPotentialValues] for CtxI ' %6 = call ptr @checkAndAdvance(ptr %5)' at position {cs_ret: [@-1]} with state set-state(< { %5 = getelementptr inbounds i32, ptr %0, i64 4[3], %5 = getelementptr inbounds i32, ptr %0, i64 4[3], } >) ; GRAPH-NEXT: updates [AAPotentialValues] for CtxI ' %.0 = phi ptr [ %6, %4 ], [ %0, %7 ]' at position {flt:.0 [.0@-1]} with state set-state(< {ptr %0[3], %5 = getelementptr inbounds i32, ptr %0, i64 4[3], %5 = getelementptr inbounds i32, ptr %0, i64 4[3], } >) ; GRAPH-EMPTY: -; GRAPH-NEXT: [AAPotentialValues] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn_ret:checkAndAdvance [checkAndAdvance@-1]} with state set-state(< {ptr %0[3], %5 = getelementptr inbounds i32, ptr %0, i64 4[3], } >) +; GRAPH-NEXT: [AAPotentialValues] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn_ret:checkAndAdvance [checkAndAdvance@-1]} with state set-state(< { %.0 = phi ptr [ %6, %4 ], [ %0, %7 ][3], } >) ; GRAPH-NEXT: updates [AAPotentialValues] for CtxI ' %6 = call ptr @checkAndAdvance(ptr %5)' at position {cs_ret: [@-1]} with state set-state(< { %5 = getelementptr inbounds i32, ptr %0, i64 4[3], %5 = getelementptr inbounds i32, ptr %0, i64 4[3], } >) -; GRAPH-EMPTY: -; GRAPH-NEXT: [AAReturnedValues] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance@-1]} with state may-return(#2) -; GRAPH-NEXT: updates [AAPotentialValues] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn_ret:checkAndAdvance [checkAndAdvance@-1]} with state set-state(< {ptr %0[3], %5 = getelementptr inbounds i32, ptr %0, i64 4[3], } >) +; GRAPH-NEXT: updates [AANoUndef] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn_ret:checkAndAdvance [checkAndAdvance@-1]} with state may-undef-or-poison ; GRAPH-NEXT: updates [AANoCapture] for CtxI ' %2 = load i32, ptr %0, align 4' at position {arg: [@0]} with state assumed not-captured-maybe-returned ; GRAPH-NEXT: updates [AAAlign] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn_ret:checkAndAdvance [checkAndAdvance@-1]} with state align<1-16> ; GRAPH-NEXT: updates [AANonNull] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn_ret:checkAndAdvance [checkAndAdvance@-1]} with state nonnull -; GRAPH-EMPTY: +; GRAPH-NEXT: updates [AADereferenceable] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn_ret:checkAndAdvance [checkAndAdvance@-1]} with state unknown-dereferenceable +; GRAPH-EMPTY: ; GRAPH-NEXT: [AAPotentialValues] for CtxI ' %.0 = phi ptr [ %6, %4 ], [ %0, %7 ]' at position {flt:.0 [.0@-1]} with state set-state(< {ptr %0[3], %5 = getelementptr inbounds i32, ptr %0, i64 4[3], %5 = getelementptr inbounds i32, ptr %0, i64 4[3], } >) -; GRAPH-NEXT: updates [AAReturnedValues] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance@-1]} with state may-return(#2) -; GRAPH-EMPTY: +; GRAPH-NEXT: updates [AAPotentialValues] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn_ret:checkAndAdvance [checkAndAdvance@-1]} with state set-state(< { %.0 = phi ptr [ %6, %4 ], [ %0, %7 ][3], } >) +; GRAPH-NEXT: updates [AAPotentialValues] for CtxI ' %6 = call ptr @checkAndAdvance(ptr %5)' at position {cs_ret: [@-1]} with state set-state(< { %5 = getelementptr inbounds i32, ptr %0, i64 4[3], %5 = getelementptr inbounds i32, ptr %0, i64 4[3], } >) +; GRAPH-NEXT: updates [AANoCapture] for CtxI ' %2 = load i32, ptr %0, align 4' at position {arg: [@0]} with state assumed not-captured-maybe-returned +; GRAPH-NEXT: updates [AAAlign] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn_ret:checkAndAdvance [checkAndAdvance@-1]} with state align<1-16> +; GRAPH-NEXT: updates [AANonNull] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn_ret:checkAndAdvance [checkAndAdvance@-1]} with state nonnull +; GRAPH-EMPTY: ; GRAPH-NEXT: [AAPotentialValues] for CtxI ' %5 = getelementptr inbounds i32, ptr %0, i64 4' at position {flt: [@-1]} with state set-state(< { %5 = getelementptr inbounds i32, ptr %0, i64 4[3], } >) -; GRAPH-EMPTY: +; GRAPH-EMPTY: ; GRAPH-NEXT: [AAPotentialValues] for CtxI <> at position {flt: [@-1]} with state set-state(< {i64 4[3], } >) -; GRAPH-EMPTY: +; GRAPH-EMPTY: ; GRAPH-NEXT: [AAPotentialValues] for CtxI ' %2 = load i32, ptr %0, align 4' at position {flt:checkAndAdvance [checkAndAdvance@-1]} with state set-state(< {@checkAndAdvance[3], } >) -; GRAPH-EMPTY: +; GRAPH-EMPTY: ; GRAPH-NEXT: [AAPotentialValues] for CtxI ' %6 = call ptr @checkAndAdvance(ptr %5)' at position {cs_arg: [@0]} with state set-state(< { %5 = getelementptr inbounds i32, ptr %0, i64 4[3], } >) -; GRAPH-EMPTY: +; GRAPH-EMPTY: ; GRAPH-NEXT: [AAInstanceInfo] for CtxI ' %5 = getelementptr inbounds i32, ptr %0, i64 4' at position {flt: [@-1]} with state ; GRAPH-EMPTY: ; GRAPH-NEXT: [AANoRecurse] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance@-1]} with state may-recurse @@ -258,7 +261,6 @@ ; DOT-DAG: Node[[Node18:0x[a-z0-9]+]] [shape=record,label="{[AAMemoryBehavior] ; DOT-DAG: Node[[Node19:0x[a-z0-9]+]] [shape=record,label="{[AAPotentialValues] ; DOT-DAG: Node[[Node20:0x[a-z0-9]+]] [shape=record,label="{[AAPotentialValues] -; DOT-DAG: Node[[Node21:0x[a-z0-9]+]] [shape=record,label="{[AAReturnedValues] ; DOT-DAG: Node[[Node22:0x[a-z0-9]+]] [shape=record,label="{[AAPotentialValues] ; DOT-DAG: Node[[Node23:0x[a-z0-9]+]] [shape=record,label="{[AAPotentialValues] ; DOT-DAG: Node[[Node24:0x[a-z0-9]+]] [shape=record,label="{[AAPotentialValues] @@ -314,25 +316,20 @@ ; DOT-DAG: Node[[Node13]] -> Node[[Node12]]; ; DOT-DAG: Node[[Node55]] -> Node[[Node56]]; ; DOT-DAG: Node[[Node68]] -> Node[[Node73]]; -; DOT-DAG: Node[[Node21]] -> Node[[Node20]]; ; DOT-DAG: Node[[Node64]] -> Node[[Node61]]; ; DOT-DAG: Node[[Node61]] -> Node[[Node64]]; ; DOT-DAG: Node[[Node12]] -> Node[[Node13]]; ; DOT-DAG: Node[[Node11]] -> Node[[Node61]]; ; DOT-DAG: Node[[Node14]] -> Node[[Node18]]; -; DOT-DAG: Node[[Node22]] -> Node[[Node21]]; ; DOT-DAG: Node[[Node43]] -> Node[[Node68]]; ; DOT-DAG: Node[[Node19]] -> Node[[Node22]]; -; DOT-DAG: Node[[Node21]] -> Node[[Node51]]; ; DOT-DAG: Node[[Node10]] -> Node[[Node11]]; ; DOT-DAG: Node[[Node41]] -> Node[[Node42]]; ; DOT-DAG: Node[[Node42]] -> Node[[Node41]]; ; DOT-DAG: Node[[Node11]] -> Node[[Node10]]; -; DOT-DAG: Node[[Node21]] -> Node[[Node61]]; ; DOT-DAG: Node[[Node67]] -> Node[[Node66]]; ; DOT-DAG: Node[[Node18]] -> Node[[Node14]]; ; DOT-DAG: Node[[Node66]] -> Node[[Node67]]; -; DOT-DAG: Node[[Node21]] -> Node[[Node47]]; ; DOT-DAG: Node[[Node44]] -> Node[[Node43]]; ; DOT-DAG: Node[[Node43]] -> Node[[Node44]]; ;. Index: llvm/test/Transforms/Attributor/nocapture-1.ll =================================================================== --- llvm/test/Transforms/Attributor/nocapture-1.ll +++ llvm/test/Transforms/Attributor/nocapture-1.ll @@ -5,10 +5,10 @@ @g = global ptr null ; [#uses=1] ;. -; CHECK: @[[G:[a-zA-Z0-9_$"\\.-]+]] = global ptr null -; CHECK: @[[LOOKUP_TABLE:[a-zA-Z0-9_$"\\.-]+]] = global [2 x i1] [i1 false, i1 true] -; CHECK: @[[G2:[a-zA-Z0-9_$"\\.-]+]] = global ptr null -; CHECK: @[[G3:[a-zA-Z0-9_$"\\.-]+]] = global ptr null +; CHECK: @g = global ptr null +; CHECK: @lookup_table = global [2 x i1] [i1 false, i1 true] +; CHECK: @g2 = global ptr null +; CHECK: @g3 = global ptr null ;. define ptr @c1(ptr %q) { ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) @@ -35,13 +35,13 @@ ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write) ; TUNIT-LABEL: define {{[^@]+}}@c3 ; TUNIT-SAME: (ptr nofree writeonly [[Q:%.*]]) #[[ATTR1]] { -; TUNIT-NEXT: call void @c2(ptr nofree writeonly [[Q]]) #[[ATTR14:[0-9]+]] +; TUNIT-NEXT: call void @c2(ptr nofree writeonly [[Q]]) #[[ATTR16:[0-9]+]] ; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write) ; CGSCC-LABEL: define {{[^@]+}}@c3 ; CGSCC-SAME: (ptr nofree writeonly [[Q:%.*]]) #[[ATTR2:[0-9]+]] { -; CGSCC-NEXT: call void @c2(ptr nofree writeonly [[Q]]) #[[ATTR17:[0-9]+]] +; CGSCC-NEXT: call void @c2(ptr nofree writeonly [[Q]]) #[[ATTR19:[0-9]+]] ; CGSCC-NEXT: ret void ; call void @c2(ptr %q) @@ -187,14 +187,14 @@ ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read) ; TUNIT-LABEL: define {{[^@]+}}@c7 ; TUNIT-SAME: (ptr nofree readonly [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR2]] { -; TUNIT-NEXT: [[PTR:%.*]] = call ptr @lookup_bit(ptr noalias nofree readnone [[Q]], i32 [[BITNO]]) #[[ATTR15:[0-9]+]] +; TUNIT-NEXT: [[PTR:%.*]] = call ptr @lookup_bit(ptr noalias nofree readnone [[Q]], i32 [[BITNO]]) #[[ATTR17:[0-9]+]] ; TUNIT-NEXT: [[VAL:%.*]] = load i1, ptr [[PTR]], align 1 ; TUNIT-NEXT: ret i1 [[VAL]] ; ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(read) ; CGSCC-LABEL: define {{[^@]+}}@c7 ; CGSCC-SAME: (ptr nofree readonly [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR5:[0-9]+]] { -; CGSCC-NEXT: [[PTR:%.*]] = call ptr @lookup_bit(ptr noalias nofree readnone [[Q]], i32 [[BITNO]]) #[[ATTR18:[0-9]+]] +; CGSCC-NEXT: [[PTR:%.*]] = call ptr @lookup_bit(ptr noalias nofree readnone [[Q]], i32 [[BITNO]]) #[[ATTR20:[0-9]+]] ; CGSCC-NEXT: [[VAL:%.*]] = load i1, ptr [[PTR]], align 1 ; CGSCC-NEXT: ret i1 [[VAL]] ; @@ -293,13 +293,13 @@ ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn ; TUNIT-LABEL: define {{[^@]+}}@nc2 ; TUNIT-SAME: (ptr nocapture nofree [[P:%.*]], ptr nofree [[Q:%.*]]) #[[ATTR4]] { -; TUNIT-NEXT: [[TMP1:%.*]] = call i32 @nc1(ptr nofree [[Q]], ptr nocapture nofree [[P]], i1 noundef false) #[[ATTR16:[0-9]+]] +; TUNIT-NEXT: [[TMP1:%.*]] = call i32 @nc1(ptr nofree [[Q]], ptr nocapture nofree [[P]], i1 noundef false) #[[ATTR18:[0-9]+]] ; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn ; CGSCC-LABEL: define {{[^@]+}}@nc2 ; CGSCC-SAME: (ptr nocapture nofree align 4 [[P:%.*]], ptr nofree [[Q:%.*]]) #[[ATTR7:[0-9]+]] { -; CGSCC-NEXT: [[TMP1:%.*]] = call i32 @nc1(ptr nofree [[Q]], ptr nocapture nofree align 4 [[P]], i1 noundef false) #[[ATTR19:[0-9]+]] +; CGSCC-NEXT: [[TMP1:%.*]] = call i32 @nc1(ptr nofree [[Q]], ptr nocapture nofree align 4 [[P]], i1 noundef false) #[[ATTR21:[0-9]+]] ; CGSCC-NEXT: ret void ; %1 = call i32 @nc1(ptr %q, ptr %p, i1 0) ; [#uses=0] @@ -324,13 +324,13 @@ ; TUNIT: Function Attrs: nounwind memory(argmem: readwrite) ; TUNIT-LABEL: define {{[^@]+}}@nc4 ; TUNIT-SAME: (ptr [[P:%.*]]) #[[ATTR5:[0-9]+]] { -; TUNIT-NEXT: call void @external(ptr readonly [[P]]) #[[ATTR17:[0-9]+]] +; TUNIT-NEXT: call void @external(ptr readonly [[P]]) #[[ATTR19:[0-9]+]] ; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: nounwind memory(argmem: readwrite) ; CGSCC-LABEL: define {{[^@]+}}@nc4 ; CGSCC-SAME: (ptr [[P:%.*]]) #[[ATTR8:[0-9]+]] { -; CGSCC-NEXT: call void @external(ptr readonly [[P]]) #[[ATTR20:[0-9]+]] +; CGSCC-NEXT: call void @external(ptr readonly [[P]]) #[[ATTR22:[0-9]+]] ; CGSCC-NEXT: ret void ; call void @external(ptr %p) @@ -596,19 +596,19 @@ } define void @nocaptureLaunder(ptr %p) { -; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn +; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite) ; TUNIT-LABEL: define {{[^@]+}}@nocaptureLaunder -; TUNIT-SAME: (ptr nocapture nofree [[P:%.*]]) #[[ATTR4]] { +; TUNIT-SAME: (ptr nocapture nofree [[P:%.*]]) #[[ATTR8:[0-9]+]] { ; TUNIT-NEXT: entry: -; TUNIT-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr nofree [[P]]) #[[ATTR18:[0-9]+]] +; TUNIT-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr nofree [[P]]) #[[ATTR20:[0-9]+]] ; TUNIT-NEXT: store i8 42, ptr [[B]], align 1 ; TUNIT-NEXT: ret void ; -; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn +; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite) ; CGSCC-LABEL: define {{[^@]+}}@nocaptureLaunder -; CGSCC-SAME: (ptr nocapture nofree [[P:%.*]]) #[[ATTR6]] { +; CGSCC-SAME: (ptr nocapture nofree [[P:%.*]]) #[[ATTR11:[0-9]+]] { ; CGSCC-NEXT: entry: -; CGSCC-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr nofree [[P]]) #[[ATTR18]] +; CGSCC-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr nofree [[P]]) #[[ATTR20]] ; CGSCC-NEXT: store i8 42, ptr [[B]], align 1 ; CGSCC-NEXT: ret void ; @@ -623,14 +623,14 @@ ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn ; TUNIT-LABEL: define {{[^@]+}}@captureLaunder ; TUNIT-SAME: (ptr nofree [[P:%.*]]) #[[ATTR4]] { -; TUNIT-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr nofree [[P]]) #[[ATTR18]] +; TUNIT-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr nofree [[P]]) #[[ATTR20]] ; TUNIT-NEXT: store ptr [[B]], ptr @g2, align 8 ; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn ; CGSCC-LABEL: define {{[^@]+}}@captureLaunder ; CGSCC-SAME: (ptr nofree [[P:%.*]]) #[[ATTR6]] { -; CGSCC-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr nofree [[P]]) #[[ATTR18]] +; CGSCC-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr nofree [[P]]) #[[ATTR20]] ; CGSCC-NEXT: store ptr [[B]], ptr @g2, align 8 ; CGSCC-NEXT: ret void ; @@ -640,13 +640,21 @@ } define void @nocaptureStrip(ptr %p) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write) -; CHECK-LABEL: define {{[^@]+}}@nocaptureStrip -; CHECK-SAME: (ptr nocapture nofree writeonly [[P:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr noalias nofree readnone [[P]]) #[[ATTR18:[0-9]+]] -; CHECK-NEXT: store i8 42, ptr [[B]], align 1 -; CHECK-NEXT: ret void +; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; TUNIT-LABEL: define {{[^@]+}}@nocaptureStrip +; TUNIT-SAME: (ptr nocapture nofree writeonly [[P:%.*]]) #[[ATTR9:[0-9]+]] { +; TUNIT-NEXT: entry: +; TUNIT-NEXT: [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr noalias nofree readnone [[P]]) #[[ATTR20]] +; TUNIT-NEXT: store i8 42, ptr [[B]], align 1 +; TUNIT-NEXT: ret void +; +; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; CGSCC-LABEL: define {{[^@]+}}@nocaptureStrip +; CGSCC-SAME: (ptr nocapture nofree writeonly [[P:%.*]]) #[[ATTR12:[0-9]+]] { +; CGSCC-NEXT: entry: +; CGSCC-NEXT: [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr noalias nofree readnone [[P]]) #[[ATTR20]] +; CGSCC-NEXT: store i8 42, ptr [[B]], align 1 +; CGSCC-NEXT: ret void ; entry: %b = call ptr @llvm.strip.invariant.group.p0(ptr %p) @@ -659,7 +667,7 @@ ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write) ; CHECK-LABEL: define {{[^@]+}}@captureStrip ; CHECK-SAME: (ptr nofree writeonly [[P:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr noalias nofree readnone [[P]]) #[[ATTR18]] +; CHECK-NEXT: [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr noalias nofree readnone [[P]]) #[[ATTR20:[0-9]+]] ; CHECK-NEXT: store ptr [[B]], ptr @g3, align 8 ; CHECK-NEXT: ret void ; @@ -726,13 +734,13 @@ define i1 @captureDereferenceableOrNullICmp(ptr dereferenceable_or_null(4) %x) null_pointer_is_valid { ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none) ; TUNIT-LABEL: define {{[^@]+}}@captureDereferenceableOrNullICmp -; TUNIT-SAME: (ptr nofree noundef readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR8:[0-9]+]] { +; TUNIT-SAME: (ptr nofree noundef readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR10:[0-9]+]] { ; TUNIT-NEXT: [[TMP1:%.*]] = icmp eq ptr [[X]], null ; TUNIT-NEXT: ret i1 [[TMP1]] ; ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@captureDereferenceableOrNullICmp -; CGSCC-SAME: (ptr nofree noundef readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR11:[0-9]+]] { +; CGSCC-SAME: (ptr nofree noundef readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR13:[0-9]+]] { ; CGSCC-NEXT: [[TMP1:%.*]] = icmp eq ptr [[X]], null ; CGSCC-NEXT: ret i1 [[TMP1]] ; @@ -757,14 +765,14 @@ define ptr @test_returned1(ptr %A, ptr returned %B) nounwind readonly { ; TUNIT: Function Attrs: nounwind memory(read) ; TUNIT-LABEL: define {{[^@]+}}@test_returned1 -; TUNIT-SAME: (ptr nocapture [[A:%.*]], ptr returned [[B:%.*]]) #[[ATTR9:[0-9]+]] { +; TUNIT-SAME: (ptr nocapture [[A:%.*]], ptr returned [[B:%.*]]) #[[ATTR11:[0-9]+]] { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: [[P:%.*]] = call ptr @unknownpi8pi8(ptr [[A]], ptr [[B]]) ; TUNIT-NEXT: ret ptr [[P]] ; ; CGSCC: Function Attrs: nounwind memory(read) ; CGSCC-LABEL: define {{[^@]+}}@test_returned1 -; CGSCC-SAME: (ptr nocapture [[A:%.*]], ptr returned [[B:%.*]]) #[[ATTR12:[0-9]+]] { +; CGSCC-SAME: (ptr nocapture [[A:%.*]], ptr returned [[B:%.*]]) #[[ATTR14:[0-9]+]] { ; CGSCC-NEXT: entry: ; CGSCC-NEXT: [[P:%.*]] = call ptr @unknownpi8pi8(ptr [[A]], ptr [[B]]) ; CGSCC-NEXT: ret ptr [[P]] @@ -777,16 +785,16 @@ define ptr @test_returned2(ptr %A, ptr %B) { ; TUNIT: Function Attrs: nounwind memory(read) ; TUNIT-LABEL: define {{[^@]+}}@test_returned2 -; TUNIT-SAME: (ptr readonly [[A:%.*]], ptr readonly [[B:%.*]]) #[[ATTR9]] { +; TUNIT-SAME: (ptr readonly [[A:%.*]], ptr readonly [[B:%.*]]) #[[ATTR11]] { ; TUNIT-NEXT: entry: -; TUNIT-NEXT: [[P:%.*]] = call ptr @unknownpi8pi8(ptr readonly [[A]], ptr readonly [[B]]) #[[ATTR9]] +; TUNIT-NEXT: [[P:%.*]] = call ptr @unknownpi8pi8(ptr readonly [[A]], ptr readonly [[B]]) #[[ATTR11]] ; TUNIT-NEXT: ret ptr [[P]] ; ; CGSCC: Function Attrs: nounwind memory(read) ; CGSCC-LABEL: define {{[^@]+}}@test_returned2 -; CGSCC-SAME: (ptr readonly [[A:%.*]], ptr readonly [[B:%.*]]) #[[ATTR12]] { +; CGSCC-SAME: (ptr readonly [[A:%.*]], ptr readonly [[B:%.*]]) #[[ATTR14]] { ; CGSCC-NEXT: entry: -; CGSCC-NEXT: [[P:%.*]] = call ptr @unknownpi8pi8(ptr readonly [[A]], ptr readonly [[B]]) #[[ATTR12]] +; CGSCC-NEXT: [[P:%.*]] = call ptr @unknownpi8pi8(ptr readonly [[A]], ptr readonly [[B]]) #[[ATTR14]] ; CGSCC-NEXT: ret ptr [[P]] ; entry: @@ -802,13 +810,13 @@ define void @ptr_uses(ptr %ptr, ptr %wptr) { ; TUNIT: Function Attrs: mustprogress nounwind willreturn ; TUNIT-LABEL: define {{[^@]+}}@ptr_uses -; TUNIT-SAME: (ptr [[PTR:%.*]], ptr nocapture nofree noundef nonnull writeonly dereferenceable(1) [[WPTR:%.*]]) #[[ATTR11:[0-9]+]] { +; TUNIT-SAME: (ptr [[PTR:%.*]], ptr nocapture nofree noundef nonnull writeonly dereferenceable(1) [[WPTR:%.*]]) #[[ATTR13:[0-9]+]] { ; TUNIT-NEXT: store i8 0, ptr [[WPTR]], align 1 ; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: mustprogress nounwind willreturn ; CGSCC-LABEL: define {{[^@]+}}@ptr_uses -; CGSCC-SAME: (ptr [[PTR:%.*]], ptr nocapture nofree noundef nonnull writeonly dereferenceable(1) [[WPTR:%.*]]) #[[ATTR14:[0-9]+]] { +; CGSCC-SAME: (ptr [[PTR:%.*]], ptr nocapture nofree noundef nonnull writeonly dereferenceable(1) [[WPTR:%.*]]) #[[ATTR16:[0-9]+]] { ; CGSCC-NEXT: store i8 0, ptr [[WPTR]], align 1 ; CGSCC-NEXT: ret void ; @@ -830,17 +838,19 @@ ; TUNIT: attributes #[[ATTR5]] = { nounwind memory(argmem: readwrite) } ; TUNIT: attributes #[[ATTR6]] = { nofree nosync nounwind memory(write) } ; TUNIT: attributes #[[ATTR7]] = { mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) } -; TUNIT: attributes #[[ATTR8]] = { mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none) } -; TUNIT: attributes #[[ATTR9]] = { nounwind memory(read) } -; TUNIT: attributes #[[ATTR10:[0-9]+]] = { nounwind willreturn memory(read) } -; TUNIT: attributes #[[ATTR11]] = { mustprogress nounwind willreturn } -; TUNIT: attributes #[[ATTR12:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(inaccessiblemem: readwrite) } -; TUNIT: attributes #[[ATTR13:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } -; TUNIT: attributes #[[ATTR14]] = { nofree nosync nounwind willreturn memory(write) } -; TUNIT: attributes #[[ATTR15]] = { nofree nounwind willreturn memory(none) } -; TUNIT: attributes #[[ATTR16]] = { nofree nosync nounwind willreturn } -; TUNIT: attributes #[[ATTR17]] = { nounwind } -; TUNIT: attributes #[[ATTR18]] = { nofree willreturn } +; TUNIT: attributes #[[ATTR8]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite) } +; TUNIT: attributes #[[ATTR9]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) } +; TUNIT: attributes #[[ATTR10]] = { mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none) } +; TUNIT: attributes #[[ATTR11]] = { nounwind memory(read) } +; TUNIT: attributes #[[ATTR12:[0-9]+]] = { nounwind willreturn memory(read) } +; TUNIT: attributes #[[ATTR13]] = { mustprogress nounwind willreturn } +; TUNIT: attributes #[[ATTR14:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(inaccessiblemem: readwrite) } +; TUNIT: attributes #[[ATTR15:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } +; TUNIT: attributes #[[ATTR16]] = { nofree nosync nounwind willreturn memory(write) } +; TUNIT: attributes #[[ATTR17]] = { nofree nounwind willreturn memory(none) } +; TUNIT: attributes #[[ATTR18]] = { nofree nosync nounwind willreturn } +; TUNIT: attributes #[[ATTR19]] = { nounwind } +; TUNIT: attributes #[[ATTR20]] = { nofree willreturn } ;. ; CGSCC: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } ; CGSCC: attributes #[[ATTR1]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(write) } @@ -853,14 +863,16 @@ ; CGSCC: attributes #[[ATTR8]] = { nounwind memory(argmem: readwrite) } ; CGSCC: attributes #[[ATTR9]] = { nofree nosync nounwind memory(write) } ; CGSCC: attributes #[[ATTR10]] = { mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) } -; CGSCC: attributes #[[ATTR11]] = { mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none) } -; CGSCC: attributes #[[ATTR12]] = { nounwind memory(read) } -; CGSCC: attributes #[[ATTR13:[0-9]+]] = { nounwind willreturn memory(read) } -; CGSCC: attributes #[[ATTR14]] = { mustprogress nounwind willreturn } -; CGSCC: attributes #[[ATTR15:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(inaccessiblemem: readwrite) } -; CGSCC: attributes #[[ATTR16:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } -; CGSCC: attributes #[[ATTR17]] = { nofree nounwind willreturn memory(write) } -; CGSCC: attributes #[[ATTR18]] = { nofree willreturn } -; CGSCC: attributes #[[ATTR19]] = { nofree nounwind willreturn } -; CGSCC: attributes #[[ATTR20]] = { nounwind } +; CGSCC: attributes #[[ATTR11]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite) } +; CGSCC: attributes #[[ATTR12]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) } +; CGSCC: attributes #[[ATTR13]] = { mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none) } +; CGSCC: attributes #[[ATTR14]] = { nounwind memory(read) } +; CGSCC: attributes #[[ATTR15:[0-9]+]] = { nounwind willreturn memory(read) } +; CGSCC: attributes #[[ATTR16]] = { mustprogress nounwind willreturn } +; CGSCC: attributes #[[ATTR17:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(inaccessiblemem: readwrite) } +; CGSCC: attributes #[[ATTR18:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } +; CGSCC: attributes #[[ATTR19]] = { nofree nounwind willreturn memory(write) } +; CGSCC: attributes #[[ATTR20]] = { nofree willreturn } +; CGSCC: attributes #[[ATTR21]] = { nofree nounwind willreturn } +; CGSCC: attributes #[[ATTR22]] = { nounwind } ;. Index: llvm/test/Transforms/Attributor/nofpclass-select.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Attributor/nofpclass-select.ll @@ -0,0 +1,28 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT + +define float @ret_select_nnan_flag(i1 %cond, float %arg0, float %arg1) { +; CHECK-LABEL: define nofpclass(nan) float @ret_select_nnan_flag +; CHECK-SAME: (i1 [[COND:%.*]], float [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[SELECT:%.*]] = select nnan i1 [[COND]], float [[ARG0]], float [[ARG1]] +; CHECK-NEXT: ret float [[SELECT]] +; + %select = select nnan i1 %cond, float %arg0, float %arg1 + ret float %select +} + +declare float @llvm.arithmetic.fence.f32(float) + +define float @ret_fence_select_nnan_flag(i1 %cond, float %arg0, float %arg1) { +; CHECK-LABEL: define nofpclass(nan) float @ret_fence_select_nnan_flag +; CHECK-SAME: (i1 [[COND:%.*]], float [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[SELECT:%.*]] = select nnan i1 [[COND]], float [[ARG0]], float [[ARG1]] +; CHECK-NEXT: [[FENCE:%.*]] = call nofpclass(nan) float @llvm.arithmetic.fence.f32(float [[SELECT]]) #[[ATTR2:[0-9]+]] +; CHECK-NEXT: ret float [[FENCE]] +; + %select = select nnan i1 %cond, float %arg0, float %arg1 + %fence = call float @llvm.arithmetic.fence.f32(float %select) + ret float %fence +} +;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +; TUNIT: {{.*}} Index: llvm/test/Transforms/Attributor/potential.ll =================================================================== --- llvm/test/Transforms/Attributor/potential.ll +++ llvm/test/Transforms/Attributor/potential.ll @@ -12,8 +12,7 @@ ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@iszero1 ; CGSCC-SAME: (i32 noundef [[C:%.*]]) #[[ATTR0:[0-9]+]] { -; CGSCC-NEXT: [[CMP:%.*]] = icmp eq i32 [[C]], 0 -; CGSCC-NEXT: ret i1 [[CMP]] +; CGSCC-NEXT: ret i1 false ; %cmp = icmp eq i32 %c, 0 ret i1 %cmp @@ -29,7 +28,7 @@ ; CGSCC-LABEL: define {{[^@]+}}@potential_test1 ; CGSCC-SAME: (i1 [[C:%.*]]) #[[ATTR1:[0-9]+]] { ; CGSCC-NEXT: [[ARG:%.*]] = select i1 [[C]], i32 -1, i32 1 -; CGSCC-NEXT: [[RET:%.*]] = call i1 @iszero1(i32 noundef [[ARG]]) #[[ATTR2:[0-9]+]] +; CGSCC-NEXT: [[RET:%.*]] = call noundef i1 @iszero1(i32 noundef [[ARG]]) #[[ATTR2:[0-9]+]] ; CGSCC-NEXT: ret i1 [[RET]] ; %arg = select i1 %c, i32 -1, i32 1 @@ -379,8 +378,7 @@ ; CHECK-NEXT: [[I_1]] = add i32 [[I_0]], 1 ; CHECK-NEXT: br label [[COND]] ; CHECK: end: -; CHECK-NEXT: [[RET:%.*]] = icmp eq i32 [[C_0]], 0 -; CHECK-NEXT: ret i1 [[RET]] +; CHECK-NEXT: ret i1 false ; entry: br label %cond @@ -401,23 +399,23 @@ } ; Test 10 -; FIXME: potential returned values of @may_return_undef is {1, -1} -; and returned value of @potential_test10 can be simplified to 0(false) +; potential returned values of @may_return_undef is {1, -1} +; and returned value of @potential_test10 can be simplified to 0(false) define internal i32 @may_return_undef(i32 %c) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define {{[^@]+}}@may_return_undef -; CHECK-SAME: (i32 noundef [[C:%.*]]) #[[ATTR0]] { -; CHECK-NEXT: switch i32 [[C]], label [[OTHERWISE:%.*]] [ -; CHECK-NEXT: i32 1, label [[A:%.*]] -; CHECK-NEXT: i32 -1, label [[B:%.*]] -; CHECK-NEXT: ] -; CHECK: a: -; CHECK-NEXT: ret i32 1 -; CHECK: b: -; CHECK-NEXT: ret i32 -1 -; CHECK: otherwise: -; CHECK-NEXT: ret i32 undef +; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; CGSCC-LABEL: define {{[^@]+}}@may_return_undef +; CGSCC-SAME: (i32 noundef [[C:%.*]]) #[[ATTR0]] { +; CGSCC-NEXT: switch i32 [[C]], label [[OTHERWISE:%.*]] [ +; CGSCC-NEXT: i32 1, label [[A:%.*]] +; CGSCC-NEXT: i32 -1, label [[B:%.*]] +; CGSCC-NEXT: ] +; CGSCC: a: +; CGSCC-NEXT: ret i32 1 +; CGSCC: b: +; CGSCC-NEXT: ret i32 -1 +; CGSCC: otherwise: +; CGSCC-NEXT: ret i32 undef ; switch i32 %c, label %otherwise [i32 1, label %a i32 -1, label %b] @@ -433,9 +431,7 @@ ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; TUNIT-LABEL: define {{[^@]+}}@potential_test10 ; TUNIT-SAME: (i32 [[C:%.*]]) #[[ATTR0]] { -; TUNIT-NEXT: [[RET:%.*]] = call i32 @may_return_undef(i32 noundef [[C]]) #[[ATTR1]] -; TUNIT-NEXT: [[CMP:%.*]] = icmp eq i32 [[RET]], 0 -; TUNIT-NEXT: ret i1 [[CMP]] +; TUNIT-NEXT: ret i1 false ; ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@potential_test10 @@ -514,9 +510,9 @@ ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; TUNIT-LABEL: define {{[^@]+}}@potential_test11 ; TUNIT-SAME: (i1 [[C:%.*]]) #[[ATTR0]] { -; TUNIT-NEXT: [[ZERO1:%.*]] = call i32 @optimize_undef_1(i1 noundef [[C]]) #[[ATTR1]], !range [[RNG0]] -; TUNIT-NEXT: [[ZERO2:%.*]] = call i32 @optimize_undef_2(i1 noundef [[C]]) #[[ATTR1]], !range [[RNG2:![0-9]+]] -; TUNIT-NEXT: [[ZERO3:%.*]] = call i32 @optimize_undef_3(i1 noundef [[C]]) #[[ATTR1]], !range [[RNG0]] +; TUNIT-NEXT: [[ZERO1:%.*]] = call i32 @optimize_undef_1(i1 noundef [[C]]) #[[ATTR1]] +; TUNIT-NEXT: [[ZERO2:%.*]] = call i32 @optimize_undef_2(i1 noundef [[C]]) #[[ATTR1]] +; TUNIT-NEXT: [[ZERO3:%.*]] = call i32 @optimize_undef_3(i1 noundef [[C]]) #[[ATTR1]] ; TUNIT-NEXT: [[ACC1:%.*]] = add i32 [[ZERO1]], [[ZERO2]] ; TUNIT-NEXT: [[ACC2:%.*]] = add i32 [[ACC1]], [[ZERO3]] ; TUNIT-NEXT: ret i32 [[ACC2]] @@ -547,8 +543,7 @@ ; CHECK: t: ; CHECK-NEXT: ret i32 0 ; CHECK: f: -; CHECK-NEXT: [[POISON:%.*]] = sub nuw i32 0, 1 -; CHECK-NEXT: ret i32 [[POISON]] +; CHECK-NEXT: ret i32 0 ; br i1 %c, label %t, label %f t: @@ -558,7 +553,7 @@ ret i32 %poison } -; FIXME: returned value can be simplified to 0 +; returned value can be simplified to 0 define i32 @potential_test12(i1 %c) { ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; TUNIT-LABEL: define {{[^@]+}}@potential_test12 @@ -568,7 +563,7 @@ ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@potential_test12 ; CGSCC-SAME: (i1 noundef [[C:%.*]]) #[[ATTR1]] { -; CGSCC-NEXT: [[ZERO:%.*]] = call i32 @optimize_poison_1(i1 noundef [[C]]) #[[ATTR2]] +; CGSCC-NEXT: [[ZERO:%.*]] = call noundef i32 @optimize_poison_1(i1 noundef [[C]]) #[[ATTR2]] ; CGSCC-NEXT: ret i32 [[ZERO]] ; %zero = call i32 @optimize_poison_1(i1 %c) @@ -595,13 +590,13 @@ ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; TUNIT-LABEL: define {{[^@]+}}@potential_test13_caller1 ; TUNIT-SAME: () #[[ATTR0]] { -; TUNIT-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 noundef 0) #[[ATTR1]], !range [[RNG0]] +; TUNIT-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 noundef 0) #[[ATTR1]] ; TUNIT-NEXT: ret i32 [[RET]] ; ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@potential_test13_caller1 ; CGSCC-SAME: () #[[ATTR1]] { -; CGSCC-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 noundef 0) #[[ATTR2]] +; CGSCC-NEXT: [[RET:%.*]] = call noundef i32 @potential_test13_callee(i32 noundef 0) #[[ATTR2]] ; CGSCC-NEXT: ret i32 [[RET]] ; %ret = call i32 @potential_test13_callee(i32 0) @@ -612,13 +607,13 @@ ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; TUNIT-LABEL: define {{[^@]+}}@potential_test13_caller2 ; TUNIT-SAME: () #[[ATTR0]] { -; TUNIT-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 noundef 1) #[[ATTR1]], !range [[RNG0]] +; TUNIT-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 noundef 1) #[[ATTR1]] ; TUNIT-NEXT: ret i32 [[RET]] ; ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@potential_test13_caller2 ; CGSCC-SAME: () #[[ATTR1]] { -; CGSCC-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 noundef 1) #[[ATTR2]] +; CGSCC-NEXT: [[RET:%.*]] = call noundef i32 @potential_test13_callee(i32 noundef 1) #[[ATTR2]] ; CGSCC-NEXT: ret i32 [[RET]] ; %ret = call i32 @potential_test13_callee(i32 1) @@ -629,13 +624,13 @@ ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; TUNIT-LABEL: define {{[^@]+}}@potential_test13_caller3 ; TUNIT-SAME: () #[[ATTR0]] { -; TUNIT-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 undef) #[[ATTR1]], !range [[RNG0]] +; TUNIT-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 undef) #[[ATTR1]] ; TUNIT-NEXT: ret i32 [[RET]] ; ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@potential_test13_caller3 ; CGSCC-SAME: () #[[ATTR1]] { -; CGSCC-NEXT: [[RET:%.*]] = call i32 @potential_test13_callee(i32 undef) #[[ATTR2]] +; CGSCC-NEXT: [[RET:%.*]] = call noundef i32 @potential_test13_callee(i32 undef) #[[ATTR2]] ; CGSCC-NEXT: ret i32 [[RET]] ; %ret = call i32 @potential_test13_callee(i32 undef) @@ -665,10 +660,7 @@ ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CHECK-LABEL: define {{[^@]+}}@potential_test15 ; CHECK-SAME: (i1 [[C0:%.*]], i1 [[C1:%.*]]) #[[ATTR0]] { -; CHECK-NEXT: [[X0:%.*]] = select i1 [[C0]], i32 0, i32 1 -; CHECK-NEXT: [[X1:%.*]] = select i1 [[C1]], i32 [[X0]], i32 undef -; CHECK-NEXT: [[RET:%.*]] = icmp eq i32 [[X1]], 7 -; CHECK-NEXT: ret i1 [[RET]] +; CHECK-NEXT: ret i1 false ; %x0 = select i1 %c0, i32 0, i32 1 %x1 = select i1 %c1, i32 %x0, i32 undef @@ -680,9 +672,7 @@ ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CHECK-LABEL: define {{[^@]+}}@potential_test16 ; CHECK-SAME: (i1 [[C0:%.*]], i1 [[C1:%.*]]) #[[ATTR0]] { -; CHECK-NEXT: [[X1:%.*]] = select i1 [[C1]], i32 0, i32 1 -; CHECK-NEXT: [[RET:%.*]] = icmp eq i32 [[X1]], 7 -; CHECK-NEXT: ret i1 [[RET]] +; CHECK-NEXT: ret i1 false ; %x0 = select i1 %c0, i32 0, i32 undef %x1 = select i1 %c1, i32 %x0, i32 1 @@ -700,5 +690,4 @@ ;. ; TUNIT: [[RNG0]] = !{i32 0, i32 2} ; TUNIT: [[RNG1]] = !{i32 0, i32 3} -; TUNIT: [[RNG2]] = !{i32 -1, i32 1} ;. Index: llvm/test/Transforms/Attributor/range.ll =================================================================== --- llvm/test/Transforms/Attributor/range.ll +++ llvm/test/Transforms/Attributor/range.ll @@ -826,25 +826,13 @@ ; TUNIT-LABEL: define {{[^@]+}}@select ; TUNIT-SAME: (i32 [[A:%.*]]) local_unnamed_addr #[[ATTR1]] { ; TUNIT-NEXT: entry: -; TUNIT-NEXT: [[CMP:%.*]] = icmp sgt i32 [[A]], 5 -; TUNIT-NEXT: [[DOT:%.*]] = select i1 [[CMP]], i32 1, i32 2 -; TUNIT-NEXT: [[CMP1:%.*]] = icmp sgt i32 [[A]], 10 -; TUNIT-NEXT: [[Y_0_V:%.*]] = select i1 [[CMP1]], i32 1, i32 2 -; TUNIT-NEXT: [[Y_0:%.*]] = add nuw nsw i32 [[DOT]], [[Y_0_V]] -; TUNIT-NEXT: [[CMP6:%.*]] = icmp eq i32 [[Y_0]], 5 -; TUNIT-NEXT: ret i1 [[CMP6]] +; TUNIT-NEXT: ret i1 false ; ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@select ; CGSCC-SAME: (i32 [[A:%.*]]) local_unnamed_addr #[[ATTR2]] { ; CGSCC-NEXT: entry: -; CGSCC-NEXT: [[CMP:%.*]] = icmp sgt i32 [[A]], 5 -; CGSCC-NEXT: [[DOT:%.*]] = select i1 [[CMP]], i32 1, i32 2 -; CGSCC-NEXT: [[CMP1:%.*]] = icmp sgt i32 [[A]], 10 -; CGSCC-NEXT: [[Y_0_V:%.*]] = select i1 [[CMP1]], i32 1, i32 2 -; CGSCC-NEXT: [[Y_0:%.*]] = add nuw nsw i32 [[DOT]], [[Y_0_V]] -; CGSCC-NEXT: [[CMP6:%.*]] = icmp eq i32 [[Y_0]], 5 -; CGSCC-NEXT: ret i1 [[CMP6]] +; CGSCC-NEXT: ret i1 false ; entry: %cmp = icmp sgt i32 %a, 5 @@ -861,27 +849,13 @@ ; TUNIT-LABEL: define {{[^@]+}}@select_zext ; TUNIT-SAME: (i32 [[A:%.*]]) local_unnamed_addr #[[ATTR1]] { ; TUNIT-NEXT: entry: -; TUNIT-NEXT: [[CMP:%.*]] = icmp sgt i32 [[A]], 5 -; TUNIT-NEXT: [[DOT:%.*]] = select i1 [[CMP]], i32 1, i32 2 -; TUNIT-NEXT: [[CMP1:%.*]] = icmp sgt i32 [[A]], 10 -; TUNIT-NEXT: [[Y_0_V:%.*]] = select i1 [[CMP1]], i32 1, i32 2 -; TUNIT-NEXT: [[Y_0:%.*]] = add nuw nsw i32 [[DOT]], [[Y_0_V]] -; TUNIT-NEXT: [[CMP6:%.*]] = icmp eq i32 [[Y_0]], 5 -; TUNIT-NEXT: [[DOT13:%.*]] = zext i1 [[CMP6]] to i32 -; TUNIT-NEXT: ret i32 [[DOT13]] +; TUNIT-NEXT: ret i32 0 ; ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@select_zext ; CGSCC-SAME: (i32 [[A:%.*]]) local_unnamed_addr #[[ATTR2]] { ; CGSCC-NEXT: entry: -; CGSCC-NEXT: [[CMP:%.*]] = icmp sgt i32 [[A]], 5 -; CGSCC-NEXT: [[DOT:%.*]] = select i1 [[CMP]], i32 1, i32 2 -; CGSCC-NEXT: [[CMP1:%.*]] = icmp sgt i32 [[A]], 10 -; CGSCC-NEXT: [[Y_0_V:%.*]] = select i1 [[CMP1]], i32 1, i32 2 -; CGSCC-NEXT: [[Y_0:%.*]] = add nuw nsw i32 [[DOT]], [[Y_0_V]] -; CGSCC-NEXT: [[CMP6:%.*]] = icmp eq i32 [[Y_0]], 5 -; CGSCC-NEXT: [[DOT13:%.*]] = zext i1 [[CMP6]] to i32 -; CGSCC-NEXT: ret i32 [[DOT13]] +; CGSCC-NEXT: ret i32 0 ; entry: %cmp = icmp sgt i32 %a, 5 @@ -1649,8 +1623,7 @@ ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@non_zero ; CGSCC-SAME: (i8 [[V:%.*]]) #[[ATTR2]] { -; CGSCC-NEXT: [[R:%.*]] = icmp ne i8 [[V]], 0 -; CGSCC-NEXT: ret i1 [[R]] +; CGSCC-NEXT: ret i1 true ; %r = icmp ne i8 %v, 0 ret i1 %r @@ -1677,7 +1650,7 @@ ; CGSCC-NEXT: [[C:%.*]] = icmp slt i8 0, [[L]] ; CGSCC-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] ; CGSCC: t: -; CGSCC-NEXT: [[R:%.*]] = call i1 @non_zero(i8 [[L]]) #[[ATTR6]] +; CGSCC-NEXT: [[R:%.*]] = call noundef i1 @non_zero(i8 [[L]]) #[[ATTR6]] ; CGSCC-NEXT: ret i1 [[R]] ; CGSCC: f: ; CGSCC-NEXT: ret i1 false Index: llvm/test/Transforms/Attributor/readattrs.ll =================================================================== --- llvm/test/Transforms/Attributor/readattrs.ll +++ llvm/test/Transforms/Attributor/readattrs.ll @@ -115,7 +115,7 @@ ; TUNIT-NEXT: store i32 10, ptr [[P]], align 4 ; TUNIT-NEXT: ret void ; -; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write) +; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: write) ; CGSCC-LABEL: define {{[^@]+}}@test8_2 ; CGSCC-SAME: (ptr nofree writeonly [[P:%.*]]) #[[ATTR4:[0-9]+]] { ; CGSCC-NEXT: entry: @@ -423,7 +423,7 @@ ; CGSCC: attributes #[[ATTR1]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } ; CGSCC: attributes #[[ATTR2]] = { memory(read) } ; CGSCC: attributes #[[ATTR3]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) } -; CGSCC: attributes #[[ATTR4]] = { mustprogress nofree nosync nounwind willreturn memory(write) } +; CGSCC: attributes #[[ATTR4]] = { mustprogress nofree nosync nounwind willreturn memory(argmem: write) } ; CGSCC: attributes #[[ATTR5:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(write) } ; CGSCC: attributes #[[ATTR6:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(read) } ; CGSCC: attributes #[[ATTR7]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(read) } Index: llvm/test/Transforms/Attributor/returned.ll =================================================================== --- llvm/test/Transforms/Attributor/returned.ll +++ llvm/test/Transforms/Attributor/returned.ll @@ -1340,7 +1340,7 @@ ; We can use the alignment information of the weak function non_exact_3 argument ; because it was given to us and not derived. ; We can use the return information of the weak function non_exact_4. -; FIXME: %c2 and %c3 should be replaced but not %c0 or %c1! +; %c2 and %c3 should be replaced but not %c0 or %c1! define i32 @exact(ptr align 8 %a, ptr align 8 %b) { ; CHECK-LABEL: define {{[^@]+}}@exact ; CHECK-SAME: (ptr align 8 [[A:%.*]], ptr align 8 [[B:%.*]]) { @@ -1349,10 +1349,10 @@ ; CHECK-NEXT: [[C2:%.*]] = call i32 @non_exact_2(i32 noundef 2) ; CHECK-NEXT: [[C3:%.*]] = call align 32 ptr @non_exact_3(ptr align 32 [[A]]) ; CHECK-NEXT: [[C4:%.*]] = call align 16 ptr @non_exact_4(ptr align 32 [[B]]) -; CHECK-NEXT: [[C3L:%.*]] = load i32, ptr [[C3]], align 32 +; CHECK-NEXT: [[C3L:%.*]] = load i32, ptr [[A]], align 32 ; CHECK-NEXT: [[C4L:%.*]] = load i32, ptr [[C4]], align 16 ; CHECK-NEXT: [[ADD1:%.*]] = add i32 [[C0]], [[C1]] -; CHECK-NEXT: [[ADD2:%.*]] = add i32 [[ADD1]], [[C2]] +; CHECK-NEXT: [[ADD2:%.*]] = add i32 [[ADD1]], 2 ; CHECK-NEXT: [[ADD3:%.*]] = add i32 [[ADD2]], [[C3L]] ; CHECK-NEXT: [[ADD4:%.*]] = add i32 [[ADD3]], [[C4L]] ; CHECK-NEXT: ret i32 [[ADD4]] Index: llvm/test/Transforms/Attributor/value-simplify.ll =================================================================== --- llvm/test/Transforms/Attributor/value-simplify.ll +++ llvm/test/Transforms/Attributor/value-simplify.ll @@ -1066,7 +1066,7 @@ ; TUNIT-LABEL: define {{[^@]+}}@test ; TUNIT-SAME: (i1 [[C:%.*]]) { ; TUNIT-NEXT: [[R1:%.*]] = call i32 @ctx_test1(i1 noundef [[C]]) -; TUNIT-NEXT: [[R2:%.*]] = call i32 @ctx_test2(i1 noundef [[C]]) +; TUNIT-NEXT: [[R2:%.*]] = call i32 @ctx_test2(i1 noundef [[C]]), !range [[RNG0:![0-9]+]] ; TUNIT-NEXT: [[ADD:%.*]] = add i32 [[R1]], [[R2]] ; TUNIT-NEXT: ret i32 [[ADD]] ; @@ -1150,9 +1150,7 @@ ; TUNIT: t: ; TUNIT-NEXT: br label [[F]] ; TUNIT: f: -; TUNIT-NEXT: [[P:%.*]] = phi i1 [ true, [[ENTRY:%.*]] ], [ false, [[T]] ] -; TUNIT-NEXT: [[RC1:%.*]] = call i1 @ret(i1 noundef [[P]]) #[[ATTR9]] -; TUNIT-NEXT: ret i1 [[RC1]] +; TUNIT-NEXT: ret i1 false ; ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@test_liveness @@ -1177,16 +1175,16 @@ } define internal i1 @ret(i1 %c) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define {{[^@]+}}@ret -; CHECK-SAME: (i1 noundef [[C:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] -; CHECK: t: -; CHECK-NEXT: br label [[F]] -; CHECK: f: -; CHECK-NEXT: [[P:%.*]] = phi i1 [ [[C]], [[ENTRY:%.*]] ], [ false, [[T]] ] -; CHECK-NEXT: ret i1 [[P]] +; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; CGSCC-LABEL: define {{[^@]+}}@ret +; CGSCC-SAME: (i1 noundef [[C:%.*]]) #[[ATTR1]] { +; CGSCC-NEXT: entry: +; CGSCC-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; CGSCC: t: +; CGSCC-NEXT: br label [[F]] +; CGSCC: f: +; CGSCC-NEXT: [[P:%.*]] = phi i1 [ [[C]], [[ENTRY:%.*]] ], [ false, [[T]] ] +; CGSCC-NEXT: ret i1 false ; entry: br i1 %c, label %t, label %f @@ -1471,3 +1469,5 @@ ; CGSCC: attributes #[[ATTR15]] = { nofree willreturn memory(readwrite) } ; CGSCC: attributes #[[ATTR16]] = { nounwind } ;. +; TUNIT: [[RNG0]] = !{i32 0, i32 -2147483648} +;.