blob: 503cd38ed1c6e3e422187c0493570e65f6570e28 [file] [log] [blame]
Heejin Ahn4934f762018-06-25 01:07:11 +00001//=== WebAssemblyLateEHPrepare.cpp - WebAssembly Exception Preparation -===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9///
10/// \file
11/// \brief Does various transformations for exception handling.
12///
13//===----------------------------------------------------------------------===//
14
15#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
16#include "WebAssembly.h"
17#include "WebAssemblySubtarget.h"
18#include "WebAssemblyUtilities.h"
19#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 {
29 return "WebAssembly Prepare Exception";
30 }
31
32 bool runOnMachineFunction(MachineFunction &MF) override;
33
34 bool replaceFuncletReturns(MachineFunction &MF);
35 bool hoistCatches(MachineFunction &MF);
36 bool addCatchAlls(MachineFunction &MF);
37 bool addRethrows(MachineFunction &MF);
38 bool ensureSingleBBTermPads(MachineFunction &MF);
39 bool mergeTerminatePads(MachineFunction &MF);
40 bool addCatchAllTerminatePads(MachineFunction &MF);
41
42public:
43 static char ID; // Pass identification, replacement for typeid
44 WebAssemblyLateEHPrepare() : MachineFunctionPass(ID) {}
45};
46} // end anonymous namespace
47
48char WebAssemblyLateEHPrepare::ID = 0;
49INITIALIZE_PASS(WebAssemblyLateEHPrepare, DEBUG_TYPE,
50 "WebAssembly Exception Preparation", false, false)
51
52FunctionPass *llvm::createWebAssemblyLateEHPrepare() {
53 return new WebAssemblyLateEHPrepare();
54}
55
56// Returns the nearest EH pad that dominates this instruction. This does not use
57// dominator analysis; it just does BFS on its predecessors until arriving at an
58// EH pad. This assumes valid EH scopes so the first EH pad it arrives in all
59// possible search paths should be the same.
60// Returns nullptr in case it does not find any EH pad in the search, or finds
61// multiple different EH pads.
62MachineBasicBlock *GetMatchingEHPad(MachineInstr *MI) {
63 MachineFunction *MF = MI->getParent()->getParent();
64 SmallVector<MachineBasicBlock *, 2> WL;
65 SmallPtrSet<MachineBasicBlock *, 2> Visited;
66 WL.push_back(MI->getParent());
67 MachineBasicBlock *EHPad = nullptr;
68 while (!WL.empty()) {
69 MachineBasicBlock *MBB = WL.pop_back_val();
70 if (Visited.count(MBB))
71 continue;
72 Visited.insert(MBB);
73 if (MBB->isEHPad()) {
74 if (EHPad && EHPad != MBB)
75 return nullptr;
76 EHPad = MBB;
77 continue;
78 }
79 if (MBB == &MF->front())
80 return nullptr;
81 WL.append(MBB->pred_begin(), MBB->pred_end());
82 }
83 return EHPad;
84}
85
Benjamin Kramer83996e42018-08-08 10:13:19 +000086// Erases the given BBs and all their children from the function. If other BBs
87// have the BB as a successor, the successor relationships will be deleted as
88// well.
89template <typename Container>
90static void EraseBBsAndChildren(const Container &MBBs) {
91 SmallVector<MachineBasicBlock *, 8> WL(MBBs.begin(), MBBs.end());
Heejin Ahn4934f762018-06-25 01:07:11 +000092 while (!WL.empty()) {
93 MachineBasicBlock *MBB = WL.pop_back_val();
94 for (auto *Pred : MBB->predecessors())
95 Pred->removeSuccessor(MBB);
96 for (auto *Succ : MBB->successors()) {
97 WL.push_back(Succ);
98 MBB->removeSuccessor(Succ);
99 }
100 MBB->eraseFromParent();
101 }
102}
103
104bool WebAssemblyLateEHPrepare::runOnMachineFunction(MachineFunction &MF) {
105 if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() !=
106 ExceptionHandling::Wasm)
107 return false;
108
109 bool Changed = false;
110 Changed |= addRethrows(MF);
111 if (!MF.getFunction().hasPersonalityFn())
112 return Changed;
113 Changed |= replaceFuncletReturns(MF);
114 Changed |= hoistCatches(MF);
115 Changed |= addCatchAlls(MF);
116 Changed |= ensureSingleBBTermPads(MF);
117 Changed |= mergeTerminatePads(MF);
118 Changed |= addCatchAllTerminatePads(MF);
119 return Changed;
120}
121
122bool WebAssemblyLateEHPrepare::replaceFuncletReturns(MachineFunction &MF) {
123 bool Changed = false;
124 const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
125 auto *EHInfo = MF.getWasmEHFuncInfo();
126
127 for (auto &MBB : MF) {
128 auto Pos = MBB.getFirstTerminator();
129 if (Pos == MBB.end())
130 continue;
131 MachineInstr *TI = &*Pos;
132
133 switch (TI->getOpcode()) {
134 case WebAssembly::CATCHRET: {
135 // Replace a catchret with a branch
136 MachineBasicBlock *TBB = TI->getOperand(0).getMBB();
137 if (!MBB.isLayoutSuccessor(TBB))
138 BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::BR))
139 .addMBB(TBB);
140 TI->eraseFromParent();
141 Changed = true;
142 break;
143 }
144 case WebAssembly::CLEANUPRET: {
145 // Replace a cleanupret with a rethrow
146 if (EHInfo->hasThrowUnwindDest(&MBB))
147 BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW))
148 .addMBB(EHInfo->getThrowUnwindDest(&MBB));
149 else
150 BuildMI(MBB, TI, TI->getDebugLoc(),
151 TII.get(WebAssembly::RETHROW_TO_CALLER));
152
153 TI->eraseFromParent();
154 Changed = true;
155 break;
156 }
157 }
158 }
159 return Changed;
160}
161
162// Hoist catch instructions to the beginning of their matching EH pad BBs in
163// case,
164// (1) catch instruction is not the first instruction in EH pad.
165// ehpad:
166// some_other_instruction
167// ...
168// %exn = catch 0
169// (2) catch instruction is in a non-EH pad BB. For example,
170// ehpad:
171// br bb0
172// bb0:
173// %exn = catch 0
174bool WebAssemblyLateEHPrepare::hoistCatches(MachineFunction &MF) {
175 bool Changed = false;
176 SmallVector<MachineInstr *, 16> Catches;
177 for (auto &MBB : MF)
178 for (auto &MI : MBB)
179 if (WebAssembly::isCatch(MI))
180 Catches.push_back(&MI);
181
182 for (auto *Catch : Catches) {
183 MachineBasicBlock *EHPad = GetMatchingEHPad(Catch);
184 assert(EHPad && "No matching EH pad for catch");
185 if (EHPad->begin() == Catch)
186 continue;
187 Changed = true;
188 EHPad->insert(EHPad->begin(), Catch->removeFromParent());
189 }
190 return Changed;
191}
192
193// Add catch_all to beginning of cleanup pads.
194bool WebAssemblyLateEHPrepare::addCatchAlls(MachineFunction &MF) {
195 bool Changed = false;
196 const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
197
198 for (auto &MBB : MF) {
199 if (!MBB.isEHPad())
200 continue;
201 // This runs after hoistCatches(), so we assume that if there is a catch,
202 // that should be the first instruction in an EH pad.
203 if (!WebAssembly::isCatch(*MBB.begin())) {
204 Changed = true;
205 BuildMI(MBB, MBB.begin(), MBB.begin()->getDebugLoc(),
206 TII.get(WebAssembly::CATCH_ALL));
207 }
208 }
209 return Changed;
210}
211
212// Add a 'rethrow' instruction after __cxa_rethrow() call
213bool WebAssemblyLateEHPrepare::addRethrows(MachineFunction &MF) {
214 bool Changed = false;
215 const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
216 auto *EHInfo = MF.getWasmEHFuncInfo();
217
218 for (auto &MBB : MF)
219 for (auto &MI : MBB) {
220 // Check if it is a call to __cxa_rethrow()
221 if (!MI.isCall())
222 continue;
223 MachineOperand &CalleeOp = MI.getOperand(0);
224 if (!CalleeOp.isGlobal() ||
225 CalleeOp.getGlobal()->getName() != WebAssembly::CxaRethrowFn)
226 continue;
227
228 // Now we have __cxa_rethrow() call
229 Changed = true;
230 auto InsertPt = std::next(MachineBasicBlock::iterator(MI));
231 while (InsertPt != MBB.end() && InsertPt->isLabel()) // Skip EH_LABELs
232 ++InsertPt;
233 MachineInstr *Rethrow = nullptr;
234 if (EHInfo->hasThrowUnwindDest(&MBB))
235 Rethrow = BuildMI(MBB, InsertPt, MI.getDebugLoc(),
236 TII.get(WebAssembly::RETHROW))
237 .addMBB(EHInfo->getThrowUnwindDest(&MBB));
238 else
239 Rethrow = BuildMI(MBB, InsertPt, MI.getDebugLoc(),
240 TII.get(WebAssembly::RETHROW_TO_CALLER));
241
242 // Becasue __cxa_rethrow does not return, the instruction after the
243 // rethrow should be an unreachable or a branch to another BB that should
244 // eventually lead to an unreachable. Delete it because rethrow itself is
245 // a terminator, and also delete non-EH pad successors if any.
246 MBB.erase(std::next(MachineBasicBlock::iterator(Rethrow)), MBB.end());
Benjamin Kramer83996e42018-08-08 10:13:19 +0000247 SmallVector<MachineBasicBlock *, 8> NonPadSuccessors;
Heejin Ahn4934f762018-06-25 01:07:11 +0000248 for (auto *Succ : MBB.successors())
249 if (!Succ->isEHPad())
Benjamin Kramer83996e42018-08-08 10:13:19 +0000250 NonPadSuccessors.push_back(Succ);
251 EraseBBsAndChildren(NonPadSuccessors);
Heejin Ahn4934f762018-06-25 01:07:11 +0000252 }
253 return Changed;
254}
255
256// Terminate pads are an single-BB EH pad in the form of
257// termpad:
258// %exn = catch 0
259// call @__clang_call_terminate(%exn)
260// unreachable
261// (There can be set_local and get_locals before the call if we didn't run
262// RegStackify)
263// But code transformations can change or add more control flow, so the call to
264// __clang_call_terminate() function may not be in the original EH pad anymore.
265// This ensures every terminate pad is a single BB in the form illustrated
266// above.
267bool WebAssemblyLateEHPrepare::ensureSingleBBTermPads(MachineFunction &MF) {
268 const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
269
270 // Find calls to __clang_call_terminate()
271 SmallVector<MachineInstr *, 8> ClangCallTerminateCalls;
272 for (auto &MBB : MF)
273 for (auto &MI : MBB)
274 if (MI.isCall()) {
275 const MachineOperand &CalleeOp = MI.getOperand(0);
276 if (CalleeOp.isGlobal() && CalleeOp.getGlobal()->getName() ==
277 WebAssembly::ClangCallTerminateFn)
278 ClangCallTerminateCalls.push_back(&MI);
279 }
280
281 bool Changed = false;
282 for (auto *Call : ClangCallTerminateCalls) {
283 MachineBasicBlock *EHPad = GetMatchingEHPad(Call);
284 assert(EHPad && "No matching EH pad for catch");
285
286 // If it is already the form we want, skip it
287 if (Call->getParent() == EHPad &&
288 Call->getNextNode()->getOpcode() == WebAssembly::UNREACHABLE)
289 continue;
290
291 // In case the __clang_call_terminate() call is not in its matching EH pad,
292 // move the call to the end of EH pad and add an unreachable instruction
293 // after that. Delete all successors and their children if any, because here
294 // the program terminates.
295 Changed = true;
296 MachineInstr *Catch = &*EHPad->begin();
297 // This runs after hoistCatches(), so catch instruction should be at the top
298 assert(WebAssembly::isCatch(*Catch));
299 // Takes the result register of the catch instruction as argument. There may
300 // have been some other set_local/get_locals in between, but at this point
301 // we don't care.
302 Call->getOperand(1).setReg(Catch->getOperand(0).getReg());
303 auto InsertPos = std::next(MachineBasicBlock::iterator(Catch));
304 EHPad->insert(InsertPos, Call->removeFromParent());
305 BuildMI(*EHPad, InsertPos, Call->getDebugLoc(),
306 TII.get(WebAssembly::UNREACHABLE));
307 EHPad->erase(InsertPos, EHPad->end());
Benjamin Kramer83996e42018-08-08 10:13:19 +0000308 EraseBBsAndChildren(EHPad->successors());
Heejin Ahn4934f762018-06-25 01:07:11 +0000309 }
310 return Changed;
311}
312
313// In case there are multiple terminate pads, merge them into one for code size.
314// This runs after ensureSingleBBTermPads() and assumes every terminate pad is a
315// single BB.
316// In principle this violates EH scope relationship because it can merge
317// multiple inner EH scopes, each of which is in different outer EH scope. But
318// getEHScopeMembership() function will not be called after this, so it is fine.
319bool WebAssemblyLateEHPrepare::mergeTerminatePads(MachineFunction &MF) {
320 SmallVector<MachineBasicBlock *, 8> TermPads;
321 for (auto &MBB : MF)
322 if (WebAssembly::isCatchTerminatePad(MBB))
323 TermPads.push_back(&MBB);
324 if (TermPads.empty())
325 return false;
326
327 MachineBasicBlock *UniqueTermPad = TermPads.front();
328 for (auto *TermPad :
329 llvm::make_range(std::next(TermPads.begin()), TermPads.end())) {
330 SmallVector<MachineBasicBlock *, 2> Preds(TermPad->pred_begin(),
331 TermPad->pred_end());
332 for (auto *Pred : Preds)
333 Pred->replaceSuccessor(TermPad, UniqueTermPad);
334 TermPad->eraseFromParent();
335 }
336 return true;
337}
338
339// Terminate pads are cleanup pads, so they should start with a 'catch_all'
340// instruction. But in the Itanium model, when we have a C++ exception object,
341// we pass them to __clang_call_terminate function, which calls __cxa_end_catch
342// with the passed exception pointer and then std::terminate. This is the reason
343// that terminate pads are generated with not a catch_all but a catch
344// instruction in clang and earlier llvm passes. Here we append a terminate pad
345// with a catch_all after each existing terminate pad so we can also catch
346// foreign exceptions. For every terminate pad:
347// %exn = catch 0
348// call @__clang_call_terminate(%exn)
349// unreachable
350// We append this BB right after that:
351// catch_all
352// call @std::terminate()
353// unreachable
354bool WebAssemblyLateEHPrepare::addCatchAllTerminatePads(MachineFunction &MF) {
355 const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
356 SmallVector<MachineBasicBlock *, 8> TermPads;
357 for (auto &MBB : MF)
358 if (WebAssembly::isCatchTerminatePad(MBB))
359 TermPads.push_back(&MBB);
360 if (TermPads.empty())
361 return false;
362
363 Function *StdTerminateFn =
364 MF.getFunction().getParent()->getFunction(WebAssembly::StdTerminateFn);
365 assert(StdTerminateFn && "There is no std::terminate() function");
366 for (auto *CatchTermPad : TermPads) {
367 DebugLoc DL = CatchTermPad->findDebugLoc(CatchTermPad->begin());
368 auto *CatchAllTermPad = MF.CreateMachineBasicBlock();
369 MF.insert(std::next(MachineFunction::iterator(CatchTermPad)),
370 CatchAllTermPad);
371 CatchAllTermPad->setIsEHPad();
372 BuildMI(CatchAllTermPad, DL, TII.get(WebAssembly::CATCH_ALL));
373 BuildMI(CatchAllTermPad, DL, TII.get(WebAssembly::CALL_VOID))
374 .addGlobalAddress(StdTerminateFn);
375 BuildMI(CatchAllTermPad, DL, TII.get(WebAssembly::UNREACHABLE));
376
377 // Actually this CatchAllTermPad (new terminate pad with a catch_all) is not
378 // a successor of an existing terminate pad. CatchAllTermPad should have all
379 // predecessors CatchTermPad has instead. This is a hack to force
380 // CatchAllTermPad be always sorted right after CatchTermPad; the correct
381 // predecessor-successor relationships will be restored in CFGStackify pass.
382 CatchTermPad->addSuccessor(CatchAllTermPad);
383 }
384 return true;
385}