Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 1 | //=== WebAssemblyLateEHPrepare.cpp - WebAssembly Exception Preparation -===// |
| 2 | // |
Chandler Carruth | 2946cd7 | 2019-01-19 08:50:56 +0000 | [diff] [blame] | 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 |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | /// |
| 9 | /// \file |
| 10 | /// \brief Does various transformations for exception handling. |
| 11 | /// |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" |
| 15 | #include "WebAssembly.h" |
| 16 | #include "WebAssemblySubtarget.h" |
| 17 | #include "WebAssemblyUtilities.h" |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 18 | #include "llvm/ADT/SmallSet.h" |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 19 | #include "llvm/CodeGen/MachineInstrBuilder.h" |
| 20 | #include "llvm/CodeGen/WasmEHFuncInfo.h" |
| 21 | #include "llvm/MC/MCAsmInfo.h" |
| 22 | using namespace llvm; |
| 23 | |
Heejin Ahn | 8b49b6b | 2019-03-13 00:37:31 +0000 | [diff] [blame] | 24 | #define DEBUG_TYPE "wasm-late-eh-prepare" |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 25 | |
| 26 | namespace { |
| 27 | class WebAssemblyLateEHPrepare final : public MachineFunctionPass { |
| 28 | StringRef getPassName() const override { |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 29 | return "WebAssembly Late Prepare Exception"; |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 30 | } |
| 31 | |
| 32 | bool runOnMachineFunction(MachineFunction &MF) override; |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 33 | bool addCatches(MachineFunction &MF); |
Heejin Ahn | b47a18c | 2019-03-16 04:46:05 +0000 | [diff] [blame] | 34 | bool replaceFuncletReturns(MachineFunction &MF); |
| 35 | bool removeUnnecessaryUnreachables(MachineFunction &MF); |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 36 | bool addExceptionExtraction(MachineFunction &MF); |
Heejin Ahn | 0bb9865 | 2019-01-30 22:44:45 +0000 | [diff] [blame] | 37 | bool restoreStackPointer(MachineFunction &MF); |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 38 | |
| 39 | public: |
| 40 | static char ID; // Pass identification, replacement for typeid |
| 41 | WebAssemblyLateEHPrepare() : MachineFunctionPass(ID) {} |
| 42 | }; |
| 43 | } // end anonymous namespace |
| 44 | |
| 45 | char WebAssemblyLateEHPrepare::ID = 0; |
| 46 | INITIALIZE_PASS(WebAssemblyLateEHPrepare, DEBUG_TYPE, |
Heejin Ahn | a93e726 | 2018-08-17 00:12:04 +0000 | [diff] [blame] | 47 | "WebAssembly Late Exception Preparation", false, false) |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 48 | |
| 49 | FunctionPass *llvm::createWebAssemblyLateEHPrepare() { |
| 50 | return new WebAssemblyLateEHPrepare(); |
| 51 | } |
| 52 | |
| 53 | // Returns the nearest EH pad that dominates this instruction. This does not use |
| 54 | // dominator analysis; it just does BFS on its predecessors until arriving at an |
| 55 | // EH pad. This assumes valid EH scopes so the first EH pad it arrives in all |
| 56 | // possible search paths should be the same. |
| 57 | // Returns nullptr in case it does not find any EH pad in the search, or finds |
| 58 | // multiple different EH pads. |
Heejin Ahn | 095796a | 2018-11-16 00:47:18 +0000 | [diff] [blame] | 59 | static MachineBasicBlock *getMatchingEHPad(MachineInstr *MI) { |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 60 | MachineFunction *MF = MI->getParent()->getParent(); |
| 61 | SmallVector<MachineBasicBlock *, 2> WL; |
| 62 | SmallPtrSet<MachineBasicBlock *, 2> Visited; |
| 63 | WL.push_back(MI->getParent()); |
| 64 | MachineBasicBlock *EHPad = nullptr; |
| 65 | while (!WL.empty()) { |
| 66 | MachineBasicBlock *MBB = WL.pop_back_val(); |
| 67 | if (Visited.count(MBB)) |
| 68 | continue; |
| 69 | Visited.insert(MBB); |
| 70 | if (MBB->isEHPad()) { |
| 71 | if (EHPad && EHPad != MBB) |
| 72 | return nullptr; |
| 73 | EHPad = MBB; |
| 74 | continue; |
| 75 | } |
| 76 | if (MBB == &MF->front()) |
| 77 | return nullptr; |
| 78 | WL.append(MBB->pred_begin(), MBB->pred_end()); |
| 79 | } |
| 80 | return EHPad; |
| 81 | } |
| 82 | |
Heejin Ahn | 095796a | 2018-11-16 00:47:18 +0000 | [diff] [blame] | 83 | // Erase the specified BBs if the BB does not have any remaining predecessors, |
| 84 | // and also all its dead children. |
Benjamin Kramer | 83996e4 | 2018-08-08 10:13:19 +0000 | [diff] [blame] | 85 | template <typename Container> |
Heejin Ahn | 095796a | 2018-11-16 00:47:18 +0000 | [diff] [blame] | 86 | static void eraseDeadBBsAndChildren(const Container &MBBs) { |
Benjamin Kramer | 83996e4 | 2018-08-08 10:13:19 +0000 | [diff] [blame] | 87 | SmallVector<MachineBasicBlock *, 8> WL(MBBs.begin(), MBBs.end()); |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 88 | while (!WL.empty()) { |
| 89 | MachineBasicBlock *MBB = WL.pop_back_val(); |
Heejin Ahn | 095796a | 2018-11-16 00:47:18 +0000 | [diff] [blame] | 90 | if (!MBB->pred_empty()) |
| 91 | continue; |
Heejin Ahn | b68d591 | 2018-10-04 21:03:35 +0000 | [diff] [blame] | 92 | SmallVector<MachineBasicBlock *, 4> Succs(MBB->succ_begin(), |
| 93 | MBB->succ_end()); |
| 94 | WL.append(MBB->succ_begin(), MBB->succ_end()); |
| 95 | for (auto *Succ : Succs) |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 96 | MBB->removeSuccessor(Succ); |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 97 | MBB->eraseFromParent(); |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | bool WebAssemblyLateEHPrepare::runOnMachineFunction(MachineFunction &MF) { |
Heejin Ahn | 569f090 | 2019-01-09 23:05:21 +0000 | [diff] [blame] | 102 | LLVM_DEBUG(dbgs() << "********** Late EH Prepare **********\n" |
| 103 | "********** Function: " |
| 104 | << MF.getName() << '\n'); |
| 105 | |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 106 | if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() != |
| 107 | ExceptionHandling::Wasm) |
| 108 | return false; |
| 109 | |
| 110 | bool Changed = false; |
Heejin Ahn | b47a18c | 2019-03-16 04:46:05 +0000 | [diff] [blame] | 111 | if (MF.getFunction().hasPersonalityFn()) { |
| 112 | Changed |= addCatches(MF); |
| 113 | Changed |= replaceFuncletReturns(MF); |
| 114 | } |
Heejin Ahn | 095796a | 2018-11-16 00:47:18 +0000 | [diff] [blame] | 115 | Changed |= removeUnnecessaryUnreachables(MF); |
Heejin Ahn | b47a18c | 2019-03-16 04:46:05 +0000 | [diff] [blame] | 116 | if (MF.getFunction().hasPersonalityFn()) { |
| 117 | Changed |= addExceptionExtraction(MF); |
| 118 | Changed |= restoreStackPointer(MF); |
| 119 | } |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 120 | return Changed; |
| 121 | } |
| 122 | |
Heejin Ahn | b47a18c | 2019-03-16 04:46:05 +0000 | [diff] [blame] | 123 | // Add catch instruction to beginning of catchpads and cleanuppads. |
| 124 | bool WebAssemblyLateEHPrepare::addCatches(MachineFunction &MF) { |
Heejin Ahn | 095796a | 2018-11-16 00:47:18 +0000 | [diff] [blame] | 125 | bool Changed = false; |
Heejin Ahn | b47a18c | 2019-03-16 04:46:05 +0000 | [diff] [blame] | 126 | const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); |
| 127 | MachineRegisterInfo &MRI = MF.getRegInfo(); |
Heejin Ahn | 095796a | 2018-11-16 00:47:18 +0000 | [diff] [blame] | 128 | for (auto &MBB : MF) { |
Heejin Ahn | b47a18c | 2019-03-16 04:46:05 +0000 | [diff] [blame] | 129 | if (MBB.isEHPad()) { |
Heejin Ahn | 095796a | 2018-11-16 00:47:18 +0000 | [diff] [blame] | 130 | Changed = true; |
Heejin Ahn | b47a18c | 2019-03-16 04:46:05 +0000 | [diff] [blame] | 131 | auto InsertPos = MBB.begin(); |
| 132 | if (InsertPos->isEHLabel()) // EH pad starts with an EH label |
| 133 | ++InsertPos; |
Daniel Sanders | 05c145d | 2019-08-12 22:40:45 +0000 | [diff] [blame] | 134 | Register DstReg = MRI.createVirtualRegister(&WebAssembly::EXNREFRegClass); |
Heejin Ahn | b47a18c | 2019-03-16 04:46:05 +0000 | [diff] [blame] | 135 | BuildMI(MBB, InsertPos, MBB.begin()->getDebugLoc(), |
| 136 | TII.get(WebAssembly::CATCH), DstReg); |
Heejin Ahn | 095796a | 2018-11-16 00:47:18 +0000 | [diff] [blame] | 137 | } |
| 138 | } |
Heejin Ahn | 095796a | 2018-11-16 00:47:18 +0000 | [diff] [blame] | 139 | return Changed; |
| 140 | } |
| 141 | |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 142 | bool WebAssemblyLateEHPrepare::replaceFuncletReturns(MachineFunction &MF) { |
| 143 | bool Changed = false; |
| 144 | const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 145 | |
| 146 | for (auto &MBB : MF) { |
| 147 | auto Pos = MBB.getFirstTerminator(); |
| 148 | if (Pos == MBB.end()) |
| 149 | continue; |
| 150 | MachineInstr *TI = &*Pos; |
| 151 | |
| 152 | switch (TI->getOpcode()) { |
| 153 | case WebAssembly::CATCHRET: { |
| 154 | // Replace a catchret with a branch |
| 155 | MachineBasicBlock *TBB = TI->getOperand(0).getMBB(); |
| 156 | if (!MBB.isLayoutSuccessor(TBB)) |
| 157 | BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::BR)) |
| 158 | .addMBB(TBB); |
| 159 | TI->eraseFromParent(); |
| 160 | Changed = true; |
| 161 | break; |
| 162 | } |
Heejin Ahn | 66ce419 | 2019-03-16 05:38:57 +0000 | [diff] [blame] | 163 | case WebAssembly::CLEANUPRET: |
| 164 | case WebAssembly::RETHROW_IN_CATCH: { |
| 165 | // Replace a cleanupret/rethrow_in_catch with a rethrow |
| 166 | auto *EHPad = getMatchingEHPad(TI); |
| 167 | auto CatchPos = EHPad->begin(); |
| 168 | if (CatchPos->isEHLabel()) // EH pad starts with an EH label |
| 169 | ++CatchPos; |
| 170 | MachineInstr *Catch = &*CatchPos; |
Daniel Sanders | 05c145d | 2019-08-12 22:40:45 +0000 | [diff] [blame] | 171 | Register ExnReg = Catch->getOperand(0).getReg(); |
Heejin Ahn | 66ce419 | 2019-03-16 05:38:57 +0000 | [diff] [blame] | 172 | BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW)) |
| 173 | .addReg(ExnReg); |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 174 | TI->eraseFromParent(); |
| 175 | Changed = true; |
| 176 | break; |
| 177 | } |
| 178 | } |
| 179 | } |
| 180 | return Changed; |
| 181 | } |
| 182 | |
Heejin Ahn | b47a18c | 2019-03-16 04:46:05 +0000 | [diff] [blame] | 183 | bool WebAssemblyLateEHPrepare::removeUnnecessaryUnreachables( |
| 184 | MachineFunction &MF) { |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 185 | bool Changed = false; |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 186 | for (auto &MBB : MF) { |
Heejin Ahn | b47a18c | 2019-03-16 04:46:05 +0000 | [diff] [blame] | 187 | for (auto &MI : MBB) { |
| 188 | if (MI.getOpcode() != WebAssembly::THROW && |
| 189 | MI.getOpcode() != WebAssembly::RETHROW) |
| 190 | continue; |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 191 | Changed = true; |
Heejin Ahn | b47a18c | 2019-03-16 04:46:05 +0000 | [diff] [blame] | 192 | |
| 193 | // The instruction after the throw should be an unreachable or a branch to |
| 194 | // another BB that should eventually lead to an unreachable. Delete it |
| 195 | // because throw itself is a terminator, and also delete successors if |
| 196 | // any. |
| 197 | MBB.erase(std::next(MI.getIterator()), MBB.end()); |
| 198 | SmallVector<MachineBasicBlock *, 8> Succs(MBB.succ_begin(), |
| 199 | MBB.succ_end()); |
| 200 | for (auto *Succ : Succs) |
Heejin Ahn | 66ce419 | 2019-03-16 05:38:57 +0000 | [diff] [blame] | 201 | if (!Succ->isEHPad()) |
| 202 | MBB.removeSuccessor(Succ); |
Heejin Ahn | b47a18c | 2019-03-16 04:46:05 +0000 | [diff] [blame] | 203 | eraseDeadBBsAndChildren(Succs); |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 204 | } |
| 205 | } |
Heejin Ahn | b47a18c | 2019-03-16 04:46:05 +0000 | [diff] [blame] | 206 | |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 207 | return Changed; |
| 208 | } |
| 209 | |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 210 | // Wasm uses 'br_on_exn' instruction to check the tag of an exception. It takes |
Heejin Ahn | 9f96a58 | 2019-07-15 22:49:25 +0000 | [diff] [blame] | 211 | // exnref type object returned by 'catch', and branches to the destination if it |
| 212 | // matches a given tag. We currently use __cpp_exception symbol to represent the |
| 213 | // tag for all C++ exceptions. |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 214 | // |
| 215 | // block $l (result i32) |
| 216 | // ... |
Heejin Ahn | 9f96a58 | 2019-07-15 22:49:25 +0000 | [diff] [blame] | 217 | // ;; exnref $e is on the stack at this point |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 218 | // br_on_exn $l $e ;; branch to $l with $e's arguments |
| 219 | // ... |
| 220 | // end |
| 221 | // ;; Here we expect the extracted values are on top of the wasm value stack |
| 222 | // ... Handle exception using values ... |
| 223 | // |
Heejin Ahn | 9f96a58 | 2019-07-15 22:49:25 +0000 | [diff] [blame] | 224 | // br_on_exn takes an exnref object and branches if it matches the given tag. |
| 225 | // There can be multiple br_on_exn instructions if we want to match for another |
| 226 | // tag, but for now we only test for __cpp_exception tag, and if it does not |
| 227 | // match, i.e., it is a foreign exception, we rethrow it. |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 228 | // |
| 229 | // In the destination BB that's the target of br_on_exn, extracted exception |
| 230 | // values (in C++'s case a single i32, which represents an exception pointer) |
| 231 | // are placed on top of the wasm stack. Because we can't model wasm stack in |
| 232 | // LLVM instruction, we use 'extract_exception' pseudo instruction to retrieve |
| 233 | // it. The pseudo instruction will be deleted later. |
| 234 | bool WebAssemblyLateEHPrepare::addExceptionExtraction(MachineFunction &MF) { |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 235 | const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); |
Heejin Ahn | 831efe0 | 2019-08-11 06:24:07 +0000 | [diff] [blame] | 236 | MachineRegisterInfo &MRI = MF.getRegInfo(); |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 237 | auto *EHInfo = MF.getWasmEHFuncInfo(); |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 238 | SmallVector<MachineInstr *, 16> ExtractInstrs; |
Heejin Ahn | d2a56ac | 2019-02-26 04:08:49 +0000 | [diff] [blame] | 239 | SmallVector<MachineInstr *, 8> ToDelete; |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 240 | for (auto &MBB : MF) { |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 241 | for (auto &MI : MBB) { |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 242 | if (MI.getOpcode() == WebAssembly::EXTRACT_EXCEPTION_I32) { |
| 243 | if (MI.getOperand(0).isDead()) |
Heejin Ahn | d2a56ac | 2019-02-26 04:08:49 +0000 | [diff] [blame] | 244 | ToDelete.push_back(&MI); |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 245 | else |
| 246 | ExtractInstrs.push_back(&MI); |
| 247 | } |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 248 | } |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 249 | } |
Heejin Ahn | d2a56ac | 2019-02-26 04:08:49 +0000 | [diff] [blame] | 250 | bool Changed = !ToDelete.empty() || !ExtractInstrs.empty(); |
| 251 | for (auto *MI : ToDelete) |
| 252 | MI->eraseFromParent(); |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 253 | if (ExtractInstrs.empty()) |
Heejin Ahn | d2a56ac | 2019-02-26 04:08:49 +0000 | [diff] [blame] | 254 | return Changed; |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 255 | |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 256 | // Find terminate pads. |
| 257 | SmallSet<MachineBasicBlock *, 8> TerminatePads; |
| 258 | for (auto &MBB : MF) { |
| 259 | for (auto &MI : MBB) { |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 260 | if (MI.isCall()) { |
| 261 | const MachineOperand &CalleeOp = MI.getOperand(0); |
| 262 | if (CalleeOp.isGlobal() && CalleeOp.getGlobal()->getName() == |
| 263 | WebAssembly::ClangCallTerminateFn) |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 264 | TerminatePads.insert(getMatchingEHPad(&MI)); |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 265 | } |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 266 | } |
| 267 | } |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 268 | |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 269 | for (auto *Extract : ExtractInstrs) { |
| 270 | MachineBasicBlock *EHPad = getMatchingEHPad(Extract); |
| 271 | assert(EHPad && "No matching EH pad for extract_exception"); |
Heejin Ahn | 8b49b6b | 2019-03-13 00:37:31 +0000 | [diff] [blame] | 272 | auto CatchPos = EHPad->begin(); |
| 273 | if (CatchPos->isEHLabel()) // EH pad starts with an EH label |
| 274 | ++CatchPos; |
| 275 | MachineInstr *Catch = &*CatchPos; |
| 276 | |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 277 | if (Catch->getNextNode() != Extract) |
| 278 | EHPad->insert(Catch->getNextNode(), Extract->removeFromParent()); |
| 279 | |
| 280 | // - Before: |
| 281 | // ehpad: |
Heejin Ahn | 9f96a58 | 2019-07-15 22:49:25 +0000 | [diff] [blame] | 282 | // %exnref:exnref = catch |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 283 | // %exn:i32 = extract_exception |
| 284 | // ... use exn ... |
| 285 | // |
| 286 | // - After: |
| 287 | // ehpad: |
Heejin Ahn | 9f96a58 | 2019-07-15 22:49:25 +0000 | [diff] [blame] | 288 | // %exnref:exnref = catch |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 289 | // br_on_exn %thenbb, $__cpp_exception, %exnref |
| 290 | // br %elsebb |
| 291 | // elsebb: |
| 292 | // rethrow |
| 293 | // thenbb: |
| 294 | // %exn:i32 = extract_exception |
| 295 | // ... use exn ... |
Daniel Sanders | 05c145d | 2019-08-12 22:40:45 +0000 | [diff] [blame] | 296 | Register ExnReg = Catch->getOperand(0).getReg(); |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 297 | auto *ThenMBB = MF.CreateMachineBasicBlock(); |
| 298 | auto *ElseMBB = MF.CreateMachineBasicBlock(); |
| 299 | MF.insert(std::next(MachineFunction::iterator(EHPad)), ElseMBB); |
| 300 | MF.insert(std::next(MachineFunction::iterator(ElseMBB)), ThenMBB); |
| 301 | ThenMBB->splice(ThenMBB->end(), EHPad, Extract, EHPad->end()); |
| 302 | ThenMBB->transferSuccessors(EHPad); |
| 303 | EHPad->addSuccessor(ThenMBB); |
| 304 | EHPad->addSuccessor(ElseMBB); |
| 305 | |
| 306 | DebugLoc DL = Extract->getDebugLoc(); |
| 307 | const char *CPPExnSymbol = MF.createExternalSymbolName("__cpp_exception"); |
| 308 | BuildMI(EHPad, DL, TII.get(WebAssembly::BR_ON_EXN)) |
| 309 | .addMBB(ThenMBB) |
Sam Clegg | ef4c66c | 2019-04-03 00:17:29 +0000 | [diff] [blame] | 310 | .addExternalSymbol(CPPExnSymbol) |
Heejin Ahn | c739761 | 2019-03-05 11:11:34 +0000 | [diff] [blame] | 311 | .addReg(ExnReg); |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 312 | BuildMI(EHPad, DL, TII.get(WebAssembly::BR)).addMBB(ElseMBB); |
| 313 | |
| 314 | // When this is a terminate pad with __clang_call_terminate() call, we don't |
| 315 | // rethrow it anymore and call __clang_call_terminate() with a nullptr |
| 316 | // argument, which will call std::terminate(). |
| 317 | // |
| 318 | // - Before: |
| 319 | // ehpad: |
Heejin Ahn | 9f96a58 | 2019-07-15 22:49:25 +0000 | [diff] [blame] | 320 | // %exnref:exnref = catch |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 321 | // %exn:i32 = extract_exception |
| 322 | // call @__clang_call_terminate(%exn) |
| 323 | // unreachable |
| 324 | // |
| 325 | // - After: |
| 326 | // ehpad: |
Heejin Ahn | 9f96a58 | 2019-07-15 22:49:25 +0000 | [diff] [blame] | 327 | // %exnref:exnref = catch |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 328 | // br_on_exn %thenbb, $__cpp_exception, %exnref |
| 329 | // br %elsebb |
| 330 | // elsebb: |
| 331 | // call @__clang_call_terminate(0) |
| 332 | // unreachable |
| 333 | // thenbb: |
| 334 | // %exn:i32 = extract_exception |
| 335 | // call @__clang_call_terminate(%exn) |
| 336 | // unreachable |
| 337 | if (TerminatePads.count(EHPad)) { |
| 338 | Function *ClangCallTerminateFn = |
| 339 | MF.getFunction().getParent()->getFunction( |
| 340 | WebAssembly::ClangCallTerminateFn); |
| 341 | assert(ClangCallTerminateFn && |
| 342 | "There is no __clang_call_terminate() function"); |
Heejin Ahn | 831efe0 | 2019-08-11 06:24:07 +0000 | [diff] [blame] | 343 | unsigned Reg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); |
| 344 | BuildMI(ElseMBB, DL, TII.get(WebAssembly::CONST_I32), Reg).addImm(0); |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 345 | BuildMI(ElseMBB, DL, TII.get(WebAssembly::CALL_VOID)) |
| 346 | .addGlobalAddress(ClangCallTerminateFn) |
Heejin Ahn | 831efe0 | 2019-08-11 06:24:07 +0000 | [diff] [blame] | 347 | .addReg(Reg); |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 348 | BuildMI(ElseMBB, DL, TII.get(WebAssembly::UNREACHABLE)); |
| 349 | |
| 350 | } else { |
Heejin Ahn | 66ce419 | 2019-03-16 05:38:57 +0000 | [diff] [blame] | 351 | BuildMI(ElseMBB, DL, TII.get(WebAssembly::RETHROW)).addReg(ExnReg); |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 352 | if (EHInfo->hasEHPadUnwindDest(EHPad)) |
Heejin Ahn | 195a62e | 2019-03-03 22:35:56 +0000 | [diff] [blame] | 353 | ElseMBB->addSuccessor(EHInfo->getEHPadUnwindDest(EHPad)); |
Heejin Ahn | d6f4878 | 2019-01-30 03:21:57 +0000 | [diff] [blame] | 354 | } |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 355 | } |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 356 | |
Heejin Ahn | 4934f76 | 2018-06-25 01:07:11 +0000 | [diff] [blame] | 357 | return true; |
| 358 | } |
Heejin Ahn | 0bb9865 | 2019-01-30 22:44:45 +0000 | [diff] [blame] | 359 | |
| 360 | // After the stack is unwound due to a thrown exception, the __stack_pointer |
| 361 | // global can point to an invalid address. This inserts instructions that |
| 362 | // restore __stack_pointer global. |
| 363 | bool WebAssemblyLateEHPrepare::restoreStackPointer(MachineFunction &MF) { |
| 364 | const auto *FrameLowering = static_cast<const WebAssemblyFrameLowering *>( |
| 365 | MF.getSubtarget().getFrameLowering()); |
| 366 | if (!FrameLowering->needsPrologForEH(MF)) |
| 367 | return false; |
| 368 | bool Changed = false; |
| 369 | |
| 370 | for (auto &MBB : MF) { |
| 371 | if (!MBB.isEHPad()) |
| 372 | continue; |
| 373 | Changed = true; |
| 374 | |
| 375 | // Insert __stack_pointer restoring instructions at the beginning of each EH |
| 376 | // pad, after the catch instruction. Here it is safe to assume that SP32 |
| 377 | // holds the latest value of __stack_pointer, because the only exception for |
| 378 | // this case is when a function uses the red zone, but that only happens |
| 379 | // with leaf functions, and we don't restore __stack_pointer in leaf |
| 380 | // functions anyway. |
| 381 | auto InsertPos = MBB.begin(); |
Heejin Ahn | 8b49b6b | 2019-03-13 00:37:31 +0000 | [diff] [blame] | 382 | if (InsertPos->isEHLabel()) // EH pad starts with an EH label |
| 383 | ++InsertPos; |
| 384 | if (InsertPos->getOpcode() == WebAssembly::CATCH) |
| 385 | ++InsertPos; |
Heejin Ahn | 0bb9865 | 2019-01-30 22:44:45 +0000 | [diff] [blame] | 386 | FrameLowering->writeSPToGlobal(WebAssembly::SP32, MF, MBB, InsertPos, |
| 387 | MBB.begin()->getDebugLoc()); |
| 388 | } |
| 389 | return Changed; |
| 390 | } |