10b57cec5SDimitry Andric //=== WebAssemblyLateEHPrepare.cpp - WebAssembly Exception Preparation -===// 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 /// \file 100b57cec5SDimitry Andric /// \brief Does various transformations for exception handling. 110b57cec5SDimitry Andric /// 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 150b57cec5SDimitry Andric #include "WebAssembly.h" 160b57cec5SDimitry Andric #include "WebAssemblySubtarget.h" 170b57cec5SDimitry Andric #include "WebAssemblyUtilities.h" 180b57cec5SDimitry Andric #include "llvm/ADT/SmallSet.h" 190b57cec5SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 200b57cec5SDimitry Andric #include "llvm/CodeGen/WasmEHFuncInfo.h" 210b57cec5SDimitry Andric #include "llvm/MC/MCAsmInfo.h" 228bcb0991SDimitry Andric #include "llvm/Support/Debug.h" 23*5ffd83dbSDimitry Andric #include "llvm/Target/TargetMachine.h" 240b57cec5SDimitry Andric using namespace llvm; 250b57cec5SDimitry Andric 260b57cec5SDimitry Andric #define DEBUG_TYPE "wasm-late-eh-prepare" 270b57cec5SDimitry Andric 280b57cec5SDimitry Andric namespace { 290b57cec5SDimitry Andric class WebAssemblyLateEHPrepare final : public MachineFunctionPass { 300b57cec5SDimitry Andric StringRef getPassName() const override { 310b57cec5SDimitry Andric return "WebAssembly Late Prepare Exception"; 320b57cec5SDimitry Andric } 330b57cec5SDimitry Andric 340b57cec5SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override; 35*5ffd83dbSDimitry Andric void recordCatchRetBBs(MachineFunction &MF); 360b57cec5SDimitry Andric bool addCatches(MachineFunction &MF); 370b57cec5SDimitry Andric bool replaceFuncletReturns(MachineFunction &MF); 380b57cec5SDimitry Andric bool removeUnnecessaryUnreachables(MachineFunction &MF); 390b57cec5SDimitry Andric bool addExceptionExtraction(MachineFunction &MF); 400b57cec5SDimitry Andric bool restoreStackPointer(MachineFunction &MF); 410b57cec5SDimitry Andric 42*5ffd83dbSDimitry Andric MachineBasicBlock *getMatchingEHPad(MachineInstr *MI); 43*5ffd83dbSDimitry Andric SmallSet<MachineBasicBlock *, 8> CatchRetBBs; 44*5ffd83dbSDimitry Andric 450b57cec5SDimitry Andric public: 460b57cec5SDimitry Andric static char ID; // Pass identification, replacement for typeid 470b57cec5SDimitry Andric WebAssemblyLateEHPrepare() : MachineFunctionPass(ID) {} 480b57cec5SDimitry Andric }; 490b57cec5SDimitry Andric } // end anonymous namespace 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric char WebAssemblyLateEHPrepare::ID = 0; 520b57cec5SDimitry Andric INITIALIZE_PASS(WebAssemblyLateEHPrepare, DEBUG_TYPE, 530b57cec5SDimitry Andric "WebAssembly Late Exception Preparation", false, false) 540b57cec5SDimitry Andric 550b57cec5SDimitry Andric FunctionPass *llvm::createWebAssemblyLateEHPrepare() { 560b57cec5SDimitry Andric return new WebAssemblyLateEHPrepare(); 570b57cec5SDimitry Andric } 580b57cec5SDimitry Andric 590b57cec5SDimitry Andric // Returns the nearest EH pad that dominates this instruction. This does not use 600b57cec5SDimitry Andric // dominator analysis; it just does BFS on its predecessors until arriving at an 610b57cec5SDimitry Andric // EH pad. This assumes valid EH scopes so the first EH pad it arrives in all 620b57cec5SDimitry Andric // possible search paths should be the same. 630b57cec5SDimitry Andric // Returns nullptr in case it does not find any EH pad in the search, or finds 640b57cec5SDimitry Andric // multiple different EH pads. 65*5ffd83dbSDimitry Andric MachineBasicBlock * 66*5ffd83dbSDimitry Andric WebAssemblyLateEHPrepare::getMatchingEHPad(MachineInstr *MI) { 670b57cec5SDimitry Andric MachineFunction *MF = MI->getParent()->getParent(); 680b57cec5SDimitry Andric SmallVector<MachineBasicBlock *, 2> WL; 690b57cec5SDimitry Andric SmallPtrSet<MachineBasicBlock *, 2> Visited; 700b57cec5SDimitry Andric WL.push_back(MI->getParent()); 710b57cec5SDimitry Andric MachineBasicBlock *EHPad = nullptr; 720b57cec5SDimitry Andric while (!WL.empty()) { 730b57cec5SDimitry Andric MachineBasicBlock *MBB = WL.pop_back_val(); 740b57cec5SDimitry Andric if (Visited.count(MBB)) 750b57cec5SDimitry Andric continue; 760b57cec5SDimitry Andric Visited.insert(MBB); 770b57cec5SDimitry Andric if (MBB->isEHPad()) { 780b57cec5SDimitry Andric if (EHPad && EHPad != MBB) 790b57cec5SDimitry Andric return nullptr; 800b57cec5SDimitry Andric EHPad = MBB; 810b57cec5SDimitry Andric continue; 820b57cec5SDimitry Andric } 830b57cec5SDimitry Andric if (MBB == &MF->front()) 840b57cec5SDimitry Andric return nullptr; 85*5ffd83dbSDimitry Andric for (auto *Pred : MBB->predecessors()) 86*5ffd83dbSDimitry Andric if (!CatchRetBBs.count(Pred)) // We don't go into child scopes 87*5ffd83dbSDimitry Andric WL.push_back(Pred); 880b57cec5SDimitry Andric } 890b57cec5SDimitry Andric return EHPad; 900b57cec5SDimitry Andric } 910b57cec5SDimitry Andric 920b57cec5SDimitry Andric // Erase the specified BBs if the BB does not have any remaining predecessors, 930b57cec5SDimitry Andric // and also all its dead children. 940b57cec5SDimitry Andric template <typename Container> 950b57cec5SDimitry Andric static void eraseDeadBBsAndChildren(const Container &MBBs) { 960b57cec5SDimitry Andric SmallVector<MachineBasicBlock *, 8> WL(MBBs.begin(), MBBs.end()); 970b57cec5SDimitry Andric while (!WL.empty()) { 980b57cec5SDimitry Andric MachineBasicBlock *MBB = WL.pop_back_val(); 990b57cec5SDimitry Andric if (!MBB->pred_empty()) 1000b57cec5SDimitry Andric continue; 1010b57cec5SDimitry Andric SmallVector<MachineBasicBlock *, 4> Succs(MBB->succ_begin(), 1020b57cec5SDimitry Andric MBB->succ_end()); 1030b57cec5SDimitry Andric WL.append(MBB->succ_begin(), MBB->succ_end()); 1040b57cec5SDimitry Andric for (auto *Succ : Succs) 1050b57cec5SDimitry Andric MBB->removeSuccessor(Succ); 1060b57cec5SDimitry Andric MBB->eraseFromParent(); 1070b57cec5SDimitry Andric } 1080b57cec5SDimitry Andric } 1090b57cec5SDimitry Andric 1100b57cec5SDimitry Andric bool WebAssemblyLateEHPrepare::runOnMachineFunction(MachineFunction &MF) { 1110b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "********** Late EH Prepare **********\n" 1120b57cec5SDimitry Andric "********** Function: " 1130b57cec5SDimitry Andric << MF.getName() << '\n'); 1140b57cec5SDimitry Andric 1150b57cec5SDimitry Andric if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() != 1160b57cec5SDimitry Andric ExceptionHandling::Wasm) 1170b57cec5SDimitry Andric return false; 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric bool Changed = false; 1200b57cec5SDimitry Andric if (MF.getFunction().hasPersonalityFn()) { 121*5ffd83dbSDimitry Andric recordCatchRetBBs(MF); 1220b57cec5SDimitry Andric Changed |= addCatches(MF); 1230b57cec5SDimitry Andric Changed |= replaceFuncletReturns(MF); 1240b57cec5SDimitry Andric } 1250b57cec5SDimitry Andric Changed |= removeUnnecessaryUnreachables(MF); 1260b57cec5SDimitry Andric if (MF.getFunction().hasPersonalityFn()) { 1270b57cec5SDimitry Andric Changed |= addExceptionExtraction(MF); 1280b57cec5SDimitry Andric Changed |= restoreStackPointer(MF); 1290b57cec5SDimitry Andric } 1300b57cec5SDimitry Andric return Changed; 1310b57cec5SDimitry Andric } 1320b57cec5SDimitry Andric 133*5ffd83dbSDimitry Andric // Record which BB ends with 'CATCHRET' instruction, because this will be 134*5ffd83dbSDimitry Andric // replaced with BRs later. This set of 'CATCHRET' BBs is necessary in 135*5ffd83dbSDimitry Andric // 'getMatchingEHPad' function. 136*5ffd83dbSDimitry Andric void WebAssemblyLateEHPrepare::recordCatchRetBBs(MachineFunction &MF) { 137*5ffd83dbSDimitry Andric CatchRetBBs.clear(); 138*5ffd83dbSDimitry Andric for (auto &MBB : MF) { 139*5ffd83dbSDimitry Andric auto Pos = MBB.getFirstTerminator(); 140*5ffd83dbSDimitry Andric if (Pos == MBB.end()) 141*5ffd83dbSDimitry Andric continue; 142*5ffd83dbSDimitry Andric MachineInstr *TI = &*Pos; 143*5ffd83dbSDimitry Andric if (TI->getOpcode() == WebAssembly::CATCHRET) 144*5ffd83dbSDimitry Andric CatchRetBBs.insert(&MBB); 145*5ffd83dbSDimitry Andric } 146*5ffd83dbSDimitry Andric } 147*5ffd83dbSDimitry Andric 1480b57cec5SDimitry Andric // Add catch instruction to beginning of catchpads and cleanuppads. 1490b57cec5SDimitry Andric bool WebAssemblyLateEHPrepare::addCatches(MachineFunction &MF) { 1500b57cec5SDimitry Andric bool Changed = false; 1510b57cec5SDimitry Andric const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 1520b57cec5SDimitry Andric MachineRegisterInfo &MRI = MF.getRegInfo(); 1530b57cec5SDimitry Andric for (auto &MBB : MF) { 1540b57cec5SDimitry Andric if (MBB.isEHPad()) { 1550b57cec5SDimitry Andric Changed = true; 1560b57cec5SDimitry Andric auto InsertPos = MBB.begin(); 1570b57cec5SDimitry Andric if (InsertPos->isEHLabel()) // EH pad starts with an EH label 1580b57cec5SDimitry Andric ++InsertPos; 1598bcb0991SDimitry Andric Register DstReg = MRI.createVirtualRegister(&WebAssembly::EXNREFRegClass); 1600b57cec5SDimitry Andric BuildMI(MBB, InsertPos, MBB.begin()->getDebugLoc(), 1610b57cec5SDimitry Andric TII.get(WebAssembly::CATCH), DstReg); 1620b57cec5SDimitry Andric } 1630b57cec5SDimitry Andric } 1640b57cec5SDimitry Andric return Changed; 1650b57cec5SDimitry Andric } 1660b57cec5SDimitry Andric 1670b57cec5SDimitry Andric bool WebAssemblyLateEHPrepare::replaceFuncletReturns(MachineFunction &MF) { 1680b57cec5SDimitry Andric bool Changed = false; 1690b57cec5SDimitry Andric const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 1700b57cec5SDimitry Andric 1710b57cec5SDimitry Andric for (auto &MBB : MF) { 1720b57cec5SDimitry Andric auto Pos = MBB.getFirstTerminator(); 1730b57cec5SDimitry Andric if (Pos == MBB.end()) 1740b57cec5SDimitry Andric continue; 1750b57cec5SDimitry Andric MachineInstr *TI = &*Pos; 1760b57cec5SDimitry Andric 1770b57cec5SDimitry Andric switch (TI->getOpcode()) { 1780b57cec5SDimitry Andric case WebAssembly::CATCHRET: { 1790b57cec5SDimitry Andric // Replace a catchret with a branch 1800b57cec5SDimitry Andric MachineBasicBlock *TBB = TI->getOperand(0).getMBB(); 1810b57cec5SDimitry Andric if (!MBB.isLayoutSuccessor(TBB)) 1820b57cec5SDimitry Andric BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::BR)) 1830b57cec5SDimitry Andric .addMBB(TBB); 1840b57cec5SDimitry Andric TI->eraseFromParent(); 1850b57cec5SDimitry Andric Changed = true; 1860b57cec5SDimitry Andric break; 1870b57cec5SDimitry Andric } 1880b57cec5SDimitry Andric case WebAssembly::CLEANUPRET: 1890b57cec5SDimitry Andric case WebAssembly::RETHROW_IN_CATCH: { 1900b57cec5SDimitry Andric // Replace a cleanupret/rethrow_in_catch with a rethrow 1910b57cec5SDimitry Andric auto *EHPad = getMatchingEHPad(TI); 1920b57cec5SDimitry Andric auto CatchPos = EHPad->begin(); 1930b57cec5SDimitry Andric if (CatchPos->isEHLabel()) // EH pad starts with an EH label 1940b57cec5SDimitry Andric ++CatchPos; 1950b57cec5SDimitry Andric MachineInstr *Catch = &*CatchPos; 1968bcb0991SDimitry Andric Register ExnReg = Catch->getOperand(0).getReg(); 1970b57cec5SDimitry Andric BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW)) 1980b57cec5SDimitry Andric .addReg(ExnReg); 1990b57cec5SDimitry Andric TI->eraseFromParent(); 2000b57cec5SDimitry Andric Changed = true; 2010b57cec5SDimitry Andric break; 2020b57cec5SDimitry Andric } 2030b57cec5SDimitry Andric } 2040b57cec5SDimitry Andric } 2050b57cec5SDimitry Andric return Changed; 2060b57cec5SDimitry Andric } 2070b57cec5SDimitry Andric 2080b57cec5SDimitry Andric bool WebAssemblyLateEHPrepare::removeUnnecessaryUnreachables( 2090b57cec5SDimitry Andric MachineFunction &MF) { 2100b57cec5SDimitry Andric bool Changed = false; 2110b57cec5SDimitry Andric for (auto &MBB : MF) { 2120b57cec5SDimitry Andric for (auto &MI : MBB) { 2130b57cec5SDimitry Andric if (MI.getOpcode() != WebAssembly::THROW && 2140b57cec5SDimitry Andric MI.getOpcode() != WebAssembly::RETHROW) 2150b57cec5SDimitry Andric continue; 2160b57cec5SDimitry Andric Changed = true; 2170b57cec5SDimitry Andric 2180b57cec5SDimitry Andric // The instruction after the throw should be an unreachable or a branch to 2190b57cec5SDimitry Andric // another BB that should eventually lead to an unreachable. Delete it 2200b57cec5SDimitry Andric // because throw itself is a terminator, and also delete successors if 2210b57cec5SDimitry Andric // any. 2220b57cec5SDimitry Andric MBB.erase(std::next(MI.getIterator()), MBB.end()); 2230b57cec5SDimitry Andric SmallVector<MachineBasicBlock *, 8> Succs(MBB.succ_begin(), 2240b57cec5SDimitry Andric MBB.succ_end()); 2250b57cec5SDimitry Andric for (auto *Succ : Succs) 2260b57cec5SDimitry Andric if (!Succ->isEHPad()) 2270b57cec5SDimitry Andric MBB.removeSuccessor(Succ); 2280b57cec5SDimitry Andric eraseDeadBBsAndChildren(Succs); 2290b57cec5SDimitry Andric } 2300b57cec5SDimitry Andric } 2310b57cec5SDimitry Andric 2320b57cec5SDimitry Andric return Changed; 2330b57cec5SDimitry Andric } 2340b57cec5SDimitry Andric 2350b57cec5SDimitry Andric // Wasm uses 'br_on_exn' instruction to check the tag of an exception. It takes 2360b57cec5SDimitry Andric // exnref type object returned by 'catch', and branches to the destination if it 2370b57cec5SDimitry Andric // matches a given tag. We currently use __cpp_exception symbol to represent the 2380b57cec5SDimitry Andric // tag for all C++ exceptions. 2390b57cec5SDimitry Andric // 2400b57cec5SDimitry Andric // block $l (result i32) 2410b57cec5SDimitry Andric // ... 2420b57cec5SDimitry Andric // ;; exnref $e is on the stack at this point 2430b57cec5SDimitry Andric // br_on_exn $l $e ;; branch to $l with $e's arguments 2440b57cec5SDimitry Andric // ... 2450b57cec5SDimitry Andric // end 2460b57cec5SDimitry Andric // ;; Here we expect the extracted values are on top of the wasm value stack 2470b57cec5SDimitry Andric // ... Handle exception using values ... 2480b57cec5SDimitry Andric // 2490b57cec5SDimitry Andric // br_on_exn takes an exnref object and branches if it matches the given tag. 2500b57cec5SDimitry Andric // There can be multiple br_on_exn instructions if we want to match for another 2510b57cec5SDimitry Andric // tag, but for now we only test for __cpp_exception tag, and if it does not 2520b57cec5SDimitry Andric // match, i.e., it is a foreign exception, we rethrow it. 2530b57cec5SDimitry Andric // 2540b57cec5SDimitry Andric // In the destination BB that's the target of br_on_exn, extracted exception 2550b57cec5SDimitry Andric // values (in C++'s case a single i32, which represents an exception pointer) 2560b57cec5SDimitry Andric // are placed on top of the wasm stack. Because we can't model wasm stack in 2570b57cec5SDimitry Andric // LLVM instruction, we use 'extract_exception' pseudo instruction to retrieve 2580b57cec5SDimitry Andric // it. The pseudo instruction will be deleted later. 2590b57cec5SDimitry Andric bool WebAssemblyLateEHPrepare::addExceptionExtraction(MachineFunction &MF) { 2600b57cec5SDimitry Andric const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 2618bcb0991SDimitry Andric MachineRegisterInfo &MRI = MF.getRegInfo(); 2620b57cec5SDimitry Andric auto *EHInfo = MF.getWasmEHFuncInfo(); 2630b57cec5SDimitry Andric SmallVector<MachineInstr *, 16> ExtractInstrs; 2640b57cec5SDimitry Andric SmallVector<MachineInstr *, 8> ToDelete; 2650b57cec5SDimitry Andric for (auto &MBB : MF) { 2660b57cec5SDimitry Andric for (auto &MI : MBB) { 2670b57cec5SDimitry Andric if (MI.getOpcode() == WebAssembly::EXTRACT_EXCEPTION_I32) { 2680b57cec5SDimitry Andric if (MI.getOperand(0).isDead()) 2690b57cec5SDimitry Andric ToDelete.push_back(&MI); 2700b57cec5SDimitry Andric else 2710b57cec5SDimitry Andric ExtractInstrs.push_back(&MI); 2720b57cec5SDimitry Andric } 2730b57cec5SDimitry Andric } 2740b57cec5SDimitry Andric } 2750b57cec5SDimitry Andric bool Changed = !ToDelete.empty() || !ExtractInstrs.empty(); 2760b57cec5SDimitry Andric for (auto *MI : ToDelete) 2770b57cec5SDimitry Andric MI->eraseFromParent(); 2780b57cec5SDimitry Andric if (ExtractInstrs.empty()) 2790b57cec5SDimitry Andric return Changed; 2800b57cec5SDimitry Andric 2810b57cec5SDimitry Andric // Find terminate pads. 2820b57cec5SDimitry Andric SmallSet<MachineBasicBlock *, 8> TerminatePads; 2830b57cec5SDimitry Andric for (auto &MBB : MF) { 2840b57cec5SDimitry Andric for (auto &MI : MBB) { 2850b57cec5SDimitry Andric if (MI.isCall()) { 2860b57cec5SDimitry Andric const MachineOperand &CalleeOp = MI.getOperand(0); 2870b57cec5SDimitry Andric if (CalleeOp.isGlobal() && CalleeOp.getGlobal()->getName() == 2880b57cec5SDimitry Andric WebAssembly::ClangCallTerminateFn) 2890b57cec5SDimitry Andric TerminatePads.insert(getMatchingEHPad(&MI)); 2900b57cec5SDimitry Andric } 2910b57cec5SDimitry Andric } 2920b57cec5SDimitry Andric } 2930b57cec5SDimitry Andric 2940b57cec5SDimitry Andric for (auto *Extract : ExtractInstrs) { 2950b57cec5SDimitry Andric MachineBasicBlock *EHPad = getMatchingEHPad(Extract); 2960b57cec5SDimitry Andric assert(EHPad && "No matching EH pad for extract_exception"); 2970b57cec5SDimitry Andric auto CatchPos = EHPad->begin(); 2980b57cec5SDimitry Andric if (CatchPos->isEHLabel()) // EH pad starts with an EH label 2990b57cec5SDimitry Andric ++CatchPos; 3000b57cec5SDimitry Andric MachineInstr *Catch = &*CatchPos; 3010b57cec5SDimitry Andric 3020b57cec5SDimitry Andric if (Catch->getNextNode() != Extract) 3030b57cec5SDimitry Andric EHPad->insert(Catch->getNextNode(), Extract->removeFromParent()); 3040b57cec5SDimitry Andric 3050b57cec5SDimitry Andric // - Before: 3060b57cec5SDimitry Andric // ehpad: 3070b57cec5SDimitry Andric // %exnref:exnref = catch 3080b57cec5SDimitry Andric // %exn:i32 = extract_exception 3090b57cec5SDimitry Andric // ... use exn ... 3100b57cec5SDimitry Andric // 3110b57cec5SDimitry Andric // - After: 3120b57cec5SDimitry Andric // ehpad: 3130b57cec5SDimitry Andric // %exnref:exnref = catch 3140b57cec5SDimitry Andric // br_on_exn %thenbb, $__cpp_exception, %exnref 3150b57cec5SDimitry Andric // br %elsebb 3160b57cec5SDimitry Andric // elsebb: 3170b57cec5SDimitry Andric // rethrow 3180b57cec5SDimitry Andric // thenbb: 3190b57cec5SDimitry Andric // %exn:i32 = extract_exception 3200b57cec5SDimitry Andric // ... use exn ... 3218bcb0991SDimitry Andric Register ExnReg = Catch->getOperand(0).getReg(); 3220b57cec5SDimitry Andric auto *ThenMBB = MF.CreateMachineBasicBlock(); 3230b57cec5SDimitry Andric auto *ElseMBB = MF.CreateMachineBasicBlock(); 3240b57cec5SDimitry Andric MF.insert(std::next(MachineFunction::iterator(EHPad)), ElseMBB); 3250b57cec5SDimitry Andric MF.insert(std::next(MachineFunction::iterator(ElseMBB)), ThenMBB); 3260b57cec5SDimitry Andric ThenMBB->splice(ThenMBB->end(), EHPad, Extract, EHPad->end()); 3270b57cec5SDimitry Andric ThenMBB->transferSuccessors(EHPad); 3280b57cec5SDimitry Andric EHPad->addSuccessor(ThenMBB); 3290b57cec5SDimitry Andric EHPad->addSuccessor(ElseMBB); 3300b57cec5SDimitry Andric 3310b57cec5SDimitry Andric DebugLoc DL = Extract->getDebugLoc(); 3320b57cec5SDimitry Andric const char *CPPExnSymbol = MF.createExternalSymbolName("__cpp_exception"); 3330b57cec5SDimitry Andric BuildMI(EHPad, DL, TII.get(WebAssembly::BR_ON_EXN)) 3340b57cec5SDimitry Andric .addMBB(ThenMBB) 3350b57cec5SDimitry Andric .addExternalSymbol(CPPExnSymbol) 3360b57cec5SDimitry Andric .addReg(ExnReg); 3370b57cec5SDimitry Andric BuildMI(EHPad, DL, TII.get(WebAssembly::BR)).addMBB(ElseMBB); 3380b57cec5SDimitry Andric 3390b57cec5SDimitry Andric // When this is a terminate pad with __clang_call_terminate() call, we don't 3400b57cec5SDimitry Andric // rethrow it anymore and call __clang_call_terminate() with a nullptr 3410b57cec5SDimitry Andric // argument, which will call std::terminate(). 3420b57cec5SDimitry Andric // 3430b57cec5SDimitry Andric // - Before: 3440b57cec5SDimitry Andric // ehpad: 3450b57cec5SDimitry Andric // %exnref:exnref = catch 3460b57cec5SDimitry Andric // %exn:i32 = extract_exception 3470b57cec5SDimitry Andric // call @__clang_call_terminate(%exn) 3480b57cec5SDimitry Andric // unreachable 3490b57cec5SDimitry Andric // 3500b57cec5SDimitry Andric // - After: 3510b57cec5SDimitry Andric // ehpad: 3520b57cec5SDimitry Andric // %exnref:exnref = catch 3530b57cec5SDimitry Andric // br_on_exn %thenbb, $__cpp_exception, %exnref 3540b57cec5SDimitry Andric // br %elsebb 3550b57cec5SDimitry Andric // elsebb: 3560b57cec5SDimitry Andric // call @__clang_call_terminate(0) 3570b57cec5SDimitry Andric // unreachable 3580b57cec5SDimitry Andric // thenbb: 3590b57cec5SDimitry Andric // %exn:i32 = extract_exception 3600b57cec5SDimitry Andric // call @__clang_call_terminate(%exn) 3610b57cec5SDimitry Andric // unreachable 3620b57cec5SDimitry Andric if (TerminatePads.count(EHPad)) { 3630b57cec5SDimitry Andric Function *ClangCallTerminateFn = 3640b57cec5SDimitry Andric MF.getFunction().getParent()->getFunction( 3650b57cec5SDimitry Andric WebAssembly::ClangCallTerminateFn); 3660b57cec5SDimitry Andric assert(ClangCallTerminateFn && 3670b57cec5SDimitry Andric "There is no __clang_call_terminate() function"); 3688bcb0991SDimitry Andric Register Reg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 3698bcb0991SDimitry Andric BuildMI(ElseMBB, DL, TII.get(WebAssembly::CONST_I32), Reg).addImm(0); 370*5ffd83dbSDimitry Andric BuildMI(ElseMBB, DL, TII.get(WebAssembly::CALL)) 3710b57cec5SDimitry Andric .addGlobalAddress(ClangCallTerminateFn) 3728bcb0991SDimitry Andric .addReg(Reg); 3730b57cec5SDimitry Andric BuildMI(ElseMBB, DL, TII.get(WebAssembly::UNREACHABLE)); 3740b57cec5SDimitry Andric 3750b57cec5SDimitry Andric } else { 3760b57cec5SDimitry Andric BuildMI(ElseMBB, DL, TII.get(WebAssembly::RETHROW)).addReg(ExnReg); 3770b57cec5SDimitry Andric if (EHInfo->hasEHPadUnwindDest(EHPad)) 3780b57cec5SDimitry Andric ElseMBB->addSuccessor(EHInfo->getEHPadUnwindDest(EHPad)); 3790b57cec5SDimitry Andric } 3800b57cec5SDimitry Andric } 3810b57cec5SDimitry Andric 3820b57cec5SDimitry Andric return true; 3830b57cec5SDimitry Andric } 3840b57cec5SDimitry Andric 3850b57cec5SDimitry Andric // After the stack is unwound due to a thrown exception, the __stack_pointer 3860b57cec5SDimitry Andric // global can point to an invalid address. This inserts instructions that 3870b57cec5SDimitry Andric // restore __stack_pointer global. 3880b57cec5SDimitry Andric bool WebAssemblyLateEHPrepare::restoreStackPointer(MachineFunction &MF) { 3890b57cec5SDimitry Andric const auto *FrameLowering = static_cast<const WebAssemblyFrameLowering *>( 3900b57cec5SDimitry Andric MF.getSubtarget().getFrameLowering()); 3910b57cec5SDimitry Andric if (!FrameLowering->needsPrologForEH(MF)) 3920b57cec5SDimitry Andric return false; 3930b57cec5SDimitry Andric bool Changed = false; 3940b57cec5SDimitry Andric 3950b57cec5SDimitry Andric for (auto &MBB : MF) { 3960b57cec5SDimitry Andric if (!MBB.isEHPad()) 3970b57cec5SDimitry Andric continue; 3980b57cec5SDimitry Andric Changed = true; 3990b57cec5SDimitry Andric 4000b57cec5SDimitry Andric // Insert __stack_pointer restoring instructions at the beginning of each EH 4010b57cec5SDimitry Andric // pad, after the catch instruction. Here it is safe to assume that SP32 4020b57cec5SDimitry Andric // holds the latest value of __stack_pointer, because the only exception for 4030b57cec5SDimitry Andric // this case is when a function uses the red zone, but that only happens 4040b57cec5SDimitry Andric // with leaf functions, and we don't restore __stack_pointer in leaf 4050b57cec5SDimitry Andric // functions anyway. 4060b57cec5SDimitry Andric auto InsertPos = MBB.begin(); 4070b57cec5SDimitry Andric if (InsertPos->isEHLabel()) // EH pad starts with an EH label 4080b57cec5SDimitry Andric ++InsertPos; 4090b57cec5SDimitry Andric if (InsertPos->getOpcode() == WebAssembly::CATCH) 4100b57cec5SDimitry Andric ++InsertPos; 411*5ffd83dbSDimitry Andric FrameLowering->writeSPToGlobal(FrameLowering->getSPReg(MF), MF, MBB, 412*5ffd83dbSDimitry Andric InsertPos, MBB.begin()->getDebugLoc()); 4130b57cec5SDimitry Andric } 4140b57cec5SDimitry Andric return Changed; 4150b57cec5SDimitry Andric } 416