LLVM  9.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  unsigned DstReg =
135  MRI.createVirtualRegister(&WebAssembly::EXCEPT_REFRegClass);
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  unsigned 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 // except_ref type object returned by 'catch', and branches to the destination
213 // if it matches a given tag. We currently use __cpp_exception symbol to
214 // represent the tag for all C++ exceptions.
215 //
216 // block $l (result i32)
217 // ...
218 // ;; except_ref $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 except_ref object and branches if it matches the given
226 // tag. There can be multiple br_on_exn instructions if we want to match for
227 // another tag, but for now we only test for __cpp_exception tag, and if it does
228 // not 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();
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:except_ref = catch
283  // %exn:i32 = extract_exception
284  // ... use exn ...
285  //
286  // - After:
287  // ehpad:
288  // %exnref:except_ref = 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  unsigned 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:except_ref = catch
321  // %exn:i32 = extract_exception
322  // call @__clang_call_terminate(%exn)
323  // unreachable
324  //
325  // - After:
326  // ehpad:
327  // %exnref:except_ref = 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  BuildMI(ElseMBB, DL, TII.get(WebAssembly::CALL_VOID))
344  .addGlobalAddress(ClangCallTerminateFn)
345  .addImm(0);
346  BuildMI(ElseMBB, DL, TII.get(WebAssembly::UNREACHABLE));
347 
348  } else {
349  BuildMI(ElseMBB, DL, TII.get(WebAssembly::RETHROW)).addReg(ExnReg);
350  if (EHInfo->hasEHPadUnwindDest(EHPad))
351  ElseMBB->addSuccessor(EHInfo->getEHPadUnwindDest(EHPad));
352  }
353  }
354 
355  return true;
356 }
357 
358 // After the stack is unwound due to a thrown exception, the __stack_pointer
359 // global can point to an invalid address. This inserts instructions that
360 // restore __stack_pointer global.
361 bool WebAssemblyLateEHPrepare::restoreStackPointer(MachineFunction &MF) {
362  const auto *FrameLowering = static_cast<const WebAssemblyFrameLowering *>(
364  if (!FrameLowering->needsPrologForEH(MF))
365  return false;
366  bool Changed = false;
367 
368  for (auto &MBB : MF) {
369  if (!MBB.isEHPad())
370  continue;
371  Changed = true;
372 
373  // Insert __stack_pointer restoring instructions at the beginning of each EH
374  // pad, after the catch instruction. Here it is safe to assume that SP32
375  // holds the latest value of __stack_pointer, because the only exception for
376  // this case is when a function uses the red zone, but that only happens
377  // with leaf functions, and we don't restore __stack_pointer in leaf
378  // functions anyway.
379  auto InsertPos = MBB.begin();
380  if (InsertPos->isEHLabel()) // EH pad starts with an EH label
381  ++InsertPos;
382  if (InsertPos->getOpcode() == WebAssembly::CATCH)
383  ++InsertPos;
384  FrameLowering->writeSPToGlobal(WebAssembly::SP32, MF, MBB, InsertPos,
385  MBB.begin()->getDebugLoc());
386  }
387  return Changed;
388 }
#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
const DebugLoc & getDebugLoc() const
Returns the debug location id of this MachineInstr.
Definition: MachineInstr.h:382
unsigned getReg() const
getReg - Returns the register number.
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:408
static void eraseDeadBBsAndChildren(const Container &MBBs)
bool hasPersonalityFn() const
Check whether this function has a personality function.
Definition: Function.h:707
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:841
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:253
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.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
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
const MachineInstrBuilder & addExternalSymbol(const char *FnName, unsigned char TargetFlags=0) const
virtual const TargetFrameLowering * getFrameLowering() const
const MachineInstrBuilder & addReg(unsigned RegNo, unsigned flags=0, unsigned SubReg=0) const
Add a new virtual register operand.
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:569
IRTranslator LLVM IR MI
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:48
#define LLVM_DEBUG(X)
Definition: Debug.h:122
const MachineOperand & getOperand(unsigned i) const
Definition: MachineInstr.h:413
unsigned createVirtualRegister(const TargetRegisterClass *RegClass, StringRef Name="")
createVirtualRegister - Create and return a new virtual register in the function with the specified r...
size_type count(const T &V) const
count - Return 1 if the element is in the set, 0 otherwise.
Definition: SmallSet.h:164