diff options
Diffstat (limited to 'llvm/lib')
| -rw-r--r-- | llvm/lib/Transforms/IPO/Attributor.cpp | 166 |
1 files changed, 94 insertions, 72 deletions
diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp index 87bdc0d0aa5..1321638d94c 100644 --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -753,11 +753,6 @@ ChangeStatus AAReturnedValuesImpl::updateImpl(Attributor &A) { SmallPtrSet<ReturnInst *, 2> &ReturnInsts = It.second; Value *RV = It.first; - // Ignore dead ReturnValues. - if (LivenessAA && - !LivenessAA->isLiveInstSet(ReturnInsts.begin(), ReturnInsts.end())) - continue; - LLVM_DEBUG(dbgs() << "[AAReturnedValues] Potentially returned value " << *RV << "\n"); @@ -771,6 +766,14 @@ ChangeStatus AAReturnedValuesImpl::updateImpl(Attributor &A) { // sites will be removed and we will fix the information for this state. HasCallSite = true; + // Ignore dead ReturnValues. + if (LivenessAA && + !LivenessAA->isLiveInstSet(ReturnInsts.begin(), ReturnInsts.end())) { + LLVM_DEBUG(dbgs() << "[AAReturnedValues] all returns are assumed dead, " + "skip it for now\n"); + continue; + } + // Try to find a assumed unique return value for the called function. auto *RetCSAA = A.getAAFor<AAReturnedValuesImpl>(*this, *RV); if (!RetCSAA) { @@ -1533,12 +1536,20 @@ struct AAIsDeadFunction : AAIsDead, BooleanState { ToBeExploredPaths.insert(&(F.getEntryBlock().front())); AssumedLiveBlocks.insert(&(F.getEntryBlock())); for (size_t i = 0; i < ToBeExploredPaths.size(); ++i) - explorePath(A, ToBeExploredPaths[i]); + if (const Instruction *NextNoReturnI = + findNextNoReturn(A, ToBeExploredPaths[i])) + NoReturnCalls.insert(NextNoReturnI); } - /// Explores new instructions starting from \p I. If instruction is dead, stop - /// and return true if it discovered a new instruction. - bool explorePath(Attributor &A, Instruction *I); + /// Find the next assumed noreturn instruction in the block of \p I starting + /// from, thus including, \p I. + /// + /// The caller is responsible to monitor the ToBeExploredPaths set as new + /// instructions discovered in other basic block will be placed in there. + /// + /// \returns The next assumed noreturn instructions in the block of \p I + /// starting from, thus including, \p I. + const Instruction *findNextNoReturn(Attributor &A, const Instruction *I); const std::string getAsStr() const override { return "LiveBBs(" + std::to_string(AssumedLiveBlocks.size()) + "/" + @@ -1552,18 +1563,31 @@ struct AAIsDeadFunction : AAIsDead, BooleanState { ChangeStatus HasChanged = ChangeStatus::UNCHANGED; - for (Instruction *I : NoReturnCalls) { + for (const Instruction *NRC : NoReturnCalls) { + Instruction *I = const_cast<Instruction *>(NRC); BasicBlock *BB = I->getParent(); + Instruction *SplitPos = I->getNextNode(); - /// Invoke is replaced with a call and unreachable is placed after it. if (auto *II = dyn_cast<InvokeInst>(I)) { - changeToCall(II); - changeToUnreachable(BB->getTerminator(), /* UseLLVMTrap */ false); - LLVM_DEBUG(dbgs() << "[AAIsDead] Replaced invoke with call inst\n"); - continue; + /// Invoke is replaced with a call and unreachable is placed after it if + /// the callee is nounwind and noreturn. Otherwise, we keep the invoke + /// and only place an unreachable in the normal successor. + if (Function *Callee = II->getCalledFunction()) { + auto *AANoUnw = A.getAAFor<AANoUnwind>(*this, *Callee); + if (Callee->hasFnAttribute(Attribute::NoUnwind) || + (AANoUnw && AANoUnw->isAssumedNoUnwind())) { + LLVM_DEBUG(dbgs() << "[AAIsDead] Replace invoke with call inst\n"); + changeToCall(II); + changeToUnreachable(BB->getTerminator(), /* UseLLVMTrap */ false); + continue; + } + } + + BB = II->getNormalDest(); + SplitPos = &BB->front(); } - SplitBlock(BB, I->getNextNode()); + SplitBlock(BB, SplitPos); changeToUnreachable(BB->getTerminator(), /* UseLLVMTrap */ false); HasChanged = ChangeStatus::CHANGED; } @@ -1575,7 +1599,7 @@ struct AAIsDeadFunction : AAIsDead, BooleanState { ChangeStatus updateImpl(Attributor &A) override; /// See AAIsDead::isAssumedDead(BasicBlock *). - bool isAssumedDead(BasicBlock *BB) const override { + bool isAssumedDead(const BasicBlock *BB) const override { assert(BB->getParent() == &getAnchorScope() && "BB must be in the same anchor scope function."); @@ -1585,12 +1609,12 @@ struct AAIsDeadFunction : AAIsDead, BooleanState { } /// See AAIsDead::isKnownDead(BasicBlock *). - bool isKnownDead(BasicBlock *BB) const override { + bool isKnownDead(const BasicBlock *BB) const override { return getKnown() && isAssumedDead(BB); } /// See AAIsDead::isAssumed(Instruction *I). - bool isAssumedDead(Instruction *I) const override { + bool isAssumedDead(const Instruction *I) const override { assert(I->getParent()->getParent() == &getAnchorScope() && "Instruction must be in the same anchor scope function."); @@ -1603,33 +1627,29 @@ struct AAIsDeadFunction : AAIsDead, BooleanState { return true; // If it is not after a noreturn call, than it is live. - if (!isAfterNoReturn(I)) - return false; - - // Definitely dead. - return true; + return isAfterNoReturn(I); } /// See AAIsDead::isKnownDead(Instruction *I). - bool isKnownDead(Instruction *I) const override { + bool isKnownDead(const Instruction *I) const override { return getKnown() && isAssumedDead(I); } /// Check if instruction is after noreturn call, in other words, assumed dead. - bool isAfterNoReturn(Instruction *I) const; + bool isAfterNoReturn(const Instruction *I) const; /// Collection of to be explored paths. - SmallSetVector<Instruction *, 8> ToBeExploredPaths; + SmallSetVector<const Instruction *, 8> ToBeExploredPaths; /// Collection of all assumed live BasicBlocks. - DenseSet<BasicBlock *> AssumedLiveBlocks; + DenseSet<const BasicBlock *> AssumedLiveBlocks; /// Collection of calls with noreturn attribute, assumed or knwon. - SmallSetVector<Instruction *, 4> NoReturnCalls; + SmallSetVector<const Instruction *, 4> NoReturnCalls; }; -bool AAIsDeadFunction::isAfterNoReturn(Instruction *I) const { - Instruction *PrevI = I->getPrevNode(); +bool AAIsDeadFunction::isAfterNoReturn(const Instruction *I) const { + const Instruction *PrevI = I->getPrevNode(); while (PrevI) { if (NoReturnCalls.count(PrevI)) return true; @@ -1638,75 +1658,77 @@ bool AAIsDeadFunction::isAfterNoReturn(Instruction *I) const { return false; } -bool AAIsDeadFunction::explorePath(Attributor &A, Instruction *I) { - BasicBlock *BB = I->getParent(); +const Instruction *AAIsDeadFunction::findNextNoReturn(Attributor &A, + const Instruction *I) { + const BasicBlock *BB = I->getParent(); + + // TODO: We should have a function that determines if an "edge" is dead. + // Edges could be from an instruction to the next or from a terminator + // to the successor. For now, we need to special case the unwind block + // of InvokeInst below. while (I) { ImmutableCallSite ICS(I); if (ICS) { - auto *NoReturnAA = A.getAAFor<AANoReturn>(*this, *I); - - if (NoReturnAA && NoReturnAA->isAssumedNoReturn()) { - if (!NoReturnCalls.insert(I)) - // If I is already in the NoReturnCalls set, then it stayed noreturn - // and we didn't discover any new instructions. - return false; - - // Discovered new noreturn call, return true to indicate that I is not - // noreturn anymore and should be deleted from NoReturnCalls. - return true; + // Regarless of the no-return property of an invoke instruction we only + // learn that the regular successor is not reachable through this + // instruction but the unwind block might still be. + if (auto *Invoke = dyn_cast<InvokeInst>(I)) { + // Use nounwind to justify the unwind block is dead as well. + auto *AANoUnw = A.getAAFor<AANoUnwind>(*this, *Invoke); + if (!AANoUnw || !AANoUnw->isAssumedNoUnwind()) { + AssumedLiveBlocks.insert(Invoke->getUnwindDest()); + ToBeExploredPaths.insert(&Invoke->getUnwindDest()->front()); + } } - if (ICS.hasFnAttr(Attribute::NoReturn)) { - if (!NoReturnCalls.insert(I)) - return false; - - return true; - } + auto *NoReturnAA = A.getAAFor<AANoReturn>(*this, *I); + if (ICS.hasFnAttr(Attribute::NoReturn) || + (NoReturnAA && NoReturnAA->isAssumedNoReturn())) + return I; } I = I->getNextNode(); } // get new paths (reachable blocks). - for (BasicBlock *SuccBB : successors(BB)) { - Instruction *Inst = &(SuccBB->front()); + for (const BasicBlock *SuccBB : successors(BB)) { AssumedLiveBlocks.insert(SuccBB); - ToBeExploredPaths.insert(Inst); + ToBeExploredPaths.insert(&SuccBB->front()); } - return true; + // No noreturn instruction found. + return nullptr; } ChangeStatus AAIsDeadFunction::updateImpl(Attributor &A) { // Temporary collection to iterate over existing noreturn instructions. This // will alow easier modification of NoReturnCalls collection - SmallVector<Instruction *, 8> NoReturnChanged; + SmallVector<const Instruction *, 8> NoReturnChanged; ChangeStatus Status = ChangeStatus::UNCHANGED; - for (Instruction *I : NoReturnCalls) + for (const Instruction *I : NoReturnCalls) NoReturnChanged.push_back(I); - for (Instruction *I : NoReturnChanged) { + for (const Instruction *I : NoReturnChanged) { size_t Size = ToBeExploredPaths.size(); - // Still noreturn. - if (!explorePath(A, I)) - continue; - - NoReturnCalls.remove(I); - - // At least one new path. - Status = ChangeStatus::CHANGED; - - // No new paths. - if (Size == ToBeExploredPaths.size()) - continue; + const Instruction *NextNoReturnI = findNextNoReturn(A, I); + if (NextNoReturnI != I) { + Status = ChangeStatus::CHANGED; + NoReturnCalls.remove(I); + if (NextNoReturnI) + NoReturnCalls.insert(NextNoReturnI); + } - // explore new paths. - while (Size != ToBeExploredPaths.size()) - explorePath(A, ToBeExploredPaths[Size++]); + // Explore new paths. + while (Size != ToBeExploredPaths.size()) { + Status = ChangeStatus::CHANGED; + if (const Instruction *NextNoReturnI = + findNextNoReturn(A, ToBeExploredPaths[Size++])) + NoReturnCalls.insert(NextNoReturnI); + } } LLVM_DEBUG( |

