LLVM  10.0.0svn
WebAssemblyLateEHPrepare.cpp
Go to the documentation of this file.
1 //=== WebAssemblyLateEHPrepare.cpp - WebAssembly Exception Preparation -===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// \brief Does various transformations for exception handling.
11 ///
12 //===----------------------------------------------------------------------===//
13 
15 #include "WebAssembly.h"
16 #include "WebAssemblySubtarget.h"
17 #include "WebAssemblyUtilities.h"
18 #include "llvm/ADT/SmallSet.h"
21 #include "llvm/MC/MCAsmInfo.h"
22 using namespace llvm;
23 
24 #define DEBUG_TYPE "wasm-late-eh-prepare"
25 
26 namespace {
27 class WebAssemblyLateEHPrepare final : public MachineFunctionPass {
28  StringRef getPassName() const override {
29  return "WebAssembly Late Prepare Exception";
30  }
31 
32  bool runOnMachineFunction(MachineFunction &MF) override;
33  bool addCatches(MachineFunction &MF);
34  bool replaceFuncletReturns(MachineFunction &MF);
35  bool removeUnnecessaryUnreachables(MachineFunction &MF);
36  bool addExceptionExtraction(MachineFunction &MF);
37  bool restoreStackPointer(MachineFunction &MF);
38 
39 public:
40  static char ID; // Pass identification, replacement for typeid
41  WebAssemblyLateEHPrepare() : MachineFunctionPass(ID) {}
42 };
43 } // end anonymous namespace
44 
46 INITIALIZE_PASS(WebAssemblyLateEHPrepare, DEBUG_TYPE,
47  "WebAssembly Late Exception Preparation", false, false)
48 
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.
60  MachineFunction *MF = MI->getParent()->getParent();
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 
83 // Erase the specified BBs if the BB does not have any remaining predecessors,
84 // and also all its dead children.
85 template <typename Container>
86 static void eraseDeadBBsAndChildren(const Container &MBBs) {
87  SmallVector<MachineBasicBlock *, 8> WL(MBBs.begin(), MBBs.end());
88  while (!WL.empty()) {
89  MachineBasicBlock *MBB = WL.pop_back_val();
90  if (!MBB->pred_empty())
91  continue;
93  MBB->succ_end());
94  WL.append(MBB->succ_begin(), MBB->succ_end());
95  for (auto *Succ : Succs)
96  MBB->removeSuccessor(Succ);
97  MBB->eraseFromParent();
98  }
99 }
100 
101 bool WebAssemblyLateEHPrepare::runOnMachineFunction(MachineFunction &MF) {
102  LLVM_DEBUG(dbgs() << "********** Late EH Prepare **********\n"
103  "********** Function: "
104  << MF.getName() << '\n');
105 
108  return false;
109 
110  bool Changed = false;
111  if (MF.getFunction().hasPersonalityFn()) {
112  Changed |= addCatches(MF);
113  Changed |= replaceFuncletReturns(MF);
114  }
115  Changed |= removeUnnecessaryUnreachables(MF);
116  if (MF.getFunction().hasPersonalityFn()) {
117  Changed |= addExceptionExtraction(MF);
118  Changed |= restoreStackPointer(MF);
119  }
120  return Changed;
121 }
122 
123 // Add catch instruction to beginning of catchpads and cleanuppads.
124 bool WebAssemblyLateEHPrepare::addCatches(MachineFunction &MF) {
125  bool Changed = false;
126  const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
128  for (auto &MBB : MF) {
129  if (MBB.isEHPad()) {
130  Changed = true;
131  auto InsertPos = MBB.begin();
132  if (InsertPos->isEHLabel()) // EH pad starts with an EH label
133  ++InsertPos;
134  Register DstReg = MRI.createVirtualRegister(&WebAssembly::EXNREFRegClass);
135  BuildMI(MBB, InsertPos, MBB.begin()->getDebugLoc(),
136  TII.get(WebAssembly::CATCH), DstReg);
137  }
138  }
139  return Changed;
140 }
141 
142 bool WebAssemblyLateEHPrepare::replaceFuncletReturns(MachineFunction &MF) {
143  bool Changed = false;
144  const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
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  }
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;
171  Register ExnReg = Catch->getOperand(0).getReg();
172  BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW))
173  .addReg(ExnReg);
174  TI->eraseFromParent();
175  Changed = true;
176  break;
177  }
178  }
179  }
180  return Changed;
181 }
182 
183 bool WebAssemblyLateEHPrepare::removeUnnecessaryUnreachables(
184  MachineFunction &MF) {
185  bool Changed = false;
186  for (auto &MBB : MF) {
187  for (auto &MI : MBB) {
188  if (MI.getOpcode() != WebAssembly::THROW &&
189  MI.getOpcode() != WebAssembly::RETHROW)
190  continue;
191  Changed = true;
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)
201  if (!Succ->isEHPad())
202  MBB.removeSuccessor(Succ);
204  }
205  }
206 
207  return Changed;
208 }
209 
210 // Wasm uses 'br_on_exn' instruction to check the tag of an exception. It takes
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.
214 //
215 // block $l (result i32)
216 // ...
217 // ;; exnref $e is on the stack at this point
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 //
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.
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) {
235  const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
237  auto *EHInfo = MF.getWasmEHFuncInfo();
238  SmallVector<MachineInstr *, 16> ExtractInstrs;
240  for (auto &MBB : MF) {
241  for (auto &MI : MBB) {
242  if (MI.getOpcode() == WebAssembly::EXTRACT_EXCEPTION_I32) {
243  if (MI.getOperand(0).isDead())
244  ToDelete.push_back(&MI);
245  else
246  ExtractInstrs.push_back(&MI);
247  }
248  }
249  }
250  bool Changed = !ToDelete.empty() || !ExtractInstrs.empty();
251  for (auto *MI : ToDelete)
252  MI->eraseFromParent();
253  if (ExtractInstrs.empty())
254  return Changed;
255 
256  // Find terminate pads.
257  SmallSet<MachineBasicBlock *, 8> TerminatePads;
258  for (auto &MBB : MF) {
259  for (auto &MI : MBB) {
260  if (MI.isCall()) {
261  const MachineOperand &CalleeOp = MI.getOperand(0);
262  if (CalleeOp.isGlobal() && CalleeOp.getGlobal()->getName() ==
264  TerminatePads.insert(getMatchingEHPad(&MI));
265  }
266  }
267  }
268 
269  for (auto *Extract : ExtractInstrs) {
270  MachineBasicBlock *EHPad = getMatchingEHPad(Extract);
271  assert(EHPad && "No matching EH pad for extract_exception");
272  auto CatchPos = EHPad->begin();
273  if (CatchPos->isEHLabel()) // EH pad starts with an EH label
274  ++CatchPos;
275  MachineInstr *Catch = &*CatchPos;
276 
277  if (Catch->getNextNode() != Extract)
278  EHPad->insert(Catch->getNextNode(), Extract->removeFromParent());
279 
280  // - Before:
281  // ehpad:
282  // %exnref:exnref = catch
283  // %exn:i32 = extract_exception
284  // ... use exn ...
285  //
286  // - After:
287  // ehpad:
288  // %exnref:exnref = catch
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 ...
296  Register ExnReg = Catch->getOperand(0).getReg();
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)
310  .addExternalSymbol(CPPExnSymbol)
311  .addReg(ExnReg);
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:
320  // %exnref:exnref = catch
321  // %exn:i32 = extract_exception
322  // call @__clang_call_terminate(%exn)
323  // unreachable
324  //
325  // - After:
326  // ehpad:
327  // %exnref:exnref = catch
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)) {
339  MF.getFunction().getParent()->getFunction(
341  assert(ClangCallTerminateFn &&
342  "There is no __clang_call_terminate() function");
343  Register Reg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
344  BuildMI(ElseMBB, DL, TII.get(WebAssembly::CONST_I32), Reg).addImm(0);
345  BuildMI(ElseMBB, DL, TII.get(WebAssembly::CALL_VOID))
346  .addGlobalAddress(ClangCallTerminateFn)
347  .addReg(Reg);
348  BuildMI(ElseMBB, DL, TII.get(WebAssembly::UNREACHABLE));
349 
350  } else {
351  BuildMI(ElseMBB, DL, TII.get(WebAssembly::RETHROW)).addReg(ExnReg);
352  if (EHInfo->hasEHPadUnwindDest(EHPad))
353  ElseMBB->addSuccessor(EHInfo->getEHPadUnwindDest(EHPad));
354  }
355  }
356 
357  return true;
358 }
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 *>(
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();
382  if (InsertPos->isEHLabel()) // EH pad starts with an EH label
383  ++InsertPos;
384  if (InsertPos->getOpcode() == WebAssembly::CATCH)
385  ++InsertPos;
386  FrameLowering->writeSPToGlobal(WebAssembly::SP32, MF, MBB, InsertPos,
387  MBB.begin()->getDebugLoc());
388  }
389  return Changed;
390 }
#define DEBUG_TYPE
MachineBasicBlock * getMBB() const
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
Definition: ilist_node.h:288
This class represents lattice values for constants.
Definition: AllocatorList.h:23
Register createVirtualRegister(const TargetRegisterClass *RegClass, StringRef Name="")
createVirtualRegister - Create and return a new virtual register in the function with the specified r...
const DebugLoc & getDebugLoc() const
Returns the debug location id of this MachineInstr.
Definition: MachineInstr.h:385
unsigned Reg
A debug info location.
Definition: DebugLoc.h:33
This file contains the entry points for global functions defined in the LLVM WebAssembly back-end...
CLEANUPRET - Represents a return from a cleanup block funclet.
Definition: ISDOpcodes.h:721
void eraseFromParent()
This method unlinks &#39;this&#39; from the containing function and deletes it.
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
const HexagonInstrInfo * TII
void eraseFromParent()
Unlink &#39;this&#39; from the containing basic block and delete it.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
Definition: MachineInstr.h:411
static void eraseDeadBBsAndChildren(const Container &MBBs)
bool hasPersonalityFn() const
Check whether this function has a personality function.
Definition: Function.h:732
instr_iterator insert(instr_iterator I, MachineInstr *M)
Insert MI into the instruction list before I, possibly inside a bundle.
StringRef getName() const
getName - Return the name of the corresponding LLVM function.
WebAssembly Exception Handling.
This file contains the declaration of the WebAssembly-specific utility functions. ...
MachineInstrBuilder BuildMI(MachineFunction &MF, const DebugLoc &DL, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
Control flow instructions. These all have token chains.
Definition: ISDOpcodes.h:657
unsigned const MachineRegisterInfo * MRI
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition: SmallSet.h:134
const GlobalValue * getGlobal() const
const MCAsmInfo * getMCAsmInfo() const
Return target specific asm information.
This file provides WebAssembly-specific target descriptions.
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
Definition: SmallPtrSet.h:370
FunctionPass class - This class is used to implement most global optimizations.
Definition: Pass.h:284
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
Definition: SmallPtrSet.h:381
std::pair< NoneType, bool > insert(const T &V)
insert - Insert an element into the set if it isn&#39;t already there.
Definition: SmallSet.h:180
const MachineBasicBlock & front() const
INITIALIZE_PASS(WebAssemblyLateEHPrepare, DEBUG_TYPE, "WebAssembly Late Exception Preparation", false, false) FunctionPass *llvm
This file declares the WebAssembly-specific subclass of TargetSubtarget.
Iterator for intrusive lists based on ilist_node.
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements...
Definition: SmallPtrSet.h:417
void addSuccessor(MachineBasicBlock *Succ, BranchProbability Prob=BranchProbability::getUnknown())
Add Succ as a successor of this MachineBasicBlock.
bool isGlobal() const
isGlobal - Tests if this is a MO_GlobalAddress operand.
const char *const ClangCallTerminateFn
MachineOperand class - Representation of each machine instruction operand.
This is a &#39;vector&#39; (really, a variable-sized array), optimized for the case when the array is small...
Definition: SmallVector.h:837
LLVM_NODISCARD T pop_back_val()
Definition: SmallVector.h:374
const Function & getFunction() const
Return the LLVM function that this machine code represents.
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:132
static MachineBasicBlock * getMatchingEHPad(MachineInstr *MI)
void append(in_iter in_start, in_iter in_end)
Add the specified range to the end of the SmallVector.
Definition: SmallVector.h:387
CATCHRET - Represents a return from a catch block funclet.
Definition: ISDOpcodes.h:717
const MachineBasicBlock * getParent() const
Definition: MachineInstr.h:256
MachineRegisterInfo - Keep track of information for virtual and physical registers, including vreg register classes, use/def chains for registers, etc.
const WasmEHFuncInfo * getWasmEHFuncInfo() const
getWasmEHFuncInfo - Return information about how the current function uses Wasm exception handling...
Representation of each machine instruction.
Definition: MachineInstr.h:64
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
bool isEHPad() const
Returns true if the block is a landing pad.
LLVM_NODISCARD bool empty() const
Definition: SmallVector.h:55
FunctionPass * createWebAssemblyLateEHPrepare()
StringRef getName() const
Return a constant reference to the value&#39;s name.
Definition: Value.cpp:214
virtual const TargetFrameLowering * getFrameLowering() const
const LLVMTargetMachine & getTarget() const
getTarget - Return the target machine this machine code is compiled with
MachineInstr * removeFromParent()
Unlink &#39;this&#39; from the containing basic block, and return it without deleting it. ...
void removeSuccessor(MachineBasicBlock *Succ, bool NormalizeSuccProbs=false)
Remove successor from the successors list of this MachineBasicBlock.
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
ExceptionHandling getExceptionHandlingType() const
Definition: MCAsmInfo.h:576
IRTranslator LLVM IR MI
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:48
Register getReg() const
getReg - Returns the register number.
#define LLVM_DEBUG(X)
Definition: Debug.h:122
const MachineOperand & getOperand(unsigned i) const
Definition: MachineInstr.h:416
const MachineInstrBuilder & addReg(Register RegNo, unsigned flags=0, unsigned SubReg=0) const
Add a new virtual register operand.
const MachineInstrBuilder & addExternalSymbol(const char *FnName, unsigned TargetFlags=0) const
Wrapper class representing virtual and physical registers.
Definition: Register.h:19
size_type count(const T &V) const
count - Return 1 if the element is in the set, 0 otherwise.
Definition: SmallSet.h:164