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