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"
87480093f4SDimitry Andric #include "llvm/InitializePasses.h"
880b57cec5SDimitry Andric #include "llvm/Transforms/Utils/BasicBlockUtils.h"
890b57cec5SDimitry Andric 
900b57cec5SDimitry Andric using namespace llvm;
910b57cec5SDimitry Andric 
925f757f3fSDimitry Andric #define DEBUG_TYPE "wasm-eh-prepare"
930b57cec5SDimitry Andric 
940b57cec5SDimitry Andric namespace {
955f757f3fSDimitry Andric class WasmEHPrepareImpl {
965f757f3fSDimitry Andric   friend class WasmEHPrepare;
975f757f3fSDimitry Andric 
980b57cec5SDimitry Andric   Type *LPadContextTy = nullptr; // type of 'struct _Unwind_LandingPadContext'
990b57cec5SDimitry Andric   GlobalVariable *LPadContextGV = nullptr; // __wasm_lpad_context
1000b57cec5SDimitry Andric 
1010b57cec5SDimitry Andric   // Field addresses of struct _Unwind_LandingPadContext
1020b57cec5SDimitry Andric   Value *LPadIndexField = nullptr; // lpad_index field
1030b57cec5SDimitry Andric   Value *LSDAField = nullptr;      // lsda field
1040b57cec5SDimitry Andric   Value *SelectorField = nullptr;  // selector
1050b57cec5SDimitry Andric 
1060b57cec5SDimitry Andric   Function *ThrowF = nullptr;       // wasm.throw() intrinsic
1070b57cec5SDimitry Andric   Function *LPadIndexF = nullptr;   // wasm.landingpad.index() intrinsic
1080b57cec5SDimitry Andric   Function *LSDAF = nullptr;        // wasm.lsda() intrinsic
1090b57cec5SDimitry Andric   Function *GetExnF = nullptr;      // wasm.get.exception() intrinsic
110e8d8bef9SDimitry Andric   Function *CatchF = nullptr;       // wasm.catch() intrinsic
1110b57cec5SDimitry Andric   Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
1120b57cec5SDimitry Andric   FunctionCallee CallPersonalityF =
1130b57cec5SDimitry Andric       nullptr; // _Unwind_CallPersonality() wrapper
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric   bool prepareThrows(Function &F);
116fe6060f1SDimitry Andric   bool prepareEHPads(Function &F);
117fe6060f1SDimitry Andric   void prepareEHPad(BasicBlock *BB, bool NeedPersonality, unsigned Index = 0);
1180b57cec5SDimitry Andric 
1190b57cec5SDimitry Andric public:
1205f757f3fSDimitry Andric   WasmEHPrepareImpl() = default;
WasmEHPrepareImpl(Type * LPadContextTy_)1215f757f3fSDimitry Andric   WasmEHPrepareImpl(Type *LPadContextTy_) : LPadContextTy(LPadContextTy_) {}
1225f757f3fSDimitry Andric   bool runOnFunction(Function &F);
1235f757f3fSDimitry Andric };
1245f757f3fSDimitry Andric 
1255f757f3fSDimitry Andric class WasmEHPrepare : public FunctionPass {
1265f757f3fSDimitry Andric   WasmEHPrepareImpl P;
1275f757f3fSDimitry Andric 
1285f757f3fSDimitry Andric public:
1290b57cec5SDimitry Andric   static char ID; // Pass identification, replacement for typeid
1300b57cec5SDimitry Andric 
WasmEHPrepare()1310b57cec5SDimitry Andric   WasmEHPrepare() : FunctionPass(ID) {}
1320b57cec5SDimitry Andric   bool doInitialization(Module &M) override;
runOnFunction(Function & F)1335f757f3fSDimitry Andric   bool runOnFunction(Function &F) override { return P.runOnFunction(F); }
1340b57cec5SDimitry Andric 
getPassName() const1350b57cec5SDimitry Andric   StringRef getPassName() const override {
1360b57cec5SDimitry Andric     return "WebAssembly Exception handling preparation";
1370b57cec5SDimitry Andric   }
1380b57cec5SDimitry Andric };
1395f757f3fSDimitry Andric 
1400b57cec5SDimitry Andric } // end anonymous namespace
1410b57cec5SDimitry Andric 
run(Function & F,FunctionAnalysisManager &)1425f757f3fSDimitry Andric PreservedAnalyses WasmEHPreparePass::run(Function &F,
1435f757f3fSDimitry Andric                                          FunctionAnalysisManager &) {
1445f757f3fSDimitry Andric   auto &Context = F.getContext();
1455f757f3fSDimitry Andric   auto *I32Ty = Type::getInt32Ty(Context);
1465f757f3fSDimitry Andric   auto *PtrTy = PointerType::get(Context, 0);
1475f757f3fSDimitry Andric   auto *LPadContextTy =
1485f757f3fSDimitry Andric       StructType::get(I32Ty /*lpad_index*/, PtrTy /*lsda*/, I32Ty /*selector*/);
1495f757f3fSDimitry Andric   WasmEHPrepareImpl P(LPadContextTy);
1505f757f3fSDimitry Andric   bool Changed = P.runOnFunction(F);
1515f757f3fSDimitry Andric   return Changed ? PreservedAnalyses::none() : PreservedAnalyses ::all();
1525f757f3fSDimitry Andric }
1535f757f3fSDimitry Andric 
1540b57cec5SDimitry Andric char WasmEHPrepare::ID = 0;
1555ffd83dbSDimitry Andric INITIALIZE_PASS_BEGIN(WasmEHPrepare, DEBUG_TYPE,
1565ffd83dbSDimitry Andric                       "Prepare WebAssembly exceptions", false, false)
1575ffd83dbSDimitry Andric INITIALIZE_PASS_END(WasmEHPrepare, DEBUG_TYPE, "Prepare WebAssembly exceptions",
1580b57cec5SDimitry Andric                     false, false)
1590b57cec5SDimitry Andric 
createWasmEHPass()1600b57cec5SDimitry Andric FunctionPass *llvm::createWasmEHPass() { return new WasmEHPrepare(); }
1610b57cec5SDimitry Andric 
doInitialization(Module & M)1620b57cec5SDimitry Andric bool WasmEHPrepare::doInitialization(Module &M) {
1630b57cec5SDimitry Andric   IRBuilder<> IRB(M.getContext());
1645f757f3fSDimitry Andric   P.LPadContextTy = StructType::get(IRB.getInt32Ty(), // lpad_index
1655f757f3fSDimitry Andric                                     IRB.getPtrTy(),   // lsda
1660b57cec5SDimitry Andric                                     IRB.getInt32Ty()  // selector
1670b57cec5SDimitry Andric   );
1680b57cec5SDimitry Andric   return false;
1690b57cec5SDimitry Andric }
1700b57cec5SDimitry Andric 
1710b57cec5SDimitry Andric // Erase the specified BBs if the BB does not have any remaining predecessors,
1720b57cec5SDimitry Andric // and also all its dead children.
1730b57cec5SDimitry Andric template <typename Container>
eraseDeadBBsAndChildren(const Container & BBs)174fe6060f1SDimitry Andric static void eraseDeadBBsAndChildren(const Container &BBs) {
1750b57cec5SDimitry Andric   SmallVector<BasicBlock *, 8> WL(BBs.begin(), BBs.end());
1760b57cec5SDimitry Andric   while (!WL.empty()) {
1770b57cec5SDimitry Andric     auto *BB = WL.pop_back_val();
178e8d8bef9SDimitry Andric     if (!pred_empty(BB))
1790b57cec5SDimitry Andric       continue;
1800b57cec5SDimitry Andric     WL.append(succ_begin(BB), succ_end(BB));
181fe6060f1SDimitry Andric     DeleteDeadBlock(BB);
1820b57cec5SDimitry Andric   }
1830b57cec5SDimitry Andric }
1840b57cec5SDimitry Andric 
runOnFunction(Function & F)1855f757f3fSDimitry Andric bool WasmEHPrepareImpl::runOnFunction(Function &F) {
1860b57cec5SDimitry Andric   bool Changed = false;
1870b57cec5SDimitry Andric   Changed |= prepareThrows(F);
1880b57cec5SDimitry Andric   Changed |= prepareEHPads(F);
1890b57cec5SDimitry Andric   return Changed;
1900b57cec5SDimitry Andric }
1910b57cec5SDimitry Andric 
prepareThrows(Function & F)1925f757f3fSDimitry Andric bool WasmEHPrepareImpl::prepareThrows(Function &F) {
1930b57cec5SDimitry Andric   Module &M = *F.getParent();
1940b57cec5SDimitry Andric   IRBuilder<> IRB(F.getContext());
1950b57cec5SDimitry Andric   bool Changed = false;
1960b57cec5SDimitry Andric 
1970b57cec5SDimitry Andric   // wasm.throw() intinsic, which will be lowered to wasm 'throw' instruction.
1980b57cec5SDimitry Andric   ThrowF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_throw);
1990b57cec5SDimitry Andric   // Insert an unreachable instruction after a call to @llvm.wasm.throw and
2000b57cec5SDimitry Andric   // delete all following instructions within the BB, and delete all the dead
2010b57cec5SDimitry Andric   // children of the BB as well.
2020b57cec5SDimitry Andric   for (User *U : ThrowF->users()) {
2030b57cec5SDimitry Andric     // A call to @llvm.wasm.throw() is only generated from __cxa_throw()
2040b57cec5SDimitry Andric     // builtin call within libcxxabi, and cannot be an InvokeInst.
2050b57cec5SDimitry Andric     auto *ThrowI = cast<CallInst>(U);
2060b57cec5SDimitry Andric     if (ThrowI->getFunction() != &F)
2070b57cec5SDimitry Andric       continue;
2080b57cec5SDimitry Andric     Changed = true;
2090b57cec5SDimitry Andric     auto *BB = ThrowI->getParent();
210e8d8bef9SDimitry Andric     SmallVector<BasicBlock *, 4> Succs(successors(BB));
211bdd1243dSDimitry Andric     BB->erase(std::next(BasicBlock::iterator(ThrowI)), BB->end());
2120b57cec5SDimitry Andric     IRB.SetInsertPoint(BB);
2130b57cec5SDimitry Andric     IRB.CreateUnreachable();
214fe6060f1SDimitry Andric     eraseDeadBBsAndChildren(Succs);
2150b57cec5SDimitry Andric   }
2160b57cec5SDimitry Andric 
2170b57cec5SDimitry Andric   return Changed;
2180b57cec5SDimitry Andric }
2190b57cec5SDimitry Andric 
prepareEHPads(Function & F)2205f757f3fSDimitry Andric bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
2215ffd83dbSDimitry Andric   Module &M = *F.getParent();
2225ffd83dbSDimitry Andric   IRBuilder<> IRB(F.getContext());
223fe6060f1SDimitry Andric 
224fe6060f1SDimitry Andric   SmallVector<BasicBlock *, 16> CatchPads;
225fe6060f1SDimitry Andric   SmallVector<BasicBlock *, 16> CleanupPads;
226fe6060f1SDimitry Andric   for (BasicBlock &BB : F) {
227fe6060f1SDimitry Andric     if (!BB.isEHPad())
228fe6060f1SDimitry Andric       continue;
229fe6060f1SDimitry Andric     auto *Pad = BB.getFirstNonPHI();
230fe6060f1SDimitry Andric     if (isa<CatchPadInst>(Pad))
231fe6060f1SDimitry Andric       CatchPads.push_back(&BB);
232fe6060f1SDimitry Andric     else if (isa<CleanupPadInst>(Pad))
233fe6060f1SDimitry Andric       CleanupPads.push_back(&BB);
234fe6060f1SDimitry Andric   }
235fe6060f1SDimitry Andric   if (CatchPads.empty() && CleanupPads.empty())
236fe6060f1SDimitry Andric     return false;
237fe6060f1SDimitry Andric 
23806c3fb27SDimitry Andric   if (!F.hasPersonalityFn() ||
23906c3fb27SDimitry Andric       !isScopedEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) {
24006c3fb27SDimitry Andric     report_fatal_error("Function '" + F.getName() +
24106c3fb27SDimitry Andric                        "' does not have a correct Wasm personality function "
24206c3fb27SDimitry Andric                        "'__gxx_wasm_personality_v0'");
24306c3fb27SDimitry Andric   }
2440b57cec5SDimitry Andric   assert(F.hasPersonalityFn() && "Personality function not found");
2450b57cec5SDimitry Andric 
24681ad6265SDimitry Andric   // __wasm_lpad_context global variable.
24781ad6265SDimitry Andric   // This variable should be thread local. If the target does not support TLS,
24881ad6265SDimitry Andric   // we depend on CoalesceFeaturesAndStripAtomics to downgrade it to
24981ad6265SDimitry Andric   // non-thread-local ones, in which case we don't allow this object to be
25081ad6265SDimitry Andric   // linked with other objects using shared memory.
2510b57cec5SDimitry Andric   LPadContextGV = cast<GlobalVariable>(
2520b57cec5SDimitry Andric       M.getOrInsertGlobal("__wasm_lpad_context", LPadContextTy));
25381ad6265SDimitry Andric   LPadContextGV->setThreadLocalMode(GlobalValue::GeneralDynamicTLSModel);
25481ad6265SDimitry Andric 
2550b57cec5SDimitry Andric   LPadIndexField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 0,
2560b57cec5SDimitry Andric                                           "lpad_index_gep");
2570b57cec5SDimitry Andric   LSDAField =
2580b57cec5SDimitry Andric       IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 1, "lsda_gep");
2590b57cec5SDimitry Andric   SelectorField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 2,
2600b57cec5SDimitry Andric                                          "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