diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -874,14 +874,40 @@ /// 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 BlockLivenessOnly = 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 BlockLivenessOnly = 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 BlockLivenessOnly = 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 BlockLivenessOnly = 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 @@ -1007,7 +1033,8 @@ /// 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 BlockLivenessOnly = false); /// Check \p Pred on all call-like instructions (=CallBased derived). /// @@ -2088,6 +2115,10 @@ 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; @@ -2117,6 +2148,7 @@ return false; } +public: /// Return an IR position, see struct IRPosition. const IRPosition &getIRPosition() const override { return *this; } @@ -2125,6 +2157,8 @@ /// 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 --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -336,7 +336,9 @@ "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, + /* BlockLivenessOnly */ true)) { AnyDead = true; continue; } @@ -2104,8 +2106,10 @@ A.checkForAllInstructions(InspectMemAccessInstForUB, *this, {Instruction::Load, Instruction::Store, Instruction::AtomicCmpXchg, - Instruction::AtomicRMW}); - A.checkForAllInstructions(InspectBrInstForUB, *this, {Instruction::Br}); + Instruction::AtomicRMW}, + /* BlockLivenessOnly */ true); + A.checkForAllInstructions(InspectBrInstForUB, *this, {Instruction::Br}, + /* BlockLivenessOnly */ true); if (NoUBPrevSize != AssumedNoUBInsts.size() || UBPrevSize != KnownUBInsts.size()) return ChangeStatus::CHANGED; @@ -2626,27 +2630,13 @@ } /// 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. @@ -2692,7 +2682,7 @@ if (!isAssumedSideEffectFree(A, I)) return indicatePessimisticFixpoint(); - if (!areAllUsesAssumedDead(A)) + if (!areAllUsesAssumedDead(A, getAssociatedValue())) return indicatePessimisticFixpoint(); return ChangeStatus::UNCHANGED; } @@ -2800,9 +2790,6 @@ 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())) { @@ -2822,7 +2809,7 @@ Changed = ChangeStatus::CHANGED; } - if (!areAllUsesAssumedDead(A)) + if (!areAllUsesAssumedDead(A, getAssociatedValue())) return indicatePessimisticFixpoint(); return Changed; } @@ -2861,13 +2848,9 @@ ChangeStatus updateImpl(Attributor &A) override { 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); - return static_cast(RetIsDeadAA) - .hasOnlyAssumedDeadUses(); + return areAllUsesAssumedDead(A, *ACS.getInstruction()); }; if (!A.checkForAllCallSites(PredForCallSite, *this, true)) @@ -4021,12 +4004,8 @@ /// 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 @@ -5153,10 +5132,9 @@ 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; } @@ -5719,34 +5697,119 @@ /// ---------------------------------------------------------------------------- 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 BlockLivenessOnly, DepClassTy DepClass) { + const IRPosition &IRP = AA.getIRPosition(); + if (!Functions.count(IRP.getAnchorScope())) return false; + return isAssumedDead(IRP, &AA, FnLivenessAA, BlockLivenessOnly, DepClass); +} + +bool Attributor::isAssumedDead(const Use &U, + const AbstractAttribute *QueryingAA, + const AAIsDead *FnLivenessAA, + bool BlockLivenessOnly, DepClassTy DepClass) { + Instruction *UserI = dyn_cast(U.getUser()); + if (!UserI) + return isAssumedDead(IRPosition::value(*U.get()), QueryingAA, FnLivenessAA, + BlockLivenessOnly, 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, + BlockLivenessOnly, DepClass); + } + } else if (ReturnInst *RI = dyn_cast(UserI)) { + const IRPosition &RetPos = IRPosition::returned(*RI->getFunction()); + return isAssumedDead(RetPos, QueryingAA, FnLivenessAA, BlockLivenessOnly, + DepClass); + } + + return isAssumedDead(IRPosition::value(*UserI), QueryingAA, FnLivenessAA, + BlockLivenessOnly, DepClass); +} + +bool Attributor::isAssumedDead(const Instruction &I, + const AbstractAttribute *QueryingAA, + const AAIsDead *FnLivenessAA, + bool BlockLivenessOnly, 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, + BlockLivenessOnly ? DepClass : DepClassTy::OPTIONAL); + return true; + } - // TODO: Find a good way to utilize fine and coarse grained liveness - // information. - if (!LivenessAA) - LivenessAA = - &getAAFor(AA, IRPosition::function(*CtxI->getFunction()), - /* TrackDependence */ false); + if (BlockLivenessOnly) + 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 BlockLivenessOnly, DepClassTy DepClass) { + Instruction *CtxI = IRP.getCtxI(); + if (CtxI && + isAssumedDead(*CtxI, QueryingAA, FnLivenessAA, + /* BlockLivenessOnly */ true, + BlockLivenessOnly ? DepClass : DepClassTy::OPTIONAL)) + return true; + + if (BlockLivenessOnly) 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 livess 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()) @@ -5773,7 +5836,6 @@ 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), @@ -5784,14 +5846,13 @@ const Use *U = Worklist.pop_back_val(); if (!Visited.insert(U).second) continue; - LLVM_DEBUG(dbgs() << "[Attributor] Check use: " << **U << "\n"); - if (Instruction *UserI = dyn_cast(U->getUser())) - if (LivenessAA && LivenessAA->isAssumedDead(UserI)) { - 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, + /* BlockLivenessOnly */ false, LivenessDepClass)) { + LLVM_DEBUG(dbgs() << "[Attributor] Dead use, skip!\n"); + continue; + } bool Follow = false; if (!Pred(*U, Follow)) @@ -5802,9 +5863,6 @@ Worklist.push_back(&UU); } - if (AnyDead) - recordDependence(*LivenessAA, QueryingAA, DepClassTy::OPTIONAL); - return true; } @@ -5838,6 +5896,13 @@ } for (const Use &U : Fn.uses()) { + LLVM_DEBUG(dbgs() << "[Attributor] Check use: " << *U << " in " + << *U.getUser() << "\n"); + if (isAssumedDead(U, QueryingAA, nullptr, /* BlockLivenessOnly */ true)) { + LLVM_DEBUG(dbgs() << "[Attributor] Dead use, skip!\n"); + continue; + } + AbstractCallSite ACS(&U); if (!ACS) { LLVM_DEBUG(dbgs() << "[Attributor] Function " << Fn.getName() @@ -5849,22 +5914,6 @@ 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); - continue; - } - const Use *EffectiveUse = ACS.isCallbackCall() ? &ACS.getCalleeUseForCallback() : &U; if (!ACS.isCallee(EffectiveUse)) { @@ -5930,18 +5979,17 @@ }); } -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 BlockLivenessOnly = 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, + BlockLivenessOnly)) continue; - } if (!Pred(*I)) return false; @@ -5952,7 +6000,8 @@ bool Attributor::checkForAllInstructions( const llvm::function_ref &Pred, - const AbstractAttribute &QueryingAA, const ArrayRef &Opcodes) { + const AbstractAttribute &QueryingAA, const ArrayRef &Opcodes, + bool BlockLivenessOnly) { const IRPosition &IRP = QueryingAA.getIRPosition(); // Since we need to provide instructions we have to have an exact definition. @@ -5964,18 +6013,13 @@ 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, BlockLivenessOnly)) return false; - // If we actually used liveness information so we have to record a dependence. - if (AnyDead) - recordDependence(LivenessAA, QueryingAA, DepClassTy::OPTIONAL); - return true; } @@ -5992,24 +6036,17 @@ 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; } @@ -6090,7 +6127,8 @@ // 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, /* BlockLivenessOnly */ true)) { QueriedNonFixAA = false; if (AA->update(*this) == ChangeStatus::CHANGED) { ChangedAAs.push_back(AA); @@ -6175,7 +6213,7 @@ continue; // Skip dead code. - if (isAssumedDead(*AA, nullptr)) + if (isAssumedDead(*AA, nullptr, /* BlockLivenessOnly */ true)) continue; // Manifest the state and record if we changed the IR. ChangeStatus LocalChange = AA->manifest(*this); @@ -6407,10 +6445,9 @@ // 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; } @@ -6847,13 +6884,13 @@ }; 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)) @@ -6865,10 +6902,10 @@ 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 --- 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=5 -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 --- 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 --- 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/chained.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/chained.ll --- 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 --- 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 --- 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=6 < %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 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll @@ -30,7 +30,7 @@ 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 --- 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=5 < %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=5 < %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 --- 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 @@ 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 @@ ; OLDPM_CGSCC-NEXT: [[C:%.*]] = call i32 @test(i32* 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 --- /dev/null +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/live_called_from_dead_2.ll @@ -0,0 +1,102 @@ +; 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) { +; CHECK-LABEL: define {{[^@]+}}@test +; CHECK-SAME: (i32* nocapture nofree writeonly [[X:%.*]]) +; CHECK-NEXT: br i1 true, label [[LIVE:%.*]], label [[DEAD:%.*]] +; CHECK: live: +; CHECK-NEXT: store i32 0, i32* [[X]] +; CHECK-NEXT: ret i32 undef +; CHECK: dead: +; CHECK-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* 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 --- 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 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 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/reserve-tbaa.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/reserve-tbaa.ll @@ -17,11 +17,9 @@ ; 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 @@ 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 --- 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 --- a/llvm/test/Transforms/Attributor/IPConstantProp/PR43857.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/PR43857.ll @@ -20,7 +20,6 @@ ; 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 --- 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 --- 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=7 < %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 --- a/llvm/test/Transforms/Attributor/IPConstantProp/return-constants.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/return-constants.ll @@ -32,6 +32,17 @@ } 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 @@ ; 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 @@ %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/IPConstantProp/solve-after-each-resolving-undefs-for-function.ll b/llvm/test/Transforms/Attributor/IPConstantProp/solve-after-each-resolving-undefs-for-function.ll --- a/llvm/test/Transforms/Attributor/IPConstantProp/solve-after-each-resolving-undefs-for-function.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/solve-after-each-resolving-undefs-for-function.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 define internal i32 @testf(i1 %c) { entry: diff --git a/llvm/test/Transforms/Attributor/dereferenceable-2.ll b/llvm/test/Transforms/Attributor/dereferenceable-2.ll --- a/llvm/test/Transforms/Attributor/dereferenceable-2.ll +++ b/llvm/test/Transforms/Attributor/dereferenceable-2.ll @@ -74,7 +74,7 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ 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 @@ ; 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 @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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @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_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 @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 @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 --- 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=4 < %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 --- a/llvm/test/Transforms/Attributor/liveness.ll +++ b/llvm/test/Transforms/Attributor/liveness.ll @@ -57,8 +57,8 @@ ; 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 --- /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 --- a/llvm/test/Transforms/Attributor/misc.ll +++ b/llvm/test/Transforms/Attributor/misc.ll @@ -9,7 +9,6 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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*)*)) diff --git a/llvm/test/Transforms/Attributor/nocapture-1.ll b/llvm/test/Transforms/Attributor/nocapture-1.ll --- a/llvm/test/Transforms/Attributor/nocapture-1.ll +++ b/llvm/test/Transforms/Attributor/nocapture-1.ll @@ -135,8 +135,10 @@ 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 --- a/llvm/test/Transforms/Attributor/nofree.ll +++ b/llvm/test/Transforms/Attributor/nofree.ll @@ -183,7 +183,7 @@ 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 @@ 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 --- 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 --- a/llvm/test/Transforms/Attributor/nosync.ll +++ b/llvm/test/Transforms/Attributor/nosync.ll @@ -311,9 +311,16 @@ ; 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/returned.ll b/llvm/test/Transforms/Attributor/returned.ll --- a/llvm/test/Transforms/Attributor/returned.ll +++ b/llvm/test/Transforms/Attributor/returned.ll @@ -246,8 +246,8 @@ ; 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 @@ ; 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 --- a/llvm/test/Transforms/Attributor/undefined_behavior.ll +++ b/llvm/test/Transforms/Attributor/undefined_behavior.ll @@ -44,7 +44,6 @@ 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 @@ ; 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 --- a/llvm/test/Transforms/Attributor/value-simplify.ll +++ b/llvm/test/Transforms/Attributor/value-simplify.ll @@ -145,7 +145,7 @@ } 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 @@ } define i1 @ipccp2b() { -; CHECK-LABEL: define {{[^@]+}}@ipccp2b() #1 +; CHECK-LABEL: define {{[^@]+}}@ipccp2b() ; CHECK-NEXT: ret i1 true ; %r = call i1 @ipccp2ib(i1 true) diff --git a/llvm/test/Transforms/Attributor/willreturn.ll b/llvm/test/Transforms/Attributor/willreturn.ll --- a/llvm/test/Transforms/Attributor/willreturn.ll +++ b/llvm/test/Transforms/Attributor/willreturn.ll @@ -192,19 +192,25 @@ ; 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