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-exception-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 removeUnnecessaryUnreachables(MachineFunction &MF);
34  bool replaceFuncletReturns(MachineFunction &MF);
35  bool addCatches(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  Changed |= removeUnnecessaryUnreachables(MF);
112  if (!MF.getFunction().hasPersonalityFn())
113  return Changed;
114  Changed |= replaceFuncletReturns(MF);
115  Changed |= addCatches(MF);
116  Changed |= addExceptionExtraction(MF);
117  Changed |= restoreStackPointer(MF);
118  return Changed;
119 }
120 
121 bool WebAssemblyLateEHPrepare::removeUnnecessaryUnreachables(
122  MachineFunction &MF) {
123  bool Changed = false;
124  for (auto &MBB : MF) {
125  for (auto &MI : MBB) {
126  if (MI.getOpcode() != WebAssembly::THROW &&
127  MI.getOpcode() != WebAssembly::RETHROW)
128  continue;
129  Changed = true;
130 
131  // The instruction after the throw should be an unreachable or a branch to
132  // another BB that should eventually lead to an unreachable. Delete it
133  // because throw itself is a terminator, and also delete successors if
134  // any.
135  MBB.erase(std::next(MachineBasicBlock::iterator(MI)), MBB.end());
136  SmallVector<MachineBasicBlock *, 8> Succs(MBB.succ_begin(),
137  MBB.succ_end());
138  for (auto *Succ : Succs)
139  MBB.removeSuccessor(Succ);
141  }
142  }
143 
144  return Changed;
145 }
146 
147 bool WebAssemblyLateEHPrepare::replaceFuncletReturns(MachineFunction &MF) {
148  bool Changed = false;
149  const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
150 
151  for (auto &MBB : MF) {
152  auto Pos = MBB.getFirstTerminator();
153  if (Pos == MBB.end())
154  continue;
155  MachineInstr *TI = &*Pos;
156 
157  switch (TI->getOpcode()) {
158  case WebAssembly::CATCHRET: {
159  // Replace a catchret with a branch
160  MachineBasicBlock *TBB = TI->getOperand(0).getMBB();
161  if (!MBB.isLayoutSuccessor(TBB))
162  BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::BR))
163  .addMBB(TBB);
164  TI->eraseFromParent();
165  Changed = true;
166  break;
167  }
169  // Replace a cleanupret with a rethrow
170  BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW));
171  TI->eraseFromParent();
172  Changed = true;
173  break;
174  }
175  }
176  }
177  return Changed;
178 }
179 
180 // Add catch instruction to beginning of catchpads and cleanuppads.
181 bool WebAssemblyLateEHPrepare::addCatches(MachineFunction &MF) {
182  bool Changed = false;
183  const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
185  for (auto &MBB : MF) {
186  if (MBB.isEHPad()) {
187  Changed = true;
188  unsigned DstReg =
189  MRI.createVirtualRegister(&WebAssembly::EXCEPT_REFRegClass);
190  BuildMI(MBB, MBB.begin(), MBB.begin()->getDebugLoc(),
191  TII.get(WebAssembly::CATCH), DstReg);
192  }
193  }
194  return Changed;
195 }
196 
197 // Wasm uses 'br_on_exn' instruction to check the tag of an exception. It takes
198 // except_ref type object returned by 'catch', and branches to the destination
199 // if it matches a given tag. We currently use __cpp_exception symbol to
200 // represent the tag for all C++ exceptions.
201 //
202 // block $l (result i32)
203 // ...
204 // ;; except_ref $e is on the stack at this point
205 // br_on_exn $l $e ;; branch to $l with $e's arguments
206 // ...
207 // end
208 // ;; Here we expect the extracted values are on top of the wasm value stack
209 // ... Handle exception using values ...
210 //
211 // br_on_exn takes an except_ref object and branches if it matches the given
212 // tag. There can be multiple br_on_exn instructions if we want to match for
213 // another tag, but for now we only test for __cpp_exception tag, and if it does
214 // not match, i.e., it is a foreign exception, we rethrow it.
215 //
216 // In the destination BB that's the target of br_on_exn, extracted exception
217 // values (in C++'s case a single i32, which represents an exception pointer)
218 // are placed on top of the wasm stack. Because we can't model wasm stack in
219 // LLVM instruction, we use 'extract_exception' pseudo instruction to retrieve
220 // it. The pseudo instruction will be deleted later.
221 bool WebAssemblyLateEHPrepare::addExceptionExtraction(MachineFunction &MF) {
222  const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
223  auto *EHInfo = MF.getWasmEHFuncInfo();
224  SmallVector<MachineInstr *, 16> ExtractInstrs;
225  for (auto &MBB : MF) {
226  for (auto &MI : MBB) {
227  if (MI.getOpcode() == WebAssembly::EXTRACT_EXCEPTION_I32) {
228  if (MI.getOperand(0).isDead())
229  MI.eraseFromParent();
230  else
231  ExtractInstrs.push_back(&MI);
232  }
233  }
234  }
235  if (ExtractInstrs.empty())
236  return false;
237 
238  // Find terminate pads.
239  SmallSet<MachineBasicBlock *, 8> TerminatePads;
240  for (auto &MBB : MF) {
241  for (auto &MI : MBB) {
242  if (MI.isCall()) {
243  const MachineOperand &CalleeOp = MI.getOperand(0);
244  if (CalleeOp.isGlobal() && CalleeOp.getGlobal()->getName() ==
246  TerminatePads.insert(getMatchingEHPad(&MI));
247  }
248  }
249  }
250 
251  for (auto *Extract : ExtractInstrs) {
252  MachineBasicBlock *EHPad = getMatchingEHPad(Extract);
253  assert(EHPad && "No matching EH pad for extract_exception");
254  MachineInstr *Catch = &*EHPad->begin();
255  if (Catch->getNextNode() != Extract)
256  EHPad->insert(Catch->getNextNode(), Extract->removeFromParent());
257 
258  // - Before:
259  // ehpad:
260  // %exnref:except_ref = catch
261  // %exn:i32 = extract_exception
262  // ... use exn ...
263  //
264  // - After:
265  // ehpad:
266  // %exnref:except_ref = catch
267  // br_on_exn %thenbb, $__cpp_exception, %exnref
268  // br %elsebb
269  // elsebb:
270  // rethrow
271  // thenbb:
272  // %exn:i32 = extract_exception
273  // ... use exn ...
274  unsigned ExnRefReg = Catch->getOperand(0).getReg();
275  auto *ThenMBB = MF.CreateMachineBasicBlock();
276  auto *ElseMBB = MF.CreateMachineBasicBlock();
277  MF.insert(std::next(MachineFunction::iterator(EHPad)), ElseMBB);
278  MF.insert(std::next(MachineFunction::iterator(ElseMBB)), ThenMBB);
279  ThenMBB->splice(ThenMBB->end(), EHPad, Extract, EHPad->end());
280  ThenMBB->transferSuccessors(EHPad);
281  EHPad->addSuccessor(ThenMBB);
282  EHPad->addSuccessor(ElseMBB);
283 
284  DebugLoc DL = Extract->getDebugLoc();
285  const char *CPPExnSymbol = MF.createExternalSymbolName("__cpp_exception");
286  BuildMI(EHPad, DL, TII.get(WebAssembly::BR_ON_EXN))
287  .addMBB(ThenMBB)
289  .addReg(ExnRefReg);
290  BuildMI(EHPad, DL, TII.get(WebAssembly::BR)).addMBB(ElseMBB);
291 
292  // When this is a terminate pad with __clang_call_terminate() call, we don't
293  // rethrow it anymore and call __clang_call_terminate() with a nullptr
294  // argument, which will call std::terminate().
295  //
296  // - Before:
297  // ehpad:
298  // %exnref:except_ref = catch
299  // %exn:i32 = extract_exception
300  // call @__clang_call_terminate(%exn)
301  // unreachable
302  //
303  // - After:
304  // ehpad:
305  // %exnref:except_ref = catch
306  // br_on_exn %thenbb, $__cpp_exception, %exnref
307  // br %elsebb
308  // elsebb:
309  // call @__clang_call_terminate(0)
310  // unreachable
311  // thenbb:
312  // %exn:i32 = extract_exception
313  // call @__clang_call_terminate(%exn)
314  // unreachable
315  if (TerminatePads.count(EHPad)) {
317  MF.getFunction().getParent()->getFunction(
319  assert(ClangCallTerminateFn &&
320  "There is no __clang_call_terminate() function");
321  BuildMI(ElseMBB, DL, TII.get(WebAssembly::CALL_VOID))
322  .addGlobalAddress(ClangCallTerminateFn)
323  .addImm(0);
324  BuildMI(ElseMBB, DL, TII.get(WebAssembly::UNREACHABLE));
325 
326  } else {
327  BuildMI(ElseMBB, DL, TII.get(WebAssembly::RETHROW));
328  if (EHInfo->hasEHPadUnwindDest(EHPad))
329  EHInfo->setThrowUnwindDest(ElseMBB, EHInfo->getEHPadUnwindDest(EHPad));
330  }
331  }
332 
333  return true;
334 }
335 
336 // After the stack is unwound due to a thrown exception, the __stack_pointer
337 // global can point to an invalid address. This inserts instructions that
338 // restore __stack_pointer global.
339 bool WebAssemblyLateEHPrepare::restoreStackPointer(MachineFunction &MF) {
340  const auto *FrameLowering = static_cast<const WebAssemblyFrameLowering *>(
342  if (!FrameLowering->needsPrologForEH(MF))
343  return false;
344  bool Changed = false;
345 
346  for (auto &MBB : MF) {
347  if (!MBB.isEHPad())
348  continue;
349  Changed = true;
350 
351  // Insert __stack_pointer restoring instructions at the beginning of each EH
352  // pad, after the catch instruction. Here it is safe to assume that SP32
353  // holds the latest value of __stack_pointer, because the only exception for
354  // this case is when a function uses the red zone, but that only happens
355  // with leaf functions, and we don't restore __stack_pointer in leaf
356  // functions anyway.
357  auto InsertPos = MBB.begin();
358  if (MBB.begin()->getOpcode() == WebAssembly::CATCH)
359  InsertPos++;
360  FrameLowering->writeSPToGlobal(WebAssembly::SP32, MF, MBB, InsertPos,
361  MBB.begin()->getDebugLoc());
362  }
363  return Changed;
364 }
#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:694
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:701
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.
Windows 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:630
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:839
LLVM_NODISCARD T pop_back_val()
Definition: SmallVector.h:373
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:386
CATCHRET - Represents a return from a catch block funclet.
Definition: ISDOpcodes.h:690
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