blob: c2c7bf6ee94e1d0ab2ff9ad0fed49e07049bb5e3 [file] [log] [blame]
Heejin Ahn99d60e02018-05-31 22:02:34 +00001//===-- WasmEHPrepare - Prepare excepton handling for WebAssembly --------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This transformation is designed for use by code generators which use
11// WebAssembly exception handling scheme.
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(0); // 0 is a tag for C++
27// wasm.landingpad.index(index);
28// // Only add below in case it's not a single catch (...)
29// __wasm_lpad_context.lpad_index = index;
30// __wasm_lpad_context.lsda = wasm.lsda();
31// _Unwind_CallPersonality(exn);
32// int selector = __wasm.landingpad_context.selector;
33// ...
34//
35// Also, does the following for a cleanuppad block with a call to
36// __clang_call_terminate():
37// - Before:
38// cleanuppad ...
39// exn = wasm.get.exception();
40// __clang_call_terminate(exn);
41//
42// - After:
43// cleanuppad ...
44// exn = wasm.catch(0); // 0 is a tag for C++
45// __clang_call_terminate(exn);
46//
47//
48// * Background: WebAssembly EH instructions
49// WebAssembly's try and catch instructions are structured as follows:
50// try
51// instruction*
52// catch (C++ tag)
53// instruction*
54// ...
55// catch_all
56// instruction*
57// try_end
58//
59// A catch instruction in WebAssembly does not correspond to a C++ catch clause.
60// In WebAssembly, there is a single catch instruction for all C++ exceptions.
61// There can be more catch instructions for exceptions in other languages, but
62// they are not generated for now. catch_all catches all exceptions including
63// foreign exceptions (e.g. JavaScript). We turn catchpads into catch (C++ tag)
64// and cleanuppads into catch_all, with one exception: cleanuppad with a call to
65// __clang_call_terminate should be both in catch (C++ tag) and catch_all.
66//
67//
68// * Background: Direct personality function call
69// In WebAssembly EH, the VM is responsible for unwinding the stack once an
70// exception is thrown. After the stack is unwound, the control flow is
71// transfered to WebAssembly 'catch' instruction, which returns a caught
72// exception object.
73//
74// Unwinding the stack is not done by libunwind but the VM, so the personality
75// function in libcxxabi cannot be called from libunwind during the unwinding
76// process. So after a catch instruction, we insert a call to a wrapper function
77// in libunwind that in turn calls the real personality function.
78//
79// In Itanium EH, if the personality function decides there is no matching catch
80// clause in a call frame and no cleanup action to perform, the unwinder doesn't
81// stop there and continues unwinding. But in Wasm EH, the unwinder stops at
82// every call frame with a catch intruction, after which the personality
83// function is called from the compiler-generated user code here.
84//
85// In libunwind, we have this struct that serves as a communincation channel
86// between the compiler-generated user code and the personality function in
87// libcxxabi.
88//
89// struct _Unwind_LandingPadContext {
90// uintptr_t lpad_index;
91// uintptr_t lsda;
92// uintptr_t selector;
93// };
94// struct _Unwind_LandingPadContext __wasm_lpad_context = ...;
95//
96// And this wrapper in libunwind calls the personality function.
97//
98// _Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
99// struct _Unwind_Exception *exception_obj =
100// (struct _Unwind_Exception *)exception_ptr;
101// _Unwind_Reason_Code ret = __gxx_personality_v0(
102// 1, _UA_CLEANUP_PHASE, exception_obj->exception_class, exception_obj,
103// (struct _Unwind_Context *)__wasm_lpad_context);
104// return ret;
105// }
106//
107// We pass a landing pad index, and the address of LSDA for the current function
108// to the wrapper function _Unwind_CallPersonality in libunwind, and we retrieve
109// the selector after it returns.
110//
111//===----------------------------------------------------------------------===//
112
113#include "llvm/ADT/SetVector.h"
114#include "llvm/ADT/Statistic.h"
115#include "llvm/ADT/Triple.h"
116#include "llvm/CodeGen/Passes.h"
117#include "llvm/CodeGen/TargetLowering.h"
118#include "llvm/CodeGen/TargetSubtargetInfo.h"
119#include "llvm/IR/Dominators.h"
120#include "llvm/IR/IRBuilder.h"
121#include "llvm/IR/Intrinsics.h"
122#include "llvm/Pass.h"
123#include "llvm/Transforms/Utils/BasicBlockUtils.h"
124
125using namespace llvm;
126
127#define DEBUG_TYPE "wasmehprepare"
128
129namespace {
130class WasmEHPrepare : public FunctionPass {
131 Type *LPadContextTy = nullptr; // type of 'struct _Unwind_LandingPadContext'
132 GlobalVariable *LPadContextGV = nullptr; // __wasm_lpad_context
133
134 // Field addresses of struct _Unwind_LandingPadContext
135 Value *LPadIndexField = nullptr; // lpad_index field
136 Value *LSDAField = nullptr; // lsda field
137 Value *SelectorField = nullptr; // selector
138
139 Function *CatchF = nullptr; // wasm.catch.extract() intrinsic
140 Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic
141 Function *LSDAF = nullptr; // wasm.lsda() intrinsic
142 Function *GetExnF = nullptr; // wasm.get.exception() intrinsic
143 Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
144 Function *CallPersonalityF = nullptr; // _Unwind_CallPersonality() wrapper
145 Function *ClangCallTermF = nullptr; // __clang_call_terminate() function
146
147 void prepareEHPad(BasicBlock *BB, unsigned Index);
148 void prepareTerminateCleanupPad(BasicBlock *BB);
149
150public:
151 static char ID; // Pass identification, replacement for typeid
152
153 WasmEHPrepare() : FunctionPass(ID) {}
154
155 bool doInitialization(Module &M) override;
156 bool runOnFunction(Function &F) override;
157
158 StringRef getPassName() const override {
159 return "WebAssembly Exception handling preparation";
160 }
161};
162} // end anonymous namespace
163
164char WasmEHPrepare::ID = 0;
165INITIALIZE_PASS(WasmEHPrepare, DEBUG_TYPE, "Prepare WebAssembly exceptions",
166 false, false);
167
168FunctionPass *llvm::createWasmEHPass() { return new WasmEHPrepare(); }
169
170bool WasmEHPrepare::doInitialization(Module &M) {
171 IRBuilder<> IRB(M.getContext());
172 LPadContextTy = StructType::get(IRB.getInt32Ty(), // lpad_index
173 IRB.getInt8PtrTy(), // lsda
174 IRB.getInt32Ty() // selector
175 );
176 return false;
177}
178
179bool WasmEHPrepare::runOnFunction(Function &F) {
180 SmallVector<BasicBlock *, 16> CatchPads;
181 SmallVector<BasicBlock *, 16> CleanupPads;
182 for (BasicBlock &BB : F) {
183 if (!BB.isEHPad())
184 continue;
185 auto *Pad = BB.getFirstNonPHI();
186 if (isa<CatchPadInst>(Pad))
187 CatchPads.push_back(&BB);
188 else if (isa<CleanupPadInst>(Pad))
189 CleanupPads.push_back(&BB);
190 }
191
192 if (CatchPads.empty() && CleanupPads.empty())
193 return false;
194 assert(F.hasPersonalityFn() && "Personality function not found");
195
196 Module &M = *F.getParent();
197 IRBuilder<> IRB(F.getContext());
198
199 // __wasm_lpad_context global variable
200 LPadContextGV = cast<GlobalVariable>(
201 M.getOrInsertGlobal("__wasm_lpad_context", LPadContextTy));
202 LPadIndexField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 0,
203 "lpad_index_gep");
204 LSDAField =
205 IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 1, "lsda_gep");
206 SelectorField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 2,
207 "selector_gep");
208
209 // wasm.catch() intinsic, which will be lowered to wasm 'catch' instruction.
210 CatchF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_catch);
211 // wasm.landingpad.index() intrinsic, which is to specify landingpad index
212 LPadIndexF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_landingpad_index);
213 // wasm.lsda() intrinsic. Returns the address of LSDA table for the current
214 // function.
215 LSDAF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_lsda);
216 // wasm.get.exception() and wasm.get.ehselector() intrinsics. Calls to these
217 // are generated in clang.
218 GetExnF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_exception);
219 GetSelectorF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_ehselector);
220
221 // _Unwind_CallPersonality() wrapper function, which calls the personality
222 CallPersonalityF = cast<Function>(M.getOrInsertFunction(
223 "_Unwind_CallPersonality", IRB.getInt32Ty(), IRB.getInt8PtrTy()));
224 CallPersonalityF->setDoesNotThrow();
225
226 // __clang_call_terminate() function, which is inserted by clang in case a
227 // cleanup throws
228 ClangCallTermF = M.getFunction("__clang_call_terminate");
229
230 unsigned Index = 0;
231 for (auto *BB : CatchPads) {
232 auto *CPI = cast<CatchPadInst>(BB->getFirstNonPHI());
233 // In case of a single catch (...), we don't need to emit LSDA
234 if (CPI->getNumArgOperands() == 1 &&
235 cast<Constant>(CPI->getArgOperand(0))->isNullValue())
236 prepareEHPad(BB, -1);
237 else
238 prepareEHPad(BB, Index++);
239 }
240
241 if (!ClangCallTermF)
242 return !CatchPads.empty();
243
244 // Cleanuppads will turn into catch_all later, but cleanuppads with a call to
245 // __clang_call_terminate() is a special case. __clang_call_terminate() takes
246 // an exception object, so we have to duplicate call in both 'catch <C++ tag>'
247 // and 'catch_all' clauses. Here we only insert a call to catch; the
248 // duplication will be done later. In catch_all, the exception object will be
249 // set to null.
250 for (auto *BB : CleanupPads)
251 for (auto &I : *BB)
252 if (auto *CI = dyn_cast<CallInst>(&I))
253 if (CI->getCalledValue() == ClangCallTermF)
254 prepareEHPad(BB, -1);
255
256 return true;
257}
258
259void WasmEHPrepare::prepareEHPad(BasicBlock *BB, unsigned Index) {
260 assert(BB->isEHPad() && "BB is not an EHPad!");
261 IRBuilder<> IRB(BB->getContext());
262
263 IRB.SetInsertPoint(&*BB->getFirstInsertionPt());
264 // The argument to wasm.catch() is the tag for C++ exceptions, which we set to
265 // 0 for this module.
266 // Pseudocode: void *exn = wasm.catch(0);
267 Instruction *Exn = IRB.CreateCall(CatchF, IRB.getInt32(0), "exn");
268 // Replace the return value of wasm.get.exception() with the return value from
269 // wasm.catch().
270 auto *FPI = cast<FuncletPadInst>(BB->getFirstNonPHI());
271 Instruction *GetExnCI = nullptr, *GetSelectorCI = nullptr;
272 for (auto &U : FPI->uses()) {
273 if (auto *CI = dyn_cast<CallInst>(U.getUser())) {
274 if (CI->getCalledValue() == GetExnF)
275 GetExnCI = CI;
276 else if (CI->getCalledValue() == GetSelectorF)
277 GetSelectorCI = CI;
278 }
279 }
280
281 assert(GetExnCI && "wasm.get.exception() call does not exist");
282 GetExnCI->replaceAllUsesWith(Exn);
283 GetExnCI->eraseFromParent();
284
285 // In case it is a catchpad with single catch (...) or a cleanuppad, we don't
286 // need to call personality function because we don't need a selector.
287 if (FPI->getNumArgOperands() == 0 ||
288 (FPI->getNumArgOperands() == 1 &&
289 cast<Constant>(FPI->getArgOperand(0))->isNullValue())) {
290 if (GetSelectorCI) {
291 assert(GetSelectorCI->use_empty() &&
292 "wasm.get.ehselector() still has uses!");
293 GetSelectorCI->eraseFromParent();
294 }
295 return;
296 }
297 IRB.SetInsertPoint(Exn->getNextNode());
298
299 // This is to create a map of <landingpad EH label, landingpad index> in
300 // SelectionDAGISel, which is to be used in EHStreamer to emit LSDA tables.
301 // Pseudocode: wasm.landingpad.index(Index);
302 IRB.CreateCall(LPadIndexF, IRB.getInt32(Index));
303
304 // Pseudocode: __wasm_lpad_context.lpad_index = index;
305 IRB.CreateStore(IRB.getInt32(Index), LPadIndexField);
306
307 // Store LSDA address only if this catchpad belongs to a top-level
308 // catchswitch. If there is another catchpad that dominates this pad, we don't
309 // need to store LSDA address again, because they are the same throughout the
310 // function and have been already stored before.
311 // TODO Can we not store LSDA address in user function but make libcxxabi
312 // compute it?
313 auto *CPI = cast<CatchPadInst>(FPI);
314 if (isa<ConstantTokenNone>(CPI->getCatchSwitch()->getParentPad()))
315 // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
316 IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
317
318 // Pseudocode: _Unwind_CallPersonality(exn);
319 CallInst *PersCI =
320 IRB.CreateCall(CallPersonalityF, Exn, OperandBundleDef("funclet", CPI));
321 PersCI->setDoesNotThrow();
322
323 // Pseudocode: int selector = __wasm.landingpad_context.selector;
324 Instruction *Selector = IRB.CreateLoad(SelectorField, "selector");
325
326 // Replace the return value from wasm.get.ehselector() with the selector value
327 // loaded from __wasm_lpad_context.selector.
328 assert(GetSelectorCI && "wasm.get.ehselector() call does not exist");
329 GetSelectorCI->replaceAllUsesWith(Selector);
330 GetSelectorCI->eraseFromParent();
331}