xref: /freebsd/contrib/llvm-project/llvm/lib/CodeGen/DwarfEHPrepare.cpp (revision 5ffd83dbcc34f10e07f6d3e968ae6365869615f4)
10b57cec5SDimitry Andric //===- DwarfEHPrepare - Prepare exception handling for code generation ----===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This pass mulches exception handling code into a form adapted to code
100b57cec5SDimitry Andric // generation. Required if using dwarf exception handling.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "llvm/ADT/BitVector.h"
150b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
160b57cec5SDimitry Andric #include "llvm/ADT/Statistic.h"
170b57cec5SDimitry Andric #include "llvm/Analysis/CFG.h"
180b57cec5SDimitry Andric #include "llvm/Analysis/EHPersonalities.h"
190b57cec5SDimitry Andric #include "llvm/Analysis/TargetTransformInfo.h"
200b57cec5SDimitry Andric #include "llvm/CodeGen/RuntimeLibcalls.h"
210b57cec5SDimitry Andric #include "llvm/CodeGen/TargetLowering.h"
220b57cec5SDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h"
230b57cec5SDimitry Andric #include "llvm/CodeGen/TargetSubtargetInfo.h"
240b57cec5SDimitry Andric #include "llvm/IR/BasicBlock.h"
250b57cec5SDimitry Andric #include "llvm/IR/Constants.h"
260b57cec5SDimitry Andric #include "llvm/IR/DerivedTypes.h"
270b57cec5SDimitry Andric #include "llvm/IR/Dominators.h"
280b57cec5SDimitry Andric #include "llvm/IR/Function.h"
290b57cec5SDimitry Andric #include "llvm/IR/Instructions.h"
300b57cec5SDimitry Andric #include "llvm/IR/Module.h"
310b57cec5SDimitry Andric #include "llvm/IR/Type.h"
32480093f4SDimitry Andric #include "llvm/InitializePasses.h"
330b57cec5SDimitry Andric #include "llvm/Pass.h"
340b57cec5SDimitry Andric #include "llvm/Support/Casting.h"
350b57cec5SDimitry Andric #include "llvm/Target/TargetMachine.h"
36480093f4SDimitry Andric #include "llvm/Transforms/Utils/Local.h"
370b57cec5SDimitry Andric #include <cstddef>
380b57cec5SDimitry Andric 
390b57cec5SDimitry Andric using namespace llvm;
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric #define DEBUG_TYPE "dwarfehprepare"
420b57cec5SDimitry Andric 
430b57cec5SDimitry Andric STATISTIC(NumResumesLowered, "Number of resume calls lowered");
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric namespace {
460b57cec5SDimitry Andric 
470b57cec5SDimitry Andric   class DwarfEHPrepare : public FunctionPass {
480b57cec5SDimitry Andric     // RewindFunction - _Unwind_Resume or the target equivalent.
490b57cec5SDimitry Andric     FunctionCallee RewindFunction = nullptr;
500b57cec5SDimitry Andric 
51*5ffd83dbSDimitry Andric     CodeGenOpt::Level OptLevel;
520b57cec5SDimitry Andric     DominatorTree *DT = nullptr;
530b57cec5SDimitry Andric     const TargetLowering *TLI = nullptr;
540b57cec5SDimitry Andric 
550b57cec5SDimitry Andric     bool InsertUnwindResumeCalls(Function &Fn);
560b57cec5SDimitry Andric     Value *GetExceptionObject(ResumeInst *RI);
570b57cec5SDimitry Andric     size_t
580b57cec5SDimitry Andric     pruneUnreachableResumes(Function &Fn,
590b57cec5SDimitry Andric                             SmallVectorImpl<ResumeInst *> &Resumes,
600b57cec5SDimitry Andric                             SmallVectorImpl<LandingPadInst *> &CleanupLPads);
610b57cec5SDimitry Andric 
620b57cec5SDimitry Andric   public:
630b57cec5SDimitry Andric     static char ID; // Pass identification, replacement for typeid.
640b57cec5SDimitry Andric 
65*5ffd83dbSDimitry Andric     DwarfEHPrepare(CodeGenOpt::Level OptLevel = CodeGenOpt::Default)
66*5ffd83dbSDimitry Andric       : FunctionPass(ID), OptLevel(OptLevel) {}
670b57cec5SDimitry Andric 
680b57cec5SDimitry Andric     bool runOnFunction(Function &Fn) override;
690b57cec5SDimitry Andric 
700b57cec5SDimitry Andric     bool doFinalization(Module &M) override {
710b57cec5SDimitry Andric       RewindFunction = nullptr;
720b57cec5SDimitry Andric       return false;
730b57cec5SDimitry Andric     }
740b57cec5SDimitry Andric 
750b57cec5SDimitry Andric     void getAnalysisUsage(AnalysisUsage &AU) const override;
760b57cec5SDimitry Andric 
770b57cec5SDimitry Andric     StringRef getPassName() const override {
780b57cec5SDimitry Andric       return "Exception handling preparation";
790b57cec5SDimitry Andric     }
800b57cec5SDimitry Andric   };
810b57cec5SDimitry Andric 
820b57cec5SDimitry Andric } // end anonymous namespace
830b57cec5SDimitry Andric 
840b57cec5SDimitry Andric char DwarfEHPrepare::ID = 0;
850b57cec5SDimitry Andric 
860b57cec5SDimitry Andric INITIALIZE_PASS_BEGIN(DwarfEHPrepare, DEBUG_TYPE,
870b57cec5SDimitry Andric                       "Prepare DWARF exceptions", false, false)
880b57cec5SDimitry Andric INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
890b57cec5SDimitry Andric INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
900b57cec5SDimitry Andric INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
910b57cec5SDimitry Andric INITIALIZE_PASS_END(DwarfEHPrepare, DEBUG_TYPE,
920b57cec5SDimitry Andric                     "Prepare DWARF exceptions", false, false)
930b57cec5SDimitry Andric 
94*5ffd83dbSDimitry Andric FunctionPass *llvm::createDwarfEHPass(CodeGenOpt::Level OptLevel) {
95*5ffd83dbSDimitry Andric   return new DwarfEHPrepare(OptLevel);
96*5ffd83dbSDimitry Andric }
970b57cec5SDimitry Andric 
980b57cec5SDimitry Andric void DwarfEHPrepare::getAnalysisUsage(AnalysisUsage &AU) const {
990b57cec5SDimitry Andric   AU.addRequired<TargetPassConfig>();
1000b57cec5SDimitry Andric   AU.addRequired<TargetTransformInfoWrapperPass>();
101*5ffd83dbSDimitry Andric   if (OptLevel != CodeGenOpt::None)
1020b57cec5SDimitry Andric     AU.addRequired<DominatorTreeWrapperPass>();
1030b57cec5SDimitry Andric }
1040b57cec5SDimitry Andric 
1050b57cec5SDimitry Andric /// GetExceptionObject - Return the exception object from the value passed into
1060b57cec5SDimitry Andric /// the 'resume' instruction (typically an aggregate). Clean up any dead
1070b57cec5SDimitry Andric /// instructions, including the 'resume' instruction.
1080b57cec5SDimitry Andric Value *DwarfEHPrepare::GetExceptionObject(ResumeInst *RI) {
1090b57cec5SDimitry Andric   Value *V = RI->getOperand(0);
1100b57cec5SDimitry Andric   Value *ExnObj = nullptr;
1110b57cec5SDimitry Andric   InsertValueInst *SelIVI = dyn_cast<InsertValueInst>(V);
1120b57cec5SDimitry Andric   LoadInst *SelLoad = nullptr;
1130b57cec5SDimitry Andric   InsertValueInst *ExcIVI = nullptr;
1140b57cec5SDimitry Andric   bool EraseIVIs = false;
1150b57cec5SDimitry Andric 
1160b57cec5SDimitry Andric   if (SelIVI) {
1170b57cec5SDimitry Andric     if (SelIVI->getNumIndices() == 1 && *SelIVI->idx_begin() == 1) {
1180b57cec5SDimitry Andric       ExcIVI = dyn_cast<InsertValueInst>(SelIVI->getOperand(0));
1190b57cec5SDimitry Andric       if (ExcIVI && isa<UndefValue>(ExcIVI->getOperand(0)) &&
1200b57cec5SDimitry Andric           ExcIVI->getNumIndices() == 1 && *ExcIVI->idx_begin() == 0) {
1210b57cec5SDimitry Andric         ExnObj = ExcIVI->getOperand(1);
1220b57cec5SDimitry Andric         SelLoad = dyn_cast<LoadInst>(SelIVI->getOperand(1));
1230b57cec5SDimitry Andric         EraseIVIs = true;
1240b57cec5SDimitry Andric       }
1250b57cec5SDimitry Andric     }
1260b57cec5SDimitry Andric   }
1270b57cec5SDimitry Andric 
1280b57cec5SDimitry Andric   if (!ExnObj)
1290b57cec5SDimitry Andric     ExnObj = ExtractValueInst::Create(RI->getOperand(0), 0, "exn.obj", RI);
1300b57cec5SDimitry Andric 
1310b57cec5SDimitry Andric   RI->eraseFromParent();
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric   if (EraseIVIs) {
1340b57cec5SDimitry Andric     if (SelIVI->use_empty())
1350b57cec5SDimitry Andric       SelIVI->eraseFromParent();
1360b57cec5SDimitry Andric     if (ExcIVI->use_empty())
1370b57cec5SDimitry Andric       ExcIVI->eraseFromParent();
1380b57cec5SDimitry Andric     if (SelLoad && SelLoad->use_empty())
1390b57cec5SDimitry Andric       SelLoad->eraseFromParent();
1400b57cec5SDimitry Andric   }
1410b57cec5SDimitry Andric 
1420b57cec5SDimitry Andric   return ExnObj;
1430b57cec5SDimitry Andric }
1440b57cec5SDimitry Andric 
1450b57cec5SDimitry Andric /// Replace resumes that are not reachable from a cleanup landing pad with
1460b57cec5SDimitry Andric /// unreachable and then simplify those blocks.
1470b57cec5SDimitry Andric size_t DwarfEHPrepare::pruneUnreachableResumes(
1480b57cec5SDimitry Andric     Function &Fn, SmallVectorImpl<ResumeInst *> &Resumes,
1490b57cec5SDimitry Andric     SmallVectorImpl<LandingPadInst *> &CleanupLPads) {
1500b57cec5SDimitry Andric   BitVector ResumeReachable(Resumes.size());
1510b57cec5SDimitry Andric   size_t ResumeIndex = 0;
1520b57cec5SDimitry Andric   for (auto *RI : Resumes) {
1530b57cec5SDimitry Andric     for (auto *LP : CleanupLPads) {
1540b57cec5SDimitry Andric       if (isPotentiallyReachable(LP, RI, nullptr, DT)) {
1550b57cec5SDimitry Andric         ResumeReachable.set(ResumeIndex);
1560b57cec5SDimitry Andric         break;
1570b57cec5SDimitry Andric       }
1580b57cec5SDimitry Andric     }
1590b57cec5SDimitry Andric     ++ResumeIndex;
1600b57cec5SDimitry Andric   }
1610b57cec5SDimitry Andric 
1620b57cec5SDimitry Andric   // If everything is reachable, there is no change.
1630b57cec5SDimitry Andric   if (ResumeReachable.all())
1640b57cec5SDimitry Andric     return Resumes.size();
1650b57cec5SDimitry Andric 
1660b57cec5SDimitry Andric   const TargetTransformInfo &TTI =
1670b57cec5SDimitry Andric       getAnalysis<TargetTransformInfoWrapperPass>().getTTI(Fn);
1680b57cec5SDimitry Andric   LLVMContext &Ctx = Fn.getContext();
1690b57cec5SDimitry Andric 
1700b57cec5SDimitry Andric   // Otherwise, insert unreachable instructions and call simplifycfg.
1710b57cec5SDimitry Andric   size_t ResumesLeft = 0;
1720b57cec5SDimitry Andric   for (size_t I = 0, E = Resumes.size(); I < E; ++I) {
1730b57cec5SDimitry Andric     ResumeInst *RI = Resumes[I];
1740b57cec5SDimitry Andric     if (ResumeReachable[I]) {
1750b57cec5SDimitry Andric       Resumes[ResumesLeft++] = RI;
1760b57cec5SDimitry Andric     } else {
1770b57cec5SDimitry Andric       BasicBlock *BB = RI->getParent();
1780b57cec5SDimitry Andric       new UnreachableInst(Ctx, RI);
1790b57cec5SDimitry Andric       RI->eraseFromParent();
1800b57cec5SDimitry Andric       simplifyCFG(BB, TTI);
1810b57cec5SDimitry Andric     }
1820b57cec5SDimitry Andric   }
1830b57cec5SDimitry Andric   Resumes.resize(ResumesLeft);
1840b57cec5SDimitry Andric   return ResumesLeft;
1850b57cec5SDimitry Andric }
1860b57cec5SDimitry Andric 
1870b57cec5SDimitry Andric /// InsertUnwindResumeCalls - Convert the ResumeInsts that are still present
1880b57cec5SDimitry Andric /// into calls to the appropriate _Unwind_Resume function.
1890b57cec5SDimitry Andric bool DwarfEHPrepare::InsertUnwindResumeCalls(Function &Fn) {
1900b57cec5SDimitry Andric   SmallVector<ResumeInst*, 16> Resumes;
1910b57cec5SDimitry Andric   SmallVector<LandingPadInst*, 16> CleanupLPads;
1920b57cec5SDimitry Andric   for (BasicBlock &BB : Fn) {
1930b57cec5SDimitry Andric     if (auto *RI = dyn_cast<ResumeInst>(BB.getTerminator()))
1940b57cec5SDimitry Andric       Resumes.push_back(RI);
1950b57cec5SDimitry Andric     if (auto *LP = BB.getLandingPadInst())
1960b57cec5SDimitry Andric       if (LP->isCleanup())
1970b57cec5SDimitry Andric         CleanupLPads.push_back(LP);
1980b57cec5SDimitry Andric   }
1990b57cec5SDimitry Andric 
2000b57cec5SDimitry Andric   if (Resumes.empty())
2010b57cec5SDimitry Andric     return false;
2020b57cec5SDimitry Andric 
2030b57cec5SDimitry Andric   // Check the personality, don't do anything if it's scope-based.
2040b57cec5SDimitry Andric   EHPersonality Pers = classifyEHPersonality(Fn.getPersonalityFn());
2050b57cec5SDimitry Andric   if (isScopedEHPersonality(Pers))
2060b57cec5SDimitry Andric     return false;
2070b57cec5SDimitry Andric 
2080b57cec5SDimitry Andric   LLVMContext &Ctx = Fn.getContext();
2090b57cec5SDimitry Andric 
210*5ffd83dbSDimitry Andric   size_t ResumesLeft = Resumes.size();
211*5ffd83dbSDimitry Andric   if (OptLevel != CodeGenOpt::None)
212*5ffd83dbSDimitry Andric     ResumesLeft = pruneUnreachableResumes(Fn, Resumes, CleanupLPads);
213*5ffd83dbSDimitry Andric 
2140b57cec5SDimitry Andric   if (ResumesLeft == 0)
2150b57cec5SDimitry Andric     return true; // We pruned them all.
2160b57cec5SDimitry Andric 
2170b57cec5SDimitry Andric   // Find the rewind function if we didn't already.
2180b57cec5SDimitry Andric   if (!RewindFunction) {
2190b57cec5SDimitry Andric     FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx),
2200b57cec5SDimitry Andric                                           Type::getInt8PtrTy(Ctx), false);
2210b57cec5SDimitry Andric     const char *RewindName = TLI->getLibcallName(RTLIB::UNWIND_RESUME);
2220b57cec5SDimitry Andric     RewindFunction = Fn.getParent()->getOrInsertFunction(RewindName, FTy);
2230b57cec5SDimitry Andric   }
2240b57cec5SDimitry Andric 
2250b57cec5SDimitry Andric   // Create the basic block where the _Unwind_Resume call will live.
2260b57cec5SDimitry Andric   if (ResumesLeft == 1) {
2270b57cec5SDimitry Andric     // Instead of creating a new BB and PHI node, just append the call to
2280b57cec5SDimitry Andric     // _Unwind_Resume to the end of the single resume block.
2290b57cec5SDimitry Andric     ResumeInst *RI = Resumes.front();
2300b57cec5SDimitry Andric     BasicBlock *UnwindBB = RI->getParent();
2310b57cec5SDimitry Andric     Value *ExnObj = GetExceptionObject(RI);
2320b57cec5SDimitry Andric 
2330b57cec5SDimitry Andric     // Call the _Unwind_Resume function.
2340b57cec5SDimitry Andric     CallInst *CI = CallInst::Create(RewindFunction, ExnObj, "", UnwindBB);
2350b57cec5SDimitry Andric     CI->setCallingConv(TLI->getLibcallCallingConv(RTLIB::UNWIND_RESUME));
2360b57cec5SDimitry Andric 
2370b57cec5SDimitry Andric     // We never expect _Unwind_Resume to return.
2380b57cec5SDimitry Andric     new UnreachableInst(Ctx, UnwindBB);
2390b57cec5SDimitry Andric     return true;
2400b57cec5SDimitry Andric   }
2410b57cec5SDimitry Andric 
2420b57cec5SDimitry Andric   BasicBlock *UnwindBB = BasicBlock::Create(Ctx, "unwind_resume", &Fn);
2430b57cec5SDimitry Andric   PHINode *PN = PHINode::Create(Type::getInt8PtrTy(Ctx), ResumesLeft,
2440b57cec5SDimitry Andric                                 "exn.obj", UnwindBB);
2450b57cec5SDimitry Andric 
2460b57cec5SDimitry Andric   // Extract the exception object from the ResumeInst and add it to the PHI node
2470b57cec5SDimitry Andric   // that feeds the _Unwind_Resume call.
2480b57cec5SDimitry Andric   for (ResumeInst *RI : Resumes) {
2490b57cec5SDimitry Andric     BasicBlock *Parent = RI->getParent();
2500b57cec5SDimitry Andric     BranchInst::Create(UnwindBB, Parent);
2510b57cec5SDimitry Andric 
2520b57cec5SDimitry Andric     Value *ExnObj = GetExceptionObject(RI);
2530b57cec5SDimitry Andric     PN->addIncoming(ExnObj, Parent);
2540b57cec5SDimitry Andric 
2550b57cec5SDimitry Andric     ++NumResumesLowered;
2560b57cec5SDimitry Andric   }
2570b57cec5SDimitry Andric 
2580b57cec5SDimitry Andric   // Call the function.
2590b57cec5SDimitry Andric   CallInst *CI = CallInst::Create(RewindFunction, PN, "", UnwindBB);
2600b57cec5SDimitry Andric   CI->setCallingConv(TLI->getLibcallCallingConv(RTLIB::UNWIND_RESUME));
2610b57cec5SDimitry Andric 
2620b57cec5SDimitry Andric   // We never expect _Unwind_Resume to return.
2630b57cec5SDimitry Andric   new UnreachableInst(Ctx, UnwindBB);
2640b57cec5SDimitry Andric   return true;
2650b57cec5SDimitry Andric }
2660b57cec5SDimitry Andric 
2670b57cec5SDimitry Andric bool DwarfEHPrepare::runOnFunction(Function &Fn) {
2680b57cec5SDimitry Andric   const TargetMachine &TM =
2690b57cec5SDimitry Andric       getAnalysis<TargetPassConfig>().getTM<TargetMachine>();
270*5ffd83dbSDimitry Andric   DT = OptLevel != CodeGenOpt::None
271*5ffd83dbSDimitry Andric       ? &getAnalysis<DominatorTreeWrapperPass>().getDomTree() : nullptr;
2720b57cec5SDimitry Andric   TLI = TM.getSubtargetImpl(Fn)->getTargetLowering();
2730b57cec5SDimitry Andric   bool Changed = InsertUnwindResumeCalls(Fn);
2740b57cec5SDimitry Andric   DT = nullptr;
2750b57cec5SDimitry Andric   TLI = nullptr;
2760b57cec5SDimitry Andric   return Changed;
2770b57cec5SDimitry Andric }
278