/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/lib/CodeGen/WasmEHPrepare.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- WasmEHPrepare - Prepare excepton handling for WebAssembly --------===// |
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 transformation is designed for use by code generators which use |
10 | | // WebAssembly exception handling scheme. This currently supports C++ |
11 | | // exceptions. |
12 | | // |
13 | | // WebAssembly exception handling uses Windows exception IR for the middle level |
14 | | // representation. This pass does the following transformation for every |
15 | | // catchpad block: |
16 | | // (In C-style pseudocode) |
17 | | // |
18 | | // - Before: |
19 | | // catchpad ... |
20 | | // exn = wasm.get.exception(); |
21 | | // selector = wasm.get.selector(); |
22 | | // ... |
23 | | // |
24 | | // - After: |
25 | | // catchpad ... |
26 | | // exn = wasm.extract.exception(); |
27 | | // // Only add below in case it's not a single catch (...) |
28 | | // wasm.landingpad.index(index); |
29 | | // __wasm_lpad_context.lpad_index = index; |
30 | | // __wasm_lpad_context.lsda = wasm.lsda(); |
31 | | // _Unwind_CallPersonality(exn); |
32 | | // selector = __wasm.landingpad_context.selector; |
33 | | // ... |
34 | | // |
35 | | // |
36 | | // * Background: Direct personality function call |
37 | | // In WebAssembly EH, the VM is responsible for unwinding the stack once an |
38 | | // exception is thrown. After the stack is unwound, the control flow is |
39 | | // transfered to WebAssembly 'catch' instruction. |
40 | | // |
41 | | // Unwinding the stack is not done by libunwind but the VM, so the personality |
42 | | // function in libcxxabi cannot be called from libunwind during the unwinding |
43 | | // process. So after a catch instruction, we insert a call to a wrapper function |
44 | | // in libunwind that in turn calls the real personality function. |
45 | | // |
46 | | // In Itanium EH, if the personality function decides there is no matching catch |
47 | | // clause in a call frame and no cleanup action to perform, the unwinder doesn't |
48 | | // stop there and continues unwinding. But in Wasm EH, the unwinder stops at |
49 | | // every call frame with a catch intruction, after which the personality |
50 | | // function is called from the compiler-generated user code here. |
51 | | // |
52 | | // In libunwind, we have this struct that serves as a communincation channel |
53 | | // between the compiler-generated user code and the personality function in |
54 | | // libcxxabi. |
55 | | // |
56 | | // struct _Unwind_LandingPadContext { |
57 | | // uintptr_t lpad_index; |
58 | | // uintptr_t lsda; |
59 | | // uintptr_t selector; |
60 | | // }; |
61 | | // struct _Unwind_LandingPadContext __wasm_lpad_context = ...; |
62 | | // |
63 | | // And this wrapper in libunwind calls the personality function. |
64 | | // |
65 | | // _Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) { |
66 | | // struct _Unwind_Exception *exception_obj = |
67 | | // (struct _Unwind_Exception *)exception_ptr; |
68 | | // _Unwind_Reason_Code ret = __gxx_personality_v0( |
69 | | // 1, _UA_CLEANUP_PHASE, exception_obj->exception_class, exception_obj, |
70 | | // (struct _Unwind_Context *)__wasm_lpad_context); |
71 | | // return ret; |
72 | | // } |
73 | | // |
74 | | // We pass a landing pad index, and the address of LSDA for the current function |
75 | | // to the wrapper function _Unwind_CallPersonality in libunwind, and we retrieve |
76 | | // the selector after it returns. |
77 | | // |
78 | | //===----------------------------------------------------------------------===// |
79 | | |
80 | | #include "llvm/ADT/SetVector.h" |
81 | | #include "llvm/ADT/Statistic.h" |
82 | | #include "llvm/ADT/Triple.h" |
83 | | #include "llvm/CodeGen/Passes.h" |
84 | | #include "llvm/CodeGen/TargetLowering.h" |
85 | | #include "llvm/CodeGen/TargetSubtargetInfo.h" |
86 | | #include "llvm/CodeGen/WasmEHFuncInfo.h" |
87 | | #include "llvm/IR/Dominators.h" |
88 | | #include "llvm/IR/IRBuilder.h" |
89 | | #include "llvm/IR/Intrinsics.h" |
90 | | #include "llvm/Pass.h" |
91 | | #include "llvm/Transforms/Utils/BasicBlockUtils.h" |
92 | | |
93 | | using namespace llvm; |
94 | | |
95 | | #define DEBUG_TYPE "wasmehprepare" |
96 | | |
97 | | namespace { |
98 | | class WasmEHPrepare : public FunctionPass { |
99 | | Type *LPadContextTy = nullptr; // type of 'struct _Unwind_LandingPadContext' |
100 | | GlobalVariable *LPadContextGV = nullptr; // __wasm_lpad_context |
101 | | |
102 | | // Field addresses of struct _Unwind_LandingPadContext |
103 | | Value *LPadIndexField = nullptr; // lpad_index field |
104 | | Value *LSDAField = nullptr; // lsda field |
105 | | Value *SelectorField = nullptr; // selector |
106 | | |
107 | | Function *ThrowF = nullptr; // wasm.throw() intrinsic |
108 | | Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic |
109 | | Function *LSDAF = nullptr; // wasm.lsda() intrinsic |
110 | | Function *GetExnF = nullptr; // wasm.get.exception() intrinsic |
111 | | Function *ExtractExnF = nullptr; // wasm.extract.exception() intrinsic |
112 | | Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic |
113 | | FunctionCallee CallPersonalityF = |
114 | | nullptr; // _Unwind_CallPersonality() wrapper |
115 | | |
116 | | bool prepareEHPads(Function &F); |
117 | | bool prepareThrows(Function &F); |
118 | | |
119 | | void prepareEHPad(BasicBlock *BB, bool NeedLSDA, unsigned Index = 0); |
120 | | void prepareTerminateCleanupPad(BasicBlock *BB); |
121 | | |
122 | | public: |
123 | | static char ID; // Pass identification, replacement for typeid |
124 | | |
125 | 15 | WasmEHPrepare() : FunctionPass(ID) {} |
126 | | |
127 | | bool doInitialization(Module &M) override; |
128 | | bool runOnFunction(Function &F) override; |
129 | | |
130 | 59 | StringRef getPassName() const override { |
131 | 59 | return "WebAssembly Exception handling preparation"; |
132 | 59 | } |
133 | | }; |
134 | | } // end anonymous namespace |
135 | | |
136 | | char WasmEHPrepare::ID = 0; |
137 | | INITIALIZE_PASS(WasmEHPrepare, DEBUG_TYPE, "Prepare WebAssembly exceptions", |
138 | | false, false) |
139 | | |
140 | 14 | FunctionPass *llvm::createWasmEHPass() { return new WasmEHPrepare(); } |
141 | | |
142 | 15 | bool WasmEHPrepare::doInitialization(Module &M) { |
143 | 15 | IRBuilder<> IRB(M.getContext()); |
144 | 15 | LPadContextTy = StructType::get(IRB.getInt32Ty(), // lpad_index |
145 | 15 | IRB.getInt8PtrTy(), // lsda |
146 | 15 | IRB.getInt32Ty() // selector |
147 | 15 | ); |
148 | 15 | return false; |
149 | 15 | } |
150 | | |
151 | | // Erase the specified BBs if the BB does not have any remaining predecessors, |
152 | | // and also all its dead children. |
153 | | template <typename Container> |
154 | 12 | static void eraseDeadBBsAndChildren(const Container &BBs) { |
155 | 12 | SmallVector<BasicBlock *, 8> WL(BBs.begin(), BBs.end()); |
156 | 14 | while (!WL.empty()) { |
157 | 2 | auto *BB = WL.pop_back_val(); |
158 | 2 | if (pred_begin(BB) != pred_end(BB)) |
159 | 1 | continue; |
160 | 1 | WL.append(succ_begin(BB), succ_end(BB)); |
161 | 1 | DeleteDeadBlock(BB); |
162 | 1 | } |
163 | 12 | } |
164 | | |
165 | 59 | bool WasmEHPrepare::runOnFunction(Function &F) { |
166 | 59 | bool Changed = false; |
167 | 59 | Changed |= prepareThrows(F); |
168 | 59 | Changed |= prepareEHPads(F); |
169 | 59 | return Changed; |
170 | 59 | } |
171 | | |
172 | 59 | bool WasmEHPrepare::prepareThrows(Function &F) { |
173 | 59 | Module &M = *F.getParent(); |
174 | 59 | IRBuilder<> IRB(F.getContext()); |
175 | 59 | bool Changed = false; |
176 | 59 | |
177 | 59 | // wasm.throw() intinsic, which will be lowered to wasm 'throw' instruction. |
178 | 59 | ThrowF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_throw); |
179 | 59 | // Insert an unreachable instruction after a call to @llvm.wasm.throw and |
180 | 59 | // delete all following instructions within the BB, and delete all the dead |
181 | 59 | // children of the BB as well. |
182 | 59 | for (User *U : ThrowF->users()) { |
183 | 33 | // A call to @llvm.wasm.throw() is only generated from __cxa_throw() |
184 | 33 | // builtin call within libcxxabi, and cannot be an InvokeInst. |
185 | 33 | auto *ThrowI = cast<CallInst>(U); |
186 | 33 | if (ThrowI->getFunction() != &F) |
187 | 21 | continue; |
188 | 12 | Changed = true; |
189 | 12 | auto *BB = ThrowI->getParent(); |
190 | 12 | SmallVector<BasicBlock *, 4> Succs(succ_begin(BB), succ_end(BB)); |
191 | 12 | auto &InstList = BB->getInstList(); |
192 | 12 | InstList.erase(std::next(BasicBlock::iterator(ThrowI)), InstList.end()); |
193 | 12 | IRB.SetInsertPoint(BB); |
194 | 12 | IRB.CreateUnreachable(); |
195 | 12 | eraseDeadBBsAndChildren(Succs); |
196 | 12 | } |
197 | 59 | |
198 | 59 | return Changed; |
199 | 59 | } |
200 | | |
201 | 59 | bool WasmEHPrepare::prepareEHPads(Function &F) { |
202 | 59 | Module &M = *F.getParent(); |
203 | 59 | IRBuilder<> IRB(F.getContext()); |
204 | 59 | |
205 | 59 | SmallVector<BasicBlock *, 16> CatchPads; |
206 | 59 | SmallVector<BasicBlock *, 16> CleanupPads; |
207 | 365 | for (BasicBlock &BB : F) { |
208 | 365 | if (!BB.isEHPad()) |
209 | 225 | continue; |
210 | 140 | auto *Pad = BB.getFirstNonPHI(); |
211 | 140 | if (isa<CatchPadInst>(Pad)) |
212 | 58 | CatchPads.push_back(&BB); |
213 | 82 | else if (isa<CleanupPadInst>(Pad)) |
214 | 24 | CleanupPads.push_back(&BB); |
215 | 140 | } |
216 | 59 | |
217 | 59 | if (CatchPads.empty() && CleanupPads.empty()15 ) |
218 | 13 | return false; |
219 | 46 | assert(F.hasPersonalityFn() && "Personality function not found"); |
220 | 46 | |
221 | 46 | // __wasm_lpad_context global variable |
222 | 46 | LPadContextGV = cast<GlobalVariable>( |
223 | 46 | M.getOrInsertGlobal("__wasm_lpad_context", LPadContextTy)); |
224 | 46 | LPadIndexField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 0, |
225 | 46 | "lpad_index_gep"); |
226 | 46 | LSDAField = |
227 | 46 | IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 1, "lsda_gep"); |
228 | 46 | SelectorField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 2, |
229 | 46 | "selector_gep"); |
230 | 46 | |
231 | 46 | // wasm.landingpad.index() intrinsic, which is to specify landingpad index |
232 | 46 | LPadIndexF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_landingpad_index); |
233 | 46 | // wasm.lsda() intrinsic. Returns the address of LSDA table for the current |
234 | 46 | // function. |
235 | 46 | LSDAF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_lsda); |
236 | 46 | // wasm.get.exception() and wasm.get.ehselector() intrinsics. Calls to these |
237 | 46 | // are generated in clang. |
238 | 46 | GetExnF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_exception); |
239 | 46 | GetSelectorF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_ehselector); |
240 | 46 | |
241 | 46 | // wasm.extract.exception() is the same as wasm.get.exception() but it does |
242 | 46 | // not take a token argument. This will be lowered down to EXTRACT_EXCEPTION |
243 | 46 | // pseudo instruction in instruction selection, which will be expanded using |
244 | 46 | // 'br_on_exn' instruction later. |
245 | 46 | ExtractExnF = |
246 | 46 | Intrinsic::getDeclaration(&M, Intrinsic::wasm_extract_exception); |
247 | 46 | |
248 | 46 | // _Unwind_CallPersonality() wrapper function, which calls the personality |
249 | 46 | CallPersonalityF = M.getOrInsertFunction( |
250 | 46 | "_Unwind_CallPersonality", IRB.getInt32Ty(), IRB.getInt8PtrTy()); |
251 | 46 | if (Function *F = dyn_cast<Function>(CallPersonalityF.getCallee())) |
252 | 46 | F->setDoesNotThrow(); |
253 | 46 | |
254 | 46 | unsigned Index = 0; |
255 | 58 | for (auto *BB : CatchPads) { |
256 | 58 | auto *CPI = cast<CatchPadInst>(BB->getFirstNonPHI()); |
257 | 58 | // In case of a single catch (...), we don't need to emit LSDA |
258 | 58 | if (CPI->getNumArgOperands() == 1 && |
259 | 58 | cast<Constant>(CPI->getArgOperand(0))->isNullValue()52 ) |
260 | 38 | prepareEHPad(BB, false); |
261 | 20 | else |
262 | 20 | prepareEHPad(BB, true, Index++); |
263 | 58 | } |
264 | 46 | |
265 | 46 | // Cleanup pads don't need LSDA. |
266 | 46 | for (auto *BB : CleanupPads) |
267 | 24 | prepareEHPad(BB, false); |
268 | 46 | |
269 | 46 | return true; |
270 | 46 | } |
271 | | |
272 | | // Prepare an EH pad for Wasm EH handling. If NeedLSDA is false, Index is |
273 | | // ignored. |
274 | | void WasmEHPrepare::prepareEHPad(BasicBlock *BB, bool NeedLSDA, |
275 | 82 | unsigned Index) { |
276 | 82 | assert(BB->isEHPad() && "BB is not an EHPad!"); |
277 | 82 | IRBuilder<> IRB(BB->getContext()); |
278 | 82 | IRB.SetInsertPoint(&*BB->getFirstInsertionPt()); |
279 | 82 | |
280 | 82 | auto *FPI = cast<FuncletPadInst>(BB->getFirstNonPHI()); |
281 | 82 | Instruction *GetExnCI = nullptr, *GetSelectorCI = nullptr; |
282 | 380 | for (auto &U : FPI->uses()) { |
283 | 380 | if (auto *CI = dyn_cast<CallInst>(U.getUser())) { |
284 | 246 | if (CI->getCalledValue() == GetExnF) |
285 | 64 | GetExnCI = CI; |
286 | 246 | if (CI->getCalledValue() == GetSelectorF) |
287 | 58 | GetSelectorCI = CI; |
288 | 246 | } |
289 | 380 | } |
290 | 82 | |
291 | 82 | // Cleanup pads w/o __clang_call_terminate call do not have any of |
292 | 82 | // wasm.get.exception() or wasm.get.ehselector() calls. We need to do nothing. |
293 | 82 | if (!GetExnCI) { |
294 | 18 | assert(!GetSelectorCI && |
295 | 18 | "wasm.get.ehselector() cannot exist w/o wasm.get.exception()"); |
296 | 18 | return; |
297 | 18 | } |
298 | 64 | |
299 | 64 | Instruction *ExtractExnCI = IRB.CreateCall(ExtractExnF, {}, "exn"); |
300 | 64 | GetExnCI->replaceAllUsesWith(ExtractExnCI); |
301 | 64 | GetExnCI->eraseFromParent(); |
302 | 64 | |
303 | 64 | // In case it is a catchpad with single catch (...) or a cleanuppad, we don't |
304 | 64 | // need to call personality function because we don't need a selector. |
305 | 64 | if (!NeedLSDA) { |
306 | 44 | if (GetSelectorCI) { |
307 | 38 | assert(GetSelectorCI->use_empty() && |
308 | 38 | "wasm.get.ehselector() still has uses!"); |
309 | 38 | GetSelectorCI->eraseFromParent(); |
310 | 38 | } |
311 | 44 | return; |
312 | 44 | } |
313 | 20 | IRB.SetInsertPoint(ExtractExnCI->getNextNode()); |
314 | 20 | |
315 | 20 | // This is to create a map of <landingpad EH label, landingpad index> in |
316 | 20 | // SelectionDAGISel, which is to be used in EHStreamer to emit LSDA tables. |
317 | 20 | // Pseudocode: wasm.landingpad.index(Index); |
318 | 20 | IRB.CreateCall(LPadIndexF, {FPI, IRB.getInt32(Index)}); |
319 | 20 | |
320 | 20 | // Pseudocode: __wasm_lpad_context.lpad_index = index; |
321 | 20 | IRB.CreateStore(IRB.getInt32(Index), LPadIndexField); |
322 | 20 | |
323 | 20 | // Store LSDA address only if this catchpad belongs to a top-level |
324 | 20 | // catchswitch. If there is another catchpad that dominates this pad, we don't |
325 | 20 | // need to store LSDA address again, because they are the same throughout the |
326 | 20 | // function and have been already stored before. |
327 | 20 | // TODO Can we not store LSDA address in user function but make libcxxabi |
328 | 20 | // compute it? |
329 | 20 | auto *CPI = cast<CatchPadInst>(FPI); |
330 | 20 | if (isa<ConstantTokenNone>(CPI->getCatchSwitch()->getParentPad())) |
331 | 16 | // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda(); |
332 | 16 | IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField); |
333 | 20 | |
334 | 20 | // Pseudocode: _Unwind_CallPersonality(exn); |
335 | 20 | CallInst *PersCI = IRB.CreateCall(CallPersonalityF, ExtractExnCI, |
336 | 20 | OperandBundleDef("funclet", CPI)); |
337 | 20 | PersCI->setDoesNotThrow(); |
338 | 20 | |
339 | 20 | // Pseudocode: int selector = __wasm.landingpad_context.selector; |
340 | 20 | Instruction *Selector = |
341 | 20 | IRB.CreateLoad(IRB.getInt32Ty(), SelectorField, "selector"); |
342 | 20 | |
343 | 20 | // Replace the return value from wasm.get.ehselector() with the selector value |
344 | 20 | // loaded from __wasm_lpad_context.selector. |
345 | 20 | assert(GetSelectorCI && "wasm.get.ehselector() call does not exist"); |
346 | 20 | GetSelectorCI->replaceAllUsesWith(Selector); |
347 | 20 | GetSelectorCI->eraseFromParent(); |
348 | 20 | } |
349 | | |
350 | 41 | void llvm::calculateWasmEHInfo(const Function *F, WasmEHFuncInfo &EHInfo) { |
351 | 41 | // If an exception is not caught by a catchpad (i.e., it is a foreign |
352 | 41 | // exception), it will unwind to its parent catchswitch's unwind destination. |
353 | 41 | // We don't record an unwind destination for cleanuppads because every |
354 | 41 | // exception should be caught by it. |
355 | 302 | for (const auto &BB : *F) { |
356 | 302 | if (!BB.isEHPad()) |
357 | 182 | continue; |
358 | 120 | const Instruction *Pad = BB.getFirstNonPHI(); |
359 | 120 | |
360 | 120 | if (const auto *CatchPad = dyn_cast<CatchPadInst>(Pad)) { |
361 | 50 | const auto *UnwindBB = CatchPad->getCatchSwitch()->getUnwindDest(); |
362 | 50 | if (!UnwindBB) |
363 | 47 | continue; |
364 | 3 | const Instruction *UnwindPad = UnwindBB->getFirstNonPHI(); |
365 | 3 | if (const auto *CatchSwitch = dyn_cast<CatchSwitchInst>(UnwindPad)) |
366 | 0 | // Currently there should be only one handler per a catchswitch. |
367 | 0 | EHInfo.setEHPadUnwindDest(&BB, *CatchSwitch->handlers().begin()); |
368 | 3 | else // cleanuppad |
369 | 3 | EHInfo.setEHPadUnwindDest(&BB, UnwindBB); |
370 | 3 | } |
371 | 120 | } |
372 | 41 | } |