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/CodeGen/DwarfEHPrepare.h" |
15 | #include "llvm/ADT/BitVector.h" |
16 | #include "llvm/ADT/SmallVector.h" |
17 | #include "llvm/ADT/Statistic.h" |
18 | #include "llvm/Analysis/CFG.h" |
19 | #include "llvm/Analysis/DomTreeUpdater.h" |
20 | #include "llvm/Analysis/TargetTransformInfo.h" |
21 | #include "llvm/CodeGen/RuntimeLibcalls.h" |
22 | #include "llvm/CodeGen/TargetLowering.h" |
23 | #include "llvm/CodeGen/TargetPassConfig.h" |
24 | #include "llvm/CodeGen/TargetSubtargetInfo.h" |
25 | #include "llvm/IR/BasicBlock.h" |
26 | #include "llvm/IR/Constants.h" |
27 | #include "llvm/IR/DebugInfoMetadata.h" |
28 | #include "llvm/IR/DerivedTypes.h" |
29 | #include "llvm/IR/Dominators.h" |
30 | #include "llvm/IR/EHPersonalities.h" |
31 | #include "llvm/IR/Function.h" |
32 | #include "llvm/IR/Instructions.h" |
33 | #include "llvm/IR/Module.h" |
34 | #include "llvm/IR/Type.h" |
35 | #include "llvm/InitializePasses.h" |
36 | #include "llvm/Pass.h" |
37 | #include "llvm/Support/Casting.h" |
38 | #include "llvm/Target/TargetMachine.h" |
39 | #include "llvm/TargetParser/Triple.h" |
40 | #include "llvm/Transforms/Utils/Local.h" |
41 | #include <cstddef> |
42 | |
43 | using namespace llvm; |
44 | |
45 | #define DEBUG_TYPE "dwarf-eh-prepare" |
46 | |
47 | STATISTIC(NumResumesLowered, "Number of resume calls lowered" ); |
48 | STATISTIC(NumCleanupLandingPadsUnreachable, |
49 | "Number of cleanup landing pads found unreachable" ); |
50 | STATISTIC(NumCleanupLandingPadsRemaining, |
51 | "Number of cleanup landing pads remaining" ); |
52 | STATISTIC(NumNoUnwind, "Number of functions with nounwind" ); |
53 | STATISTIC(NumUnwind, "Number of functions with unwind" ); |
54 | |
55 | namespace { |
56 | |
57 | class DwarfEHPrepare { |
58 | CodeGenOptLevel OptLevel; |
59 | |
60 | Function &F; |
61 | const TargetLowering &TLI; |
62 | DomTreeUpdater *DTU; |
63 | const TargetTransformInfo *TTI; |
64 | const Triple &TargetTriple; |
65 | |
66 | /// Return the exception object from the value passed into |
67 | /// the 'resume' instruction (typically an aggregate). Clean up any dead |
68 | /// instructions, including the 'resume' instruction. |
69 | Value *GetExceptionObject(ResumeInst *RI); |
70 | |
71 | /// Replace resumes that are not reachable from a cleanup landing pad with |
72 | /// unreachable and then simplify those blocks. |
73 | size_t |
74 | pruneUnreachableResumes(SmallVectorImpl<ResumeInst *> &Resumes, |
75 | SmallVectorImpl<LandingPadInst *> &CleanupLPads); |
76 | |
77 | /// Convert the ResumeInsts that are still present |
78 | /// into calls to the appropriate _Unwind_Resume function. |
79 | bool InsertUnwindResumeCalls(); |
80 | |
81 | public: |
82 | DwarfEHPrepare(CodeGenOptLevel OptLevel_, Function &F_, |
83 | const TargetLowering &TLI_, DomTreeUpdater *DTU_, |
84 | const TargetTransformInfo *TTI_, const Triple &TargetTriple_) |
85 | : OptLevel(OptLevel_), F(F_), TLI(TLI_), DTU(DTU_), TTI(TTI_), |
86 | TargetTriple(TargetTriple_) {} |
87 | |
88 | bool run(); |
89 | }; |
90 | |
91 | } // namespace |
92 | |
93 | Value *DwarfEHPrepare::GetExceptionObject(ResumeInst *RI) { |
94 | Value *V = RI->getOperand(i_nocapture: 0); |
95 | Value *ExnObj = nullptr; |
96 | InsertValueInst *SelIVI = dyn_cast<InsertValueInst>(Val: V); |
97 | LoadInst *SelLoad = nullptr; |
98 | InsertValueInst *ExcIVI = nullptr; |
99 | bool EraseIVIs = false; |
100 | |
101 | if (SelIVI) { |
102 | if (SelIVI->getNumIndices() == 1 && *SelIVI->idx_begin() == 1) { |
103 | ExcIVI = dyn_cast<InsertValueInst>(Val: SelIVI->getOperand(i_nocapture: 0)); |
104 | if (ExcIVI && isa<UndefValue>(Val: ExcIVI->getOperand(i_nocapture: 0)) && |
105 | ExcIVI->getNumIndices() == 1 && *ExcIVI->idx_begin() == 0) { |
106 | ExnObj = ExcIVI->getOperand(i_nocapture: 1); |
107 | SelLoad = dyn_cast<LoadInst>(Val: SelIVI->getOperand(i_nocapture: 1)); |
108 | EraseIVIs = true; |
109 | } |
110 | } |
111 | } |
112 | |
113 | if (!ExnObj) |
114 | ExnObj = ExtractValueInst::Create(Agg: RI->getOperand(i_nocapture: 0), Idxs: 0, NameStr: "exn.obj" , InsertBefore: RI); |
115 | |
116 | RI->eraseFromParent(); |
117 | |
118 | if (EraseIVIs) { |
119 | if (SelIVI->use_empty()) |
120 | SelIVI->eraseFromParent(); |
121 | if (ExcIVI->use_empty()) |
122 | ExcIVI->eraseFromParent(); |
123 | if (SelLoad && SelLoad->use_empty()) |
124 | SelLoad->eraseFromParent(); |
125 | } |
126 | |
127 | return ExnObj; |
128 | } |
129 | |
130 | size_t DwarfEHPrepare::pruneUnreachableResumes( |
131 | SmallVectorImpl<ResumeInst *> &Resumes, |
132 | SmallVectorImpl<LandingPadInst *> &CleanupLPads) { |
133 | assert(DTU && "Should have DomTreeUpdater here." ); |
134 | |
135 | BitVector ResumeReachable(Resumes.size()); |
136 | size_t ResumeIndex = 0; |
137 | for (auto *RI : Resumes) { |
138 | for (auto *LP : CleanupLPads) { |
139 | if (isPotentiallyReachable(From: LP, To: RI, ExclusionSet: nullptr, DT: &DTU->getDomTree())) { |
140 | ResumeReachable.set(ResumeIndex); |
141 | break; |
142 | } |
143 | } |
144 | ++ResumeIndex; |
145 | } |
146 | |
147 | // If everything is reachable, there is no change. |
148 | if (ResumeReachable.all()) |
149 | return Resumes.size(); |
150 | |
151 | LLVMContext &Ctx = F.getContext(); |
152 | |
153 | // Otherwise, insert unreachable instructions and call simplifycfg. |
154 | size_t ResumesLeft = 0; |
155 | for (size_t I = 0, E = Resumes.size(); I < E; ++I) { |
156 | ResumeInst *RI = Resumes[I]; |
157 | if (ResumeReachable[I]) { |
158 | Resumes[ResumesLeft++] = RI; |
159 | } else { |
160 | BasicBlock *BB = RI->getParent(); |
161 | new UnreachableInst(Ctx, RI); |
162 | RI->eraseFromParent(); |
163 | simplifyCFG(BB, TTI: *TTI, DTU); |
164 | } |
165 | } |
166 | Resumes.resize(N: ResumesLeft); |
167 | return ResumesLeft; |
168 | } |
169 | |
170 | bool DwarfEHPrepare::InsertUnwindResumeCalls() { |
171 | SmallVector<ResumeInst *, 16> Resumes; |
172 | SmallVector<LandingPadInst *, 16> CleanupLPads; |
173 | if (F.doesNotThrow()) |
174 | NumNoUnwind++; |
175 | else |
176 | NumUnwind++; |
177 | for (BasicBlock &BB : F) { |
178 | if (auto *RI = dyn_cast<ResumeInst>(Val: BB.getTerminator())) |
179 | Resumes.push_back(Elt: RI); |
180 | if (auto *LP = BB.getLandingPadInst()) |
181 | if (LP->isCleanup()) |
182 | CleanupLPads.push_back(Elt: LP); |
183 | } |
184 | |
185 | NumCleanupLandingPadsRemaining += CleanupLPads.size(); |
186 | |
187 | if (Resumes.empty()) |
188 | return false; |
189 | |
190 | // Check the personality, don't do anything if it's scope-based. |
191 | EHPersonality Pers = classifyEHPersonality(Pers: F.getPersonalityFn()); |
192 | if (isScopedEHPersonality(Pers)) |
193 | return false; |
194 | |
195 | LLVMContext &Ctx = F.getContext(); |
196 | |
197 | size_t ResumesLeft = Resumes.size(); |
198 | if (OptLevel != CodeGenOptLevel::None) { |
199 | ResumesLeft = pruneUnreachableResumes(Resumes, CleanupLPads); |
200 | #if LLVM_ENABLE_STATS |
201 | unsigned NumRemainingLPs = 0; |
202 | for (BasicBlock &BB : F) { |
203 | if (auto *LP = BB.getLandingPadInst()) |
204 | if (LP->isCleanup()) |
205 | NumRemainingLPs++; |
206 | } |
207 | NumCleanupLandingPadsUnreachable += CleanupLPads.size() - NumRemainingLPs; |
208 | NumCleanupLandingPadsRemaining -= CleanupLPads.size() - NumRemainingLPs; |
209 | #endif |
210 | } |
211 | |
212 | if (ResumesLeft == 0) |
213 | return true; // We pruned them all. |
214 | |
215 | // RewindFunction - _Unwind_Resume or the target equivalent. |
216 | FunctionCallee RewindFunction; |
217 | CallingConv::ID RewindFunctionCallingConv; |
218 | FunctionType *FTy; |
219 | const char *RewindName; |
220 | bool DoesRewindFunctionNeedExceptionObject; |
221 | |
222 | if ((Pers == EHPersonality::GNU_CXX || Pers == EHPersonality::GNU_CXX_SjLj) && |
223 | TargetTriple.isTargetEHABICompatible()) { |
224 | RewindName = TLI.getLibcallName(Call: RTLIB::CXA_END_CLEANUP); |
225 | FTy = FunctionType::get(Result: Type::getVoidTy(C&: Ctx), isVarArg: false); |
226 | RewindFunctionCallingConv = |
227 | TLI.getLibcallCallingConv(Call: RTLIB::CXA_END_CLEANUP); |
228 | DoesRewindFunctionNeedExceptionObject = false; |
229 | } else { |
230 | RewindName = TLI.getLibcallName(Call: RTLIB::UNWIND_RESUME); |
231 | FTy = FunctionType::get(Result: Type::getVoidTy(C&: Ctx), Params: PointerType::getUnqual(C&: Ctx), |
232 | isVarArg: false); |
233 | RewindFunctionCallingConv = TLI.getLibcallCallingConv(Call: RTLIB::UNWIND_RESUME); |
234 | DoesRewindFunctionNeedExceptionObject = true; |
235 | } |
236 | RewindFunction = F.getParent()->getOrInsertFunction(Name: RewindName, T: FTy); |
237 | |
238 | // Create the basic block where the _Unwind_Resume call will live. |
239 | if (ResumesLeft == 1) { |
240 | // Instead of creating a new BB and PHI node, just append the call to |
241 | // _Unwind_Resume to the end of the single resume block. |
242 | ResumeInst *RI = Resumes.front(); |
243 | BasicBlock *UnwindBB = RI->getParent(); |
244 | Value *ExnObj = GetExceptionObject(RI); |
245 | llvm::SmallVector<Value *, 1> RewindFunctionArgs; |
246 | if (DoesRewindFunctionNeedExceptionObject) |
247 | RewindFunctionArgs.push_back(Elt: ExnObj); |
248 | |
249 | // Call the rewind function. |
250 | CallInst *CI = |
251 | CallInst::Create(Func: RewindFunction, Args: RewindFunctionArgs, NameStr: "" , InsertAtEnd: UnwindBB); |
252 | // The verifier requires that all calls of debug-info-bearing functions |
253 | // from debug-info-bearing functions have a debug location (for inlining |
254 | // purposes). Assign a dummy location to satisfy the constraint. |
255 | Function *RewindFn = dyn_cast<Function>(Val: RewindFunction.getCallee()); |
256 | if (RewindFn && RewindFn->getSubprogram()) |
257 | if (DISubprogram *SP = F.getSubprogram()) |
258 | CI->setDebugLoc(DILocation::get(Context&: SP->getContext(), Line: 0, Column: 0, Scope: SP)); |
259 | CI->setCallingConv(RewindFunctionCallingConv); |
260 | |
261 | // We never expect _Unwind_Resume to return. |
262 | CI->setDoesNotReturn(); |
263 | new UnreachableInst(Ctx, UnwindBB); |
264 | return true; |
265 | } |
266 | |
267 | std::vector<DominatorTree::UpdateType> Updates; |
268 | Updates.reserve(n: Resumes.size()); |
269 | |
270 | llvm::SmallVector<Value *, 1> RewindFunctionArgs; |
271 | |
272 | BasicBlock *UnwindBB = BasicBlock::Create(Context&: Ctx, Name: "unwind_resume" , Parent: &F); |
273 | PHINode *PN = PHINode::Create(Ty: PointerType::getUnqual(C&: Ctx), NumReservedValues: ResumesLeft, |
274 | NameStr: "exn.obj" , InsertAtEnd: UnwindBB); |
275 | |
276 | // Extract the exception object from the ResumeInst and add it to the PHI node |
277 | // that feeds the _Unwind_Resume call. |
278 | for (ResumeInst *RI : Resumes) { |
279 | BasicBlock *Parent = RI->getParent(); |
280 | BranchInst::Create(IfTrue: UnwindBB, InsertAtEnd: Parent); |
281 | Updates.push_back(x: {DominatorTree::Insert, Parent, UnwindBB}); |
282 | |
283 | Value *ExnObj = GetExceptionObject(RI); |
284 | PN->addIncoming(V: ExnObj, BB: Parent); |
285 | |
286 | ++NumResumesLowered; |
287 | } |
288 | |
289 | if (DoesRewindFunctionNeedExceptionObject) |
290 | RewindFunctionArgs.push_back(Elt: PN); |
291 | |
292 | // Call the function. |
293 | CallInst *CI = |
294 | CallInst::Create(Func: RewindFunction, Args: RewindFunctionArgs, NameStr: "" , InsertAtEnd: UnwindBB); |
295 | CI->setCallingConv(RewindFunctionCallingConv); |
296 | |
297 | // We never expect _Unwind_Resume to return. |
298 | CI->setDoesNotReturn(); |
299 | new UnreachableInst(Ctx, UnwindBB); |
300 | |
301 | if (DTU) |
302 | DTU->applyUpdates(Updates); |
303 | |
304 | return true; |
305 | } |
306 | |
307 | bool DwarfEHPrepare::run() { |
308 | bool Changed = InsertUnwindResumeCalls(); |
309 | |
310 | return Changed; |
311 | } |
312 | |
313 | static bool prepareDwarfEH(CodeGenOptLevel OptLevel, Function &F, |
314 | const TargetLowering &TLI, DominatorTree *DT, |
315 | const TargetTransformInfo *TTI, |
316 | const Triple &TargetTriple) { |
317 | DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Lazy); |
318 | |
319 | return DwarfEHPrepare(OptLevel, F, TLI, DT ? &DTU : nullptr, TTI, |
320 | TargetTriple) |
321 | .run(); |
322 | } |
323 | |
324 | namespace { |
325 | |
326 | class DwarfEHPrepareLegacyPass : public FunctionPass { |
327 | |
328 | CodeGenOptLevel OptLevel; |
329 | |
330 | public: |
331 | static char ID; // Pass identification, replacement for typeid. |
332 | |
333 | DwarfEHPrepareLegacyPass(CodeGenOptLevel OptLevel = CodeGenOptLevel::Default) |
334 | : FunctionPass(ID), OptLevel(OptLevel) {} |
335 | |
336 | bool runOnFunction(Function &F) override { |
337 | const TargetMachine &TM = |
338 | getAnalysis<TargetPassConfig>().getTM<TargetMachine>(); |
339 | const TargetLowering &TLI = *TM.getSubtargetImpl(F)->getTargetLowering(); |
340 | DominatorTree *DT = nullptr; |
341 | const TargetTransformInfo *TTI = nullptr; |
342 | if (auto *DTWP = getAnalysisIfAvailable<DominatorTreeWrapperPass>()) |
343 | DT = &DTWP->getDomTree(); |
344 | if (OptLevel != CodeGenOptLevel::None) { |
345 | if (!DT) |
346 | DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree(); |
347 | TTI = &getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F); |
348 | } |
349 | return prepareDwarfEH(OptLevel, F, TLI, DT, TTI, TargetTriple: TM.getTargetTriple()); |
350 | } |
351 | |
352 | void getAnalysisUsage(AnalysisUsage &AU) const override { |
353 | AU.addRequired<TargetPassConfig>(); |
354 | AU.addRequired<TargetTransformInfoWrapperPass>(); |
355 | if (OptLevel != CodeGenOptLevel::None) { |
356 | AU.addRequired<DominatorTreeWrapperPass>(); |
357 | AU.addRequired<TargetTransformInfoWrapperPass>(); |
358 | } |
359 | AU.addPreserved<DominatorTreeWrapperPass>(); |
360 | } |
361 | |
362 | StringRef getPassName() const override { |
363 | return "Exception handling preparation" ; |
364 | } |
365 | }; |
366 | |
367 | } // end anonymous namespace |
368 | |
369 | PreservedAnalyses DwarfEHPreparePass::run(Function &F, |
370 | FunctionAnalysisManager &FAM) { |
371 | const auto &TLI = *TM->getSubtargetImpl(F)->getTargetLowering(); |
372 | auto *DT = FAM.getCachedResult<DominatorTreeAnalysis>(IR&: F); |
373 | const TargetTransformInfo *TTI = nullptr; |
374 | auto OptLevel = TM->getOptLevel(); |
375 | if (OptLevel != CodeGenOptLevel::None) { |
376 | if (!DT) |
377 | DT = &FAM.getResult<DominatorTreeAnalysis>(IR&: F); |
378 | TTI = &FAM.getResult<TargetIRAnalysis>(IR&: F); |
379 | } |
380 | bool Changed = |
381 | prepareDwarfEH(OptLevel, F, TLI, DT, TTI, TargetTriple: TM->getTargetTriple()); |
382 | |
383 | if (!Changed) |
384 | return PreservedAnalyses::all(); |
385 | PreservedAnalyses PA; |
386 | PA.preserve<DominatorTreeAnalysis>(); |
387 | return PA; |
388 | } |
389 | |
390 | char DwarfEHPrepareLegacyPass::ID = 0; |
391 | |
392 | INITIALIZE_PASS_BEGIN(DwarfEHPrepareLegacyPass, DEBUG_TYPE, |
393 | "Prepare DWARF exceptions" , false, false) |
394 | INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) |
395 | INITIALIZE_PASS_DEPENDENCY(TargetPassConfig) |
396 | INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass) |
397 | INITIALIZE_PASS_END(DwarfEHPrepareLegacyPass, DEBUG_TYPE, |
398 | "Prepare DWARF exceptions" , false, false) |
399 | |
400 | FunctionPass *llvm::createDwarfEHPass(CodeGenOptLevel OptLevel) { |
401 | return new DwarfEHPrepareLegacyPass(OptLevel); |
402 | } |
403 | |