//===-- DwarfEHPrepare - Prepare exception handling for code generation ---===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This pass mulches exception handling code into a form adapted to code // generation. Required if using dwarf exception handling. // //===----------------------------------------------------------------------===// #include "llvm/CodeGen/Passes.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/CFG.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/Pass.h" #include "llvm/Target/TargetLowering.h" #include "llvm/Target/TargetSubtargetInfo.h" #include "llvm/Transforms/Utils/Local.h" using namespace llvm; #define DEBUG_TYPE "dwarfehprepare" STATISTIC(NumResumesLowered, "Number of resume calls lowered"); namespace { class DwarfEHPrepare : public FunctionPass { const TargetMachine *TM; // RewindFunction - _Unwind_Resume or the target equivalent. Constant *RewindFunction; DominatorTree *DT; const TargetLowering *TLI; bool InsertUnwindResumeCalls(Function &Fn); Value *GetExceptionObject(ResumeInst *RI); size_t pruneUnreachableResumes(Function &Fn, SmallVectorImpl &Resumes, SmallVectorImpl &CleanupLPads); public: static char ID; // Pass identification, replacement for typeid. // INITIALIZE_TM_PASS requires a default constructor, but it isn't used in // practice. DwarfEHPrepare() : FunctionPass(ID), TM(nullptr), RewindFunction(nullptr), DT(nullptr), TLI(nullptr) {} DwarfEHPrepare(const TargetMachine *TM) : FunctionPass(ID), TM(TM), RewindFunction(nullptr), DT(nullptr), TLI(nullptr) {} bool runOnFunction(Function &Fn) override; bool doFinalization(Module &M) override { RewindFunction = nullptr; return false; } void getAnalysisUsage(AnalysisUsage &AU) const override; const char *getPassName() const override { return "Exception handling preparation"; } }; } // end anonymous namespace char DwarfEHPrepare::ID = 0; INITIALIZE_TM_PASS_BEGIN(DwarfEHPrepare, "dwarfehprepare", "Prepare DWARF exceptions", false, false) INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass) INITIALIZE_TM_PASS_END(DwarfEHPrepare, "dwarfehprepare", "Prepare DWARF exceptions", false, false) FunctionPass *llvm::createDwarfEHPass(const TargetMachine *TM) { return new DwarfEHPrepare(TM); } void DwarfEHPrepare::getAnalysisUsage(AnalysisUsage &AU) const { AU.addRequired(); AU.addRequired(); } /// GetExceptionObject - Return the exception object from the value passed into /// the 'resume' instruction (typically an aggregate). Clean up any dead /// instructions, including the 'resume' instruction. Value *DwarfEHPrepare::GetExceptionObject(ResumeInst *RI) { Value *V = RI->getOperand(0); Value *ExnObj = nullptr; InsertValueInst *SelIVI = dyn_cast(V); LoadInst *SelLoad = nullptr; InsertValueInst *ExcIVI = nullptr; bool EraseIVIs = false; if (SelIVI) { if (SelIVI->getNumIndices() == 1 && *SelIVI->idx_begin() == 1) { ExcIVI = dyn_cast(SelIVI->getOperand(0)); if (ExcIVI && isa(ExcIVI->getOperand(0)) && ExcIVI->getNumIndices() == 1 && *ExcIVI->idx_begin() == 0) { ExnObj = ExcIVI->getOperand(1); SelLoad = dyn_cast(SelIVI->getOperand(1)); EraseIVIs = true; } } } if (!ExnObj) ExnObj = ExtractValueInst::Create(RI->getOperand(0), 0, "exn.obj", RI); RI->eraseFromParent(); if (EraseIVIs) { if (SelIVI->use_empty()) SelIVI->eraseFromParent(); if (ExcIVI->use_empty()) ExcIVI->eraseFromParent(); if (SelLoad && SelLoad->use_empty()) SelLoad->eraseFromParent(); } return ExnObj; } /// Replace resumes that are not reachable from a cleanup landing pad with /// unreachable and then simplify those blocks. size_t DwarfEHPrepare::pruneUnreachableResumes( Function &Fn, SmallVectorImpl &Resumes, SmallVectorImpl &CleanupLPads) { BitVector ResumeReachable(Resumes.size()); size_t ResumeIndex = 0; for (auto *RI : Resumes) { for (auto *LP : CleanupLPads) { if (isPotentiallyReachable(LP, RI, DT)) { ResumeReachable.set(ResumeIndex); break; } } ++ResumeIndex; } // If everything is reachable, there is no change. if (ResumeReachable.all()) return Resumes.size(); const TargetTransformInfo &TTI = getAnalysis().getTTI(Fn); LLVMContext &Ctx = Fn.getContext(); // Otherwise, insert unreachable instructions and call simplifycfg. size_t ResumesLeft = 0; for (size_t I = 0, E = Resumes.size(); I < E; ++I) { ResumeInst *RI = Resumes[I]; if (ResumeReachable[I]) { Resumes[ResumesLeft++] = RI; } else { BasicBlock *BB = RI->getParent(); new UnreachableInst(Ctx, RI); RI->eraseFromParent(); SimplifyCFG(BB, TTI, 1); } } Resumes.resize(ResumesLeft); return ResumesLeft; } /// InsertUnwindResumeCalls - Convert the ResumeInsts that are still present /// into calls to the appropriate _Unwind_Resume function. bool DwarfEHPrepare::InsertUnwindResumeCalls(Function &Fn) { SmallVector Resumes; SmallVector CleanupLPads; for (BasicBlock &BB : Fn) { if (auto *RI = dyn_cast(BB.getTerminator())) Resumes.push_back(RI); if (auto *LP = BB.getLandingPadInst()) if (LP->isCleanup()) CleanupLPads.push_back(LP); } if (Resumes.empty()) return false; LLVMContext &Ctx = Fn.getContext(); size_t ResumesLeft = pruneUnreachableResumes(Fn, Resumes, CleanupLPads); if (ResumesLeft == 0) return true; // We pruned them all. // Find the rewind function if we didn't already. if (!RewindFunction) { FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx), Type::getInt8PtrTy(Ctx), false); const char *RewindName = TLI->getLibcallName(RTLIB::UNWIND_RESUME); RewindFunction = Fn.getParent()->getOrInsertFunction(RewindName, FTy); } // Create the basic block where the _Unwind_Resume call will live. if (ResumesLeft == 1) { // Instead of creating a new BB and PHI node, just append the call to // _Unwind_Resume to the end of the single resume block. ResumeInst *RI = Resumes.front(); BasicBlock *UnwindBB = RI->getParent(); Value *ExnObj = GetExceptionObject(RI); // Call the _Unwind_Resume function. CallInst *CI = CallInst::Create(RewindFunction, ExnObj, "", UnwindBB); CI->setCallingConv(TLI->getLibcallCallingConv(RTLIB::UNWIND_RESUME)); // We never expect _Unwind_Resume to return. new UnreachableInst(Ctx, UnwindBB); return true; } BasicBlock *UnwindBB = BasicBlock::Create(Ctx, "unwind_resume", &Fn); PHINode *PN = PHINode::Create(Type::getInt8PtrTy(Ctx), ResumesLeft, "exn.obj", UnwindBB); // Extract the exception object from the ResumeInst and add it to the PHI node // that feeds the _Unwind_Resume call. for (ResumeInst *RI : Resumes) { BasicBlock *Parent = RI->getParent(); BranchInst::Create(UnwindBB, Parent); Value *ExnObj = GetExceptionObject(RI); PN->addIncoming(ExnObj, Parent); ++NumResumesLowered; } // Call the function. CallInst *CI = CallInst::Create(RewindFunction, PN, "", UnwindBB); CI->setCallingConv(TLI->getLibcallCallingConv(RTLIB::UNWIND_RESUME)); // We never expect _Unwind_Resume to return. new UnreachableInst(Ctx, UnwindBB); return true; } bool DwarfEHPrepare::runOnFunction(Function &Fn) { assert(TM && "DWARF EH preparation requires a target machine"); DT = &getAnalysis().getDomTree(); TLI = TM->getSubtargetImpl(Fn)->getTargetLowering(); bool Changed = InsertUnwindResumeCalls(Fn); DT = nullptr; TLI = nullptr; return Changed; }