1 //===-- WasmEHPrepare - Prepare excepton handling for WebAssembly --------===//
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
7 //===----------------------------------------------------------------------===//
9 // This transformation is designed for use by code generators which use
10 // WebAssembly exception handling scheme. This currently supports C++
13 // WebAssembly exception handling uses Windows exception IR for the middle level
14 // representation. This pass does the following transformation for every
16 // (In C-style pseudocode)
20 // exn = wasm.get.exception();
21 // selector = wasm.get.selector();
26 // exn = wasm.catch(WebAssembly::CPP_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_lpad_context.selector;
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.
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.
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.
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
56 // struct _Unwind_LandingPadContext {
57 // uintptr_t lpad_index;
59 // uintptr_t selector;
61 // struct _Unwind_LandingPadContext __wasm_lpad_context = ...;
63 // And this wrapper in libunwind calls the personality function.
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);
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.
78 //===----------------------------------------------------------------------===//
80 #include "llvm/CodeGen/TargetLowering.h"
81 #include "llvm/CodeGen/TargetSubtargetInfo.h"
82 #include "llvm/CodeGen/WasmEHFuncInfo.h"
83 #include "llvm/IR/IRBuilder.h"
84 #include "llvm/IR/IntrinsicsWebAssembly.h"
85 #include "llvm/InitializePasses.h"
86 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
90 #define DEBUG_TYPE "wasmehprepare"
93 class WasmEHPrepare
: public FunctionPass
{
94 Type
*LPadContextTy
= nullptr; // type of 'struct _Unwind_LandingPadContext'
95 GlobalVariable
*LPadContextGV
= nullptr; // __wasm_lpad_context
97 // Field addresses of struct _Unwind_LandingPadContext
98 Value
*LPadIndexField
= nullptr; // lpad_index field
99 Value
*LSDAField
= nullptr; // lsda field
100 Value
*SelectorField
= nullptr; // selector
102 Function
*ThrowF
= nullptr; // wasm.throw() intrinsic
103 Function
*LPadIndexF
= nullptr; // wasm.landingpad.index() intrinsic
104 Function
*LSDAF
= nullptr; // wasm.lsda() intrinsic
105 Function
*GetExnF
= nullptr; // wasm.get.exception() intrinsic
106 Function
*CatchF
= nullptr; // wasm.catch() intrinsic
107 Function
*GetSelectorF
= nullptr; // wasm.get.ehselector() intrinsic
108 FunctionCallee CallPersonalityF
=
109 nullptr; // _Unwind_CallPersonality() wrapper
111 bool prepareThrows(Function
&F
);
112 bool prepareEHPads(Function
&F
);
113 void prepareEHPad(BasicBlock
*BB
, bool NeedPersonality
, unsigned Index
= 0);
116 static char ID
; // Pass identification, replacement for typeid
118 WasmEHPrepare() : FunctionPass(ID
) {}
119 bool doInitialization(Module
&M
) override
;
120 bool runOnFunction(Function
&F
) override
;
122 StringRef
getPassName() const override
{
123 return "WebAssembly Exception handling preparation";
126 } // end anonymous namespace
128 char WasmEHPrepare::ID
= 0;
129 INITIALIZE_PASS_BEGIN(WasmEHPrepare
, DEBUG_TYPE
,
130 "Prepare WebAssembly exceptions", false, false)
131 INITIALIZE_PASS_END(WasmEHPrepare
, DEBUG_TYPE
, "Prepare WebAssembly exceptions",
134 FunctionPass
*llvm::createWasmEHPass() { return new WasmEHPrepare(); }
136 bool WasmEHPrepare::doInitialization(Module
&M
) {
137 IRBuilder
<> IRB(M
.getContext());
138 LPadContextTy
= StructType::get(IRB
.getInt32Ty(), // lpad_index
139 IRB
.getInt8PtrTy(), // lsda
140 IRB
.getInt32Ty() // selector
145 // Erase the specified BBs if the BB does not have any remaining predecessors,
146 // and also all its dead children.
147 template <typename Container
>
148 static void eraseDeadBBsAndChildren(const Container
&BBs
) {
149 SmallVector
<BasicBlock
*, 8> WL(BBs
.begin(), BBs
.end());
150 while (!WL
.empty()) {
151 auto *BB
= WL
.pop_back_val();
154 WL
.append(succ_begin(BB
), succ_end(BB
));
159 bool WasmEHPrepare::runOnFunction(Function
&F
) {
160 bool Changed
= false;
161 Changed
|= prepareThrows(F
);
162 Changed
|= prepareEHPads(F
);
166 bool WasmEHPrepare::prepareThrows(Function
&F
) {
167 Module
&M
= *F
.getParent();
168 IRBuilder
<> IRB(F
.getContext());
169 bool Changed
= false;
171 // wasm.throw() intinsic, which will be lowered to wasm 'throw' instruction.
172 ThrowF
= Intrinsic::getDeclaration(&M
, Intrinsic::wasm_throw
);
173 // Insert an unreachable instruction after a call to @llvm.wasm.throw and
174 // delete all following instructions within the BB, and delete all the dead
175 // children of the BB as well.
176 for (User
*U
: ThrowF
->users()) {
177 // A call to @llvm.wasm.throw() is only generated from __cxa_throw()
178 // builtin call within libcxxabi, and cannot be an InvokeInst.
179 auto *ThrowI
= cast
<CallInst
>(U
);
180 if (ThrowI
->getFunction() != &F
)
183 auto *BB
= ThrowI
->getParent();
184 SmallVector
<BasicBlock
*, 4> Succs(successors(BB
));
185 auto &InstList
= BB
->getInstList();
186 InstList
.erase(std::next(BasicBlock::iterator(ThrowI
)), InstList
.end());
187 IRB
.SetInsertPoint(BB
);
188 IRB
.CreateUnreachable();
189 eraseDeadBBsAndChildren(Succs
);
195 bool WasmEHPrepare::prepareEHPads(Function
&F
) {
196 Module
&M
= *F
.getParent();
197 IRBuilder
<> IRB(F
.getContext());
199 SmallVector
<BasicBlock
*, 16> CatchPads
;
200 SmallVector
<BasicBlock
*, 16> CleanupPads
;
201 for (BasicBlock
&BB
: F
) {
204 auto *Pad
= BB
.getFirstNonPHI();
205 if (isa
<CatchPadInst
>(Pad
))
206 CatchPads
.push_back(&BB
);
207 else if (isa
<CleanupPadInst
>(Pad
))
208 CleanupPads
.push_back(&BB
);
210 if (CatchPads
.empty() && CleanupPads
.empty())
213 assert(F
.hasPersonalityFn() && "Personality function not found");
215 // __wasm_lpad_context global variable
216 LPadContextGV
= cast
<GlobalVariable
>(
217 M
.getOrInsertGlobal("__wasm_lpad_context", LPadContextTy
));
218 LPadIndexField
= IRB
.CreateConstGEP2_32(LPadContextTy
, LPadContextGV
, 0, 0,
221 IRB
.CreateConstGEP2_32(LPadContextTy
, LPadContextGV
, 0, 1, "lsda_gep");
222 SelectorField
= IRB
.CreateConstGEP2_32(LPadContextTy
, LPadContextGV
, 0, 2,
225 // wasm.landingpad.index() intrinsic, which is to specify landingpad index
226 LPadIndexF
= Intrinsic::getDeclaration(&M
, Intrinsic::wasm_landingpad_index
);
227 // wasm.lsda() intrinsic. Returns the address of LSDA table for the current
229 LSDAF
= Intrinsic::getDeclaration(&M
, Intrinsic::wasm_lsda
);
230 // wasm.get.exception() and wasm.get.ehselector() intrinsics. Calls to these
231 // are generated in clang.
232 GetExnF
= Intrinsic::getDeclaration(&M
, Intrinsic::wasm_get_exception
);
233 GetSelectorF
= Intrinsic::getDeclaration(&M
, Intrinsic::wasm_get_ehselector
);
235 // wasm.catch() will be lowered down to wasm 'catch' instruction in
236 // instruction selection.
237 CatchF
= Intrinsic::getDeclaration(&M
, Intrinsic::wasm_catch
);
239 // _Unwind_CallPersonality() wrapper function, which calls the personality
240 CallPersonalityF
= M
.getOrInsertFunction(
241 "_Unwind_CallPersonality", IRB
.getInt32Ty(), IRB
.getInt8PtrTy());
242 if (Function
*F
= dyn_cast
<Function
>(CallPersonalityF
.getCallee()))
243 F
->setDoesNotThrow();
246 for (auto *BB
: CatchPads
) {
247 auto *CPI
= cast
<CatchPadInst
>(BB
->getFirstNonPHI());
248 // In case of a single catch (...), we don't need to emit a personalify
250 if (CPI
->getNumArgOperands() == 1 &&
251 cast
<Constant
>(CPI
->getArgOperand(0))->isNullValue())
252 prepareEHPad(BB
, false);
254 prepareEHPad(BB
, true, Index
++);
257 // Cleanup pads don't need a personality function call.
258 for (auto *BB
: CleanupPads
)
259 prepareEHPad(BB
, false);
264 // Prepare an EH pad for Wasm EH handling. If NeedPersonality is false, Index is
266 void WasmEHPrepare::prepareEHPad(BasicBlock
*BB
, bool NeedPersonality
,
268 assert(BB
->isEHPad() && "BB is not an EHPad!");
269 IRBuilder
<> IRB(BB
->getContext());
270 IRB
.SetInsertPoint(&*BB
->getFirstInsertionPt());
272 auto *FPI
= cast
<FuncletPadInst
>(BB
->getFirstNonPHI());
273 Instruction
*GetExnCI
= nullptr, *GetSelectorCI
= nullptr;
274 for (auto &U
: FPI
->uses()) {
275 if (auto *CI
= dyn_cast
<CallInst
>(U
.getUser())) {
276 if (CI
->getCalledOperand() == GetExnF
)
278 if (CI
->getCalledOperand() == GetSelectorF
)
283 // Cleanup pads do not have any of wasm.get.exception() or
284 // wasm.get.ehselector() calls. We need to do nothing.
286 assert(!GetSelectorCI
&&
287 "wasm.get.ehselector() cannot exist w/o wasm.get.exception()");
291 // Replace wasm.get.exception intrinsic with wasm.catch intrinsic, which will
292 // be lowered to wasm 'catch' instruction. We do this mainly because
293 // instruction selection cannot handle wasm.get.exception intrinsic's token
295 Instruction
*CatchCI
=
296 IRB
.CreateCall(CatchF
, {IRB
.getInt32(WebAssembly::CPP_EXCEPTION
)}, "exn");
297 GetExnCI
->replaceAllUsesWith(CatchCI
);
298 GetExnCI
->eraseFromParent();
300 // In case it is a catchpad with single catch (...) or a cleanuppad, we don't
301 // need to call personality function because we don't need a selector.
302 if (!NeedPersonality
) {
304 assert(GetSelectorCI
->use_empty() &&
305 "wasm.get.ehselector() still has uses!");
306 GetSelectorCI
->eraseFromParent();
310 IRB
.SetInsertPoint(CatchCI
->getNextNode());
312 // This is to create a map of <landingpad EH label, landingpad index> in
313 // SelectionDAGISel, which is to be used in EHStreamer to emit LSDA tables.
314 // Pseudocode: wasm.landingpad.index(Index);
315 IRB
.CreateCall(LPadIndexF
, {FPI
, IRB
.getInt32(Index
)});
317 // Pseudocode: __wasm_lpad_context.lpad_index = index;
318 IRB
.CreateStore(IRB
.getInt32(Index
), LPadIndexField
);
320 auto *CPI
= cast
<CatchPadInst
>(FPI
);
321 // TODO Sometimes storing the LSDA address every time is not necessary, in
322 // case it is already set in a dominating EH pad and there is no function call
323 // between from that EH pad to here. Consider optimizing those cases.
324 // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
325 IRB
.CreateStore(IRB
.CreateCall(LSDAF
), LSDAField
);
327 // Pseudocode: _Unwind_CallPersonality(exn);
328 CallInst
*PersCI
= IRB
.CreateCall(CallPersonalityF
, CatchCI
,
329 OperandBundleDef("funclet", CPI
));
330 PersCI
->setDoesNotThrow();
332 // Pseudocode: int selector = __wasm_lpad_context.selector;
333 Instruction
*Selector
=
334 IRB
.CreateLoad(IRB
.getInt32Ty(), SelectorField
, "selector");
336 // Replace the return value from wasm.get.ehselector() with the selector value
337 // loaded from __wasm_lpad_context.selector.
338 assert(GetSelectorCI
&& "wasm.get.ehselector() call does not exist");
339 GetSelectorCI
->replaceAllUsesWith(Selector
);
340 GetSelectorCI
->eraseFromParent();
343 void llvm::calculateWasmEHInfo(const Function
*F
, WasmEHFuncInfo
&EHInfo
) {
344 // If an exception is not caught by a catchpad (i.e., it is a foreign
345 // exception), it will unwind to its parent catchswitch's unwind destination.
346 // We don't record an unwind destination for cleanuppads because every
347 // exception should be caught by it.
348 for (const auto &BB
: *F
) {
351 const Instruction
*Pad
= BB
.getFirstNonPHI();
353 if (const auto *CatchPad
= dyn_cast
<CatchPadInst
>(Pad
)) {
354 const auto *UnwindBB
= CatchPad
->getCatchSwitch()->getUnwindDest();
357 const Instruction
*UnwindPad
= UnwindBB
->getFirstNonPHI();
358 if (const auto *CatchSwitch
= dyn_cast
<CatchSwitchInst
>(UnwindPad
))
359 // Currently there should be only one handler per a catchswitch.
360 EHInfo
.setUnwindDest(&BB
, *CatchSwitch
->handlers().begin());
362 EHInfo
.setUnwindDest(&BB
, UnwindBB
);