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