1 //===- DwarfEHPrepare - Prepare exception handling for code generation ----===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This pass mulches exception handling code into a form adapted to code
10 // generation. Required if using dwarf exception handling.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/ADT/BitVector.h"
15 #include "llvm/ADT/SmallVector.h"
16 #include "llvm/ADT/Statistic.h"
17 #include "llvm/Analysis/CFG.h"
18 #include "llvm/Analysis/EHPersonalities.h"
19 #include "llvm/Analysis/TargetTransformInfo.h"
20 #include "llvm/CodeGen/RuntimeLibcalls.h"
21 #include "llvm/CodeGen/TargetLowering.h"
22 #include "llvm/CodeGen/TargetPassConfig.h"
23 #include "llvm/CodeGen/TargetSubtargetInfo.h"
24 #include "llvm/IR/BasicBlock.h"
25 #include "llvm/IR/Constants.h"
26 #include "llvm/IR/DerivedTypes.h"
27 #include "llvm/IR/Dominators.h"
28 #include "llvm/IR/Function.h"
29 #include "llvm/IR/Instructions.h"
30 #include "llvm/IR/Module.h"
31 #include "llvm/IR/Type.h"
32 #include "llvm/InitializePasses.h"
33 #include "llvm/Pass.h"
34 #include "llvm/Support/Casting.h"
35 #include "llvm/Target/TargetMachine.h"
36 #include "llvm/Transforms/Utils/Local.h"
37 #include <cstddef>
38 
39 using namespace llvm;
40 
41 #define DEBUG_TYPE "dwarfehprepare"
42 
43 STATISTIC(NumResumesLowered, "Number of resume calls lowered");
44 
45 namespace {
46 
47   class DwarfEHPrepare : public FunctionPass {
48     // RewindFunction - _Unwind_Resume or the target equivalent.
49     FunctionCallee RewindFunction = nullptr;
50 
51     CodeGenOpt::Level OptLevel;
52     DominatorTree *DT = nullptr;
53     const TargetLowering *TLI = nullptr;
54 
55     bool InsertUnwindResumeCalls(Function &Fn);
56     Value *GetExceptionObject(ResumeInst *RI);
57     size_t
58     pruneUnreachableResumes(Function &Fn,
59                             SmallVectorImpl<ResumeInst *> &Resumes,
60                             SmallVectorImpl<LandingPadInst *> &CleanupLPads);
61 
62   public:
63     static char ID; // Pass identification, replacement for typeid.
64 
DwarfEHPrepare(CodeGenOpt::Level OptLevel=CodeGenOpt::Default)65     DwarfEHPrepare(CodeGenOpt::Level OptLevel = CodeGenOpt::Default)
66       : FunctionPass(ID), OptLevel(OptLevel) {}
67 
68     bool runOnFunction(Function &Fn) override;
69 
doFinalization(Module & M)70     bool doFinalization(Module &M) override {
71       RewindFunction = nullptr;
72       return false;
73     }
74 
75     void getAnalysisUsage(AnalysisUsage &AU) const override;
76 
getPassName() const77     StringRef getPassName() const override {
78       return "Exception handling preparation";
79     }
80   };
81 
82 } // end anonymous namespace
83 
84 char DwarfEHPrepare::ID = 0;
85 
86 INITIALIZE_PASS_BEGIN(DwarfEHPrepare, DEBUG_TYPE,
87                       "Prepare DWARF exceptions", false, false)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)88 INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
89 INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
90 INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
91 INITIALIZE_PASS_END(DwarfEHPrepare, DEBUG_TYPE,
92                     "Prepare DWARF exceptions", false, false)
93 
94 FunctionPass *llvm::createDwarfEHPass(CodeGenOpt::Level OptLevel) {
95   return new DwarfEHPrepare(OptLevel);
96 }
97 
getAnalysisUsage(AnalysisUsage & AU) const98 void DwarfEHPrepare::getAnalysisUsage(AnalysisUsage &AU) const {
99   AU.addRequired<TargetPassConfig>();
100   AU.addRequired<TargetTransformInfoWrapperPass>();
101   if (OptLevel != CodeGenOpt::None)
102     AU.addRequired<DominatorTreeWrapperPass>();
103 }
104 
105 /// GetExceptionObject - Return the exception object from the value passed into
106 /// the 'resume' instruction (typically an aggregate). Clean up any dead
107 /// instructions, including the 'resume' instruction.
GetExceptionObject(ResumeInst * RI)108 Value *DwarfEHPrepare::GetExceptionObject(ResumeInst *RI) {
109   Value *V = RI->getOperand(0);
110   Value *ExnObj = nullptr;
111   InsertValueInst *SelIVI = dyn_cast<InsertValueInst>(V);
112   LoadInst *SelLoad = nullptr;
113   InsertValueInst *ExcIVI = nullptr;
114   bool EraseIVIs = false;
115 
116   if (SelIVI) {
117     if (SelIVI->getNumIndices() == 1 && *SelIVI->idx_begin() == 1) {
118       ExcIVI = dyn_cast<InsertValueInst>(SelIVI->getOperand(0));
119       if (ExcIVI && isa<UndefValue>(ExcIVI->getOperand(0)) &&
120           ExcIVI->getNumIndices() == 1 && *ExcIVI->idx_begin() == 0) {
121         ExnObj = ExcIVI->getOperand(1);
122         SelLoad = dyn_cast<LoadInst>(SelIVI->getOperand(1));
123         EraseIVIs = true;
124       }
125     }
126   }
127 
128   if (!ExnObj)
129     ExnObj = ExtractValueInst::Create(RI->getOperand(0), 0, "exn.obj", RI);
130 
131   RI->eraseFromParent();
132 
133   if (EraseIVIs) {
134     if (SelIVI->use_empty())
135       SelIVI->eraseFromParent();
136     if (ExcIVI->use_empty())
137       ExcIVI->eraseFromParent();
138     if (SelLoad && SelLoad->use_empty())
139       SelLoad->eraseFromParent();
140   }
141 
142   return ExnObj;
143 }
144 
145 /// Replace resumes that are not reachable from a cleanup landing pad with
146 /// unreachable and then simplify those blocks.
pruneUnreachableResumes(Function & Fn,SmallVectorImpl<ResumeInst * > & Resumes,SmallVectorImpl<LandingPadInst * > & CleanupLPads)147 size_t DwarfEHPrepare::pruneUnreachableResumes(
148     Function &Fn, SmallVectorImpl<ResumeInst *> &Resumes,
149     SmallVectorImpl<LandingPadInst *> &CleanupLPads) {
150   BitVector ResumeReachable(Resumes.size());
151   size_t ResumeIndex = 0;
152   for (auto *RI : Resumes) {
153     for (auto *LP : CleanupLPads) {
154       if (isPotentiallyReachable(LP, RI, nullptr, DT)) {
155         ResumeReachable.set(ResumeIndex);
156         break;
157       }
158     }
159     ++ResumeIndex;
160   }
161 
162   // If everything is reachable, there is no change.
163   if (ResumeReachable.all())
164     return Resumes.size();
165 
166   const TargetTransformInfo &TTI =
167       getAnalysis<TargetTransformInfoWrapperPass>().getTTI(Fn);
168   LLVMContext &Ctx = Fn.getContext();
169 
170   // Otherwise, insert unreachable instructions and call simplifycfg.
171   size_t ResumesLeft = 0;
172   for (size_t I = 0, E = Resumes.size(); I < E; ++I) {
173     ResumeInst *RI = Resumes[I];
174     if (ResumeReachable[I]) {
175       Resumes[ResumesLeft++] = RI;
176     } else {
177       BasicBlock *BB = RI->getParent();
178       new UnreachableInst(Ctx, RI);
179       RI->eraseFromParent();
180       simplifyCFG(BB, TTI);
181     }
182   }
183   Resumes.resize(ResumesLeft);
184   return ResumesLeft;
185 }
186 
187 /// InsertUnwindResumeCalls - Convert the ResumeInsts that are still present
188 /// into calls to the appropriate _Unwind_Resume function.
InsertUnwindResumeCalls(Function & Fn)189 bool DwarfEHPrepare::InsertUnwindResumeCalls(Function &Fn) {
190   SmallVector<ResumeInst*, 16> Resumes;
191   SmallVector<LandingPadInst*, 16> CleanupLPads;
192   for (BasicBlock &BB : Fn) {
193     if (auto *RI = dyn_cast<ResumeInst>(BB.getTerminator()))
194       Resumes.push_back(RI);
195     if (auto *LP = BB.getLandingPadInst())
196       if (LP->isCleanup())
197         CleanupLPads.push_back(LP);
198   }
199 
200   if (Resumes.empty())
201     return false;
202 
203   // Check the personality, don't do anything if it's scope-based.
204   EHPersonality Pers = classifyEHPersonality(Fn.getPersonalityFn());
205   if (isScopedEHPersonality(Pers))
206     return false;
207 
208   LLVMContext &Ctx = Fn.getContext();
209 
210   size_t ResumesLeft = Resumes.size();
211   if (OptLevel != CodeGenOpt::None)
212     ResumesLeft = pruneUnreachableResumes(Fn, Resumes, CleanupLPads);
213 
214   if (ResumesLeft == 0)
215     return true; // We pruned them all.
216 
217   // Find the rewind function if we didn't already.
218   if (!RewindFunction) {
219     FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx),
220                                           Type::getInt8PtrTy(Ctx), false);
221     const char *RewindName = TLI->getLibcallName(RTLIB::UNWIND_RESUME);
222     RewindFunction = Fn.getParent()->getOrInsertFunction(RewindName, FTy);
223   }
224 
225   // Create the basic block where the _Unwind_Resume call will live.
226   if (ResumesLeft == 1) {
227     // Instead of creating a new BB and PHI node, just append the call to
228     // _Unwind_Resume to the end of the single resume block.
229     ResumeInst *RI = Resumes.front();
230     BasicBlock *UnwindBB = RI->getParent();
231     Value *ExnObj = GetExceptionObject(RI);
232 
233     // Call the _Unwind_Resume function.
234     CallInst *CI = CallInst::Create(RewindFunction, ExnObj, "", UnwindBB);
235     CI->setCallingConv(TLI->getLibcallCallingConv(RTLIB::UNWIND_RESUME));
236 
237     // We never expect _Unwind_Resume to return.
238     new UnreachableInst(Ctx, UnwindBB);
239     return true;
240   }
241 
242   BasicBlock *UnwindBB = BasicBlock::Create(Ctx, "unwind_resume", &Fn);
243   PHINode *PN = PHINode::Create(Type::getInt8PtrTy(Ctx), ResumesLeft,
244                                 "exn.obj", UnwindBB);
245 
246   // Extract the exception object from the ResumeInst and add it to the PHI node
247   // that feeds the _Unwind_Resume call.
248   for (ResumeInst *RI : Resumes) {
249     BasicBlock *Parent = RI->getParent();
250     BranchInst::Create(UnwindBB, Parent);
251 
252     Value *ExnObj = GetExceptionObject(RI);
253     PN->addIncoming(ExnObj, Parent);
254 
255     ++NumResumesLowered;
256   }
257 
258   // Call the function.
259   CallInst *CI = CallInst::Create(RewindFunction, PN, "", UnwindBB);
260   CI->setCallingConv(TLI->getLibcallCallingConv(RTLIB::UNWIND_RESUME));
261 
262   // We never expect _Unwind_Resume to return.
263   new UnreachableInst(Ctx, UnwindBB);
264   return true;
265 }
266 
runOnFunction(Function & Fn)267 bool DwarfEHPrepare::runOnFunction(Function &Fn) {
268   const TargetMachine &TM =
269       getAnalysis<TargetPassConfig>().getTM<TargetMachine>();
270   DT = OptLevel != CodeGenOpt::None
271       ? &getAnalysis<DominatorTreeWrapperPass>().getDomTree() : nullptr;
272   TLI = TM.getSubtargetImpl(Fn)->getTargetLowering();
273   bool Changed = InsertUnwindResumeCalls(Fn);
274   DT = nullptr;
275   TLI = nullptr;
276   return Changed;
277 }
278