//===-- 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/Statistic.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" 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; bool InsertUnwindResumeCalls(Function &Fn); Value *GetExceptionObject(ResumeInst *RI); 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) {} DwarfEHPrepare(const TargetMachine *TM) : FunctionPass(ID), TM(TM), RewindFunction(nullptr) {} bool runOnFunction(Function &Fn) override; bool doFinalization(Module &M) override { RewindFunction = nullptr; return false; } const char *getPassName() const override { return "Exception handling preparation"; } }; } // end anonymous namespace char DwarfEHPrepare::ID = 0; INITIALIZE_TM_PASS(DwarfEHPrepare, "dwarfehprepare", "Prepare DWARF exceptions", false, false) FunctionPass *llvm::createDwarfEHPass(const TargetMachine *TM) { return new DwarfEHPrepare(TM); } /// 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; } /// InsertUnwindResumeCalls - Convert the ResumeInsts that are still present /// into calls to the appropriate _Unwind_Resume function. bool DwarfEHPrepare::InsertUnwindResumeCalls(Function &Fn) { SmallVector Resumes; for (BasicBlock &BB : Fn) { if (auto *RI = dyn_cast(BB.getTerminator())) Resumes.push_back(RI); } if (Resumes.empty()) return false; // Find the rewind function if we didn't already. const TargetLowering *TLI = TM->getSubtargetImpl(Fn)->getTargetLowering(); LLVMContext &Ctx = Fn.getContext(); 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. unsigned ResumesSize = Resumes.size(); if (ResumesSize == 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), ResumesSize, "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"); bool Changed = InsertUnwindResumeCalls(Fn); return Changed; }