10b57cec5SDimitry Andric //===-- WasmEHPrepare - Prepare excepton handling for WebAssembly --------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This transformation is designed for use by code generators which use
100b57cec5SDimitry Andric // WebAssembly exception handling scheme. This currently supports C++
110b57cec5SDimitry Andric // exceptions.
120b57cec5SDimitry Andric //
130b57cec5SDimitry Andric // WebAssembly exception handling uses Windows exception IR for the middle level
140b57cec5SDimitry Andric // representation. This pass does the following transformation for every
150b57cec5SDimitry Andric // catchpad block:
160b57cec5SDimitry Andric // (In C-style pseudocode)
170b57cec5SDimitry Andric //
180b57cec5SDimitry Andric // - Before:
190b57cec5SDimitry Andric // catchpad ...
200b57cec5SDimitry Andric // exn = wasm.get.exception();
210b57cec5SDimitry Andric // selector = wasm.get.selector();
220b57cec5SDimitry Andric // ...
230b57cec5SDimitry Andric //
240b57cec5SDimitry Andric // - After:
250b57cec5SDimitry Andric // catchpad ...
26e8d8bef9SDimitry Andric // exn = wasm.catch(WebAssembly::CPP_EXCEPTION);
270b57cec5SDimitry Andric // // Only add below in case it's not a single catch (...)
280b57cec5SDimitry Andric // wasm.landingpad.index(index);
290b57cec5SDimitry Andric // __wasm_lpad_context.lpad_index = index;
300b57cec5SDimitry Andric // __wasm_lpad_context.lsda = wasm.lsda();
310b57cec5SDimitry Andric // _Unwind_CallPersonality(exn);
32349cc55cSDimitry Andric // selector = __wasm_lpad_context.selector;
330b57cec5SDimitry Andric // ...
340b57cec5SDimitry Andric //
350b57cec5SDimitry Andric //
360b57cec5SDimitry Andric // * Background: Direct personality function call
370b57cec5SDimitry Andric // In WebAssembly EH, the VM is responsible for unwinding the stack once an
380b57cec5SDimitry Andric // exception is thrown. After the stack is unwound, the control flow is
390b57cec5SDimitry Andric // transfered to WebAssembly 'catch' instruction.
400b57cec5SDimitry Andric //
410b57cec5SDimitry Andric // Unwinding the stack is not done by libunwind but the VM, so the personality
420b57cec5SDimitry Andric // function in libcxxabi cannot be called from libunwind during the unwinding
430b57cec5SDimitry Andric // process. So after a catch instruction, we insert a call to a wrapper function
440b57cec5SDimitry Andric // in libunwind that in turn calls the real personality function.
450b57cec5SDimitry Andric //
460b57cec5SDimitry Andric // In Itanium EH, if the personality function decides there is no matching catch
470b57cec5SDimitry Andric // clause in a call frame and no cleanup action to perform, the unwinder doesn't
480b57cec5SDimitry Andric // stop there and continues unwinding. But in Wasm EH, the unwinder stops at
490b57cec5SDimitry Andric // every call frame with a catch intruction, after which the personality
500b57cec5SDimitry Andric // function is called from the compiler-generated user code here.
510b57cec5SDimitry Andric //
520b57cec5SDimitry Andric // In libunwind, we have this struct that serves as a communincation channel
530b57cec5SDimitry Andric // between the compiler-generated user code and the personality function in
540b57cec5SDimitry Andric // libcxxabi.
550b57cec5SDimitry Andric //
560b57cec5SDimitry Andric // struct _Unwind_LandingPadContext {
570b57cec5SDimitry Andric // uintptr_t lpad_index;
580b57cec5SDimitry Andric // uintptr_t lsda;
590b57cec5SDimitry Andric // uintptr_t selector;
600b57cec5SDimitry Andric // };
610b57cec5SDimitry Andric // struct _Unwind_LandingPadContext __wasm_lpad_context = ...;
620b57cec5SDimitry Andric //
630b57cec5SDimitry Andric // And this wrapper in libunwind calls the personality function.
640b57cec5SDimitry Andric //
650b57cec5SDimitry Andric // _Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
660b57cec5SDimitry Andric // struct _Unwind_Exception *exception_obj =
670b57cec5SDimitry Andric // (struct _Unwind_Exception *)exception_ptr;
680b57cec5SDimitry Andric // _Unwind_Reason_Code ret = __gxx_personality_v0(
690b57cec5SDimitry Andric // 1, _UA_CLEANUP_PHASE, exception_obj->exception_class, exception_obj,
700b57cec5SDimitry Andric // (struct _Unwind_Context *)__wasm_lpad_context);
710b57cec5SDimitry Andric // return ret;
720b57cec5SDimitry Andric // }
730b57cec5SDimitry Andric //
740b57cec5SDimitry Andric // We pass a landing pad index, and the address of LSDA for the current function
750b57cec5SDimitry Andric // to the wrapper function _Unwind_CallPersonality in libunwind, and we retrieve
760b57cec5SDimitry Andric // the selector after it returns.
770b57cec5SDimitry Andric //
780b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
790b57cec5SDimitry Andric
805f757f3fSDimitry Andric #include "llvm/CodeGen/WasmEHPrepare.h"
8181ad6265SDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h"
8281ad6265SDimitry Andric #include "llvm/CodeGen/Passes.h"
830b57cec5SDimitry Andric #include "llvm/CodeGen/WasmEHFuncInfo.h"
8406c3fb27SDimitry Andric #include "llvm/IR/EHPersonalities.h"
850b57cec5SDimitry Andric #include "llvm/IR/IRBuilder.h"
86480093f4SDimitry Andric #include "llvm/IR/IntrinsicsWebAssembly.h"
87*0fca6ea1SDimitry Andric #include "llvm/IR/Module.h"
88480093f4SDimitry Andric #include "llvm/InitializePasses.h"
890b57cec5SDimitry Andric #include "llvm/Transforms/Utils/BasicBlockUtils.h"
900b57cec5SDimitry Andric
910b57cec5SDimitry Andric using namespace llvm;
920b57cec5SDimitry Andric
935f757f3fSDimitry Andric #define DEBUG_TYPE "wasm-eh-prepare"
940b57cec5SDimitry Andric
950b57cec5SDimitry Andric namespace {
965f757f3fSDimitry Andric class WasmEHPrepareImpl {
975f757f3fSDimitry Andric friend class WasmEHPrepare;
985f757f3fSDimitry Andric
990b57cec5SDimitry Andric Type *LPadContextTy = nullptr; // type of 'struct _Unwind_LandingPadContext'
1000b57cec5SDimitry Andric GlobalVariable *LPadContextGV = nullptr; // __wasm_lpad_context
1010b57cec5SDimitry Andric
1020b57cec5SDimitry Andric // Field addresses of struct _Unwind_LandingPadContext
1030b57cec5SDimitry Andric Value *LPadIndexField = nullptr; // lpad_index field
1040b57cec5SDimitry Andric Value *LSDAField = nullptr; // lsda field
1050b57cec5SDimitry Andric Value *SelectorField = nullptr; // selector
1060b57cec5SDimitry Andric
1070b57cec5SDimitry Andric Function *ThrowF = nullptr; // wasm.throw() intrinsic
1080b57cec5SDimitry Andric Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic
1090b57cec5SDimitry Andric Function *LSDAF = nullptr; // wasm.lsda() intrinsic
1100b57cec5SDimitry Andric Function *GetExnF = nullptr; // wasm.get.exception() intrinsic
111e8d8bef9SDimitry Andric Function *CatchF = nullptr; // wasm.catch() intrinsic
1120b57cec5SDimitry Andric Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
1130b57cec5SDimitry Andric FunctionCallee CallPersonalityF =
1140b57cec5SDimitry Andric nullptr; // _Unwind_CallPersonality() wrapper
1150b57cec5SDimitry Andric
1160b57cec5SDimitry Andric bool prepareThrows(Function &F);
117fe6060f1SDimitry Andric bool prepareEHPads(Function &F);
118fe6060f1SDimitry Andric void prepareEHPad(BasicBlock *BB, bool NeedPersonality, unsigned Index = 0);
1190b57cec5SDimitry Andric
1200b57cec5SDimitry Andric public:
1215f757f3fSDimitry Andric WasmEHPrepareImpl() = default;
WasmEHPrepareImpl(Type * LPadContextTy_)1225f757f3fSDimitry Andric WasmEHPrepareImpl(Type *LPadContextTy_) : LPadContextTy(LPadContextTy_) {}
1235f757f3fSDimitry Andric bool runOnFunction(Function &F);
1245f757f3fSDimitry Andric };
1255f757f3fSDimitry Andric
1265f757f3fSDimitry Andric class WasmEHPrepare : public FunctionPass {
1275f757f3fSDimitry Andric WasmEHPrepareImpl P;
1285f757f3fSDimitry Andric
1295f757f3fSDimitry Andric public:
1300b57cec5SDimitry Andric static char ID; // Pass identification, replacement for typeid
1310b57cec5SDimitry Andric
WasmEHPrepare()1320b57cec5SDimitry Andric WasmEHPrepare() : FunctionPass(ID) {}
1330b57cec5SDimitry Andric bool doInitialization(Module &M) override;
runOnFunction(Function & F)1345f757f3fSDimitry Andric bool runOnFunction(Function &F) override { return P.runOnFunction(F); }
1350b57cec5SDimitry Andric
getPassName() const1360b57cec5SDimitry Andric StringRef getPassName() const override {
1370b57cec5SDimitry Andric return "WebAssembly Exception handling preparation";
1380b57cec5SDimitry Andric }
1390b57cec5SDimitry Andric };
1405f757f3fSDimitry Andric
1410b57cec5SDimitry Andric } // end anonymous namespace
1420b57cec5SDimitry Andric
run(Function & F,FunctionAnalysisManager &)1435f757f3fSDimitry Andric PreservedAnalyses WasmEHPreparePass::run(Function &F,
1445f757f3fSDimitry Andric FunctionAnalysisManager &) {
1455f757f3fSDimitry Andric auto &Context = F.getContext();
1465f757f3fSDimitry Andric auto *I32Ty = Type::getInt32Ty(Context);
1475f757f3fSDimitry Andric auto *PtrTy = PointerType::get(Context, 0);
1485f757f3fSDimitry Andric auto *LPadContextTy =
1495f757f3fSDimitry Andric StructType::get(I32Ty /*lpad_index*/, PtrTy /*lsda*/, I32Ty /*selector*/);
1505f757f3fSDimitry Andric WasmEHPrepareImpl P(LPadContextTy);
1515f757f3fSDimitry Andric bool Changed = P.runOnFunction(F);
1525f757f3fSDimitry Andric return Changed ? PreservedAnalyses::none() : PreservedAnalyses ::all();
1535f757f3fSDimitry Andric }
1545f757f3fSDimitry Andric
1550b57cec5SDimitry Andric char WasmEHPrepare::ID = 0;
1565ffd83dbSDimitry Andric INITIALIZE_PASS_BEGIN(WasmEHPrepare, DEBUG_TYPE,
1575ffd83dbSDimitry Andric "Prepare WebAssembly exceptions", false, false)
1585ffd83dbSDimitry Andric INITIALIZE_PASS_END(WasmEHPrepare, DEBUG_TYPE, "Prepare WebAssembly exceptions",
1590b57cec5SDimitry Andric false, false)
1600b57cec5SDimitry Andric
createWasmEHPass()1610b57cec5SDimitry Andric FunctionPass *llvm::createWasmEHPass() { return new WasmEHPrepare(); }
1620b57cec5SDimitry Andric
doInitialization(Module & M)1630b57cec5SDimitry Andric bool WasmEHPrepare::doInitialization(Module &M) {
1640b57cec5SDimitry Andric IRBuilder<> IRB(M.getContext());
1655f757f3fSDimitry Andric P.LPadContextTy = StructType::get(IRB.getInt32Ty(), // lpad_index
1665f757f3fSDimitry Andric IRB.getPtrTy(), // lsda
1670b57cec5SDimitry Andric IRB.getInt32Ty() // selector
1680b57cec5SDimitry Andric );
1690b57cec5SDimitry Andric return false;
1700b57cec5SDimitry Andric }
1710b57cec5SDimitry Andric
1720b57cec5SDimitry Andric // Erase the specified BBs if the BB does not have any remaining predecessors,
1730b57cec5SDimitry Andric // and also all its dead children.
1740b57cec5SDimitry Andric template <typename Container>
eraseDeadBBsAndChildren(const Container & BBs)175fe6060f1SDimitry Andric static void eraseDeadBBsAndChildren(const Container &BBs) {
1760b57cec5SDimitry Andric SmallVector<BasicBlock *, 8> WL(BBs.begin(), BBs.end());
1770b57cec5SDimitry Andric while (!WL.empty()) {
1780b57cec5SDimitry Andric auto *BB = WL.pop_back_val();
179e8d8bef9SDimitry Andric if (!pred_empty(BB))
1800b57cec5SDimitry Andric continue;
1810b57cec5SDimitry Andric WL.append(succ_begin(BB), succ_end(BB));
182fe6060f1SDimitry Andric DeleteDeadBlock(BB);
1830b57cec5SDimitry Andric }
1840b57cec5SDimitry Andric }
1850b57cec5SDimitry Andric
runOnFunction(Function & F)1865f757f3fSDimitry Andric bool WasmEHPrepareImpl::runOnFunction(Function &F) {
1870b57cec5SDimitry Andric bool Changed = false;
1880b57cec5SDimitry Andric Changed |= prepareThrows(F);
1890b57cec5SDimitry Andric Changed |= prepareEHPads(F);
1900b57cec5SDimitry Andric return Changed;
1910b57cec5SDimitry Andric }
1920b57cec5SDimitry Andric
prepareThrows(Function & F)1935f757f3fSDimitry Andric bool WasmEHPrepareImpl::prepareThrows(Function &F) {
1940b57cec5SDimitry Andric Module &M = *F.getParent();
1950b57cec5SDimitry Andric IRBuilder<> IRB(F.getContext());
1960b57cec5SDimitry Andric bool Changed = false;
1970b57cec5SDimitry Andric
1980b57cec5SDimitry Andric // wasm.throw() intinsic, which will be lowered to wasm 'throw' instruction.
1990b57cec5SDimitry Andric ThrowF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_throw);
2000b57cec5SDimitry Andric // Insert an unreachable instruction after a call to @llvm.wasm.throw and
2010b57cec5SDimitry Andric // delete all following instructions within the BB, and delete all the dead
2020b57cec5SDimitry Andric // children of the BB as well.
2030b57cec5SDimitry Andric for (User *U : ThrowF->users()) {
2040b57cec5SDimitry Andric // A call to @llvm.wasm.throw() is only generated from __cxa_throw()
2050b57cec5SDimitry Andric // builtin call within libcxxabi, and cannot be an InvokeInst.
2060b57cec5SDimitry Andric auto *ThrowI = cast<CallInst>(U);
2070b57cec5SDimitry Andric if (ThrowI->getFunction() != &F)
2080b57cec5SDimitry Andric continue;
2090b57cec5SDimitry Andric Changed = true;
2100b57cec5SDimitry Andric auto *BB = ThrowI->getParent();
211e8d8bef9SDimitry Andric SmallVector<BasicBlock *, 4> Succs(successors(BB));
212bdd1243dSDimitry Andric BB->erase(std::next(BasicBlock::iterator(ThrowI)), BB->end());
2130b57cec5SDimitry Andric IRB.SetInsertPoint(BB);
2140b57cec5SDimitry Andric IRB.CreateUnreachable();
215fe6060f1SDimitry Andric eraseDeadBBsAndChildren(Succs);
2160b57cec5SDimitry Andric }
2170b57cec5SDimitry Andric
2180b57cec5SDimitry Andric return Changed;
2190b57cec5SDimitry Andric }
2200b57cec5SDimitry Andric
prepareEHPads(Function & F)2215f757f3fSDimitry Andric bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
2225ffd83dbSDimitry Andric Module &M = *F.getParent();
2235ffd83dbSDimitry Andric IRBuilder<> IRB(F.getContext());
224fe6060f1SDimitry Andric
225fe6060f1SDimitry Andric SmallVector<BasicBlock *, 16> CatchPads;
226fe6060f1SDimitry Andric SmallVector<BasicBlock *, 16> CleanupPads;
227fe6060f1SDimitry Andric for (BasicBlock &BB : F) {
228fe6060f1SDimitry Andric if (!BB.isEHPad())
229fe6060f1SDimitry Andric continue;
230fe6060f1SDimitry Andric auto *Pad = BB.getFirstNonPHI();
231fe6060f1SDimitry Andric if (isa<CatchPadInst>(Pad))
232fe6060f1SDimitry Andric CatchPads.push_back(&BB);
233fe6060f1SDimitry Andric else if (isa<CleanupPadInst>(Pad))
234fe6060f1SDimitry Andric CleanupPads.push_back(&BB);
235fe6060f1SDimitry Andric }
236fe6060f1SDimitry Andric if (CatchPads.empty() && CleanupPads.empty())
237fe6060f1SDimitry Andric return false;
238fe6060f1SDimitry Andric
23906c3fb27SDimitry Andric if (!F.hasPersonalityFn() ||
24006c3fb27SDimitry Andric !isScopedEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) {
24106c3fb27SDimitry Andric report_fatal_error("Function '" + F.getName() +
24206c3fb27SDimitry Andric "' does not have a correct Wasm personality function "
24306c3fb27SDimitry Andric "'__gxx_wasm_personality_v0'");
24406c3fb27SDimitry Andric }
2450b57cec5SDimitry Andric assert(F.hasPersonalityFn() && "Personality function not found");
2460b57cec5SDimitry Andric
24781ad6265SDimitry Andric // __wasm_lpad_context global variable.
24881ad6265SDimitry Andric // This variable should be thread local. If the target does not support TLS,
24981ad6265SDimitry Andric // we depend on CoalesceFeaturesAndStripAtomics to downgrade it to
25081ad6265SDimitry Andric // non-thread-local ones, in which case we don't allow this object to be
25181ad6265SDimitry Andric // linked with other objects using shared memory.
2520b57cec5SDimitry Andric LPadContextGV = cast<GlobalVariable>(
2530b57cec5SDimitry Andric M.getOrInsertGlobal("__wasm_lpad_context", LPadContextTy));
25481ad6265SDimitry Andric LPadContextGV->setThreadLocalMode(GlobalValue::GeneralDynamicTLSModel);
25581ad6265SDimitry Andric
256*0fca6ea1SDimitry Andric LPadIndexField = LPadContextGV;
257*0fca6ea1SDimitry Andric LSDAField = IRB.CreateConstInBoundsGEP2_32(LPadContextTy, LPadContextGV, 0, 1,
258*0fca6ea1SDimitry Andric "lsda_gep");
259*0fca6ea1SDimitry Andric SelectorField = IRB.CreateConstInBoundsGEP2_32(LPadContextTy, LPadContextGV,
260*0fca6ea1SDimitry Andric 0, 2, "selector_gep");
2610b57cec5SDimitry Andric
2620b57cec5SDimitry Andric // wasm.landingpad.index() intrinsic, which is to specify landingpad index
2630b57cec5SDimitry Andric LPadIndexF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_landingpad_index);
2640b57cec5SDimitry Andric // wasm.lsda() intrinsic. Returns the address of LSDA table for the current
2650b57cec5SDimitry Andric // function.
2660b57cec5SDimitry Andric LSDAF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_lsda);
2670b57cec5SDimitry Andric // wasm.get.exception() and wasm.get.ehselector() intrinsics. Calls to these
2680b57cec5SDimitry Andric // are generated in clang.
2690b57cec5SDimitry Andric GetExnF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_exception);
2700b57cec5SDimitry Andric GetSelectorF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_ehselector);
2710b57cec5SDimitry Andric
272e8d8bef9SDimitry Andric // wasm.catch() will be lowered down to wasm 'catch' instruction in
273e8d8bef9SDimitry Andric // instruction selection.
274e8d8bef9SDimitry Andric CatchF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_catch);
2750b57cec5SDimitry Andric
2760b57cec5SDimitry Andric // _Unwind_CallPersonality() wrapper function, which calls the personality
2775f757f3fSDimitry Andric CallPersonalityF = M.getOrInsertFunction("_Unwind_CallPersonality",
2785f757f3fSDimitry Andric IRB.getInt32Ty(), IRB.getPtrTy());
2790b57cec5SDimitry Andric if (Function *F = dyn_cast<Function>(CallPersonalityF.getCallee()))
2800b57cec5SDimitry Andric F->setDoesNotThrow();
281fe6060f1SDimitry Andric
282fe6060f1SDimitry Andric unsigned Index = 0;
283fe6060f1SDimitry Andric for (auto *BB : CatchPads) {
284fe6060f1SDimitry Andric auto *CPI = cast<CatchPadInst>(BB->getFirstNonPHI());
285fe6060f1SDimitry Andric // In case of a single catch (...), we don't need to emit a personalify
286fe6060f1SDimitry Andric // function call
287bdd1243dSDimitry Andric if (CPI->arg_size() == 1 &&
288fe6060f1SDimitry Andric cast<Constant>(CPI->getArgOperand(0))->isNullValue())
289fe6060f1SDimitry Andric prepareEHPad(BB, false);
290fe6060f1SDimitry Andric else
291fe6060f1SDimitry Andric prepareEHPad(BB, true, Index++);
292fe6060f1SDimitry Andric }
293fe6060f1SDimitry Andric
294fe6060f1SDimitry Andric // Cleanup pads don't need a personality function call.
295fe6060f1SDimitry Andric for (auto *BB : CleanupPads)
296fe6060f1SDimitry Andric prepareEHPad(BB, false);
297fe6060f1SDimitry Andric
298fe6060f1SDimitry Andric return true;
2990b57cec5SDimitry Andric }
3000b57cec5SDimitry Andric
3015ffd83dbSDimitry Andric // Prepare an EH pad for Wasm EH handling. If NeedPersonality is false, Index is
3020b57cec5SDimitry Andric // ignored.
prepareEHPad(BasicBlock * BB,bool NeedPersonality,unsigned Index)3035f757f3fSDimitry Andric void WasmEHPrepareImpl::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
304fe6060f1SDimitry Andric unsigned Index) {
3050b57cec5SDimitry Andric assert(BB->isEHPad() && "BB is not an EHPad!");
3060b57cec5SDimitry Andric IRBuilder<> IRB(BB->getContext());
3075f757f3fSDimitry Andric IRB.SetInsertPoint(BB, BB->getFirstInsertionPt());
3080b57cec5SDimitry Andric
3090b57cec5SDimitry Andric auto *FPI = cast<FuncletPadInst>(BB->getFirstNonPHI());
3100b57cec5SDimitry Andric Instruction *GetExnCI = nullptr, *GetSelectorCI = nullptr;
3110b57cec5SDimitry Andric for (auto &U : FPI->uses()) {
3120b57cec5SDimitry Andric if (auto *CI = dyn_cast<CallInst>(U.getUser())) {
3135ffd83dbSDimitry Andric if (CI->getCalledOperand() == GetExnF)
3140b57cec5SDimitry Andric GetExnCI = CI;
3155ffd83dbSDimitry Andric if (CI->getCalledOperand() == GetSelectorF)
3160b57cec5SDimitry Andric GetSelectorCI = CI;
3170b57cec5SDimitry Andric }
3180b57cec5SDimitry Andric }
3190b57cec5SDimitry Andric
320fe6060f1SDimitry Andric // Cleanup pads do not have any of wasm.get.exception() or
321fe6060f1SDimitry Andric // wasm.get.ehselector() calls. We need to do nothing.
3220b57cec5SDimitry Andric if (!GetExnCI) {
3230b57cec5SDimitry Andric assert(!GetSelectorCI &&
3240b57cec5SDimitry Andric "wasm.get.ehselector() cannot exist w/o wasm.get.exception()");
3250b57cec5SDimitry Andric return;
3260b57cec5SDimitry Andric }
3270b57cec5SDimitry Andric
328e8d8bef9SDimitry Andric // Replace wasm.get.exception intrinsic with wasm.catch intrinsic, which will
329e8d8bef9SDimitry Andric // be lowered to wasm 'catch' instruction. We do this mainly because
330e8d8bef9SDimitry Andric // instruction selection cannot handle wasm.get.exception intrinsic's token
331e8d8bef9SDimitry Andric // argument.
332e8d8bef9SDimitry Andric Instruction *CatchCI =
333e8d8bef9SDimitry Andric IRB.CreateCall(CatchF, {IRB.getInt32(WebAssembly::CPP_EXCEPTION)}, "exn");
334e8d8bef9SDimitry Andric GetExnCI->replaceAllUsesWith(CatchCI);
3350b57cec5SDimitry Andric GetExnCI->eraseFromParent();
3360b57cec5SDimitry Andric
3370b57cec5SDimitry Andric // In case it is a catchpad with single catch (...) or a cleanuppad, we don't
3380b57cec5SDimitry Andric // need to call personality function because we don't need a selector.
3395ffd83dbSDimitry Andric if (!NeedPersonality) {
3400b57cec5SDimitry Andric if (GetSelectorCI) {
3410b57cec5SDimitry Andric assert(GetSelectorCI->use_empty() &&
3420b57cec5SDimitry Andric "wasm.get.ehselector() still has uses!");
3430b57cec5SDimitry Andric GetSelectorCI->eraseFromParent();
3440b57cec5SDimitry Andric }
3450b57cec5SDimitry Andric return;
3460b57cec5SDimitry Andric }
347e8d8bef9SDimitry Andric IRB.SetInsertPoint(CatchCI->getNextNode());
3480b57cec5SDimitry Andric
3490b57cec5SDimitry Andric // This is to create a map of <landingpad EH label, landingpad index> in
3500b57cec5SDimitry Andric // SelectionDAGISel, which is to be used in EHStreamer to emit LSDA tables.
3510b57cec5SDimitry Andric // Pseudocode: wasm.landingpad.index(Index);
3520b57cec5SDimitry Andric IRB.CreateCall(LPadIndexF, {FPI, IRB.getInt32(Index)});
3530b57cec5SDimitry Andric
3540b57cec5SDimitry Andric // Pseudocode: __wasm_lpad_context.lpad_index = index;
3550b57cec5SDimitry Andric IRB.CreateStore(IRB.getInt32(Index), LPadIndexField);
3560b57cec5SDimitry Andric
3570b57cec5SDimitry Andric auto *CPI = cast<CatchPadInst>(FPI);
358fe6060f1SDimitry Andric // TODO Sometimes storing the LSDA address every time is not necessary, in
359fe6060f1SDimitry Andric // case it is already set in a dominating EH pad and there is no function call
360fe6060f1SDimitry Andric // between from that EH pad to here. Consider optimizing those cases.
3610b57cec5SDimitry Andric // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
3620b57cec5SDimitry Andric IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
3630b57cec5SDimitry Andric
3640b57cec5SDimitry Andric // Pseudocode: _Unwind_CallPersonality(exn);
365e8d8bef9SDimitry Andric CallInst *PersCI = IRB.CreateCall(CallPersonalityF, CatchCI,
3660b57cec5SDimitry Andric OperandBundleDef("funclet", CPI));
3670b57cec5SDimitry Andric PersCI->setDoesNotThrow();
3680b57cec5SDimitry Andric
369349cc55cSDimitry Andric // Pseudocode: int selector = __wasm_lpad_context.selector;
3700b57cec5SDimitry Andric Instruction *Selector =
3710b57cec5SDimitry Andric IRB.CreateLoad(IRB.getInt32Ty(), SelectorField, "selector");
3720b57cec5SDimitry Andric
3730b57cec5SDimitry Andric // Replace the return value from wasm.get.ehselector() with the selector value
3740b57cec5SDimitry Andric // loaded from __wasm_lpad_context.selector.
3750b57cec5SDimitry Andric assert(GetSelectorCI && "wasm.get.ehselector() call does not exist");
3760b57cec5SDimitry Andric GetSelectorCI->replaceAllUsesWith(Selector);
3770b57cec5SDimitry Andric GetSelectorCI->eraseFromParent();
3780b57cec5SDimitry Andric }
3790b57cec5SDimitry Andric
calculateWasmEHInfo(const Function * F,WasmEHFuncInfo & EHInfo)3800b57cec5SDimitry Andric void llvm::calculateWasmEHInfo(const Function *F, WasmEHFuncInfo &EHInfo) {
3810b57cec5SDimitry Andric // If an exception is not caught by a catchpad (i.e., it is a foreign
3820b57cec5SDimitry Andric // exception), it will unwind to its parent catchswitch's unwind destination.
3830b57cec5SDimitry Andric // We don't record an unwind destination for cleanuppads because every
3840b57cec5SDimitry Andric // exception should be caught by it.
3850b57cec5SDimitry Andric for (const auto &BB : *F) {
3860b57cec5SDimitry Andric if (!BB.isEHPad())
3870b57cec5SDimitry Andric continue;
3880b57cec5SDimitry Andric const Instruction *Pad = BB.getFirstNonPHI();
3890b57cec5SDimitry Andric
3900b57cec5SDimitry Andric if (const auto *CatchPad = dyn_cast<CatchPadInst>(Pad)) {
3910b57cec5SDimitry Andric const auto *UnwindBB = CatchPad->getCatchSwitch()->getUnwindDest();
3920b57cec5SDimitry Andric if (!UnwindBB)
3930b57cec5SDimitry Andric continue;
3940b57cec5SDimitry Andric const Instruction *UnwindPad = UnwindBB->getFirstNonPHI();
3950b57cec5SDimitry Andric if (const auto *CatchSwitch = dyn_cast<CatchSwitchInst>(UnwindPad))
3960b57cec5SDimitry Andric // Currently there should be only one handler per a catchswitch.
397fe6060f1SDimitry Andric EHInfo.setUnwindDest(&BB, *CatchSwitch->handlers().begin());
3980b57cec5SDimitry Andric else // cleanuppad
399fe6060f1SDimitry Andric EHInfo.setUnwindDest(&BB, UnwindBB);
4000b57cec5SDimitry Andric }
4010b57cec5SDimitry Andric }
4020b57cec5SDimitry Andric }
403