diff --git a/llvm/include/llvm/Transforms/IPO/PruneEH.h b/llvm/include/llvm/Transforms/IPO/PruneEH.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Transforms/IPO/PruneEH.h @@ -0,0 +1,29 @@ +//===- PruneEH.h - Pass which deletes unused exception handlers -----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a simple interprocedural pass which walks the +// call-graph, turning invoke instructions into calls, iff the callee cannot +// throw an exception, and marking functions 'nounwind' if they cannot throw. +// It implements this as a bottom-up traversal of the call-graph. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_IPO_PRUNEEH_H +#define LLVM_TRANSFORMS_IPO_PRUNEEH_H + +#include "llvm/Analysis/CGSCCPassManager.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { +struct PruneEHPass : PassInfoMixin { + PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG, CGSCCUpdateResult &UR); +}; +} // namespace llvm + +#endif // LLVM_TRANSFORMS_IPO_PRUNEEH_H diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -97,6 +97,7 @@ #include "llvm/Transforms/IPO/MergeFunctions.h" #include "llvm/Transforms/IPO/OpenMPOpt.h" #include "llvm/Transforms/IPO/PartialInlining.h" +#include "llvm/Transforms/IPO/PruneEH.h" #include "llvm/Transforms/IPO/SCCP.h" #include "llvm/Transforms/IPO/SampleProfile.h" #include "llvm/Transforms/IPO/StripDeadPrototypes.h" diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -121,6 +121,7 @@ CGSCC_PASS("openmpopt", OpenMPOptPass()) CGSCC_PASS("coro-split", CoroSplitPass()) CGSCC_PASS("no-op-cgscc", NoOpCGSCCPass()) +CGSCC_PASS("prune-eh", PruneEHPass()) #undef CGSCC_PASS #ifndef FUNCTION_ANALYSIS diff --git a/llvm/lib/Transforms/IPO/PruneEH.cpp b/llvm/lib/Transforms/IPO/PruneEH.cpp --- a/llvm/lib/Transforms/IPO/PruneEH.cpp +++ b/llvm/lib/Transforms/IPO/PruneEH.cpp @@ -13,6 +13,8 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Transforms/IPO/PruneEH.h" +#include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/CallGraph.h" @@ -24,11 +26,14 @@ #include "llvm/IR/InlineAsm.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/PassManager.h" #include "llvm/InitializePasses.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Utils/CallGraphUpdater.h" #include "llvm/Transforms/Utils/Local.h" #include + using namespace llvm; #define DEBUG_TYPE "prune-eh" @@ -45,11 +50,10 @@ // runOnSCC - Analyze the SCC, performing the transformation if possible. bool runOnSCC(CallGraphSCC &SCC) override; - }; } -static bool SimplifyFunction(Function *F, CallGraph &CG); -static void DeleteBasicBlock(BasicBlock *BB, CallGraph &CG); +static bool SimplifyFunction(Function *F, CallGraphUpdater &CGU); +static void DeleteBasicBlock(BasicBlock *BB, CallGraphUpdater &CGU); char PruneEH::ID = 0; INITIALIZE_PASS_BEGIN(PruneEH, "prune-eh", @@ -60,20 +64,13 @@ Pass *llvm::createPruneEHPass() { return new PruneEH(); } -static bool runImpl(CallGraphSCC &SCC, CallGraph &CG) { - SmallPtrSet SCCNodes; +static bool runImpl(CallGraphUpdater &CGU, SetVector &Functions) { bool MadeChange = false; - // Fill SCCNodes with the elements of the SCC. Used for quickly - // looking up whether a given CallGraphNode is in this SCC. - for (CallGraphNode *I : SCC) - SCCNodes.insert(I); - // First pass, scan all of the functions in the SCC, simplifying them // according to what we know. - for (CallGraphNode *I : SCC) - if (Function *F = I->getFunction()) - MadeChange |= SimplifyFunction(F, CG); + for (Function *F : Functions) + MadeChange |= SimplifyFunction(F, CGU); // Next, check to see if any callees might throw or if there are any external // functions in this SCC: if so, we cannot prune any functions in this SCC. @@ -83,9 +80,7 @@ // obviously the SCC might throw. // bool SCCMightUnwind = false, SCCMightReturn = false; - for (CallGraphSCC::iterator I = SCC.begin(), E = SCC.end(); - (!SCCMightUnwind || !SCCMightReturn) && I != E; ++I) { - Function *F = (*I)->getFunction(); + for (Function *F : Functions) { if (!F) { SCCMightUnwind = true; SCCMightReturn = true; @@ -125,10 +120,9 @@ bool InstMightUnwind = true; if (const auto *CI = dyn_cast(&I)) { if (Function *Callee = CI->getCalledFunction()) { - CallGraphNode *CalleeNode = CG[Callee]; // If the callee is outside our current SCC then we may throw // because it might. If it is inside, do nothing. - if (SCCNodes.count(CalleeNode) > 0) + if (Functions.contains(Callee)) InstMightUnwind = false; } } @@ -149,9 +143,7 @@ // If the SCC doesn't unwind or doesn't throw, note this fact. if (!SCCMightUnwind || !SCCMightReturn) - for (CallGraphNode *I : SCC) { - Function *F = I->getFunction(); - + for (Function *F : Functions) { if (!SCCMightUnwind && !F->hasFnAttribute(Attribute::NoUnwind)) { F->addFnAttr(Attribute::NoUnwind); MadeChange = true; @@ -163,30 +155,35 @@ } } - for (CallGraphNode *I : SCC) { + for (Function *F : Functions) { // Convert any invoke instructions to non-throwing functions in this node // into call instructions with a branch. This makes the exception blocks // dead. - if (Function *F = I->getFunction()) - MadeChange |= SimplifyFunction(F, CG); + MadeChange |= SimplifyFunction(F, CGU); } return MadeChange; } - bool PruneEH::runOnSCC(CallGraphSCC &SCC) { if (skipSCC(SCC)) return false; + SetVector Functions; + for (auto &N : SCC) { + if (auto *F = N->getFunction()) + Functions.insert(F); + } CallGraph &CG = getAnalysis().getCallGraph(); - return runImpl(SCC, CG); + CallGraphUpdater CGU; + CGU.initialize(CG, SCC); + return runImpl(CGU, Functions); } // SimplifyFunction - Given information about callees, simplify the specified // function if we have invokes to non-unwinding functions or code after calls to // no-return functions. -static bool SimplifyFunction(Function *F, CallGraph &CG) { +static bool SimplifyFunction(Function *F, CallGraphUpdater &CGU) { bool MadeChange = false; for (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB) { if (InvokeInst *II = dyn_cast(BB->getTerminator())) @@ -196,7 +193,7 @@ // If the unwind block is now dead, nuke it. if (pred_empty(UnwindBlock)) - DeleteBasicBlock(UnwindBlock, CG); // Delete the new BB. + DeleteBasicBlock(UnwindBlock, CGU); // Delete the new BB. ++NumRemoved; MadeChange = true; @@ -216,7 +213,7 @@ BB->getInstList().pop_back(); new UnreachableInst(BB->getContext(), &*BB); - DeleteBasicBlock(New, CG); // Delete the new BB. + DeleteBasicBlock(New, CGU); // Delete the new BB. MadeChange = true; ++NumUnreach; break; @@ -229,12 +226,11 @@ /// DeleteBasicBlock - remove the specified basic block from the program, /// updating the callgraph to reflect any now-obsolete edges due to calls that /// exist in the BB. -static void DeleteBasicBlock(BasicBlock *BB, CallGraph &CG) { +static void DeleteBasicBlock(BasicBlock *BB, CallGraphUpdater &CGU) { assert(pred_empty(BB) && "BB is not dead!"); Instruction *TokenInst = nullptr; - CallGraphNode *CGN = CG[BB->getParent()]; for (BasicBlock::iterator I = BB->end(), E = BB->begin(); I != E; ) { --I; @@ -246,9 +242,9 @@ if (auto *Call = dyn_cast(&*I)) { const Function *Callee = Call->getCalledFunction(); if (!Callee || !Intrinsic::isLeaf(Callee->getIntrinsicID())) - CGN->removeCallEdgeFor(*Call); + CGU.removeCallSite(*Call); else if (!Callee->isIntrinsic()) - CGN->removeCallEdgeFor(*Call); + CGU.removeCallSite(*Call); } if (!I->use_empty()) @@ -268,3 +264,15 @@ BB->eraseFromParent(); } } + +PreservedAnalyses PruneEHPass::run(LazyCallGraph::SCC &C, + CGSCCAnalysisManager &AM, LazyCallGraph &CG, + CGSCCUpdateResult &UR) { + SetVector Functions; + for (auto &N : C) + Functions.insert(&N.getFunction()); + CallGraphUpdater CGU; + CGU.initialize(CG, C, AM, UR); + return runImpl(CGU, Functions) ? PreservedAnalyses::none() + : PreservedAnalyses::all(); +} diff --git a/llvm/test/Transforms/PruneEH/looptest.ll b/llvm/test/Transforms/PruneEH/looptest.ll --- a/llvm/test/Transforms/PruneEH/looptest.ll +++ b/llvm/test/Transforms/PruneEH/looptest.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -prune-eh -S | FileCheck %s +; RUN: opt < %s -passes=prune-eh -S | FileCheck %s declare void @nounwind() nounwind