blob: 030f09b8bf4663f153cf85e17cda4290f1529344 [file] [log] [blame]
Heejin Ahn4934f762018-06-25 01:07:11 +00001//=== WebAssemblyLateEHPrepare.cpp - WebAssembly Exception Preparation -===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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 Ahn4934f762018-06-25 01:07:11 +00006//
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 Ahnd6f48782019-01-30 03:21:57 +000018#include "llvm/ADT/SmallSet.h"
Heejin Ahn4934f762018-06-25 01:07:11 +000019#include "llvm/CodeGen/MachineInstrBuilder.h"
20#include "llvm/CodeGen/WasmEHFuncInfo.h"
21#include "llvm/MC/MCAsmInfo.h"
22using namespace llvm;
23
24#define DEBUG_TYPE "wasm-exception-prepare"
25
26namespace {
27class WebAssemblyLateEHPrepare final : public MachineFunctionPass {
28 StringRef getPassName() const override {
Heejin Ahnd6f48782019-01-30 03:21:57 +000029 return "WebAssembly Late Prepare Exception";
Heejin Ahn4934f762018-06-25 01:07:11 +000030 }
31
32 bool runOnMachineFunction(MachineFunction &MF) override;
Heejin Ahn095796a2018-11-16 00:47:18 +000033 bool removeUnnecessaryUnreachables(MachineFunction &MF);
Heejin Ahn4934f762018-06-25 01:07:11 +000034 bool replaceFuncletReturns(MachineFunction &MF);
Heejin Ahnd6f48782019-01-30 03:21:57 +000035 bool addCatches(MachineFunction &MF);
36 bool addExceptionExtraction(MachineFunction &MF);
Heejin Ahn4934f762018-06-25 01:07:11 +000037
38public:
39 static char ID; // Pass identification, replacement for typeid
40 WebAssemblyLateEHPrepare() : MachineFunctionPass(ID) {}
41};
42} // end anonymous namespace
43
44char WebAssemblyLateEHPrepare::ID = 0;
45INITIALIZE_PASS(WebAssemblyLateEHPrepare, DEBUG_TYPE,
Heejin Ahna93e7262018-08-17 00:12:04 +000046 "WebAssembly Late Exception Preparation", false, false)
Heejin Ahn4934f762018-06-25 01:07:11 +000047
48FunctionPass *llvm::createWebAssemblyLateEHPrepare() {
49 return new WebAssemblyLateEHPrepare();
50}
51
52// Returns the nearest EH pad that dominates this instruction. This does not use
53// dominator analysis; it just does BFS on its predecessors until arriving at an
54// EH pad. This assumes valid EH scopes so the first EH pad it arrives in all
55// possible search paths should be the same.
56// Returns nullptr in case it does not find any EH pad in the search, or finds
57// multiple different EH pads.
Heejin Ahn095796a2018-11-16 00:47:18 +000058static MachineBasicBlock *getMatchingEHPad(MachineInstr *MI) {
Heejin Ahn4934f762018-06-25 01:07:11 +000059 MachineFunction *MF = MI->getParent()->getParent();
60 SmallVector<MachineBasicBlock *, 2> WL;
61 SmallPtrSet<MachineBasicBlock *, 2> Visited;
62 WL.push_back(MI->getParent());
63 MachineBasicBlock *EHPad = nullptr;
64 while (!WL.empty()) {
65 MachineBasicBlock *MBB = WL.pop_back_val();
66 if (Visited.count(MBB))
67 continue;
68 Visited.insert(MBB);
69 if (MBB->isEHPad()) {
70 if (EHPad && EHPad != MBB)
71 return nullptr;
72 EHPad = MBB;
73 continue;
74 }
75 if (MBB == &MF->front())
76 return nullptr;
77 WL.append(MBB->pred_begin(), MBB->pred_end());
78 }
79 return EHPad;
80}
81
Heejin Ahn095796a2018-11-16 00:47:18 +000082// Erase the specified BBs if the BB does not have any remaining predecessors,
83// and also all its dead children.
Benjamin Kramer83996e42018-08-08 10:13:19 +000084template <typename Container>
Heejin Ahn095796a2018-11-16 00:47:18 +000085static void eraseDeadBBsAndChildren(const Container &MBBs) {
Benjamin Kramer83996e42018-08-08 10:13:19 +000086 SmallVector<MachineBasicBlock *, 8> WL(MBBs.begin(), MBBs.end());
Heejin Ahn4934f762018-06-25 01:07:11 +000087 while (!WL.empty()) {
88 MachineBasicBlock *MBB = WL.pop_back_val();
Heejin Ahn095796a2018-11-16 00:47:18 +000089 if (!MBB->pred_empty())
90 continue;
Heejin Ahnb68d5912018-10-04 21:03:35 +000091 SmallVector<MachineBasicBlock *, 4> Succs(MBB->succ_begin(),
92 MBB->succ_end());
93 WL.append(MBB->succ_begin(), MBB->succ_end());
94 for (auto *Succ : Succs)
Heejin Ahn4934f762018-06-25 01:07:11 +000095 MBB->removeSuccessor(Succ);
Heejin Ahn4934f762018-06-25 01:07:11 +000096 MBB->eraseFromParent();
97 }
98}
99
100bool WebAssemblyLateEHPrepare::runOnMachineFunction(MachineFunction &MF) {
Heejin Ahn569f0902019-01-09 23:05:21 +0000101 LLVM_DEBUG(dbgs() << "********** Late EH Prepare **********\n"
102 "********** Function: "
103 << MF.getName() << '\n');
104
Heejin Ahn4934f762018-06-25 01:07:11 +0000105 if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() !=
106 ExceptionHandling::Wasm)
107 return false;
108
109 bool Changed = false;
Heejin Ahn095796a2018-11-16 00:47:18 +0000110 Changed |= removeUnnecessaryUnreachables(MF);
Heejin Ahn4934f762018-06-25 01:07:11 +0000111 if (!MF.getFunction().hasPersonalityFn())
112 return Changed;
113 Changed |= replaceFuncletReturns(MF);
Heejin Ahnd6f48782019-01-30 03:21:57 +0000114 Changed |= addCatches(MF);
115 Changed |= addExceptionExtraction(MF);
Heejin Ahn4934f762018-06-25 01:07:11 +0000116 return Changed;
117}
118
Heejin Ahn095796a2018-11-16 00:47:18 +0000119bool WebAssemblyLateEHPrepare::removeUnnecessaryUnreachables(
120 MachineFunction &MF) {
121 bool Changed = false;
122 for (auto &MBB : MF) {
123 for (auto &MI : MBB) {
Heejin Ahnd6f48782019-01-30 03:21:57 +0000124 if (MI.getOpcode() != WebAssembly::THROW &&
125 MI.getOpcode() != WebAssembly::RETHROW)
Heejin Ahn095796a2018-11-16 00:47:18 +0000126 continue;
127 Changed = true;
128
129 // The instruction after the throw should be an unreachable or a branch to
130 // another BB that should eventually lead to an unreachable. Delete it
131 // because throw itself is a terminator, and also delete successors if
132 // any.
133 MBB.erase(std::next(MachineBasicBlock::iterator(MI)), MBB.end());
134 SmallVector<MachineBasicBlock *, 8> Succs(MBB.succ_begin(),
135 MBB.succ_end());
136 for (auto *Succ : Succs)
137 MBB.removeSuccessor(Succ);
138 eraseDeadBBsAndChildren(Succs);
139 }
140 }
141
142 return Changed;
143}
144
Heejin Ahn4934f762018-06-25 01:07:11 +0000145bool WebAssemblyLateEHPrepare::replaceFuncletReturns(MachineFunction &MF) {
146 bool Changed = false;
147 const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
Heejin Ahn4934f762018-06-25 01:07:11 +0000148
149 for (auto &MBB : MF) {
150 auto Pos = MBB.getFirstTerminator();
151 if (Pos == MBB.end())
152 continue;
153 MachineInstr *TI = &*Pos;
154
155 switch (TI->getOpcode()) {
156 case WebAssembly::CATCHRET: {
157 // Replace a catchret with a branch
158 MachineBasicBlock *TBB = TI->getOperand(0).getMBB();
159 if (!MBB.isLayoutSuccessor(TBB))
160 BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::BR))
161 .addMBB(TBB);
162 TI->eraseFromParent();
163 Changed = true;
164 break;
165 }
166 case WebAssembly::CLEANUPRET: {
167 // Replace a cleanupret with a rethrow
Heejin Ahnd6f48782019-01-30 03:21:57 +0000168 BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW));
Heejin Ahn4934f762018-06-25 01:07:11 +0000169 TI->eraseFromParent();
170 Changed = true;
171 break;
172 }
173 }
174 }
175 return Changed;
176}
177
Heejin Ahnd6f48782019-01-30 03:21:57 +0000178// Add catch instruction to beginning of catchpads and cleanuppads.
179bool WebAssemblyLateEHPrepare::addCatches(MachineFunction &MF) {
Heejin Ahn4934f762018-06-25 01:07:11 +0000180 bool Changed = false;
181 const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
Heejin Ahnd6f48782019-01-30 03:21:57 +0000182 MachineRegisterInfo &MRI = MF.getRegInfo();
Heejin Ahn4934f762018-06-25 01:07:11 +0000183 for (auto &MBB : MF) {
Heejin Ahnd6f48782019-01-30 03:21:57 +0000184 if (MBB.isEHPad()) {
Heejin Ahn4934f762018-06-25 01:07:11 +0000185 Changed = true;
Heejin Ahnd6f48782019-01-30 03:21:57 +0000186 unsigned DstReg =
187 MRI.createVirtualRegister(&WebAssembly::EXCEPT_REFRegClass);
Heejin Ahn4934f762018-06-25 01:07:11 +0000188 BuildMI(MBB, MBB.begin(), MBB.begin()->getDebugLoc(),
Heejin Ahnd6f48782019-01-30 03:21:57 +0000189 TII.get(WebAssembly::CATCH), DstReg);
Heejin Ahn4934f762018-06-25 01:07:11 +0000190 }
191 }
192 return Changed;
193}
194
Heejin Ahnd6f48782019-01-30 03:21:57 +0000195// Wasm uses 'br_on_exn' instruction to check the tag of an exception. It takes
196// except_ref type object returned by 'catch', and branches to the destination
197// if it matches a given tag. We currently use __cpp_exception symbol to
198// represent the tag for all C++ exceptions.
199//
200// block $l (result i32)
201// ...
202// ;; except_ref $e is on the stack at this point
203// br_on_exn $l $e ;; branch to $l with $e's arguments
204// ...
205// end
206// ;; Here we expect the extracted values are on top of the wasm value stack
207// ... Handle exception using values ...
208//
209// br_on_exn takes an except_ref object and branches if it matches the given
210// tag. There can be multiple br_on_exn instructions if we want to match for
211// another tag, but for now we only test for __cpp_exception tag, and if it does
212// not match, i.e., it is a foreign exception, we rethrow it.
213//
214// In the destination BB that's the target of br_on_exn, extracted exception
215// values (in C++'s case a single i32, which represents an exception pointer)
216// are placed on top of the wasm stack. Because we can't model wasm stack in
217// LLVM instruction, we use 'extract_exception' pseudo instruction to retrieve
218// it. The pseudo instruction will be deleted later.
219bool WebAssemblyLateEHPrepare::addExceptionExtraction(MachineFunction &MF) {
Heejin Ahn4934f762018-06-25 01:07:11 +0000220 const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
221 auto *EHInfo = MF.getWasmEHFuncInfo();
Heejin Ahnd6f48782019-01-30 03:21:57 +0000222 SmallVector<MachineInstr *, 16> ExtractInstrs;
223 for (auto &MBB : MF) {
Heejin Ahn4934f762018-06-25 01:07:11 +0000224 for (auto &MI : MBB) {
Heejin Ahnd6f48782019-01-30 03:21:57 +0000225 if (MI.getOpcode() == WebAssembly::EXTRACT_EXCEPTION_I32) {
226 if (MI.getOperand(0).isDead())
227 MI.eraseFromParent();
228 else
229 ExtractInstrs.push_back(&MI);
230 }
Heejin Ahn4934f762018-06-25 01:07:11 +0000231 }
Heejin Ahnd6f48782019-01-30 03:21:57 +0000232 }
233 if (ExtractInstrs.empty())
234 return false;
Heejin Ahn4934f762018-06-25 01:07:11 +0000235
Heejin Ahnd6f48782019-01-30 03:21:57 +0000236 // Find terminate pads.
237 SmallSet<MachineBasicBlock *, 8> TerminatePads;
238 for (auto &MBB : MF) {
239 for (auto &MI : MBB) {
Heejin Ahn4934f762018-06-25 01:07:11 +0000240 if (MI.isCall()) {
241 const MachineOperand &CalleeOp = MI.getOperand(0);
242 if (CalleeOp.isGlobal() && CalleeOp.getGlobal()->getName() ==
243 WebAssembly::ClangCallTerminateFn)
Heejin Ahnd6f48782019-01-30 03:21:57 +0000244 TerminatePads.insert(getMatchingEHPad(&MI));
Heejin Ahn4934f762018-06-25 01:07:11 +0000245 }
Heejin Ahnd6f48782019-01-30 03:21:57 +0000246 }
247 }
Heejin Ahn4934f762018-06-25 01:07:11 +0000248
Heejin Ahnd6f48782019-01-30 03:21:57 +0000249 for (auto *Extract : ExtractInstrs) {
250 MachineBasicBlock *EHPad = getMatchingEHPad(Extract);
251 assert(EHPad && "No matching EH pad for extract_exception");
Heejin Ahn4934f762018-06-25 01:07:11 +0000252 MachineInstr *Catch = &*EHPad->begin();
Heejin Ahnd6f48782019-01-30 03:21:57 +0000253 if (Catch->getNextNode() != Extract)
254 EHPad->insert(Catch->getNextNode(), Extract->removeFromParent());
255
256 // - Before:
257 // ehpad:
258 // %exnref:except_ref = catch
259 // %exn:i32 = extract_exception
260 // ... use exn ...
261 //
262 // - After:
263 // ehpad:
264 // %exnref:except_ref = catch
265 // br_on_exn %thenbb, $__cpp_exception, %exnref
266 // br %elsebb
267 // elsebb:
268 // rethrow
269 // thenbb:
270 // %exn:i32 = extract_exception
271 // ... use exn ...
272 unsigned ExnRefReg = Catch->getOperand(0).getReg();
273 auto *ThenMBB = MF.CreateMachineBasicBlock();
274 auto *ElseMBB = MF.CreateMachineBasicBlock();
275 MF.insert(std::next(MachineFunction::iterator(EHPad)), ElseMBB);
276 MF.insert(std::next(MachineFunction::iterator(ElseMBB)), ThenMBB);
277 ThenMBB->splice(ThenMBB->end(), EHPad, Extract, EHPad->end());
278 ThenMBB->transferSuccessors(EHPad);
279 EHPad->addSuccessor(ThenMBB);
280 EHPad->addSuccessor(ElseMBB);
281
282 DebugLoc DL = Extract->getDebugLoc();
283 const char *CPPExnSymbol = MF.createExternalSymbolName("__cpp_exception");
284 BuildMI(EHPad, DL, TII.get(WebAssembly::BR_ON_EXN))
285 .addMBB(ThenMBB)
286 .addExternalSymbol(CPPExnSymbol, WebAssemblyII::MO_SYMBOL_EVENT)
287 .addReg(ExnRefReg);
288 BuildMI(EHPad, DL, TII.get(WebAssembly::BR)).addMBB(ElseMBB);
289
290 // When this is a terminate pad with __clang_call_terminate() call, we don't
291 // rethrow it anymore and call __clang_call_terminate() with a nullptr
292 // argument, which will call std::terminate().
293 //
294 // - Before:
295 // ehpad:
296 // %exnref:except_ref = catch
297 // %exn:i32 = extract_exception
298 // call @__clang_call_terminate(%exn)
299 // unreachable
300 //
301 // - After:
302 // ehpad:
303 // %exnref:except_ref = catch
304 // br_on_exn %thenbb, $__cpp_exception, %exnref
305 // br %elsebb
306 // elsebb:
307 // call @__clang_call_terminate(0)
308 // unreachable
309 // thenbb:
310 // %exn:i32 = extract_exception
311 // call @__clang_call_terminate(%exn)
312 // unreachable
313 if (TerminatePads.count(EHPad)) {
314 Function *ClangCallTerminateFn =
315 MF.getFunction().getParent()->getFunction(
316 WebAssembly::ClangCallTerminateFn);
317 assert(ClangCallTerminateFn &&
318 "There is no __clang_call_terminate() function");
319 BuildMI(ElseMBB, DL, TII.get(WebAssembly::CALL_VOID))
320 .addGlobalAddress(ClangCallTerminateFn)
321 .addImm(0);
322 BuildMI(ElseMBB, DL, TII.get(WebAssembly::UNREACHABLE));
323
324 } else {
325 BuildMI(ElseMBB, DL, TII.get(WebAssembly::RETHROW));
326 if (EHInfo->hasEHPadUnwindDest(EHPad))
327 EHInfo->setThrowUnwindDest(ElseMBB, EHInfo->getEHPadUnwindDest(EHPad));
328 }
Heejin Ahn4934f762018-06-25 01:07:11 +0000329 }
Heejin Ahn4934f762018-06-25 01:07:11 +0000330
Heejin Ahn4934f762018-06-25 01:07:11 +0000331 return true;
332}