LLVM  16.0.0git
WasmEHPrepare.cpp
Go to the documentation of this file.
1 //===-- WasmEHPrepare - Prepare excepton handling for WebAssembly --------===//
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 // This transformation is designed for use by code generators which use
10 // WebAssembly exception handling scheme. This currently supports C++
11 // exceptions.
12 //
13 // WebAssembly exception handling uses Windows exception IR for the middle level
14 // representation. This pass does the following transformation for every
15 // catchpad block:
16 // (In C-style pseudocode)
17 //
18 // - Before:
19 // catchpad ...
20 // exn = wasm.get.exception();
21 // selector = wasm.get.selector();
22 // ...
23 //
24 // - After:
25 // catchpad ...
26 // exn = wasm.catch(WebAssembly::CPP_EXCEPTION);
27 // // Only add below in case it's not a single catch (...)
28 // wasm.landingpad.index(index);
29 // __wasm_lpad_context.lpad_index = index;
30 // __wasm_lpad_context.lsda = wasm.lsda();
31 // _Unwind_CallPersonality(exn);
32 // selector = __wasm_lpad_context.selector;
33 // ...
34 //
35 //
36 // * Background: Direct personality function call
37 // In WebAssembly EH, the VM is responsible for unwinding the stack once an
38 // exception is thrown. After the stack is unwound, the control flow is
39 // transfered to WebAssembly 'catch' instruction.
40 //
41 // Unwinding the stack is not done by libunwind but the VM, so the personality
42 // function in libcxxabi cannot be called from libunwind during the unwinding
43 // process. So after a catch instruction, we insert a call to a wrapper function
44 // in libunwind that in turn calls the real personality function.
45 //
46 // In Itanium EH, if the personality function decides there is no matching catch
47 // clause in a call frame and no cleanup action to perform, the unwinder doesn't
48 // stop there and continues unwinding. But in Wasm EH, the unwinder stops at
49 // every call frame with a catch intruction, after which the personality
50 // function is called from the compiler-generated user code here.
51 //
52 // In libunwind, we have this struct that serves as a communincation channel
53 // between the compiler-generated user code and the personality function in
54 // libcxxabi.
55 //
56 // struct _Unwind_LandingPadContext {
57 // uintptr_t lpad_index;
58 // uintptr_t lsda;
59 // uintptr_t selector;
60 // };
61 // struct _Unwind_LandingPadContext __wasm_lpad_context = ...;
62 //
63 // And this wrapper in libunwind calls the personality function.
64 //
65 // _Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
66 // struct _Unwind_Exception *exception_obj =
67 // (struct _Unwind_Exception *)exception_ptr;
68 // _Unwind_Reason_Code ret = __gxx_personality_v0(
69 // 1, _UA_CLEANUP_PHASE, exception_obj->exception_class, exception_obj,
70 // (struct _Unwind_Context *)__wasm_lpad_context);
71 // return ret;
72 // }
73 //
74 // We pass a landing pad index, and the address of LSDA for the current function
75 // to the wrapper function _Unwind_CallPersonality in libunwind, and we retrieve
76 // the selector after it returns.
77 //
78 //===----------------------------------------------------------------------===//
79 
81 #include "llvm/CodeGen/Passes.h"
83 #include "llvm/IR/IRBuilder.h"
84 #include "llvm/IR/IntrinsicsWebAssembly.h"
85 #include "llvm/InitializePasses.h"
87 
88 using namespace llvm;
89 
90 #define DEBUG_TYPE "wasmehprepare"
91 
92 namespace {
93 class WasmEHPrepare : public FunctionPass {
94  Type *LPadContextTy = nullptr; // type of 'struct _Unwind_LandingPadContext'
95  GlobalVariable *LPadContextGV = nullptr; // __wasm_lpad_context
96 
97  // Field addresses of struct _Unwind_LandingPadContext
98  Value *LPadIndexField = nullptr; // lpad_index field
99  Value *LSDAField = nullptr; // lsda field
100  Value *SelectorField = nullptr; // selector
101 
102  Function *ThrowF = nullptr; // wasm.throw() intrinsic
103  Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic
104  Function *LSDAF = nullptr; // wasm.lsda() intrinsic
105  Function *GetExnF = nullptr; // wasm.get.exception() intrinsic
106  Function *CatchF = nullptr; // wasm.catch() intrinsic
107  Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
108  FunctionCallee CallPersonalityF =
109  nullptr; // _Unwind_CallPersonality() wrapper
110 
111  bool prepareThrows(Function &F);
112  bool prepareEHPads(Function &F);
113  void prepareEHPad(BasicBlock *BB, bool NeedPersonality, unsigned Index = 0);
114 
115 public:
116  static char ID; // Pass identification, replacement for typeid
117 
118  WasmEHPrepare() : FunctionPass(ID) {}
119  bool doInitialization(Module &M) override;
120  bool runOnFunction(Function &F) override;
121 
122  StringRef getPassName() const override {
123  return "WebAssembly Exception handling preparation";
124  }
125 };
126 } // end anonymous namespace
127 
128 char WasmEHPrepare::ID = 0;
129 INITIALIZE_PASS_BEGIN(WasmEHPrepare, DEBUG_TYPE,
130  "Prepare WebAssembly exceptions", false, false)
131 INITIALIZE_PASS_END(WasmEHPrepare, DEBUG_TYPE, "Prepare WebAssembly exceptions",
133 
134 FunctionPass *llvm::createWasmEHPass() { return new WasmEHPrepare(); }
135 
136 bool WasmEHPrepare::doInitialization(Module &M) {
137  IRBuilder<> IRB(M.getContext());
138  LPadContextTy = StructType::get(IRB.getInt32Ty(), // lpad_index
139  IRB.getInt8PtrTy(), // lsda
140  IRB.getInt32Ty() // selector
141  );
142  return false;
143 }
144 
145 // Erase the specified BBs if the BB does not have any remaining predecessors,
146 // and also all its dead children.
147 template <typename Container>
148 static void eraseDeadBBsAndChildren(const Container &BBs) {
149  SmallVector<BasicBlock *, 8> WL(BBs.begin(), BBs.end());
150  while (!WL.empty()) {
151  auto *BB = WL.pop_back_val();
152  if (!pred_empty(BB))
153  continue;
154  WL.append(succ_begin(BB), succ_end(BB));
156  }
157 }
158 
160  bool Changed = false;
161  Changed |= prepareThrows(F);
162  Changed |= prepareEHPads(F);
163  return Changed;
164 }
165 
166 bool WasmEHPrepare::prepareThrows(Function &F) {
167  Module &M = *F.getParent();
168  IRBuilder<> IRB(F.getContext());
169  bool Changed = false;
170 
171  // wasm.throw() intinsic, which will be lowered to wasm 'throw' instruction.
172  ThrowF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_throw);
173  // Insert an unreachable instruction after a call to @llvm.wasm.throw and
174  // delete all following instructions within the BB, and delete all the dead
175  // children of the BB as well.
176  for (User *U : ThrowF->users()) {
177  // A call to @llvm.wasm.throw() is only generated from __cxa_throw()
178  // builtin call within libcxxabi, and cannot be an InvokeInst.
179  auto *ThrowI = cast<CallInst>(U);
180  if (ThrowI->getFunction() != &F)
181  continue;
182  Changed = true;
183  auto *BB = ThrowI->getParent();
185  BB->erase(std::next(BasicBlock::iterator(ThrowI)), BB->end());
186  IRB.SetInsertPoint(BB);
187  IRB.CreateUnreachable();
189  }
190 
191  return Changed;
192 }
193 
194 bool WasmEHPrepare::prepareEHPads(Function &F) {
195  Module &M = *F.getParent();
196  IRBuilder<> IRB(F.getContext());
197 
199  SmallVector<BasicBlock *, 16> CleanupPads;
200  for (BasicBlock &BB : F) {
201  if (!BB.isEHPad())
202  continue;
203  auto *Pad = BB.getFirstNonPHI();
204  if (isa<CatchPadInst>(Pad))
205  CatchPads.push_back(&BB);
206  else if (isa<CleanupPadInst>(Pad))
207  CleanupPads.push_back(&BB);
208  }
209  if (CatchPads.empty() && CleanupPads.empty())
210  return false;
211 
212  assert(F.hasPersonalityFn() && "Personality function not found");
213 
214  // __wasm_lpad_context global variable.
215  // This variable should be thread local. If the target does not support TLS,
216  // we depend on CoalesceFeaturesAndStripAtomics to downgrade it to
217  // non-thread-local ones, in which case we don't allow this object to be
218  // linked with other objects using shared memory.
219  LPadContextGV = cast<GlobalVariable>(
220  M.getOrInsertGlobal("__wasm_lpad_context", LPadContextTy));
221  LPadContextGV->setThreadLocalMode(GlobalValue::GeneralDynamicTLSModel);
222 
223  LPadIndexField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 0,
224  "lpad_index_gep");
225  LSDAField =
226  IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 1, "lsda_gep");
227  SelectorField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 2,
228  "selector_gep");
229 
230  // wasm.landingpad.index() intrinsic, which is to specify landingpad index
231  LPadIndexF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_landingpad_index);
232  // wasm.lsda() intrinsic. Returns the address of LSDA table for the current
233  // function.
234  LSDAF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_lsda);
235  // wasm.get.exception() and wasm.get.ehselector() intrinsics. Calls to these
236  // are generated in clang.
237  GetExnF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_exception);
238  GetSelectorF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_ehselector);
239 
240  // wasm.catch() will be lowered down to wasm 'catch' instruction in
241  // instruction selection.
242  CatchF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_catch);
243 
244  // _Unwind_CallPersonality() wrapper function, which calls the personality
245  CallPersonalityF = M.getOrInsertFunction(
246  "_Unwind_CallPersonality", IRB.getInt32Ty(), IRB.getInt8PtrTy());
247  if (Function *F = dyn_cast<Function>(CallPersonalityF.getCallee()))
248  F->setDoesNotThrow();
249 
250  unsigned Index = 0;
251  for (auto *BB : CatchPads) {
252  auto *CPI = cast<CatchPadInst>(BB->getFirstNonPHI());
253  // In case of a single catch (...), we don't need to emit a personalify
254  // function call
255  if (CPI->arg_size() == 1 &&
256  cast<Constant>(CPI->getArgOperand(0))->isNullValue())
257  prepareEHPad(BB, false);
258  else
259  prepareEHPad(BB, true, Index++);
260  }
261 
262  // Cleanup pads don't need a personality function call.
263  for (auto *BB : CleanupPads)
264  prepareEHPad(BB, false);
265 
266  return true;
267 }
268 
269 // Prepare an EH pad for Wasm EH handling. If NeedPersonality is false, Index is
270 // ignored.
271 void WasmEHPrepare::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
272  unsigned Index) {
273  assert(BB->isEHPad() && "BB is not an EHPad!");
274  IRBuilder<> IRB(BB->getContext());
275  IRB.SetInsertPoint(&*BB->getFirstInsertionPt());
276 
277  auto *FPI = cast<FuncletPadInst>(BB->getFirstNonPHI());
278  Instruction *GetExnCI = nullptr, *GetSelectorCI = nullptr;
279  for (auto &U : FPI->uses()) {
280  if (auto *CI = dyn_cast<CallInst>(U.getUser())) {
281  if (CI->getCalledOperand() == GetExnF)
282  GetExnCI = CI;
283  if (CI->getCalledOperand() == GetSelectorF)
284  GetSelectorCI = CI;
285  }
286  }
287 
288  // Cleanup pads do not have any of wasm.get.exception() or
289  // wasm.get.ehselector() calls. We need to do nothing.
290  if (!GetExnCI) {
291  assert(!GetSelectorCI &&
292  "wasm.get.ehselector() cannot exist w/o wasm.get.exception()");
293  return;
294  }
295 
296  // Replace wasm.get.exception intrinsic with wasm.catch intrinsic, which will
297  // be lowered to wasm 'catch' instruction. We do this mainly because
298  // instruction selection cannot handle wasm.get.exception intrinsic's token
299  // argument.
300  Instruction *CatchCI =
301  IRB.CreateCall(CatchF, {IRB.getInt32(WebAssembly::CPP_EXCEPTION)}, "exn");
302  GetExnCI->replaceAllUsesWith(CatchCI);
303  GetExnCI->eraseFromParent();
304 
305  // In case it is a catchpad with single catch (...) or a cleanuppad, we don't
306  // need to call personality function because we don't need a selector.
307  if (!NeedPersonality) {
308  if (GetSelectorCI) {
309  assert(GetSelectorCI->use_empty() &&
310  "wasm.get.ehselector() still has uses!");
311  GetSelectorCI->eraseFromParent();
312  }
313  return;
314  }
315  IRB.SetInsertPoint(CatchCI->getNextNode());
316 
317  // This is to create a map of <landingpad EH label, landingpad index> in
318  // SelectionDAGISel, which is to be used in EHStreamer to emit LSDA tables.
319  // Pseudocode: wasm.landingpad.index(Index);
320  IRB.CreateCall(LPadIndexF, {FPI, IRB.getInt32(Index)});
321 
322  // Pseudocode: __wasm_lpad_context.lpad_index = index;
323  IRB.CreateStore(IRB.getInt32(Index), LPadIndexField);
324 
325  auto *CPI = cast<CatchPadInst>(FPI);
326  // TODO Sometimes storing the LSDA address every time is not necessary, in
327  // case it is already set in a dominating EH pad and there is no function call
328  // between from that EH pad to here. Consider optimizing those cases.
329  // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
330  IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
331 
332  // Pseudocode: _Unwind_CallPersonality(exn);
333  CallInst *PersCI = IRB.CreateCall(CallPersonalityF, CatchCI,
334  OperandBundleDef("funclet", CPI));
335  PersCI->setDoesNotThrow();
336 
337  // Pseudocode: int selector = __wasm_lpad_context.selector;
338  Instruction *Selector =
339  IRB.CreateLoad(IRB.getInt32Ty(), SelectorField, "selector");
340 
341  // Replace the return value from wasm.get.ehselector() with the selector value
342  // loaded from __wasm_lpad_context.selector.
343  assert(GetSelectorCI && "wasm.get.ehselector() call does not exist");
344  GetSelectorCI->replaceAllUsesWith(Selector);
345  GetSelectorCI->eraseFromParent();
346 }
347 
349  // If an exception is not caught by a catchpad (i.e., it is a foreign
350  // exception), it will unwind to its parent catchswitch's unwind destination.
351  // We don't record an unwind destination for cleanuppads because every
352  // exception should be caught by it.
353  for (const auto &BB : *F) {
354  if (!BB.isEHPad())
355  continue;
356  const Instruction *Pad = BB.getFirstNonPHI();
357 
358  if (const auto *CatchPad = dyn_cast<CatchPadInst>(Pad)) {
359  const auto *UnwindBB = CatchPad->getCatchSwitch()->getUnwindDest();
360  if (!UnwindBB)
361  continue;
362  const Instruction *UnwindPad = UnwindBB->getFirstNonPHI();
363  if (const auto *CatchSwitch = dyn_cast<CatchSwitchInst>(UnwindPad))
364  // Currently there should be only one handler per a catchswitch.
365  EHInfo.setUnwindDest(&BB, *CatchSwitch->handlers().begin());
366  else // cleanuppad
367  EHInfo.setUnwindDest(&BB, UnwindBB);
368  }
369  }
370 }
llvm::WasmEHFuncInfo
Definition: WasmEHFuncInfo.h:32
llvm::WasmEHFuncInfo::setUnwindDest
void setUnwindDest(const BasicBlock *BB, const BasicBlock *Dest)
Definition: WasmEHFuncInfo.h:51
INITIALIZE_PASS_BEGIN
INITIALIZE_PASS_BEGIN(WasmEHPrepare, DEBUG_TYPE, "Prepare WebAssembly exceptions", false, false) INITIALIZE_PASS_END(WasmEHPrepare
llvm
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
M
We currently emits eax Perhaps this is what we really should generate is Is imull three or four cycles eax eax The current instruction priority is based on pattern complexity The former is more complex because it folds a load so the latter will not be emitted Perhaps we should use AddedComplexity to give LEA32r a higher priority We should always try to match LEA first since the LEA matching code does some estimate to determine whether the match is profitable if we care more about code then imull is better It s two bytes shorter than movl leal On a Pentium M
Definition: README.txt:252
llvm::StructType::get
static StructType * get(LLVMContext &Context, ArrayRef< Type * > Elements, bool isPacked=false)
This static method is the primary way to create a literal StructType.
Definition: Type.cpp:406
llvm::AArch64PACKey::ID
ID
Definition: AArch64BaseInfo.h:818
llvm::BasicBlock::iterator
InstListType::iterator iterator
Instruction iterators...
Definition: BasicBlock.h:87
llvm::succ_end
Interval::succ_iterator succ_end(Interval *I)
Definition: Interval.h:102
llvm::Function
Definition: Function.h:60
llvm::ilist_node_with_parent::getNextNode
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
Definition: ilist_node.h:289
llvm::SmallVector
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1199
llvm::IRBuilder<>
exceptions
Prepare WebAssembly exceptions
Definition: WasmEHPrepare.cpp:131
llvm::GlobalVariable
Definition: GlobalVariable.h:39
MachineBasicBlock.h
llvm::Type
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
llvm::successors
auto successors(MachineBasicBlock *BB)
Definition: MachineSSAContext.h:29
llvm::CallBase::setDoesNotThrow
void setDoesNotThrow()
Definition: InstrTypes.h:1891
F
#define F(x, y, z)
Definition: MD5.cpp:55
llvm::BasicBlock
LLVM Basic Block Representation.
Definition: BasicBlock.h:55
llvm::User
Definition: User.h:44
false
Definition: StackSlotColoring.cpp:141
llvm::dwarf::Index
Index
Definition: Dwarf.h:472
llvm::Instruction
Definition: Instruction.h:42
llvm::OperandBundleDef
OperandBundleDefT< Value * > OperandBundleDef
Definition: AutoUpgrade.h:33
llvm::SmallVectorImpl::append
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
Definition: SmallVector.h:687
llvm::CallingConv::ID
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition: CallingConv.h:24
INITIALIZE_PASS_END
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition: PassSupport.h:58
Passes.h
llvm::Instruction::eraseFromParent
SymbolTableList< Instruction >::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
Definition: Instruction.cpp:81
llvm::GlobalValue::GeneralDynamicTLSModel
@ GeneralDynamicTLSModel
Definition: GlobalValue.h:193
llvm::succ_begin
Interval::succ_iterator succ_begin(Interval *I)
succ_begin/succ_end - define methods so that Intervals may be used just like BasicBlocks can with the...
Definition: Interval.h:99
IRBuilder.h
assert
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
llvm::Module
A Module instance is used to store all the information related to an LLVM module.
Definition: Module.h:66
eraseDeadBBsAndChildren
static void eraseDeadBBsAndChildren(const Container &BBs)
Definition: WasmEHPrepare.cpp:148
llvm::StringRef
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
llvm::Value::replaceAllUsesWith
void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition: Value.cpp:532
llvm::pred_empty
bool pred_empty(const BasicBlock *BB)
Definition: CFG.h:118
llvm::Intrinsic::getDeclaration
Function * getDeclaration(Module *M, ID id, ArrayRef< Type * > Tys=std::nullopt)
Create or insert an LLVM Function declaration for an intrinsic, and return it.
Definition: Function.cpp:1481
runOnFunction
static bool runOnFunction(Function &F, bool PostInlining)
Definition: EntryExitInstrumenter.cpp:85
llvm::WebAssembly::CPP_EXCEPTION
@ CPP_EXCEPTION
Definition: WasmEHFuncInfo.h:27
WasmEHFuncInfo.h
llvm::createWasmEHPass
FunctionPass * createWasmEHPass()
createWasmEHPass - This pass adapts exception handling code to use WebAssembly's exception handling s...
Definition: WasmEHPrepare.cpp:134
DEBUG_TYPE
#define DEBUG_TYPE
Definition: WasmEHPrepare.cpp:90
llvm::SmallVectorImpl::pop_back_val
T pop_back_val()
Definition: SmallVector.h:677
llvm::FunctionCallee
A handy container for a FunctionType+Callee-pointer pair, which can be passed around as a single enti...
Definition: DerivedTypes.h:165
llvm::DeleteDeadBlock
void DeleteDeadBlock(BasicBlock *BB, DomTreeUpdater *DTU=nullptr, bool KeepOneInputPHIs=false)
Delete the specified block, which must have no predecessors.
Definition: BasicBlockUtils.cpp:95
llvm::FunctionPass
FunctionPass class - This class is used to implement most global optimizations.
Definition: Pass.h:308
llvm::CallInst
This class represents a function call, abstracting a target machine's calling convention.
Definition: Instructions.h:1474
BB
Common register allocation spilling lr str ldr sxth r3 ldr mla r4 can lr mov lr str ldr sxth r3 mla r4 and then merge mul and lr str ldr sxth r3 mla r4 It also increase the likelihood the store may become dead bb27 Successors according to LLVM BB
Definition: README.txt:39
BasicBlockUtils.h
InitializePasses.h
llvm::Value
LLVM Value Representation.
Definition: Value.h:74
llvm::Value::users
iterator_range< user_iterator > users()
Definition: Value.h:421
llvm::calculateWasmEHInfo
void calculateWasmEHInfo(const Function *F, WasmEHFuncInfo &EHInfo)
Definition: WasmEHPrepare.cpp:348