From 23f41f16d415cdcd9b3f6e7d31aa27caabf844e6 Mon Sep 17 00:00:00 2001 From: Johannes Doerfert Date: Sun, 12 Jan 2020 01:09:22 -0600 Subject: [PATCH] [Attributor] Use fine-grained liveness in all helpers We used coarse-grained liveness before, thus we looked if the instruction was executed, but we did not use fine-grained liveness, hence if the instruction was needed or could be deleted even if the surrounding ones are live. This patches introduces this level of liveness checks together with other liveness queries, e.g., for uses. For more control we enforce that all liveness queries go through the Attributor. Test have been adjusted to reflect the changes or augmented to prevent deletion of the parts we want to check. Reviewed By: sstefan1 Differential Revision: https://reviews.llvm.org/D73313 --- llvm/include/llvm/Transforms/IPO/Attributor.h | 41 ++- llvm/lib/Transforms/IPO/Attributor.cpp | 300 +++++++++++---------- .../ArgumentPromotion/2008-02-01-ReturnAttrs.ll | 2 +- .../ArgumentPromotion/2008-07-02-array-indexing.ll | 2 +- .../ArgumentPromotion/aggregate-promote.ll | 2 +- .../Attributor/ArgumentPromotion/basictest.ll | 2 +- .../Attributor/ArgumentPromotion/byval.ll | 14 +- .../Attributor/ArgumentPromotion/chained.ll | 2 +- .../Attributor/ArgumentPromotion/control-flow.ll | 2 +- .../Attributor/ArgumentPromotion/control-flow2.ll | 2 +- .../Transforms/Attributor/ArgumentPromotion/dbg.ll | 2 +- .../Attributor/ArgumentPromotion/inalloca.ll | 4 +- .../ArgumentPromotion/live_called_from_dead.ll | 22 +- .../ArgumentPromotion/live_called_from_dead_2.ll | 129 +++++++++ .../Attributor/ArgumentPromotion/musttail.ll | 4 +- .../Attributor/ArgumentPromotion/reserve-tbaa.ll | 16 +- .../IPConstantProp/2009-09-24-byval-ptr.ll | 2 +- .../Attributor/IPConstantProp/PR43857.ll | 1 - .../Attributor/IPConstantProp/recursion.ll | 2 +- .../Attributor/IPConstantProp/return-argument.ll | 2 +- .../Attributor/IPConstantProp/return-constants.ll | 40 ++- .../Transforms/Attributor/dereferenceable-2.ll | 36 +-- .../test/Transforms/Attributor/internal-noalias.ll | 2 +- llvm/test/Transforms/Attributor/liveness.ll | 4 +- llvm/test/Transforms/Attributor/liveness_chains.ll | 58 ++++ llvm/test/Transforms/Attributor/misc.ll | 6 +- llvm/test/Transforms/Attributor/nocapture-1.ll | 6 +- llvm/test/Transforms/Attributor/nofree.ll | 10 +- llvm/test/Transforms/Attributor/noreturn.ll | 2 +- llvm/test/Transforms/Attributor/nosync.ll | 9 +- llvm/test/Transforms/Attributor/range.ll | 6 +- llvm/test/Transforms/Attributor/returned.ll | 8 +- .../Transforms/Attributor/undefined_behavior.ll | 7 +- llvm/test/Transforms/Attributor/value-simplify.ll | 10 +- llvm/test/Transforms/Attributor/willreturn.ll | 18 +- 35 files changed, 530 insertions(+), 245 deletions(-) create mode 100644 llvm/test/Transforms/Attributor/ArgumentPromotion/live_called_from_dead_2.ll create mode 100644 llvm/test/Transforms/Attributor/liveness_chains.ll diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h index 8437ed2..1835b3b 100644 --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -852,14 +852,41 @@ struct Attributor { /// Return true if \p AA (or its context instruction) is assumed dead. /// /// If \p LivenessAA is not provided it is queried. - bool isAssumedDead(const AbstractAttribute &AA, const AAIsDead *LivenessAA); + bool isAssumedDead(const AbstractAttribute &AA, const AAIsDead *LivenessAA, + bool CheckBBLivenessOnly = false, + DepClassTy DepClass = DepClassTy::OPTIONAL); + + /// Return true if \p I is assumed dead. + /// + /// If \p LivenessAA is not provided it is queried. + bool isAssumedDead(const Instruction &I, const AbstractAttribute *QueryingAA, + const AAIsDead *LivenessAA, + bool CheckBBLivenessOnly = false, + DepClassTy DepClass = DepClassTy::OPTIONAL); + + /// Return true if \p U is assumed dead. + /// + /// If \p FnLivenessAA is not provided it is queried. + bool isAssumedDead(const Use &U, const AbstractAttribute *QueryingAA, + const AAIsDead *FnLivenessAA, + bool CheckBBLivenessOnly = false, + DepClassTy DepClass = DepClassTy::OPTIONAL); + + /// Return true if \p IRP is assumed dead. + /// + /// If \p FnLivenessAA is not provided it is queried. + bool isAssumedDead(const IRPosition &IRP, const AbstractAttribute *QueryingAA, + const AAIsDead *FnLivenessAA, + bool CheckBBLivenessOnly = false, + DepClassTy DepClass = DepClassTy::OPTIONAL); /// Check \p Pred on all (transitive) uses of \p V. /// /// This method will evaluate \p Pred on all (transitive) uses of the /// associated value and return true if \p Pred holds every time. bool checkForAllUses(const function_ref &Pred, - const AbstractAttribute &QueryingAA, const Value &V); + const AbstractAttribute &QueryingAA, const Value &V, + DepClassTy LivenessDepClass = DepClassTy::OPTIONAL); /// Helper struct used in the communication between an abstract attribute (AA) /// that wants to change the signature of a function and the Attributor which @@ -997,7 +1024,8 @@ struct Attributor { /// present in \p Opcode and return true if \p Pred holds on all of them. bool checkForAllInstructions(const function_ref &Pred, const AbstractAttribute &QueryingAA, - const ArrayRef &Opcodes); + const ArrayRef &Opcodes, + bool CheckBBLivenessOnly = false); /// Check \p Pred on all call-like instructions (=CallBased derived). /// @@ -2109,6 +2137,10 @@ struct AAIsDead : public StateWrapper, public IRPosition { AAIsDead(const IRPosition &IRP) : IRPosition(IRP) {} +protected: + /// The query functions are protected such that other attributes need to go + /// through the Attributor interfaces: `Attributor::isAssumedDead(...)` + /// Returns true if the underlying value is assumed dead. virtual bool isAssumedDead() const = 0; @@ -2141,6 +2173,7 @@ struct AAIsDead : public StateWrapper, return false; } +public: /// Return an IR position, see struct IRPosition. const IRPosition &getIRPosition() const override { return *this; } @@ -2149,6 +2182,8 @@ struct AAIsDead : public StateWrapper, /// Unique ID (due to the unique address) static const char ID; + + friend struct Attributor; }; /// State for dereferenceable attribute diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp index d53e8e4..0b985db 100644 --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -443,7 +443,9 @@ static bool genericValueTraversal( "Expected liveness in the presence of instructions!"); for (unsigned u = 0, e = PHI->getNumIncomingValues(); u < e; u++) { const BasicBlock *IncomingBB = PHI->getIncomingBlock(u); - if (LivenessAA->isAssumedDead(IncomingBB->getTerminator())) { + if (A.isAssumedDead(*IncomingBB->getTerminator(), &QueryingAA, + LivenessAA, + /* CheckBBLivenessOnly */ true)) { AnyDead = true; continue; } @@ -2230,8 +2232,10 @@ struct AAUndefinedBehaviorImpl : public AAUndefinedBehavior { A.checkForAllInstructions(InspectMemAccessInstForUB, *this, {Instruction::Load, Instruction::Store, Instruction::AtomicCmpXchg, - Instruction::AtomicRMW}); - A.checkForAllInstructions(InspectBrInstForUB, *this, {Instruction::Br}); + Instruction::AtomicRMW}, + /* CheckBBLivenessOnly */ true); + A.checkForAllInstructions(InspectBrInstForUB, *this, {Instruction::Br}, + /* CheckBBLivenessOnly */ true); if (NoUBPrevSize != AssumedNoUBInsts.size() || UBPrevSize != KnownUBInsts.size()) return ChangeStatus::CHANGED; @@ -2829,27 +2833,13 @@ struct AAIsDeadValueImpl : public AAIsDead { } /// Check if all uses are assumed dead. - bool areAllUsesAssumedDead(Attributor &A) { - auto UsePred = [&](const Use &U, bool &Follow) { - Instruction *UserI = cast(U.getUser()); - if (CallSite CS = CallSite(UserI)) { - if (!CS.isArgOperand(&U)) - return false; - const IRPosition &CSArgPos = - IRPosition::callsite_argument(CS, CS.getArgumentNo(&U)); - const auto &CSArgIsDead = A.getAAFor(*this, CSArgPos); - return CSArgIsDead.isAssumedDead(); - } - if (ReturnInst *RI = dyn_cast(UserI)) { - const IRPosition &RetPos = IRPosition::returned(*RI->getFunction()); - const auto &RetIsDeadAA = A.getAAFor(*this, RetPos); - return RetIsDeadAA.isAssumedDead(); - } - Follow = true; - return wouldInstructionBeTriviallyDead(UserI); - }; - - return A.checkForAllUses(UsePred, *this, getAssociatedValue()); + bool areAllUsesAssumedDead(Attributor &A, Value &V) { + auto UsePred = [&](const Use &U, bool &Follow) { return false; }; + // Explicitly set the dependence class to required because we want a long + // chain of N dependent instructions to be considered live as soon as one is + // without going through N update cycles. This is not required for + // correctness. + return A.checkForAllUses(UsePred, *this, V, DepClassTy::REQUIRED); } /// Determine if \p I is assumed to be side-effect free. @@ -2895,7 +2885,7 @@ struct AAIsDeadFloating : public AAIsDeadValueImpl { if (!isAssumedSideEffectFree(A, I)) return indicatePessimisticFixpoint(); - if (!areAllUsesAssumedDead(A)) + if (!areAllUsesAssumedDead(A, getAssociatedValue())) return indicatePessimisticFixpoint(); return ChangeStatus::UNCHANGED; } @@ -3008,9 +2998,6 @@ struct AAIsDeadCallSiteReturned : public AAIsDeadFloating { return AAIsDeadFloating::isAssumedDead() && IsAssumedSideEffectFree; } - /// Return true if all users are assumed dead. - bool hasOnlyAssumedDeadUses() const { return getAssumed(); } - /// See AbstractAttribute::initialize(...). void initialize(Attributor &A) override { if (isa(getAssociatedValue())) { @@ -3030,7 +3017,7 @@ struct AAIsDeadCallSiteReturned : public AAIsDeadFloating { Changed = ChangeStatus::CHANGED; } - if (!areAllUsesAssumedDead(A)) + if (!areAllUsesAssumedDead(A, getAssociatedValue())) return indicatePessimisticFixpoint(); return Changed; } @@ -3068,16 +3055,10 @@ struct AAIsDeadReturned : public AAIsDeadValueImpl { /// See AbstractAttribute::updateImpl(...). ChangeStatus updateImpl(Attributor &A) override { - bool AllKnownDead = true; auto PredForCallSite = [&](AbstractCallSite ACS) { - if (ACS.isCallbackCall()) + if (ACS.isCallbackCall() || !ACS.getInstruction()) return false; - const IRPosition &CSRetPos = - IRPosition::callsite_returned(ACS.getCallSite()); - const auto &RetIsDeadAA = A.getAAFor(*this, CSRetPos); - AllKnownDead &= RetIsDeadAA.isKnownDead(); - return static_cast(RetIsDeadAA) - .hasOnlyAssumedDeadUses(); + return areAllUsesAssumedDead(A, *ACS.getInstruction()); }; bool AllCallSitesKnown; @@ -3085,9 +3066,6 @@ struct AAIsDeadReturned : public AAIsDeadValueImpl { AllCallSitesKnown)) return indicatePessimisticFixpoint(); - if (AllCallSitesKnown && AllKnownDead) - indicateOptimisticFixpoint(); - return ChangeStatus::UNCHANGED; } @@ -4238,12 +4216,8 @@ struct AACaptureUseTracker final : public CaptureTracker { /// See CaptureTracker::shouldExplore(...). bool shouldExplore(const Use *U) override { - // Check liveness, if it is used to stop exploring we need a dependence. - if (IsDeadAA.isAssumedDead(cast(U->getUser()))) { - A.recordDependence(IsDeadAA, NoCaptureAA, DepClassTy::OPTIONAL); - return false; - } - return true; + // Check liveness. + return !A.isAssumedDead(*U, &NoCaptureAA, &IsDeadAA); } /// Update the state according to \p CapturedInMem, \p CapturedInInt, and @@ -5940,12 +5914,10 @@ ChangeStatus AAMemoryBehaviorFloating::updateImpl(Attributor &A) { const Use *U = Uses[i]; Instruction *UserI = cast(U->getUser()); LLVM_DEBUG(dbgs() << "[AAMemoryBehavior] Use: " << **U << " in " << *UserI - << " [Dead: " << (LivenessAA.isAssumedDead(UserI)) + << " [Dead: " << (A.isAssumedDead(*U, this, &LivenessAA)) << "]\n"); - if (LivenessAA.isAssumedDead(UserI)) { - A.recordDependence(LivenessAA, *this, DepClassTy::OPTIONAL); + if (A.isAssumedDead(*U, this, &LivenessAA)) continue; - } // Check if the users of UserI should also be visited. if (followUsersOfUseIn(A, U, UserI)) @@ -6546,34 +6518,122 @@ struct AAValueConstantRangeCallSiteArgument : AAValueConstantRangeFloating { /// ---------------------------------------------------------------------------- bool Attributor::isAssumedDead(const AbstractAttribute &AA, - const AAIsDead *LivenessAA) { - const Instruction *CtxI = AA.getIRPosition().getCtxI(); - if (!CtxI || !Functions.count(const_cast(CtxI->getFunction()))) + const AAIsDead *FnLivenessAA, + bool CheckBBLivenessOnly, DepClassTy DepClass) { + const IRPosition &IRP = AA.getIRPosition(); + if (!Functions.count(IRP.getAnchorScope())) return false; + return isAssumedDead(IRP, &AA, FnLivenessAA, CheckBBLivenessOnly, DepClass); +} + +bool Attributor::isAssumedDead(const Use &U, + const AbstractAttribute *QueryingAA, + const AAIsDead *FnLivenessAA, + bool CheckBBLivenessOnly, DepClassTy DepClass) { + Instruction *UserI = dyn_cast(U.getUser()); + if (!UserI) + return isAssumedDead(IRPosition::value(*U.get()), QueryingAA, FnLivenessAA, + CheckBBLivenessOnly, DepClass); + + if (CallSite CS = CallSite(UserI)) { + // For call site argument uses we can check if the argument is + // unused/dead. + if (CS.isArgOperand(&U)) { + const IRPosition &CSArgPos = + IRPosition::callsite_argument(CS, CS.getArgumentNo(&U)); + return isAssumedDead(CSArgPos, QueryingAA, FnLivenessAA, + CheckBBLivenessOnly, DepClass); + } + } else if (ReturnInst *RI = dyn_cast(UserI)) { + const IRPosition &RetPos = IRPosition::returned(*RI->getFunction()); + return isAssumedDead(RetPos, QueryingAA, FnLivenessAA, CheckBBLivenessOnly, + DepClass); + } else if (PHINode *PHI = dyn_cast(UserI)) { + BasicBlock *IncomingBB = PHI->getIncomingBlock(U); + return isAssumedDead(*IncomingBB->getTerminator(), QueryingAA, FnLivenessAA, + CheckBBLivenessOnly, DepClass); + } + + return isAssumedDead(IRPosition::value(*UserI), QueryingAA, FnLivenessAA, + CheckBBLivenessOnly, DepClass); +} - // TODO: Find a good way to utilize fine and coarse grained liveness - // information. - if (!LivenessAA) - LivenessAA = - &getAAFor(AA, IRPosition::function(*CtxI->getFunction()), - /* TrackDependence */ false); +bool Attributor::isAssumedDead(const Instruction &I, + const AbstractAttribute *QueryingAA, + const AAIsDead *FnLivenessAA, + bool CheckBBLivenessOnly, DepClassTy DepClass) { + if (!FnLivenessAA) + FnLivenessAA = lookupAAFor(IRPosition::function(*I.getFunction()), + QueryingAA, + /* TrackDependence */ false); + + // If we have a context instruction and a liveness AA we use it. + if (FnLivenessAA && + FnLivenessAA->getIRPosition().getAnchorScope() == I.getFunction() && + FnLivenessAA->isAssumedDead(&I)) { + if (QueryingAA) + recordDependence(*FnLivenessAA, *QueryingAA, DepClass); + return true; + } + if (CheckBBLivenessOnly) + return false; + + const AAIsDead &IsDeadAA = getOrCreateAAFor( + IRPosition::value(I), QueryingAA, /* TrackDependence */ false); // Don't check liveness for AAIsDead. - if (&AA == LivenessAA) + if (QueryingAA == &IsDeadAA) return false; - if (!LivenessAA->isAssumedDead(CtxI)) + if (IsDeadAA.isAssumedDead()) { + if (QueryingAA) + recordDependence(IsDeadAA, *QueryingAA, DepClass); + return true; + } + + return false; +} + +bool Attributor::isAssumedDead(const IRPosition &IRP, + const AbstractAttribute *QueryingAA, + const AAIsDead *FnLivenessAA, + bool CheckBBLivenessOnly, DepClassTy DepClass) { + Instruction *CtxI = IRP.getCtxI(); + if (CtxI && + isAssumedDead(*CtxI, QueryingAA, FnLivenessAA, + /* CheckBBLivenessOnly */ true, + CheckBBLivenessOnly ? DepClass : DepClassTy::OPTIONAL)) + return true; + + if (CheckBBLivenessOnly) return false; - // We actually used liveness information so we have to record a dependence. - recordDependence(*LivenessAA, AA, DepClassTy::OPTIONAL); + // If we haven't succeeded we query the specific liveness info for the IRP. + const AAIsDead *IsDeadAA; + if (IRP.getPositionKind() == IRPosition::IRP_CALL_SITE) + IsDeadAA = &getOrCreateAAFor( + IRPosition::callsite_returned(cast(IRP.getAssociatedValue())), + QueryingAA, /* TrackDependence */ false); + else + IsDeadAA = &getOrCreateAAFor(IRP, QueryingAA, + /* TrackDependence */ false); + // Don't check liveness for AAIsDead. + if (QueryingAA == IsDeadAA) + return false; - return true; + if (IsDeadAA->isAssumedDead()) { + if (QueryingAA) + recordDependence(*IsDeadAA, *QueryingAA, DepClass); + return true; + } + + return false; } bool Attributor::checkForAllUses( const function_ref &Pred, - const AbstractAttribute &QueryingAA, const Value &V) { + const AbstractAttribute &QueryingAA, const Value &V, + DepClassTy LivenessDepClass) { // Check the trivial case first as it catches void values. if (V.use_empty()) @@ -6602,7 +6662,6 @@ bool Attributor::checkForAllUses( LLVM_DEBUG(dbgs() << "[Attributor] Got " << Worklist.size() << " initial uses to check\n"); - bool AnyDead = false; const Function *ScopeFn = IRP.getAnchorScope(); const auto *LivenessAA = ScopeFn ? &getAAFor(QueryingAA, IRPosition::function(*ScopeFn), @@ -6613,26 +6672,12 @@ bool Attributor::checkForAllUses( const Use *U = Worklist.pop_back_val(); if (!Visited.insert(U).second) continue; - LLVM_DEBUG(dbgs() << "[Attributor] Check use: " << **U << " [" << LivenessAA - << "]\n"); - if (LivenessAA) { - if (Instruction *UserI = dyn_cast(U->getUser())) { - if (LivenessAA->isAssumedDead(UserI)) { - LLVM_DEBUG(dbgs() << "[Attributor] Dead user: " << *UserI << ": " - << *LivenessAA << "\n"); - AnyDead = true; - continue; - } - if (PHINode *PHI = dyn_cast(UserI)) { - BasicBlock *IncomingBB = PHI->getIncomingBlock(*U); - if (LivenessAA->isAssumedDead(IncomingBB->getTerminator())) { - LLVM_DEBUG(dbgs() << "[Attributor] Dead user: " << *UserI << ": " - << *LivenessAA << "\n"); - AnyDead = true; - continue; - } - } - } + LLVM_DEBUG(dbgs() << "[Attributor] Check use: " << **U << " in " + << *U->getUser() << "\n"); + if (isAssumedDead(*U, &QueryingAA, LivenessAA, + /* CheckBBLivenessOnly */ false, LivenessDepClass)) { + LLVM_DEBUG(dbgs() << "[Attributor] Dead use, skip!\n"); + continue; } bool Follow = false; @@ -6644,9 +6689,6 @@ bool Attributor::checkForAllUses( Worklist.push_back(&UU); } - if (AnyDead) - recordDependence(*LivenessAA, QueryingAA, DepClassTy::OPTIONAL); - return true; } @@ -6687,6 +6729,13 @@ bool Attributor::checkForAllCallSites( AllCallSitesKnown = RequireAllCallSites; for (const Use &U : Fn.uses()) { + LLVM_DEBUG(dbgs() << "[Attributor] Check use: " << *U << " in " + << *U.getUser() << "\n"); + if (isAssumedDead(U, QueryingAA, nullptr, /* CheckBBLivenessOnly */ true)) { + LLVM_DEBUG(dbgs() << "[Attributor] Dead use, skip!\n"); + continue; + } + AbstractCallSite ACS(&U); if (!ACS) { LLVM_DEBUG(dbgs() << "[Attributor] Function " << Fn.getName() @@ -6698,23 +6747,6 @@ bool Attributor::checkForAllCallSites( return false; } - Instruction *I = ACS.getInstruction(); - Function *Caller = I->getFunction(); - - const auto *LivenessAA = - lookupAAFor(IRPosition::function(*Caller), QueryingAA, - /* TrackDependence */ false); - - // Skip dead calls. - if (LivenessAA && LivenessAA->isAssumedDead(I)) { - // We actually used liveness information so we have to record a - // dependence. - if (QueryingAA) - recordDependence(*LivenessAA, *QueryingAA, DepClassTy::OPTIONAL); - AllCallSitesKnown = false; - continue; - } - const Use *EffectiveUse = ACS.isCallbackCall() ? &ACS.getCalleeUseForCallback() : &U; if (!ACS.isCallee(EffectiveUse)) { @@ -6780,18 +6812,17 @@ bool Attributor::checkForAllReturnedValues( }); } -static bool -checkForAllInstructionsImpl(InformationCache::OpcodeInstMapTy &OpcodeInstMap, - const function_ref &Pred, - const AAIsDead *LivenessAA, bool &AnyDead, - const ArrayRef &Opcodes) { +static bool checkForAllInstructionsImpl( + Attributor *A, InformationCache::OpcodeInstMapTy &OpcodeInstMap, + const function_ref &Pred, + const AbstractAttribute *QueryingAA, const AAIsDead *LivenessAA, + const ArrayRef &Opcodes, bool CheckBBLivenessOnly = false) { for (unsigned Opcode : Opcodes) { for (Instruction *I : OpcodeInstMap[Opcode]) { // Skip dead instructions. - if (LivenessAA && LivenessAA->isAssumedDead(I)) { - AnyDead = true; + if (A && A->isAssumedDead(IRPosition::value(*I), QueryingAA, LivenessAA, + CheckBBLivenessOnly)) continue; - } if (!Pred(*I)) return false; @@ -6802,7 +6833,8 @@ checkForAllInstructionsImpl(InformationCache::OpcodeInstMapTy &OpcodeInstMap, bool Attributor::checkForAllInstructions( const llvm::function_ref &Pred, - const AbstractAttribute &QueryingAA, const ArrayRef &Opcodes) { + const AbstractAttribute &QueryingAA, const ArrayRef &Opcodes, + bool CheckBBLivenessOnly) { const IRPosition &IRP = QueryingAA.getIRPosition(); // Since we need to provide instructions we have to have an exact definition. @@ -6814,18 +6846,13 @@ bool Attributor::checkForAllInstructions( const IRPosition &QueryIRP = IRPosition::function(*AssociatedFunction); const auto &LivenessAA = getAAFor(QueryingAA, QueryIRP, /* TrackDependence */ false); - bool AnyDead = false; auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(*AssociatedFunction); - if (!checkForAllInstructionsImpl(OpcodeInstMap, Pred, &LivenessAA, AnyDead, - Opcodes)) + if (!checkForAllInstructionsImpl(this, OpcodeInstMap, Pred, &QueryingAA, + &LivenessAA, Opcodes, CheckBBLivenessOnly)) return false; - // If we actually used liveness information so we have to record a dependence. - if (AnyDead) - recordDependence(LivenessAA, QueryingAA, DepClassTy::OPTIONAL); - return true; } @@ -6842,24 +6869,17 @@ bool Attributor::checkForAllReadWriteInstructions( const IRPosition &QueryIRP = IRPosition::function(*AssociatedFunction); const auto &LivenessAA = getAAFor(QueryingAA, QueryIRP, /* TrackDependence */ false); - bool AnyDead = false; for (Instruction *I : InfoCache.getReadOrWriteInstsForFunction(*AssociatedFunction)) { // Skip dead instructions. - if (LivenessAA.isAssumedDead(I)) { - AnyDead = true; + if (isAssumedDead(IRPosition::value(*I), &QueryingAA, &LivenessAA)) continue; - } if (!Pred(*I)) return false; } - // If we actually used liveness information so we have to record a dependence. - if (AnyDead) - recordDependence(LivenessAA, QueryingAA, DepClassTy::OPTIONAL); - return true; } @@ -6942,7 +6962,8 @@ ChangeStatus Attributor::run() { // Update all abstract attribute in the work list and record the ones that // changed. for (AbstractAttribute *AA : Worklist) - if (!AA->getState().isAtFixpoint() && !isAssumedDead(*AA, nullptr)) { + if (!AA->getState().isAtFixpoint() && + !isAssumedDead(*AA, nullptr, /* CheckBBLivenessOnly */ true)) { QueriedNonFixAA = false; if (AA->update(*this) == ChangeStatus::CHANGED) { ChangedAAs.push_back(AA); @@ -7027,7 +7048,7 @@ ChangeStatus Attributor::run() { continue; // Skip dead code. - if (isAssumedDead(*AA, nullptr)) + if (isAssumedDead(*AA, nullptr, /* CheckBBLivenessOnly */ true)) continue; // Manifest the state and record if we changed the IR. ChangeStatus LocalChange = AA->manifest(*this); @@ -7250,10 +7271,9 @@ bool Attributor::isValidFunctionSignatureRewrite( // Forbid must-tail calls for now. // TODO: - bool AnyDead; auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(*Fn); - if (!checkForAllInstructionsImpl(OpcodeInstMap, InstPred, nullptr, AnyDead, - {Instruction::Call})) { + if (!checkForAllInstructionsImpl(nullptr, OpcodeInstMap, InstPred, nullptr, + nullptr, {Instruction::Call})) { LLVM_DEBUG(dbgs() << "[Attributor] Cannot rewrite due to instructions\n"); return false; } @@ -7712,13 +7732,13 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) { }; auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F); - bool Success, AnyDead = false; + bool Success; Success = checkForAllInstructionsImpl( - OpcodeInstMap, CallSitePred, nullptr, AnyDead, + nullptr, OpcodeInstMap, CallSitePred, nullptr, nullptr, {(unsigned)Instruction::Invoke, (unsigned)Instruction::CallBr, (unsigned)Instruction::Call}); (void)Success; - assert(Success && !AnyDead && "Expected the check call to be successful!"); + assert(Success && "Expected the check call to be successful!"); auto LoadStorePred = [&](Instruction &I) -> bool { if (isa(I)) @@ -7730,10 +7750,10 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) { return true; }; Success = checkForAllInstructionsImpl( - OpcodeInstMap, LoadStorePred, nullptr, AnyDead, + nullptr, OpcodeInstMap, LoadStorePred, nullptr, nullptr, {(unsigned)Instruction::Load, (unsigned)Instruction::Store}); (void)Success; - assert(Success && !AnyDead && "Expected the check call to be successful!"); + assert(Success && "Expected the check call to be successful!"); } /// Helpers to ease debugging through output streams and print calls. diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/2008-02-01-ReturnAttrs.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/2008-02-01-ReturnAttrs.ll index ddb115e..d6a11193a 100644 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/2008-02-01-ReturnAttrs.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/2008-02-01-ReturnAttrs.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 -S < %s | FileCheck %s +; RUN: opt -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=6 -S < %s | FileCheck %s define internal i32 @deref(i32* %x) nounwind { ; CHECK-LABEL: define {{[^@]+}}@deref diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/2008-07-02-array-indexing.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/2008-07-02-array-indexing.ll index 982100d..717181a 100644 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/2008-07-02-array-indexing.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/2008-07-02-array-indexing.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 -S < %s | FileCheck %s +; RUN: opt -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 -S < %s | FileCheck %s ; PR2498 ; This test tries to convince CHECK about promoting the load from %A + 2, diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/aggregate-promote.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/aggregate-promote.ll index 449b05e..dc71592 100644 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/aggregate-promote.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/aggregate-promote.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -disable-output -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 < %s +; RUN: opt -disable-output -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s %T = type { i32, i32, i32, i32 } @G = constant %T { i32 0, i32 0, i32 17, i32 25 } diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/basictest.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/basictest.ll index 45721e55..7739ad7 100644 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/basictest.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/basictest.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -S -passes='attributor' -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=6 < %s | FileCheck %s +; RUN: opt -S -passes='attributor' -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=7 < %s | FileCheck %s target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" define internal i32 @test(i32* %X, i32* %Y) { diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/byval.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/byval.ll index 104d71a..917da39 100644 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/byval.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/byval.ll @@ -61,15 +61,15 @@ define i32 @main() nounwind { ; CHECK-NEXT: store i32 1, i32* [[TMP1]], align 8 ; CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 ; CHECK-NEXT: store i64 2, i64* [[TMP4]], align 4 -; CHECK-NEXT: [[S_CAST:%.*]] = bitcast %struct.ss* [[S]] to i32* -; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[S_CAST]], align 1 -; CHECK-NEXT: [[S_0_1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 -; CHECK-NEXT: [[TMP1:%.*]] = load i64, i64* [[S_0_1]], align 1 -; CHECK-NEXT: call void @f(i32 [[TMP0]], i64 [[TMP1]]) ; CHECK-NEXT: [[S_CAST1:%.*]] = bitcast %struct.ss* [[S]] to i32* -; CHECK-NEXT: [[TMP2:%.*]] = load i32, i32* [[S_CAST1]], align 1 +; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[S_CAST1]], align 1 ; CHECK-NEXT: [[S_0_12:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 -; CHECK-NEXT: [[TMP3:%.*]] = load i64, i64* [[S_0_12]], align 1 +; CHECK-NEXT: [[TMP1:%.*]] = load i64, i64* [[S_0_12]], align 1 +; CHECK-NEXT: call void @f(i32 [[TMP0]], i64 [[TMP1]]) +; CHECK-NEXT: [[S_CAST:%.*]] = bitcast %struct.ss* [[S]] to i32* +; CHECK-NEXT: [[TMP2:%.*]] = load i32, i32* [[S_CAST]], align 1 +; CHECK-NEXT: [[S_0_1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 +; CHECK-NEXT: [[TMP3:%.*]] = load i64, i64* [[S_0_1]], align 1 ; CHECK-NEXT: call void @g(i32 [[TMP2]], i64 [[TMP3]]) ; CHECK-NEXT: ret i32 0 ; diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/chained.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/chained.ll index 15ddb21..f690e77 100644 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/chained.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/chained.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -S -passes='attributor' -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 < %s | FileCheck %s +; RUN: opt -S -passes='attributor' -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s @G1 = constant i32 0 @G2 = constant i32* @G1 diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/control-flow.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/control-flow.ll index 494107d..944a12a 100644 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/control-flow.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/control-flow.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -S -passes='attributor' -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 < %s | FileCheck %s +; RUN: opt -S -passes='attributor' -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 < %s | FileCheck %s ; Don't promote around control flow. define internal i32 @callee(i1 %C, i32* %P) { diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/control-flow2.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/control-flow2.ll index 9ee1386..5636a0a 100644 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/control-flow2.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/control-flow2.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -S -passes='attributor' -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=9 < %s | FileCheck %s +; RUN: opt -S -passes='attributor' -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=7 < %s | FileCheck %s target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll index 94dad37..43ee9dd 100644 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll @@ -30,7 +30,7 @@ define internal void @test_byval(%struct.pair* byval %P) { define void @caller(i32** %Y, %struct.pair* %P) { ; CHECK-LABEL: define {{[^@]+}}@caller -; CHECK-SAME: (i32** nocapture readonly [[Y:%.*]], %struct.pair* nocapture nofree readonly [[P:%.*]]) +; CHECK-SAME: (i32** nocapture readonly [[Y:%.*]], %struct.pair* nocapture nofree readnone [[P:%.*]]) ; CHECK-NEXT: call void @test(i32** nocapture readonly align 8 [[Y]]), !dbg !4 ; CHECK-NEXT: call void @test_byval(), !dbg !5 ; CHECK-NEXT: ret void diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/inalloca.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/inalloca.ll index 4954084..c9598b3 100644 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/inalloca.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/inalloca.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 < %s | FileCheck %s --check-prefixes=ATTRIBUTOR -; RUN: opt -S -passes='globalopt,attributor' -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 < %s | FileCheck %s --check-prefixes=GLOBALOPT_ATTRIBUTOR +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=6 < %s | FileCheck %s --check-prefixes=ATTRIBUTOR +; RUN: opt -S -passes='globalopt,attributor' -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=6 < %s | FileCheck %s --check-prefixes=GLOBALOPT_ATTRIBUTOR target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/live_called_from_dead.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/live_called_from_dead.ll index 56329414..022d0f9 100644 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/live_called_from_dead.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/live_called_from_dead.ll @@ -1,7 +1,7 @@ ; 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=3 < %s | FileCheck %s --check-prefixes=CHECK,OLDPM,OLDPM_MODULE +; 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=3 < %s | FileCheck %s --check-prefixes=CHECK,NEWPM,NEWPM_MODULE +; 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 ; OLDPM_MODULE-NOT: @dead @@ -52,6 +52,8 @@ dead: 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 @@ -65,17 +67,11 @@ define internal i32 @caller(i32* %B) { ; 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_MODULE-LABEL: define {{[^@]+}}@caller() -; NEWPM_MODULE-NEXT: [[A:%.*]] = alloca i32 -; NEWPM_MODULE-NEXT: store i32 1, i32* [[A]], align 4 -; NEWPM_MODULE-NEXT: [[C:%.*]] = call i32 @test(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) -; NEWPM_MODULE-NEXT: ret i32 undef -; -; NEWPM_CGSCC-LABEL: define {{[^@]+}}@caller() -; NEWPM_CGSCC-NEXT: [[A:%.*]] = alloca i32 -; NEWPM_CGSCC-NEXT: store i32 1, i32* [[A]], align 4 -; NEWPM_CGSCC-NEXT: [[C:%.*]] = call i32 @test(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) -; NEWPM_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 diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/live_called_from_dead_2.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/live_called_from_dead_2.ll new file mode 100644 index 0000000..d2ee7af --- /dev/null +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/live_called_from_dead_2.ll @@ -0,0 +1,129 @@ +; 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 + +; OLDPM_MODULE-NOT: @dead +; NEWPM_MODULE-NOT: @dead +; OLDPM_CGSCC-NOT: @dead +; NEWPM_CGSCC-NOT: @dead + +define internal void @dead() { + call i32 @test(i32* null, i32* null) + ret void +} + +define internal i32 @test(i32* %X, i32* %Y) { +; OLDPM_MODULE-LABEL: define {{[^@]+}}@test +; OLDPM_MODULE-SAME: (i32* noalias nocapture nofree writeonly align 4 [[X:%.*]]) +; OLDPM_MODULE-NEXT: br i1 true, label [[LIVE:%.*]], label [[DEAD:%.*]] +; OLDPM_MODULE: live: +; OLDPM_MODULE-NEXT: store i32 0, i32* [[X]], align 4 +; OLDPM_MODULE-NEXT: ret i32 undef +; OLDPM_MODULE: dead: +; OLDPM_MODULE-NEXT: unreachable +; +; OLDPM_CGSCC-LABEL: define {{[^@]+}}@test +; OLDPM_CGSCC-SAME: (i32* nocapture nofree writeonly [[X:%.*]]) +; OLDPM_CGSCC-NEXT: br i1 true, label [[LIVE:%.*]], label [[DEAD:%.*]] +; OLDPM_CGSCC: live: +; OLDPM_CGSCC-NEXT: store i32 0, i32* [[X]] +; OLDPM_CGSCC-NEXT: ret i32 undef +; OLDPM_CGSCC: dead: +; OLDPM_CGSCC-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* nocapture nofree writeonly [[X:%.*]]) +; NEWPM_CGSCC-NEXT: br i1 true, label [[LIVE:%.*]], label [[DEAD:%.*]] +; NEWPM_CGSCC: live: +; NEWPM_CGSCC-NEXT: store i32 0, i32* [[X]] +; 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 + ret i32 0 +dead: + call i32 @caller(i32* null) + call void @dead() + ret i32 1 +} + +define internal i32 @caller(i32* %B) { +; OLDPM_MODULE-LABEL: define {{[^@]+}}@caller +; OLDPM_MODULE-SAME: (i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[B:%.*]]) +; 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) [[B]]) +; OLDPM_MODULE-NEXT: ret i32 undef +; +; OLDPM_CGSCC-LABEL: define {{[^@]+}}@caller +; OLDPM_CGSCC-SAME: (i32* nocapture nofree writeonly [[B:%.*]]) +; OLDPM_CGSCC-NEXT: [[A:%.*]] = alloca i32 +; OLDPM_CGSCC-NEXT: store i32 1, i32* [[A]], align 4 +; OLDPM_CGSCC-NEXT: [[C:%.*]] = call i32 @test(i32* nocapture nofree writeonly [[B]]) +; OLDPM_CGSCC-NEXT: ret i32 0 +; +; NEWPM_MODULE-LABEL: define {{[^@]+}}@caller +; NEWPM_MODULE-SAME: (i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[B:%.*]]) +; NEWPM_MODULE-NEXT: [[A:%.*]] = alloca i32 +; NEWPM_MODULE-NEXT: store i32 1, i32* [[A]], align 4 +; NEWPM_MODULE-NEXT: [[C:%.*]] = call i32 @test(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[B]]) +; NEWPM_MODULE-NEXT: ret i32 undef +; +; NEWPM_CGSCC-LABEL: define {{[^@]+}}@caller +; NEWPM_CGSCC-SAME: (i32* nocapture nofree writeonly [[B:%.*]]) +; NEWPM_CGSCC-NEXT: [[A:%.*]] = alloca i32 +; NEWPM_CGSCC-NEXT: store i32 1, i32* [[A]], align 4 +; NEWPM_CGSCC-NEXT: [[C:%.*]] = call i32 @test(i32* nocapture nofree writeonly [[B]]) +; NEWPM_CGSCC-NEXT: ret i32 undef +; + %A = alloca i32 + store i32 1, i32* %A + %C = call i32 @test(i32* %B, i32* %A) + ret i32 %C +} + +define i32 @callercaller() { +; OLDPM_MODULE-LABEL: define {{[^@]+}}@callercaller() +; OLDPM_MODULE-NEXT: [[B:%.*]] = alloca i32 +; OLDPM_MODULE-NEXT: store i32 2, i32* [[B]], align 4 +; OLDPM_MODULE-NEXT: [[X:%.*]] = call i32 @caller(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[B]]) +; OLDPM_MODULE-NEXT: ret i32 0 +; +; OLDPM_CGSCC-LABEL: define {{[^@]+}}@callercaller() +; OLDPM_CGSCC-NEXT: [[B:%.*]] = alloca i32 +; OLDPM_CGSCC-NEXT: store i32 2, i32* [[B]], align 4 +; OLDPM_CGSCC-NEXT: [[X:%.*]] = call i32 @caller(i32* noalias nofree nonnull writeonly align 4 dereferenceable(4) [[B]]) +; OLDPM_CGSCC-NEXT: ret i32 0 +; +; NEWPM_MODULE-LABEL: define {{[^@]+}}@callercaller() +; NEWPM_MODULE-NEXT: [[B:%.*]] = alloca i32 +; NEWPM_MODULE-NEXT: store i32 2, i32* [[B]], align 4 +; NEWPM_MODULE-NEXT: [[X:%.*]] = call i32 @caller(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[B]]) +; NEWPM_MODULE-NEXT: ret i32 0 +; +; NEWPM_CGSCC-LABEL: define {{[^@]+}}@callercaller() +; NEWPM_CGSCC-NEXT: [[B:%.*]] = alloca i32 +; NEWPM_CGSCC-NEXT: store i32 2, i32* [[B]], align 4 +; NEWPM_CGSCC-NEXT: [[X:%.*]] = call i32 @caller(i32* noalias nofree nonnull writeonly align 4 dereferenceable(4) [[B]]) +; NEWPM_CGSCC-NEXT: ret i32 0 +; + %B = alloca i32 + store i32 2, i32* %B + %X = call i32 @caller(i32* %B) + ret i32 %X +} + diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/musttail.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/musttail.ll index ac0fc2b..dfef64a 100644 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/musttail.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/musttail.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 < %s | FileCheck %s +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s ; PR36543 ; Don't promote arguments of musttail callee @@ -56,7 +56,7 @@ define internal i32 @test2(%T* %p, i32 %p2) { define i32 @caller2(%T* %g) { ; CHECK-LABEL: define {{[^@]+}}@caller2 -; CHECK-SAME: (%T* nocapture nofree readonly [[G:%.*]]) +; CHECK-SAME: (%T* nocapture nofree readnone [[G:%.*]]) ; CHECK-NEXT: ret i32 0 ; %v = call i32 @test2(%T* %g, i32 0) diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/reserve-tbaa.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/reserve-tbaa.ll index ef5dfb6..a889177 100644 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/reserve-tbaa.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/reserve-tbaa.ll @@ -17,11 +17,9 @@ define internal fastcc void @fn(i32* nocapture readonly %p1, i64* nocapture read ; CHECK-LABEL: define {{[^@]+}}@fn ; CHECK-SAME: (i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) [[P1:%.*]]) ; CHECK-NEXT: entry: -; CHECK-NEXT: [[TMP0:%.*]] = load i64, i64* undef, align 8, !tbaa !0 -; CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[TMP0]] to i32 -; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* @g, align 4, !tbaa !4 -; CHECK-NEXT: [[CONV1:%.*]] = trunc i32 [[TMP1]] to i8 -; CHECK-NEXT: store i8 [[CONV1]], i8* @d, align 1, !tbaa !6 +; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* @g, align 4, !tbaa !0 +; CHECK-NEXT: [[CONV1:%.*]] = trunc i32 [[TMP0]] to i8 +; CHECK-NEXT: store i8 [[CONV1]], i8* @d, align 1, !tbaa !4 ; CHECK-NEXT: ret void ; entry: @@ -36,10 +34,10 @@ entry: define i32 @main() { ; CHECK-LABEL: define {{[^@]+}}@main() ; CHECK-NEXT: entry: -; CHECK-NEXT: [[TMP0:%.*]] = load i32**, i32*** @e, align 8, !tbaa !7 -; CHECK-NEXT: store i32* @g, i32** [[TMP0]], align 8, !tbaa !7 -; CHECK-NEXT: [[TMP1:%.*]] = load i32*, i32** @a, align 8, !tbaa !7 -; CHECK-NEXT: store i32 1, i32* [[TMP1]], align 4, !tbaa !4 +; CHECK-NEXT: [[TMP0:%.*]] = load i32**, i32*** @e, align 8, !tbaa !5 +; CHECK-NEXT: store i32* @g, i32** [[TMP0]], align 8, !tbaa !5 +; CHECK-NEXT: [[TMP1:%.*]] = load i32*, i32** @a, align 8, !tbaa !5 +; CHECK-NEXT: store i32 1, i32* [[TMP1]], align 4, !tbaa !0 ; CHECK-NEXT: call fastcc void @fn(i32* nofree nonnull readonly align 4 dereferenceable(4) @g) ; CHECK-NEXT: ret i32 0 ; 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 index dedaa3d..92622b2 100644 --- a/llvm/test/Transforms/Attributor/IPConstantProp/2009-09-24-byval-ptr.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/2009-09-24-byval-ptr.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 < %s | FileCheck %s +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=5 < %s | FileCheck %s ; Don't constant-propagate byval pointers, since they are not pointers! ; PR5038 %struct.MYstr = type { i8, i32 } diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/PR43857.ll b/llvm/test/Transforms/Attributor/IPConstantProp/PR43857.ll index 82c404e..16f4544 100644 --- a/llvm/test/Transforms/Attributor/IPConstantProp/PR43857.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/PR43857.ll @@ -20,7 +20,6 @@ define void @baz(<8 x i32> %arg) local_unnamed_addr { ; CHECK-LABEL: define {{[^@]+}}@baz ; CHECK-SAME: (<8 x i32> [[ARG:%.*]]) local_unnamed_addr ; CHECK-NEXT: bb: -; CHECK-NEXT: [[TMP1:%.*]] = extractvalue [[STRUCT_ZOT:%.*]] undef, 0, 0 ; CHECK-NEXT: ret void ; bb: diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/recursion.ll b/llvm/test/Transforms/Attributor/IPConstantProp/recursion.ll index fc82342..831c0c6 100644 --- a/llvm/test/Transforms/Attributor/IPConstantProp/recursion.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/recursion.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 < %s | FileCheck %s +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s ; CHECK-NOT: %X diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll b/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll index f89da03..05b0107 100644 --- a/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=9 < %s | FileCheck %s +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=8 < %s | FileCheck %s ;; This function returns its second argument on all return statements define internal i32* @incdec(i1 %C, i32* %V) { diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/return-constants.ll b/llvm/test/Transforms/Attributor/IPConstantProp/return-constants.ll index a2be8c0..d53b11b 100644 --- a/llvm/test/Transforms/Attributor/IPConstantProp/return-constants.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/return-constants.ll @@ -32,6 +32,17 @@ F: ; preds = %0 } define internal %0 @bar(i1 %Q) { +; CHECK-LABEL: define {{[^@]+}}@bar +; CHECK-SAME: (i1 [[Q:%.*]]) +; CHECK-NEXT: [[A:%.*]] = insertvalue [[TMP0:%.*]] undef, i32 21, 0 +; CHECK-NEXT: br i1 [[Q]], label [[T:%.*]], label [[F:%.*]] +; CHECK: T: +; CHECK-NEXT: [[B:%.*]] = insertvalue [[TMP0]] %A, i32 22, 1 +; CHECK-NEXT: ret [[TMP0]] %B +; CHECK: F: +; CHECK-NEXT: [[C:%.*]] = insertvalue [[TMP0]] %A, i32 23, 1 +; CHECK-NEXT: ret [[TMP0]] %C +; %A = insertvalue %0 undef, i32 21, 0 br i1 %Q, label %T, label %F @@ -48,13 +59,33 @@ define %0 @caller(i1 %Q) { ; CHECK-LABEL: define {{[^@]+}}@caller ; CHECK-SAME: (i1 [[Q:%.*]]) ; CHECK-NEXT: [[X:%.*]] = call [[TMP0:%.*]] @foo(i1 [[Q]]) +; CHECK-NEXT: ret [[TMP0]] %X +; + %X = call %0 @foo(i1 %Q) + %A = extractvalue %0 %X, 0 + %B = extractvalue %0 %X, 1 + %Y = call %0 @bar(i1 %Q) + %C = extractvalue %0 %Y, 0 + %D = extractvalue %0 %Y, 1 + %M = add i32 %A, %C + %N = add i32 %B, %D + ret %0 %X +} + +; Similar to @caller but the result of both calls are actually used. +define i32 @caller2(i1 %Q) { +; CHECK-LABEL: define {{[^@]+}}@caller2 +; CHECK-SAME: (i1 [[Q:%.*]]) +; CHECK-NEXT: [[X:%.*]] = call [[TMP0:%.*]] @foo(i1 [[Q]]) ; CHECK-NEXT: [[A:%.*]] = extractvalue [[TMP0]] %X, 0 ; CHECK-NEXT: [[B:%.*]] = extractvalue [[TMP0]] %X, 1 -; CHECK-NEXT: [[C:%.*]] = extractvalue [[TMP0]] undef, 0 -; CHECK-NEXT: [[D:%.*]] = extractvalue [[TMP0]] undef, 1 +; CHECK-NEXT: [[Y:%.*]] = call [[TMP0]] @bar(i1 [[Q]]) +; CHECK-NEXT: [[C:%.*]] = extractvalue [[TMP0]] %Y, 0 +; CHECK-NEXT: [[D:%.*]] = extractvalue [[TMP0]] %Y, 1 ; CHECK-NEXT: [[M:%.*]] = add i32 [[A]], [[C]] ; CHECK-NEXT: [[N:%.*]] = add i32 [[B]], [[D]] -; CHECK-NEXT: ret [[TMP0]] %X +; CHECK-NEXT: [[R:%.*]] = add i32 [[N]], [[M]] +; CHECK-NEXT: ret i32 [[R]] ; %X = call %0 @foo(i1 %Q) %A = extractvalue %0 %X, 0 @@ -65,5 +96,6 @@ define %0 @caller(i1 %Q) { %M = add i32 %A, %C ;; Check that the second return values didn't get propagated %N = add i32 %B, %D - ret %0 %X + %R = add i32 %N, %M + ret i32 %R } diff --git a/llvm/test/Transforms/Attributor/dereferenceable-2.ll b/llvm/test/Transforms/Attributor/dereferenceable-2.ll index b3c0440..f2a3e3a 100644 --- a/llvm/test/Transforms/Attributor/dereferenceable-2.ll +++ b/llvm/test/Transforms/Attributor/dereferenceable-2.ll @@ -74,7 +74,7 @@ define void @gep0(i8* %unused, i8* %other, i8* %ptr) { ; Multiple arguments may be dereferenceable. define void @ordering(i8* %ptr1, i32* %ptr2) { -; ATTRIBUTOR-LABEL: @ordering(i8* nocapture nofree nonnull readonly dereferenceable(3) %ptr1, i32* nocapture nofree nonnull readonly dereferenceable(8) %ptr2) +; ATTRIBUTOR-LABEL: @ordering(i8* nocapture nofree nonnull readnone dereferenceable(3) %ptr1, i32* nocapture nofree nonnull readnone dereferenceable(8) %ptr2) %a20 = getelementptr i32, i32* %ptr2, i64 0 %a12 = getelementptr i8, i8* %ptr1, i64 2 %t12 = load i8, i8* %a12 @@ -91,7 +91,7 @@ define void @ordering(i8* %ptr1, i32* %ptr2) { ; Not in entry block. define void @not_entry_but_guaranteed_to_execute(i8* %ptr) { -; ATTRIBUTOR-LABEL: @not_entry_but_guaranteed_to_execute(i8* nocapture nofree nonnull readonly dereferenceable(3) %ptr) +; ATTRIBUTOR-LABEL: @not_entry_but_guaranteed_to_execute(i8* nocapture nofree nonnull readnone dereferenceable(3) %ptr) entry: br label %exit exit: @@ -107,7 +107,7 @@ exit: ; Not in entry block and not guaranteed to execute. define void @not_entry_not_guaranteed_to_execute(i8* %ptr, i1 %cond) { -; ATTRIBUTOR-LABEL: @not_entry_not_guaranteed_to_execute(i8* nocapture nofree readonly %ptr, i1 %cond) +; ATTRIBUTOR-LABEL: @not_entry_not_guaranteed_to_execute(i8* nocapture nofree readnone %ptr, i1 %cond) entry: br i1 %cond, label %loads, label %exit loads: @@ -125,7 +125,7 @@ exit: ; The last load may not execute, so derefenceable bytes only covers the 1st two loads. define void @partial_in_entry(i16* %ptr, i1 %cond) { -; ATTRIBUTOR-LABEL: @partial_in_entry(i16* nocapture nofree nonnull readonly dereferenceable(4) %ptr, i1 %cond) +; ATTRIBUTOR-LABEL: @partial_in_entry(i16* nocapture nofree nonnull readnone dereferenceable(4) %ptr, i1 %cond) entry: %arrayidx0 = getelementptr i16, i16* %ptr, i64 0 %arrayidx1 = getelementptr i16, i16* %ptr, i64 1 @@ -157,7 +157,7 @@ define void @volatile_is_not_dereferenceable(i16* %ptr) { ; TODO: We should allow inference for atomic (but not volatile) ops. define void @atomic_is_alright(i16* %ptr) { -; ATTRIBUTOR-LABEL: @atomic_is_alright(i16* nocapture nofree nonnull readonly align 2 dereferenceable(6) %ptr) +; ATTRIBUTOR-LABEL: @atomic_is_alright(i16* nocapture nofree nonnull readnone align 2 dereferenceable(6) %ptr) %arrayidx0 = getelementptr i16, i16* %ptr, i64 0 %arrayidx1 = getelementptr i16, i16* %ptr, i64 1 %arrayidx2 = getelementptr i16, i16* %ptr, i64 2 @@ -170,7 +170,7 @@ define void @atomic_is_alright(i16* %ptr) { declare void @may_not_return() define void @not_guaranteed_to_transfer_execution(i16* %ptr) { -; ATTRIBUTOR-LABEL: @not_guaranteed_to_transfer_execution(i16* nocapture nonnull readonly dereferenceable(2) %ptr) +; ATTRIBUTOR-LABEL: @not_guaranteed_to_transfer_execution(i16* nocapture nofree nonnull readnone dereferenceable(2) %ptr) %arrayidx0 = getelementptr i16, i16* %ptr, i64 0 %arrayidx1 = getelementptr i16, i16* %ptr, i64 1 %arrayidx2 = getelementptr i16, i16* %ptr, i64 2 @@ -184,7 +184,7 @@ define void @not_guaranteed_to_transfer_execution(i16* %ptr) { ; We must have consecutive accesses. define void @variable_gep_index(i8* %unused, i8* %ptr, i64 %variable_index) { -; ATTRIBUTOR-LABEL: @variable_gep_index(i8* nocapture nofree readnone %unused, i8* nocapture nofree nonnull readonly dereferenceable(1) %ptr, i64 %variable_index) +; ATTRIBUTOR-LABEL: @variable_gep_index(i8* nocapture nofree readnone %unused, i8* nocapture nofree nonnull readnone dereferenceable(1) %ptr, i64 %variable_index) %arrayidx1 = getelementptr i8, i8* %ptr, i64 %variable_index %arrayidx2 = getelementptr i8, i8* %ptr, i64 2 %t0 = load i8, i8* %ptr @@ -197,7 +197,7 @@ define void @variable_gep_index(i8* %unused, i8* %ptr, i64 %variable_index) { define void @multi_index_gep(<4 x i8>* %ptr) { ; FIXME: %ptr should be dereferenceable(4) -; ATTRIBUTOR-LABEL: @multi_index_gep(<4 x i8>* nocapture nofree nonnull readonly dereferenceable(1) %ptr) +; ATTRIBUTOR-LABEL: @multi_index_gep(<4 x i8>* nocapture nofree nonnull readnone dereferenceable(1) %ptr) %arrayidx00 = getelementptr <4 x i8>, <4 x i8>* %ptr, i64 0, i64 0 %t0 = load i8, i8* %arrayidx00 ret void @@ -206,7 +206,7 @@ define void @multi_index_gep(<4 x i8>* %ptr) { ; Could round weird bitwidths down? define void @not_byte_multiple(i9* %ptr) { -; ATTRIBUTOR-LABEL: @not_byte_multiple(i9* nocapture nofree nonnull readonly dereferenceable(2) %ptr) +; ATTRIBUTOR-LABEL: @not_byte_multiple(i9* nocapture nofree nonnull readnone dereferenceable(2) %ptr) %arrayidx0 = getelementptr i9, i9* %ptr, i64 0 %t0 = load i9, i9* %arrayidx0 ret void @@ -215,7 +215,7 @@ define void @not_byte_multiple(i9* %ptr) { ; Missing direct access from the pointer. define void @no_pointer_deref(i16* %ptr) { -; ATTRIBUTOR-LABEL: @no_pointer_deref(i16* nocapture nofree readonly %ptr) +; ATTRIBUTOR-LABEL: @no_pointer_deref(i16* nocapture nofree readnone %ptr) %arrayidx1 = getelementptr i16, i16* %ptr, i64 1 %arrayidx2 = getelementptr i16, i16* %ptr, i64 2 %t1 = load i16, i16* %arrayidx1 @@ -226,7 +226,7 @@ define void @no_pointer_deref(i16* %ptr) { ; Out-of-order is ok, but missing access concludes dereferenceable range. define void @non_consecutive(i32* %ptr) { -; ATTRIBUTOR-LABEL: @non_consecutive(i32* nocapture nofree nonnull readonly dereferenceable(8) %ptr) +; ATTRIBUTOR-LABEL: @non_consecutive(i32* nocapture nofree nonnull readnone dereferenceable(8) %ptr) %arrayidx1 = getelementptr i32, i32* %ptr, i64 1 %arrayidx0 = getelementptr i32, i32* %ptr, i64 0 %arrayidx3 = getelementptr i32, i32* %ptr, i64 3 @@ -239,7 +239,7 @@ define void @non_consecutive(i32* %ptr) { ; Improve on existing dereferenceable attribute. define void @more_bytes(i32* dereferenceable(8) %ptr) { -; ATTRIBUTOR-LABEL: @more_bytes(i32* nocapture nofree nonnull readonly dereferenceable(16) %ptr) +; ATTRIBUTOR-LABEL: @more_bytes(i32* nocapture nofree nonnull readnone dereferenceable(16) %ptr) %arrayidx3 = getelementptr i32, i32* %ptr, i64 3 %arrayidx1 = getelementptr i32, i32* %ptr, i64 1 %arrayidx0 = getelementptr i32, i32* %ptr, i64 0 @@ -254,7 +254,7 @@ define void @more_bytes(i32* dereferenceable(8) %ptr) { ; Improve on existing dereferenceable_or_null attribute. define void @more_bytes_and_not_null(i32* dereferenceable_or_null(8) %ptr) { -; ATTRIBUTOR-LABEL: @more_bytes_and_not_null(i32* nocapture nofree nonnull readonly dereferenceable(16) %ptr) +; ATTRIBUTOR-LABEL: @more_bytes_and_not_null(i32* nocapture nofree nonnull readnone dereferenceable(16) %ptr) %arrayidx3 = getelementptr i32, i32* %ptr, i64 3 %arrayidx1 = getelementptr i32, i32* %ptr, i64 1 %arrayidx0 = getelementptr i32, i32* %ptr, i64 0 @@ -269,7 +269,7 @@ define void @more_bytes_and_not_null(i32* dereferenceable_or_null(8) %ptr) { ; But don't pessimize existing dereferenceable attribute. define void @better_bytes(i32* dereferenceable(100) %ptr) { -; ATTRIBUTOR-LABEL: @better_bytes(i32* nocapture nofree nonnull readonly dereferenceable(100) %ptr) +; ATTRIBUTOR-LABEL: @better_bytes(i32* nocapture nofree nonnull readnone dereferenceable(100) %ptr) %arrayidx3 = getelementptr i32, i32* %ptr, i64 3 %arrayidx1 = getelementptr i32, i32* %ptr, i64 1 %arrayidx0 = getelementptr i32, i32* %ptr, i64 0 @@ -282,7 +282,7 @@ define void @better_bytes(i32* dereferenceable(100) %ptr) { } define void @bitcast(i32* %arg) { -; ATTRIBUTOR-LABEL: @bitcast(i32* nocapture nofree nonnull readonly dereferenceable(8) %arg) +; ATTRIBUTOR-LABEL: @bitcast(i32* nocapture nofree nonnull readnone dereferenceable(8) %arg) %ptr = bitcast i32* %arg to float* %arrayidx0 = getelementptr float, float* %ptr, i64 0 %arrayidx1 = getelementptr float, float* %ptr, i64 1 @@ -292,7 +292,7 @@ define void @bitcast(i32* %arg) { } define void @bitcast_different_sizes(double* %arg1, i8* %arg2) { -; ATTRIBUTOR-LABEL: @bitcast_different_sizes(double* nocapture nofree nonnull readonly dereferenceable(12) %arg1, i8* nocapture nofree nonnull readonly dereferenceable(16) %arg2) +; ATTRIBUTOR-LABEL: @bitcast_different_sizes(double* nocapture nofree nonnull readnone dereferenceable(12) %arg1, i8* nocapture nofree nonnull readnone dereferenceable(16) %arg2) %ptr1 = bitcast double* %arg1 to float* %a10 = getelementptr float, float* %ptr1, i64 0 %a11 = getelementptr float, float* %ptr1, i64 1 @@ -310,7 +310,7 @@ define void @bitcast_different_sizes(double* %arg1, i8* %arg2) { } define void @negative_offset(i32* %arg) { -; ATTRIBUTOR-LABEL: @negative_offset(i32* nocapture nofree nonnull readonly dereferenceable(4) %arg) +; ATTRIBUTOR-LABEL: @negative_offset(i32* nocapture nofree nonnull readnone dereferenceable(4) %arg) %ptr = bitcast i32* %arg to float* %arrayidx0 = getelementptr float, float* %ptr, i64 0 %arrayidx1 = getelementptr float, float* %ptr, i64 -1 @@ -330,7 +330,7 @@ define void @stores(i32* %arg) { } define void @load_store(i32* %arg) { -; ATTRIBUTOR-LABEL: @load_store(i32* nocapture nofree nonnull dereferenceable(8) %arg) +; ATTRIBUTOR-LABEL: @load_store(i32* nocapture nofree nonnull writeonly dereferenceable(8) %arg) %ptr = bitcast i32* %arg to float* %arrayidx0 = getelementptr float, float* %ptr, i64 0 %arrayidx1 = getelementptr float, float* %ptr, i64 1 diff --git a/llvm/test/Transforms/Attributor/internal-noalias.ll b/llvm/test/Transforms/Attributor/internal-noalias.ll index 14cf6f3..f04478c 100644 --- a/llvm/test/Transforms/Attributor/internal-noalias.ll +++ b/llvm/test/Transforms/Attributor/internal-noalias.ll @@ -1,4 +1,4 @@ -; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=5 < %s | FileCheck %s +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=6 < %s | FileCheck %s define dso_local i32 @visible(i32* noalias %A, i32* noalias %B) #0 { entry: diff --git a/llvm/test/Transforms/Attributor/liveness.ll b/llvm/test/Transforms/Attributor/liveness.ll index cc9fbf4..384279d 100644 --- a/llvm/test/Transforms/Attributor/liveness.ll +++ b/llvm/test/Transforms/Attributor/liveness.ll @@ -57,8 +57,8 @@ define internal i32 @internal_load(i32*) norecurse nounwind uwtable { ; TEST 1: Only first block is live. ; CHECK: Function Attrs: nofree noreturn nosync nounwind -; MODULE-NEXT: define i32 @first_block_no_return(i32 %a, i32* nocapture nofree nonnull readonly %ptr1, i32* nocapture nofree readnone %ptr2) -; CGSCC-NEXT: define i32 @first_block_no_return(i32 %a, i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) %ptr1, i32* nocapture nofree readnone %ptr2) +; MODULE-NEXT: define i32 @first_block_no_return(i32 %a, i32* nocapture nofree nonnull readnone %ptr1, i32* nocapture nofree readnone %ptr2) +; CGSCC-NEXT: define i32 @first_block_no_return(i32 %a, i32* nocapture nofree nonnull readnone align 4 dereferenceable(4) %ptr1, i32* nocapture nofree readnone %ptr2) define i32 @first_block_no_return(i32 %a, i32* nonnull %ptr1, i32* %ptr2) #0 { entry: call i32 @internal_load(i32* %ptr1) diff --git a/llvm/test/Transforms/Attributor/liveness_chains.ll b/llvm/test/Transforms/Attributor/liveness_chains.ll new file mode 100644 index 0000000..c9ab99f --- /dev/null +++ b/llvm/test/Transforms/Attributor/liveness_chains.ll @@ -0,0 +1,58 @@ +; 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=1 -S < %s | FileCheck %s +; RUN: opt -attributor-cgscc --attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s +; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s +; RUN: opt -passes='attributor-cgscc' --attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s + +; Make sure we need a single iteration to determine the chains are dead/alive. + +declare i32 @source() nounwind readonly + +define i32 @chain_dead(i32 %arg) { +; CHECK-LABEL: define {{[^@]+}}@chain_dead +; CHECK-SAME: (i32 [[ARG:%.*]]) +; CHECK-NEXT: ret i32 0 +; + %init = call i32 @source() + %v0 = add i32 %arg, %init + %v1 = add i32 %init, %v0 + %v2 = add i32 %v0, %v1 + %v3 = add i32 %v1, %v2 + %v4 = add i32 %v2, %v3 + %v5 = add i32 %v3, %v4 + %v6 = add i32 %v4, %v5 + %v7 = add i32 %v5, %v6 + %v8 = add i32 %v6, %v7 + %v9 = add i32 %v7, %v8 + ret i32 0 +} + +define i32 @chain_alive(i32 %arg) { +; CHECK-LABEL: define {{[^@]+}}@chain_alive +; CHECK-SAME: (i32 [[ARG:%.*]]) +; CHECK-NEXT: [[INIT:%.*]] = call i32 @source() +; CHECK-NEXT: [[V0:%.*]] = add i32 [[ARG]], [[INIT]] +; CHECK-NEXT: [[V1:%.*]] = add i32 [[INIT]], [[V0]] +; CHECK-NEXT: [[V2:%.*]] = add i32 [[V0]], [[V1]] +; CHECK-NEXT: [[V3:%.*]] = add i32 [[V1]], [[V2]] +; CHECK-NEXT: [[V4:%.*]] = add i32 [[V2]], [[V3]] +; CHECK-NEXT: [[V5:%.*]] = add i32 [[V3]], [[V4]] +; CHECK-NEXT: [[V6:%.*]] = add i32 [[V4]], [[V5]] +; CHECK-NEXT: [[V7:%.*]] = add i32 [[V5]], [[V6]] +; CHECK-NEXT: [[V8:%.*]] = add i32 [[V6]], [[V7]] +; CHECK-NEXT: [[V9:%.*]] = add i32 [[V7]], [[V8]] +; CHECK-NEXT: ret i32 [[V9]] +; + %init = call i32 @source() + %v0 = add i32 %arg, %init + %v1 = add i32 %init, %v0 + %v2 = add i32 %v0, %v1 + %v3 = add i32 %v1, %v2 + %v4 = add i32 %v2, %v3 + %v5 = add i32 %v3, %v4 + %v6 = add i32 %v4, %v5 + %v7 = add i32 %v5, %v6 + %v8 = add i32 %v6, %v7 + %v9 = add i32 %v7, %v8 + ret i32 %v9 +} diff --git a/llvm/test/Transforms/Attributor/misc.ll b/llvm/test/Transforms/Attributor/misc.ll index cb77579..f8b7504 100644 --- a/llvm/test/Transforms/Attributor/misc.ll +++ b/llvm/test/Transforms/Attributor/misc.ll @@ -9,7 +9,6 @@ define internal void @internal(void (i8*)* %fp) { ; CHECK-SAME: (void (i8*)* [[FP:%.*]]) ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 -; CHECK-NEXT: [[TMP:%.*]] = bitcast i32* [[A]] to i8* ; CHECK-NEXT: call void @foo(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) ; CHECK-NEXT: call void [[FP]](i8* bitcast (void (i32*)* @foo to i8*)) ; CHECK-NEXT: call void @callback1(void (i32*)* nonnull @foo) @@ -23,7 +22,6 @@ define internal void @internal(void (i8*)* %fp) { ; DECL_CS-SAME: (void (i8*)* [[FP:%.*]]) ; DECL_CS-NEXT: entry: ; DECL_CS-NEXT: [[A:%.*]] = alloca i32, align 4 -; DECL_CS-NEXT: [[TMP:%.*]] = bitcast i32* [[A]] to i8* ; DECL_CS-NEXT: call void @foo(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) ; DECL_CS-NEXT: call void [[FP]](i8* bitcast (void (i32*)* @foo to i8*)) ; DECL_CS-NEXT: call void @callback1(void (i32*)* nonnull @foo) @@ -51,7 +49,6 @@ define void @external(void (i8*)* %fp) { ; CHECK-SAME: (void (i8*)* [[FP:%.*]]) ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 -; CHECK-NEXT: [[TMP:%.*]] = bitcast i32* [[A]] to i8* ; CHECK-NEXT: call void @foo(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) ; CHECK-NEXT: call void @callback1(void (i32*)* nonnull @foo) ; CHECK-NEXT: call void @callback2(void (i8*)* bitcast (void (i32*)* @foo to void (i8*)*)) @@ -66,7 +63,6 @@ define void @external(void (i8*)* %fp) { ; DECL_CS-SAME: (void (i8*)* [[FP:%.*]]) ; DECL_CS-NEXT: entry: ; DECL_CS-NEXT: [[A:%.*]] = alloca i32, align 4 -; DECL_CS-NEXT: [[TMP:%.*]] = bitcast i32* [[A]] to i8* ; DECL_CS-NEXT: call void @foo(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) ; DECL_CS-NEXT: call void @callback1(void (i32*)* nonnull @foo) ; DECL_CS-NEXT: call void @callback2(void (i8*)* nonnull bitcast (void (i32*)* @foo to void (i8*)*)) @@ -95,7 +91,7 @@ define internal void @foo(i32* %a) { ; ALL-LABEL: define {{[^@]+}}@foo ; ALL-SAME: (i32* nocapture nofree nonnull writeonly dereferenceable(4) [[A:%.*]]) ; ALL-NEXT: entry: -; ALL-NEXT: store i32 0, i32* %a +; ALL-NEXT: store i32 0, i32* [[A]] ; ALL-NEXT: ret void ; entry: diff --git a/llvm/test/Transforms/Attributor/nocapture-1.ll b/llvm/test/Transforms/Attributor/nocapture-1.ll index 78a2ba6..98c4228 100644 --- a/llvm/test/Transforms/Attributor/nocapture-1.ll +++ b/llvm/test/Transforms/Attributor/nocapture-1.ll @@ -135,8 +135,10 @@ define void @nc3(void ()* %p) { ret void } -declare void @external(i8*) readonly nounwind -; ATTRIBUTOR: define void @nc4(i8* nocapture readonly %p) +; The following test is tricky because improvements to AAIsDead can cause the call to be removed. +; FIXME: readonly and nocapture missing on the pointer. +declare void @external(i8* readonly) nounwind argmemonly +; ATTRIBUTOR: define void @nc4(i8* %p) define void @nc4(i8* %p) { call void @external(i8* %p) ret void diff --git a/llvm/test/Transforms/Attributor/nofree.ll b/llvm/test/Transforms/Attributor/nofree.ll index d06a0ea..80d82c8 100644 --- a/llvm/test/Transforms/Attributor/nofree.ll +++ b/llvm/test/Transforms/Attributor/nofree.ll @@ -183,7 +183,7 @@ define void @call_both() #0 { declare float @llvm.floor.f32(float) ; FIXME: missing nofree -; ATTRIBUTOR: Function Attrs: noinline nosync nounwind readnone uwtable +; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable willreturn ; ATTRIBUTOR-NEXT: define void @call_floor(float %a) define void @call_floor(float %a) #0 { @@ -191,6 +191,14 @@ define void @call_floor(float %a) #0 { ret void } +; FIXME: missing nofree +; ATTRIBUTOR: Function Attrs: noinline nosync nounwind readnone uwtable +; ATTRIBUTOR-NEXT: define float @call_floor2(float %a) +define float @call_floor2(float %a) #0 { + %c = tail call float @llvm.floor.f32(float %a) + ret float %c +} + ; TEST 11 (positive case) ; Check propagation. diff --git a/llvm/test/Transforms/Attributor/noreturn.ll b/llvm/test/Transforms/Attributor/noreturn.ll index 2b15e07..06e2cc4 100644 --- a/llvm/test/Transforms/Attributor/noreturn.ll +++ b/llvm/test/Transforms/Attributor/noreturn.ll @@ -1,4 +1,4 @@ -; RUN: opt -functionattrs -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s +; RUN: opt -functionattrs -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s ; ; Test cases specifically designed for the "no-return" function attribute. ; We use FIXME's to indicate problems and missing attributes. diff --git a/llvm/test/Transforms/Attributor/nosync.ll b/llvm/test/Transforms/Attributor/nosync.ll index 67125fc..1b26c5e 100644 --- a/llvm/test/Transforms/Attributor/nosync.ll +++ b/llvm/test/Transforms/Attributor/nosync.ll @@ -311,9 +311,16 @@ declare float @llvm.cos(float %val) readnone ; TEST 19 - positive, readnone & non-convergent intrinsic. -; ATTRIBUTOR: Function Attrs: nosync nounwind +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind readnone willreturn ; ATTRIBUTOR-NEXT: define i32 @cos_test(float %x) define i32 @cos_test(float %x) { call float @llvm.cos(float %x) ret i32 4 } + +; ATTRIBUTOR: Function Attrs: nosync nounwind +; ATTRIBUTOR-NEXT: define float @cos_test2(float %x) +define float @cos_test2(float %x) { + %c = call float @llvm.cos(float %x) + ret float %c +} diff --git a/llvm/test/Transforms/Attributor/range.ll b/llvm/test/Transforms/Attributor/range.ll index efdb8a5..a3f2b2e 100644 --- a/llvm/test/Transforms/Attributor/range.ll +++ b/llvm/test/Transforms/Attributor/range.ll @@ -1,4 +1,4 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes ; RUN: opt -attributor -attributor-disable=false -S < %s | FileCheck %s --check-prefixes=CHECK,OLD_PM,MODULE_OLD_PM ; RUN: opt -passes=attributor -attributor-disable=false -S < %s | FileCheck %s --check-prefixes=CHECK,NEW_PM,MODULE_NEW_PM ; RUN: opt -attributor-cgscc -attributor-disable=false -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC_OLD_PM @@ -572,7 +572,7 @@ entry: define i32 @test2_check(i32* %p) { ; OLD_PM-LABEL: define {{[^@]+}}@test2_check -; OLD_PM-SAME: (i32* nocapture nofree readonly align 4 [[P:%.*]]) +; OLD_PM-SAME: (i32* nocapture nofree readnone align 4 [[P:%.*]]) ; OLD_PM-NEXT: entry: ; OLD_PM-NEXT: br label [[IF_THEN:%.*]] ; OLD_PM: if.then: @@ -583,7 +583,7 @@ define i32 @test2_check(i32* %p) { ; OLD_PM-NEXT: ret i32 2 ; ; NEW_PM-LABEL: define {{[^@]+}}@test2_check -; NEW_PM-SAME: (i32* nocapture nofree readonly align 4 [[P:%.*]]) +; NEW_PM-SAME: (i32* nocapture nofree readnone align 4 [[P:%.*]]) ; NEW_PM-NEXT: entry: ; NEW_PM-NEXT: br label [[IF_THEN:%.*]] ; NEW_PM: if.then: diff --git a/llvm/test/Transforms/Attributor/returned.ll b/llvm/test/Transforms/Attributor/returned.ll index 3efa581..c187453 100644 --- a/llvm/test/Transforms/Attributor/returned.ll +++ b/llvm/test/Transforms/Attributor/returned.ll @@ -246,8 +246,8 @@ return: ; preds = %cond.end, %if.then3 ; return *a ? a : rt0(a); ; } ; -; BOTH: Function Attrs: nofree noinline norecurse noreturn nosync nounwind readonly uwtable -; BOTH-NEXT: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt0(i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) %a) +; BOTH: Function Attrs: nofree noinline norecurse noreturn nosync nounwind readnone uwtable willreturn +; BOTH-NEXT: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt0(i32* nocapture nofree nonnull readnone align 4 dereferenceable(4) %a) define i32* @rt0(i32* %a) #0 { entry: %v = load i32, i32* %a, align 4 @@ -263,8 +263,8 @@ entry: ; return *a ? undef : rt1(a); ; } ; -; BOTH: Function Attrs: nofree noinline norecurse noreturn nosync nounwind readonly uwtable -; BOTH-NEXT: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt1(i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) %a) +; BOTH: Function Attrs: nofree noinline norecurse noreturn nosync nounwind readnone uwtable willreturn +; BOTH-NEXT: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt1(i32* nocapture nofree nonnull readnone align 4 dereferenceable(4) %a) define i32* @rt1(i32* %a) #0 { entry: %v = load i32, i32* %a, align 4 diff --git a/llvm/test/Transforms/Attributor/undefined_behavior.ll b/llvm/test/Transforms/Attributor/undefined_behavior.ll index 1d2f6fb..bfb7b77 100644 --- a/llvm/test/Transforms/Attributor/undefined_behavior.ll +++ b/llvm/test/Transforms/Attributor/undefined_behavior.ll @@ -44,7 +44,6 @@ e: define void @load_null_pointer_is_defined() "null-pointer-is-valid"="true" { ; ATTRIBUTOR-LABEL: @load_null_pointer_is_defined( -; ATTRIBUTOR-NEXT: [[A:%.*]] = load i32, i32* null ; ATTRIBUTOR-NEXT: ret void ; %a = load i32, i32* null @@ -57,14 +56,14 @@ define internal i32* @ret_null() { ; FIXME: null is propagated but the instruction ; is not changed to unreachable. -define void @load_null_propagated() { +define i32 @load_null_propagated() { ; ATTRIBUTOR-LABEL: @load_null_propagated( ; ATTRIBUTOR-NEXT: [[A:%.*]] = load i32, i32* null -; ATTRIBUTOR-NEXT: ret void +; ATTRIBUTOR-NEXT: ret i32 [[A]] ; %ptr = call i32* @ret_null() %a = load i32, i32* %ptr - ret void + ret i32 %a } ; -- Store tests -- diff --git a/llvm/test/Transforms/Attributor/value-simplify.ll b/llvm/test/Transforms/Attributor/value-simplify.ll index 464c3b4..77dc19b 100644 --- a/llvm/test/Transforms/Attributor/value-simplify.ll +++ b/llvm/test/Transforms/Attributor/value-simplify.ll @@ -3,7 +3,7 @@ ; TODO: Add max-iteration check ; Disable update test checks and enable it where required. -; UTC_ARGS: --turn off +; UTC_ARGS: --disable ; ModuleID = 'value-simplify.ll' source_filename = "value-simplify.ll" @@ -145,7 +145,7 @@ f: } define i1 @ipccp2() { -; CHECK-LABEL: define {{[^@]+}}@ipccp2() #1 +; CHECK-LABEL: define {{[^@]+}}@ipccp2() ; CHECK-NEXT: ret i1 true ; %r = call i1 @ipccp2i(i1 true) @@ -162,7 +162,7 @@ f: } define i1 @ipccp2b() { -; CHECK-LABEL: define {{[^@]+}}@ipccp2b() #1 +; CHECK-LABEL: define {{[^@]+}}@ipccp2b() ; CHECK-NEXT: ret i1 true ; %r = call i1 @ipccp2ib(i1 true) @@ -186,7 +186,7 @@ define i32 @ipccp3() { ret i32 %r } -; UTC_ARGS: --turn on +; UTC_ARGS: --enable ; Do not touch complicated arguments (for now) %struct.X = type { i8* } @@ -311,4 +311,4 @@ for.end: ret void } -; UTC_ARGS: --turn off +; UTC_ARGS: --disable diff --git a/llvm/test/Transforms/Attributor/willreturn.ll b/llvm/test/Transforms/Attributor/willreturn.ll index 4cea09b..54d4160 100644 --- a/llvm/test/Transforms/Attributor/willreturn.ll +++ b/llvm/test/Transforms/Attributor/willreturn.ll @@ -1,4 +1,4 @@ -; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=7 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_MODULE +; 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=ATTRIBUTOR,ATTRIBUTOR_MODULE ; RUN: opt -passes=attributor-cgscc --attributor-disable=false -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_CGSCC @@ -192,19 +192,25 @@ define void @conditional_exit(i32 %0, i32* nocapture readonly %1) local_unnamed_ ; TEST 6 (positive case) ; Call intrinsic function -; FIXME: missing willreturn -; ATTRIBUTOR: Function Attrs: nounwind readnone speculatable +; ATTRIBUTOR: Function Attrs: nounwind readnone speculatable willreturn ; ATTRIBUTOR-NEXT: declare float @llvm.floor.f32(float) declare float @llvm.floor.f32(float) -; FIXME: missing willreturn -; ATTRIBUTOR: Function Attrs: noinline nosync nounwind readnone uwtable -; ATTRIBUTOR-NEXT: define void @call_floor(float %a) +; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readnone uwtable willreturn +; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable willreturn +; ATTRIBUTOR-NEXT: define void @call_floor(float %a) define void @call_floor(float %a) #0 { tail call float @llvm.floor.f32(float %a) ret void } +; ATTRIBUTOR: Function Attrs: noinline nosync nounwind readnone uwtable willreturn +; ATTRIBUTOR-NEXT: define float @call_floor2(float %a) +define float @call_floor2(float %a) #0 { + %c = tail call float @llvm.floor.f32(float %a) + ret float %c +} + ; TEST 7 (negative case) ; Call function declaration without willreturn -- 2.7.4