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.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;
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/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"
95 #define DEBUG_TYPE "wasmehprepare"
98 class WasmEHPrepare
: public FunctionPass
{
99 Type
*LPadContextTy
= nullptr; // type of 'struct _Unwind_LandingPadContext'
100 GlobalVariable
*LPadContextGV
= nullptr; // __wasm_lpad_context
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
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
116 bool prepareEHPads(Function
&F
);
117 bool prepareThrows(Function
&F
);
119 void prepareEHPad(BasicBlock
*BB
, bool NeedLSDA
, unsigned Index
= 0);
120 void prepareTerminateCleanupPad(BasicBlock
*BB
);
123 static char ID
; // Pass identification, replacement for typeid
125 WasmEHPrepare() : FunctionPass(ID
) {}
127 bool doInitialization(Module
&M
) override
;
128 bool runOnFunction(Function
&F
) override
;
130 StringRef
getPassName() const override
{
131 return "WebAssembly Exception handling preparation";
134 } // end anonymous namespace
136 char WasmEHPrepare::ID
= 0;
137 INITIALIZE_PASS(WasmEHPrepare
, DEBUG_TYPE
, "Prepare WebAssembly exceptions",
140 FunctionPass
*llvm::createWasmEHPass() { return new WasmEHPrepare(); }
142 bool WasmEHPrepare::doInitialization(Module
&M
) {
143 IRBuilder
<> IRB(M
.getContext());
144 LPadContextTy
= StructType::get(IRB
.getInt32Ty(), // lpad_index
145 IRB
.getInt8PtrTy(), // lsda
146 IRB
.getInt32Ty() // selector
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 static void eraseDeadBBsAndChildren(const Container
&BBs
) {
155 SmallVector
<BasicBlock
*, 8> WL(BBs
.begin(), BBs
.end());
156 while (!WL
.empty()) {
157 auto *BB
= WL
.pop_back_val();
158 if (pred_begin(BB
) != pred_end(BB
))
160 WL
.append(succ_begin(BB
), succ_end(BB
));
165 bool WasmEHPrepare::runOnFunction(Function
&F
) {
166 bool Changed
= false;
167 Changed
|= prepareThrows(F
);
168 Changed
|= prepareEHPads(F
);
172 bool WasmEHPrepare::prepareThrows(Function
&F
) {
173 Module
&M
= *F
.getParent();
174 IRBuilder
<> IRB(F
.getContext());
175 bool Changed
= false;
177 // wasm.throw() intinsic, which will be lowered to wasm 'throw' instruction.
178 ThrowF
= Intrinsic::getDeclaration(&M
, Intrinsic::wasm_throw
);
179 // Insert an unreachable instruction after a call to @llvm.wasm.throw and
180 // delete all following instructions within the BB, and delete all the dead
181 // children of the BB as well.
182 for (User
*U
: ThrowF
->users()) {
183 // A call to @llvm.wasm.throw() is only generated from __cxa_throw()
184 // builtin call within libcxxabi, and cannot be an InvokeInst.
185 auto *ThrowI
= cast
<CallInst
>(U
);
186 if (ThrowI
->getFunction() != &F
)
189 auto *BB
= ThrowI
->getParent();
190 SmallVector
<BasicBlock
*, 4> Succs(succ_begin(BB
), succ_end(BB
));
191 auto &InstList
= BB
->getInstList();
192 InstList
.erase(std::next(BasicBlock::iterator(ThrowI
)), InstList
.end());
193 IRB
.SetInsertPoint(BB
);
194 IRB
.CreateUnreachable();
195 eraseDeadBBsAndChildren(Succs
);
201 bool WasmEHPrepare::prepareEHPads(Function
&F
) {
202 Module
&M
= *F
.getParent();
203 IRBuilder
<> IRB(F
.getContext());
205 SmallVector
<BasicBlock
*, 16> CatchPads
;
206 SmallVector
<BasicBlock
*, 16> CleanupPads
;
207 for (BasicBlock
&BB
: F
) {
210 auto *Pad
= BB
.getFirstNonPHI();
211 if (isa
<CatchPadInst
>(Pad
))
212 CatchPads
.push_back(&BB
);
213 else if (isa
<CleanupPadInst
>(Pad
))
214 CleanupPads
.push_back(&BB
);
217 if (CatchPads
.empty() && CleanupPads
.empty())
219 assert(F
.hasPersonalityFn() && "Personality function not found");
221 // __wasm_lpad_context global variable
222 LPadContextGV
= cast
<GlobalVariable
>(
223 M
.getOrInsertGlobal("__wasm_lpad_context", LPadContextTy
));
224 LPadIndexField
= IRB
.CreateConstGEP2_32(LPadContextTy
, LPadContextGV
, 0, 0,
227 IRB
.CreateConstGEP2_32(LPadContextTy
, LPadContextGV
, 0, 1, "lsda_gep");
228 SelectorField
= IRB
.CreateConstGEP2_32(LPadContextTy
, LPadContextGV
, 0, 2,
231 // wasm.landingpad.index() intrinsic, which is to specify landingpad index
232 LPadIndexF
= Intrinsic::getDeclaration(&M
, Intrinsic::wasm_landingpad_index
);
233 // wasm.lsda() intrinsic. Returns the address of LSDA table for the current
235 LSDAF
= Intrinsic::getDeclaration(&M
, Intrinsic::wasm_lsda
);
236 // wasm.get.exception() and wasm.get.ehselector() intrinsics. Calls to these
237 // are generated in clang.
238 GetExnF
= Intrinsic::getDeclaration(&M
, Intrinsic::wasm_get_exception
);
239 GetSelectorF
= Intrinsic::getDeclaration(&M
, Intrinsic::wasm_get_ehselector
);
241 // wasm.extract.exception() is the same as wasm.get.exception() but it does
242 // not take a token argument. This will be lowered down to EXTRACT_EXCEPTION
243 // pseudo instruction in instruction selection, which will be expanded using
244 // 'br_on_exn' instruction later.
246 Intrinsic::getDeclaration(&M
, Intrinsic::wasm_extract_exception
);
248 // _Unwind_CallPersonality() wrapper function, which calls the personality
249 CallPersonalityF
= M
.getOrInsertFunction(
250 "_Unwind_CallPersonality", IRB
.getInt32Ty(), IRB
.getInt8PtrTy());
251 if (Function
*F
= dyn_cast
<Function
>(CallPersonalityF
.getCallee()))
252 F
->setDoesNotThrow();
255 for (auto *BB
: CatchPads
) {
256 auto *CPI
= cast
<CatchPadInst
>(BB
->getFirstNonPHI());
257 // In case of a single catch (...), we don't need to emit LSDA
258 if (CPI
->getNumArgOperands() == 1 &&
259 cast
<Constant
>(CPI
->getArgOperand(0))->isNullValue())
260 prepareEHPad(BB
, false);
262 prepareEHPad(BB
, true, Index
++);
265 // Cleanup pads don't need LSDA.
266 for (auto *BB
: CleanupPads
)
267 prepareEHPad(BB
, false);
272 // Prepare an EH pad for Wasm EH handling. If NeedLSDA is false, Index is
274 void WasmEHPrepare::prepareEHPad(BasicBlock
*BB
, bool NeedLSDA
,
276 assert(BB
->isEHPad() && "BB is not an EHPad!");
277 IRBuilder
<> IRB(BB
->getContext());
278 IRB
.SetInsertPoint(&*BB
->getFirstInsertionPt());
280 auto *FPI
= cast
<FuncletPadInst
>(BB
->getFirstNonPHI());
281 Instruction
*GetExnCI
= nullptr, *GetSelectorCI
= nullptr;
282 for (auto &U
: FPI
->uses()) {
283 if (auto *CI
= dyn_cast
<CallInst
>(U
.getUser())) {
284 if (CI
->getCalledValue() == GetExnF
)
286 if (CI
->getCalledValue() == GetSelectorF
)
291 // Cleanup pads w/o __clang_call_terminate call do not have any of
292 // wasm.get.exception() or wasm.get.ehselector() calls. We need to do nothing.
294 assert(!GetSelectorCI
&&
295 "wasm.get.ehselector() cannot exist w/o wasm.get.exception()");
299 Instruction
*ExtractExnCI
= IRB
.CreateCall(ExtractExnF
, {}, "exn");
300 GetExnCI
->replaceAllUsesWith(ExtractExnCI
);
301 GetExnCI
->eraseFromParent();
303 // In case it is a catchpad with single catch (...) or a cleanuppad, we don't
304 // need to call personality function because we don't need a selector.
307 assert(GetSelectorCI
->use_empty() &&
308 "wasm.get.ehselector() still has uses!");
309 GetSelectorCI
->eraseFromParent();
313 IRB
.SetInsertPoint(ExtractExnCI
->getNextNode());
315 // This is to create a map of <landingpad EH label, landingpad index> in
316 // SelectionDAGISel, which is to be used in EHStreamer to emit LSDA tables.
317 // Pseudocode: wasm.landingpad.index(Index);
318 IRB
.CreateCall(LPadIndexF
, {FPI
, IRB
.getInt32(Index
)});
320 // Pseudocode: __wasm_lpad_context.lpad_index = index;
321 IRB
.CreateStore(IRB
.getInt32(Index
), LPadIndexField
);
323 // Store LSDA address only if this catchpad belongs to a top-level
324 // catchswitch. If there is another catchpad that dominates this pad, we don't
325 // need to store LSDA address again, because they are the same throughout the
326 // function and have been already stored before.
327 // TODO Can we not store LSDA address in user function but make libcxxabi
329 auto *CPI
= cast
<CatchPadInst
>(FPI
);
330 if (isa
<ConstantTokenNone
>(CPI
->getCatchSwitch()->getParentPad()))
331 // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
332 IRB
.CreateStore(IRB
.CreateCall(LSDAF
), LSDAField
);
334 // Pseudocode: _Unwind_CallPersonality(exn);
335 CallInst
*PersCI
= IRB
.CreateCall(CallPersonalityF
, ExtractExnCI
,
336 OperandBundleDef("funclet", CPI
));
337 PersCI
->setDoesNotThrow();
339 // Pseudocode: int selector = __wasm.landingpad_context.selector;
340 Instruction
*Selector
=
341 IRB
.CreateLoad(IRB
.getInt32Ty(), SelectorField
, "selector");
343 // Replace the return value from wasm.get.ehselector() with the selector value
344 // loaded from __wasm_lpad_context.selector.
345 assert(GetSelectorCI
&& "wasm.get.ehselector() call does not exist");
346 GetSelectorCI
->replaceAllUsesWith(Selector
);
347 GetSelectorCI
->eraseFromParent();
350 void llvm::calculateWasmEHInfo(const Function
*F
, WasmEHFuncInfo
&EHInfo
) {
351 // If an exception is not caught by a catchpad (i.e., it is a foreign
352 // exception), it will unwind to its parent catchswitch's unwind destination.
353 // We don't record an unwind destination for cleanuppads because every
354 // exception should be caught by it.
355 for (const auto &BB
: *F
) {
358 const Instruction
*Pad
= BB
.getFirstNonPHI();
360 if (const auto *CatchPad
= dyn_cast
<CatchPadInst
>(Pad
)) {
361 const auto *UnwindBB
= CatchPad
->getCatchSwitch()->getUnwindDest();
364 const Instruction
*UnwindPad
= UnwindBB
->getFirstNonPHI();
365 if (const auto *CatchSwitch
= dyn_cast
<CatchSwitchInst
>(UnwindPad
))
366 // Currently there should be only one handler per a catchswitch.
367 EHInfo
.setEHPadUnwindDest(&BB
, *CatchSwitch
->handlers().begin());
369 EHInfo
.setEHPadUnwindDest(&BB
, UnwindBB
);