blob: 6c84a38a5907f93d349bedd170ad232bed900eaf [file] [log] [blame]
Derek Schuffccdceda2016-08-18 15:27:25 +00001//=== WebAssemblyLowerEmscriptenEHSjLj.cpp - Lower exceptions for Emscripten =//
Derek Schufff41f67d2016-08-01 21:34:04 +00002//
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/// \file
Heejin Ahn10a70862016-09-01 00:44:37 +000011/// \brief This file lowers exception-related instructions in order to use
12/// Emscripten's JavaScript try and catch mechanism to handle exceptions.
Derek Schufff41f67d2016-08-01 21:34:04 +000013///
Heejin Ahn10a70862016-09-01 00:44:37 +000014/// To handle exceptions, this scheme relies on JavaScript's try and catch
15/// syntax and relevant exception-related libraries implemented in JavaScript
16/// glue code that will be produced by Emscripten. This is similar to the
17/// current Emscripten asm.js exception handling in fastcomp.
18/// For fastcomp's EH scheme, see these files in fastcomp LLVM branch:
Derek Schufff41f67d2016-08-01 21:34:04 +000019/// (Location: https://github.com/kripken/emscripten-fastcomp)
20/// lib/Target/JSBackend/NaCl/LowerEmExceptionsPass.cpp
21/// lib/Target/JSBackend/JSBackend.cpp
22/// lib/Target/JSBackend/CallHandlers.h
23///
Heejin Ahn10a70862016-09-01 00:44:37 +000024/// This pass does following things:
Derek Schufff41f67d2016-08-01 21:34:04 +000025///
Heejin Ahn10a70862016-09-01 00:44:37 +000026/// 1) Create three global variables: __THREW__, __threwValue, and tempRet0.
27/// tempRet0 will be set within __cxa_find_matching_catch() function in
Derek Schuffccdceda2016-08-18 15:27:25 +000028/// JS library, and __THREW__ and __threwValue will be set in invoke wrappers
Heejin Ahn10a70862016-09-01 00:44:37 +000029/// in JS glue code. For what invoke wrappers are, refer to 3).
Derek Schufff41f67d2016-08-01 21:34:04 +000030///
31/// 2) Create setThrew and setTempRet0 functions.
32/// The global variables created in 1) will exist in wasm address space,
33/// but their values should be set in JS code, so we provide these functions
34/// as interfaces to JS glue code. These functions are equivalent to the
35/// following JS functions, which actually exist in asm.js version of JS
36/// library.
37///
38/// function setThrew(threw, value) {
39/// if (__THREW__ == 0) {
40/// __THREW__ = threw;
Derek Schuffccdceda2016-08-18 15:27:25 +000041/// __threwValue = value;
Derek Schufff41f67d2016-08-01 21:34:04 +000042/// }
43/// }
44///
45/// function setTempRet0(value) {
Heejin Ahn10a70862016-09-01 00:44:37 +000046/// tempRet0 = value;
Derek Schufff41f67d2016-08-01 21:34:04 +000047/// }
48///
49/// 3) Lower
50/// invoke @func(arg1, arg2) to label %invoke.cont unwind label %lpad
51/// into
52/// __THREW__ = 0;
53/// call @invoke_SIG(func, arg1, arg2)
54/// %__THREW__.val = __THREW__;
55/// __THREW__ = 0;
56/// br %__THREW__.val, label %lpad, label %invoke.cont
57/// SIG is a mangled string generated based on the LLVM IR-level function
58/// signature. After LLVM IR types are lowered to the target wasm types,
59/// the names for these wrappers will change based on wasm types as well,
60/// as in invoke_vi (function takes an int and returns void). The bodies of
61/// these wrappers will be generated in JS glue code, and inside those
62/// wrappers we use JS try-catch to generate actual exception effects. It
63/// also calls the original callee function. An example wrapper in JS code
64/// would look like this:
65/// function invoke_vi(index,a1) {
66/// try {
67/// Module["dynCall_vi"](index,a1); // This calls original callee
68/// } catch(e) {
69/// if (typeof e !== 'number' && e !== 'longjmp') throw e;
70/// asm["setThrew"](1, 0); // setThrew is called here
71/// }
72/// }
73/// If an exception is thrown, __THREW__ will be set to true in a wrapper,
74/// so we can jump to the right BB based on this value.
75///
76/// 4) Lower
77/// %val = landingpad catch c1 catch c2 catch c3 ...
78/// ... use %val ...
79/// into
Derek Schuff53b9af02016-08-09 00:29:55 +000080/// %fmc = call @__cxa_find_matching_catch_N(c1, c2, c3, ...)
Heejin Ahn10a70862016-09-01 00:44:37 +000081/// %val = {%fmc, tempRet0}
Derek Schufff41f67d2016-08-01 21:34:04 +000082/// ... use %val ...
83/// Here N is a number calculated based on the number of clauses.
Heejin Ahn10a70862016-09-01 00:44:37 +000084/// Global variable tempRet0 is set within __cxa_find_matching_catch() in
Derek Schufff41f67d2016-08-01 21:34:04 +000085/// JS glue code.
86///
87/// 5) Lower
88/// resume {%a, %b}
89/// into
Derek Schuff53b9af02016-08-09 00:29:55 +000090/// call @__resumeException(%a)
91/// where __resumeException() is a function in JS glue code.
Derek Schufff41f67d2016-08-01 21:34:04 +000092///
Derek Schuff53b9af02016-08-09 00:29:55 +000093/// 6) Lower
94/// call @llvm.eh.typeid.for(type) (intrinsic)
95/// into
96/// call @llvm_eh_typeid_for(type)
97/// llvm_eh_typeid_for function will be generated in JS glue code.
Derek Schufff41f67d2016-08-01 21:34:04 +000098///
99///===----------------------------------------------------------------------===//
100
101#include "WebAssembly.h"
Derek Schuff53b9af02016-08-09 00:29:55 +0000102#include "llvm/IR/CallSite.h"
Derek Schufff41f67d2016-08-01 21:34:04 +0000103#include "llvm/IR/IRBuilder.h"
Heejin Ahn10a70862016-09-01 00:44:37 +0000104#include "llvm/IR/Module.h"
105#include "llvm/Support/raw_ostream.h"
106#include <set>
Derek Schufff41f67d2016-08-01 21:34:04 +0000107
108using namespace llvm;
109
Derek Schuffccdceda2016-08-18 15:27:25 +0000110#define DEBUG_TYPE "wasm-lower-em-ehsjlj"
Derek Schufff41f67d2016-08-01 21:34:04 +0000111
Derek Schuff66641322016-08-09 22:37:00 +0000112static cl::list<std::string>
Derek Schuffccdceda2016-08-18 15:27:25 +0000113 EHWhitelist("emscripten-cxx-exceptions-whitelist",
114 cl::desc("The list of function names in which Emscripten-style "
115 "exception handling is enabled (see emscripten "
116 "EMSCRIPTEN_CATCHING_WHITELIST options)"),
117 cl::CommaSeparated);
Derek Schuff66641322016-08-09 22:37:00 +0000118
Derek Schufff41f67d2016-08-01 21:34:04 +0000119namespace {
Derek Schuffccdceda2016-08-18 15:27:25 +0000120class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
121 static const char *ThrewGVName;
122 static const char *ThrewValueGVName;
123 static const char *TempRet0GVName;
124 static const char *ResumeFName;
125 static const char *EHTypeIDFName;
126 static const char *SetThrewFName;
127 static const char *SetTempRet0FName;
128 static const char *FindMatchingCatchPrefix;
129 static const char *InvokePrefix;
130
Heejin Ahn10a70862016-09-01 00:44:37 +0000131 bool DoEH; // Enable exception handling
132 bool DoSjLj; // Enable setjmp/longjmp handling
Derek Schuffccdceda2016-08-18 15:27:25 +0000133
134 GlobalVariable *ThrewGV;
135 GlobalVariable *ThrewValueGV;
136 GlobalVariable *TempRet0GV;
137 Function *ResumeF;
138 Function *EHTypeIDF;
139 // __cxa_find_matching_catch_N functions.
140 // Indexed by the number of clauses in an original landingpad instruction.
141 DenseMap<int, Function *> FindMatchingCatches;
142 // Map of <function signature string, invoke_ wrappers>
143 StringMap<Function *> InvokeWrappers;
144 // Set of whitelisted function names for exception handling
145 std::set<std::string> EHWhitelistSet;
146
Derek Schufff41f67d2016-08-01 21:34:04 +0000147 const char *getPassName() const override {
148 return "WebAssembly Lower Emscripten Exceptions";
149 }
150
Derek Schuffccdceda2016-08-18 15:27:25 +0000151 bool runEHOnFunction(Function &F);
152 bool runSjLjOnFunction(Function &F);
Heejin Ahn10a70862016-09-01 00:44:37 +0000153 // Returns __cxa_find_matching_catch_N function, where N = NumClauses + 2.
154 // This is because a landingpad instruction contains two more arguments,
155 // a personality function and a cleanup bit, and __cxa_find_matching_catch_N
156 // functions are named after the number of arguments in the original
157 // landingpad instruction.
Derek Schufff41f67d2016-08-01 21:34:04 +0000158 Function *getFindMatchingCatch(Module &M, unsigned NumClauses);
159
Heejin Ahn10a70862016-09-01 00:44:37 +0000160 Function *getInvokeWrapper(Module &M, InvokeInst *II);
Derek Schuffccdceda2016-08-18 15:27:25 +0000161 bool areAllExceptionsAllowed() const { return EHWhitelistSet.empty(); }
Derek Schufff41f67d2016-08-01 21:34:04 +0000162
163public:
164 static char ID;
165
Heejin Ahn10a70862016-09-01 00:44:37 +0000166 WebAssemblyLowerEmscriptenEHSjLj(bool DoEH = true, bool DoSjLj = true)
167 : ModulePass(ID), DoEH(DoEH), DoSjLj(DoSjLj), ThrewGV(nullptr),
168 ThrewValueGV(nullptr), TempRet0GV(nullptr), ResumeF(nullptr),
169 EHTypeIDF(nullptr) {
Derek Schuffccdceda2016-08-18 15:27:25 +0000170 EHWhitelistSet.insert(EHWhitelist.begin(), EHWhitelist.end());
Derek Schuff66641322016-08-09 22:37:00 +0000171 }
Derek Schufff41f67d2016-08-01 21:34:04 +0000172 bool runOnModule(Module &M) override;
173};
174} // End anonymous namespace
175
Derek Schuffccdceda2016-08-18 15:27:25 +0000176const char *WebAssemblyLowerEmscriptenEHSjLj::ThrewGVName = "__THREW__";
177const char *WebAssemblyLowerEmscriptenEHSjLj::ThrewValueGVName = "__threwValue";
178const char *WebAssemblyLowerEmscriptenEHSjLj::TempRet0GVName = "__tempRet0";
179const char *WebAssemblyLowerEmscriptenEHSjLj::ResumeFName = "__resumeException";
180const char *WebAssemblyLowerEmscriptenEHSjLj::EHTypeIDFName =
181 "llvm_eh_typeid_for";
182const char *WebAssemblyLowerEmscriptenEHSjLj::SetThrewFName = "setThrew";
183const char *WebAssemblyLowerEmscriptenEHSjLj::SetTempRet0FName = "setTempRet0";
184const char *WebAssemblyLowerEmscriptenEHSjLj::FindMatchingCatchPrefix =
185 "__cxa_find_matching_catch_";
186const char *WebAssemblyLowerEmscriptenEHSjLj::InvokePrefix = "__invoke_";
Derek Schufff41f67d2016-08-01 21:34:04 +0000187
Derek Schuffccdceda2016-08-18 15:27:25 +0000188char WebAssemblyLowerEmscriptenEHSjLj::ID = 0;
189INITIALIZE_PASS(WebAssemblyLowerEmscriptenEHSjLj, DEBUG_TYPE,
190 "WebAssembly Lower Emscripten Exceptions / Setjmp / Longjmp",
191 false, false)
192
Heejin Ahn10a70862016-09-01 00:44:37 +0000193ModulePass *llvm::createWebAssemblyLowerEmscriptenEHSjLj(bool DoEH,
194 bool DoSjLj) {
195 return new WebAssemblyLowerEmscriptenEHSjLj(DoEH, DoSjLj);
Derek Schufff41f67d2016-08-01 21:34:04 +0000196}
197
198static bool canThrow(const Value *V) {
199 if (const auto *F = dyn_cast<const Function>(V)) {
200 // Intrinsics cannot throw
201 if (F->isIntrinsic())
202 return false;
203 StringRef Name = F->getName();
204 // leave setjmp and longjmp (mostly) alone, we process them properly later
205 if (Name == "setjmp" || Name == "longjmp")
206 return false;
Heejin Ahn10a70862016-09-01 00:44:37 +0000207 return true;
Derek Schufff41f67d2016-08-01 21:34:04 +0000208 }
Heejin Ahnb6cd5122016-08-24 22:53:00 +0000209 // not a function, so an indirect call - can throw, we can't tell
210 return true;
Derek Schufff41f67d2016-08-01 21:34:04 +0000211}
212
213// Returns an available name for a global value.
214// If the proposed name already exists in the module, adds '_' at the end of
215// the name until the name is available.
216static inline std::string createGlobalValueName(const Module &M,
217 const std::string &Propose) {
218 std::string Name = Propose;
219 while (M.getNamedGlobal(Name))
220 Name += "_";
221 return Name;
222}
223
224// Simple function name mangler.
225// This function simply takes LLVM's string representation of parameter types
Derek Schuff53b9af02016-08-09 00:29:55 +0000226// and concatenate them with '_'. There are non-alphanumeric characters but llc
227// is ok with it, and we need to postprocess these names after the lowering
228// phase anyway.
Derek Schufff41f67d2016-08-01 21:34:04 +0000229static std::string getSignature(FunctionType *FTy) {
230 std::string Sig;
231 raw_string_ostream OS(Sig);
232 OS << *FTy->getReturnType();
233 for (Type *ParamTy : FTy->params())
234 OS << "_" << *ParamTy;
235 if (FTy->isVarArg())
236 OS << "_...";
237 Sig = OS.str();
David Majnemerc7004902016-08-12 04:32:37 +0000238 Sig.erase(remove_if(Sig, isspace), Sig.end());
Derek Schuff53b9af02016-08-09 00:29:55 +0000239 // When s2wasm parses .s file, a comma means the end of an argument. So a
240 // mangled function name can contain any character but a comma.
241 std::replace(Sig.begin(), Sig.end(), ',', '.');
Derek Schufff41f67d2016-08-01 21:34:04 +0000242 return Sig;
243}
244
Derek Schuffccdceda2016-08-18 15:27:25 +0000245Function *
246WebAssemblyLowerEmscriptenEHSjLj::getFindMatchingCatch(Module &M,
247 unsigned NumClauses) {
Derek Schufff41f67d2016-08-01 21:34:04 +0000248 if (FindMatchingCatches.count(NumClauses))
249 return FindMatchingCatches[NumClauses];
250 PointerType *Int8PtrTy = Type::getInt8PtrTy(M.getContext());
251 SmallVector<Type *, 16> Args(NumClauses, Int8PtrTy);
252 FunctionType *FTy = FunctionType::get(Int8PtrTy, Args, false);
Derek Schuffccdceda2016-08-18 15:27:25 +0000253 Function *F =
254 Function::Create(FTy, GlobalValue::ExternalLinkage,
255 FindMatchingCatchPrefix + Twine(NumClauses + 2), &M);
Derek Schufff41f67d2016-08-01 21:34:04 +0000256 FindMatchingCatches[NumClauses] = F;
257 return F;
258}
259
Heejin Ahn10a70862016-09-01 00:44:37 +0000260Function *WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper(Module &M,
261 InvokeInst *II) {
Derek Schufff41f67d2016-08-01 21:34:04 +0000262 SmallVector<Type *, 16> ArgTys;
Heejin Ahn10a70862016-09-01 00:44:37 +0000263 Value *Callee = II->getCalledValue();
Derek Schufff41f67d2016-08-01 21:34:04 +0000264 FunctionType *CalleeFTy;
265 if (auto *F = dyn_cast<Function>(Callee))
266 CalleeFTy = F->getFunctionType();
267 else {
Heejin Ahn10a70862016-09-01 00:44:37 +0000268 auto *CalleeTy = dyn_cast<PointerType>(Callee->getType())->getElementType();
Derek Schufff41f67d2016-08-01 21:34:04 +0000269 CalleeFTy = dyn_cast<FunctionType>(CalleeTy);
270 }
271
272 std::string Sig = getSignature(CalleeFTy);
273 if (InvokeWrappers.find(Sig) != InvokeWrappers.end())
274 return InvokeWrappers[Sig];
275
276 // Put the pointer to the callee as first argument
277 ArgTys.push_back(PointerType::getUnqual(CalleeFTy));
278 // Add argument types
279 ArgTys.append(CalleeFTy->param_begin(), CalleeFTy->param_end());
280
281 FunctionType *FTy = FunctionType::get(CalleeFTy->getReturnType(), ArgTys,
282 CalleeFTy->isVarArg());
283 Function *F = Function::Create(FTy, GlobalValue::ExternalLinkage,
Heejin Ahn10a70862016-09-01 00:44:37 +0000284 InvokePrefix + Sig, &M);
Derek Schufff41f67d2016-08-01 21:34:04 +0000285 InvokeWrappers[Sig] = F;
286 return F;
287}
288
Heejin Ahn10a70862016-09-01 00:44:37 +0000289bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
Heejin Ahn23d57102016-08-31 22:40:34 +0000290 LLVMContext &C = M.getContext();
291 IRBuilder<> IRB(C);
Heejin Ahn10a70862016-09-01 00:44:37 +0000292 IntegerType *Int1Ty = IRB.getInt1Ty();
293 PointerType *Int8PtrTy = IRB.getInt8PtrTy();
294 IntegerType *Int32Ty = IRB.getInt32Ty();
295 Type *VoidTy = IRB.getVoidTy();
Heejin Ahn23d57102016-08-31 22:40:34 +0000296
Heejin Ahn10a70862016-09-01 00:44:37 +0000297 // Create global variables __THREW__, threwValue, and tempRet0, which are
298 // used in common for both exception handling and setjmp/longjmp handling
299 ThrewGV =
300 new GlobalVariable(M, Int1Ty, false, GlobalValue::ExternalLinkage,
301 IRB.getFalse(), createGlobalValueName(M, ThrewGVName));
302 ThrewValueGV = new GlobalVariable(
303 M, Int32Ty, false, GlobalValue::ExternalLinkage, IRB.getInt32(0),
304 createGlobalValueName(M, ThrewValueGVName));
305 TempRet0GV = new GlobalVariable(M, Int32Ty, false,
306 GlobalValue::ExternalLinkage, IRB.getInt32(0),
307 createGlobalValueName(M, TempRet0GVName));
308
309 bool Changed = false;
310
311 // Exception handling
312 if (DoEH) {
313 // Register __resumeException function
314 FunctionType *ResumeFTy = FunctionType::get(VoidTy, Int8PtrTy, false);
315 ResumeF = Function::Create(ResumeFTy, GlobalValue::ExternalLinkage,
316 ResumeFName, &M);
317
318 // Register llvm_eh_typeid_for function
319 FunctionType *EHTypeIDTy = FunctionType::get(Int32Ty, Int8PtrTy, false);
320 EHTypeIDF = Function::Create(EHTypeIDTy, GlobalValue::ExternalLinkage,
321 EHTypeIDFName, &M);
322
323 for (Function &F : M) {
324 if (F.isDeclaration())
325 continue;
326 Changed |= runEHOnFunction(F);
327 }
328 }
329
330 // TODO: Run CFGSimplify like the emscripten JSBackend?
331
332 // Setjmp/longjmp handling
333 if (DoSjLj) {
334 for (Function &F : M) {
335 if (F.isDeclaration())
336 continue;
337 Changed |= runSjLjOnFunction(F);
338 }
339 }
340
341 if (!Changed)
342 return false;
343
344 // If we have made any changes while doing exception handling or
345 // setjmp/longjmp handling, we have to create these functions for JavaScript
346 // to call.
Heejin Ahn23d57102016-08-31 22:40:34 +0000347 assert(!M.getNamedGlobal(SetThrewFName) && "setThrew already exists");
Heejin Ahn10a70862016-09-01 00:44:37 +0000348 assert(!M.getNamedGlobal(SetTempRet0FName) && "setTempRet0 already exists");
349
350 // Create setThrew function
351 SmallVector<Type *, 2> Params = {Int1Ty, Int32Ty};
352 FunctionType *FTy = FunctionType::get(VoidTy, Params, false);
Derek Schufff41f67d2016-08-01 21:34:04 +0000353 Function *F =
Derek Schuffccdceda2016-08-18 15:27:25 +0000354 Function::Create(FTy, GlobalValue::ExternalLinkage, SetThrewFName, &M);
Derek Schufff41f67d2016-08-01 21:34:04 +0000355 Argument *Arg1 = &*(F->arg_begin());
356 Argument *Arg2 = &*(++F->arg_begin());
357 Arg1->setName("threw");
358 Arg2->setName("value");
Derek Schuff53b9af02016-08-09 00:29:55 +0000359 BasicBlock *EntryBB = BasicBlock::Create(C, "entry", F);
360 BasicBlock *ThenBB = BasicBlock::Create(C, "if.then", F);
361 BasicBlock *EndBB = BasicBlock::Create(C, "if.end", F);
Derek Schufff41f67d2016-08-01 21:34:04 +0000362
Derek Schuffccdceda2016-08-18 15:27:25 +0000363 IRB.SetInsertPoint(EntryBB);
364 Value *Threw = IRB.CreateLoad(ThrewGV, ThrewGV->getName() + ".val");
Heejin Ahn10a70862016-09-01 00:44:37 +0000365 Value *Cmp = IRB.CreateICmpEQ(Threw, IRB.getFalse(), "cmp");
Derek Schuffccdceda2016-08-18 15:27:25 +0000366 IRB.CreateCondBr(Cmp, ThenBB, EndBB);
Derek Schufff41f67d2016-08-01 21:34:04 +0000367
Derek Schuffccdceda2016-08-18 15:27:25 +0000368 IRB.SetInsertPoint(ThenBB);
369 IRB.CreateStore(Arg1, ThrewGV);
370 IRB.CreateStore(Arg2, ThrewValueGV);
371 IRB.CreateBr(EndBB);
Derek Schufff41f67d2016-08-01 21:34:04 +0000372
Derek Schuffccdceda2016-08-18 15:27:25 +0000373 IRB.SetInsertPoint(EndBB);
374 IRB.CreateRetVoid();
Derek Schufff41f67d2016-08-01 21:34:04 +0000375
Heejin Ahn10a70862016-09-01 00:44:37 +0000376 // Create setTempRet0 function
377 Params = {Int32Ty};
378 FTy = FunctionType::get(VoidTy, Params, false);
379 F = Function::Create(FTy, GlobalValue::ExternalLinkage, SetTempRet0FName, &M);
Derek Schufff41f67d2016-08-01 21:34:04 +0000380 F->arg_begin()->setName("value");
Heejin Ahn10a70862016-09-01 00:44:37 +0000381 EntryBB = BasicBlock::Create(C, "entry", F);
Derek Schuffccdceda2016-08-18 15:27:25 +0000382 IRB.SetInsertPoint(EntryBB);
383 IRB.CreateStore(&*F->arg_begin(), TempRet0GV);
384 IRB.CreateRetVoid();
Derek Schufff41f67d2016-08-01 21:34:04 +0000385
386 return true;
387}
388
Derek Schuffccdceda2016-08-18 15:27:25 +0000389bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) {
Derek Schufff41f67d2016-08-01 21:34:04 +0000390 Module &M = *F.getParent();
Derek Schuff53b9af02016-08-09 00:29:55 +0000391 LLVMContext &C = F.getContext();
Derek Schuffccdceda2016-08-18 15:27:25 +0000392 IRBuilder<> IRB(C);
Derek Schufff41f67d2016-08-01 21:34:04 +0000393 bool Changed = false;
394 SmallVector<Instruction *, 64> ToErase;
395 SmallPtrSet<LandingPadInst *, 32> LandingPads;
Derek Schuff66641322016-08-09 22:37:00 +0000396 bool AllowExceptions =
Derek Schuffccdceda2016-08-18 15:27:25 +0000397 areAllExceptionsAllowed() || EHWhitelistSet.count(F.getName());
Derek Schufff41f67d2016-08-01 21:34:04 +0000398
399 for (BasicBlock &BB : F) {
400 auto *II = dyn_cast<InvokeInst>(BB.getTerminator());
401 if (!II)
402 continue;
403 Changed = true;
404 LandingPads.insert(II->getLandingPadInst());
Derek Schuffccdceda2016-08-18 15:27:25 +0000405 IRB.SetInsertPoint(II);
Derek Schufff41f67d2016-08-01 21:34:04 +0000406
Derek Schuff53b9af02016-08-09 00:29:55 +0000407 bool NeedInvoke = AllowExceptions && canThrow(II->getCalledValue());
408 if (NeedInvoke) {
Heejin Ahn10a70862016-09-01 00:44:37 +0000409 // If we are calling a function that is noreturn, we must remove that
410 // attribute. The code we insert here does expect it to return, after we
411 // catch the exception.
412 if (II->doesNotReturn()) {
413 if (auto *F = dyn_cast<Function>(II->getCalledValue()))
414 F->removeFnAttr(Attribute::NoReturn);
415 AttributeSet NewAttrs = II->getAttributes();
416 NewAttrs.removeAttribute(C, AttributeSet::FunctionIndex,
417 Attribute::NoReturn);
418 II->setAttributes(NewAttrs);
419 }
420
421 // Pre-invoke
422 // __THREW__ = 0;
423 IRB.CreateStore(IRB.getFalse(), ThrewGV);
424
425 // Invoke function wrapper in JavaScript
426 SmallVector<Value *, 16> CallArgs;
427 // Put the pointer to the callee as first argument, so it can be called
428 // within the invoke wrapper later
429 CallArgs.push_back(II->getCalledValue());
430 CallArgs.append(II->arg_begin(), II->arg_end());
431 CallInst *NewCall = IRB.CreateCall(getInvokeWrapper(M, II), CallArgs);
432 NewCall->takeName(II);
433 NewCall->setCallingConv(II->getCallingConv());
434 NewCall->setDebugLoc(II->getDebugLoc());
435
436 // Because we added the pointer to the callee as first argument, all
437 // argument attribute indices have to be incremented by one.
438 SmallVector<AttributeSet, 8> AttributesVec;
439 const AttributeSet &InvokePAL = II->getAttributes();
440 CallSite::arg_iterator AI = II->arg_begin();
441 unsigned i = 1; // Argument attribute index starts from 1
442 for (unsigned e = II->getNumArgOperands(); i <= e; ++AI, ++i) {
443 if (InvokePAL.hasAttributes(i)) {
444 AttrBuilder B(InvokePAL, i);
445 AttributesVec.push_back(AttributeSet::get(C, i + 1, B));
446 }
447 }
448 // Add any return attributes.
449 if (InvokePAL.hasAttributes(AttributeSet::ReturnIndex))
450 AttributesVec.push_back(
451 AttributeSet::get(C, InvokePAL.getRetAttributes()));
452 // Add any function attributes.
453 if (InvokePAL.hasAttributes(AttributeSet::FunctionIndex))
454 AttributesVec.push_back(
455 AttributeSet::get(C, InvokePAL.getFnAttributes()));
456 // Reconstruct the AttributesList based on the vector we constructed.
457 AttributeSet NewCallPAL = AttributeSet::get(C, AttributesVec);
458 NewCall->setAttributes(NewCallPAL);
459
460 II->replaceAllUsesWith(NewCall);
Derek Schufff41f67d2016-08-01 21:34:04 +0000461 ToErase.push_back(II);
462
Heejin Ahn10a70862016-09-01 00:44:37 +0000463 // Post-invoke
464 // %__THREW__.val = __THREW__; __THREW__ = 0;
465 Value *Threw = IRB.CreateLoad(ThrewGV, ThrewGV->getName() + ".val");
466 IRB.CreateStore(IRB.getFalse(), ThrewGV);
467
Derek Schufff41f67d2016-08-01 21:34:04 +0000468 // Insert a branch based on __THREW__ variable
Heejin Ahn10a70862016-09-01 00:44:37 +0000469 IRB.CreateCondBr(Threw, II->getUnwindDest(), II->getNormalDest());
Derek Schufff41f67d2016-08-01 21:34:04 +0000470
471 } else {
472 // This can't throw, and we don't need this invoke, just replace it with a
473 // call+branch
Heejin Ahn10a70862016-09-01 00:44:37 +0000474 SmallVector<Value *, 16> CallArgs(II->arg_begin(), II->arg_end());
475 CallInst *NewCall = IRB.CreateCall(II->getCalledValue(), CallArgs);
Derek Schufff41f67d2016-08-01 21:34:04 +0000476 NewCall->takeName(II);
477 NewCall->setCallingConv(II->getCallingConv());
Derek Schufff41f67d2016-08-01 21:34:04 +0000478 NewCall->setDebugLoc(II->getDebugLoc());
Derek Schuff53b9af02016-08-09 00:29:55 +0000479 NewCall->setAttributes(II->getAttributes());
Derek Schufff41f67d2016-08-01 21:34:04 +0000480 II->replaceAllUsesWith(NewCall);
481 ToErase.push_back(II);
482
Derek Schuffccdceda2016-08-18 15:27:25 +0000483 IRB.CreateBr(II->getNormalDest());
Derek Schufff41f67d2016-08-01 21:34:04 +0000484
485 // Remove any PHI node entries from the exception destination
486 II->getUnwindDest()->removePredecessor(&BB);
487 }
488 }
489
490 // Process resume instructions
491 for (BasicBlock &BB : F) {
492 // Scan the body of the basic block for resumes
493 for (Instruction &I : BB) {
494 auto *RI = dyn_cast<ResumeInst>(&I);
495 if (!RI)
496 continue;
497
498 // Split the input into legal values
499 Value *Input = RI->getValue();
Derek Schuffccdceda2016-08-18 15:27:25 +0000500 IRB.SetInsertPoint(RI);
501 Value *Low = IRB.CreateExtractValue(Input, 0, "low");
Heejin Ahn10a70862016-09-01 00:44:37 +0000502
Derek Schuff53b9af02016-08-09 00:29:55 +0000503 // Create a call to __resumeException function
Heejin Ahn10a70862016-09-01 00:44:37 +0000504 Value *Args[] = {Low};
505 IRB.CreateCall(ResumeF, Args);
506
Derek Schufff41f67d2016-08-01 21:34:04 +0000507 // Add a terminator to the block
Derek Schuffccdceda2016-08-18 15:27:25 +0000508 IRB.CreateUnreachable();
Derek Schufff41f67d2016-08-01 21:34:04 +0000509 ToErase.push_back(RI);
510 }
511 }
512
Derek Schuff53b9af02016-08-09 00:29:55 +0000513 // Process llvm.eh.typeid.for intrinsics
514 for (BasicBlock &BB : F) {
515 for (Instruction &I : BB) {
516 auto *CI = dyn_cast<CallInst>(&I);
517 if (!CI)
518 continue;
519 const Function *Callee = CI->getCalledFunction();
520 if (!Callee)
521 continue;
522 if (Callee->getIntrinsicID() != Intrinsic::eh_typeid_for)
523 continue;
524
Derek Schuffccdceda2016-08-18 15:27:25 +0000525 IRB.SetInsertPoint(CI);
Derek Schuff53b9af02016-08-09 00:29:55 +0000526 CallInst *NewCI =
Derek Schuffccdceda2016-08-18 15:27:25 +0000527 IRB.CreateCall(EHTypeIDF, CI->getArgOperand(0), "typeid");
Derek Schuff53b9af02016-08-09 00:29:55 +0000528 CI->replaceAllUsesWith(NewCI);
529 ToErase.push_back(CI);
530 }
531 }
532
Derek Schufff41f67d2016-08-01 21:34:04 +0000533 // Look for orphan landingpads, can occur in blocks with no predecesors
534 for (BasicBlock &BB : F) {
535 Instruction *I = BB.getFirstNonPHI();
536 if (auto *LPI = dyn_cast<LandingPadInst>(I))
537 LandingPads.insert(LPI);
538 }
539
540 // Handle all the landingpad for this function together, as multiple invokes
541 // may share a single lp
542 for (LandingPadInst *LPI : LandingPads) {
Derek Schuffccdceda2016-08-18 15:27:25 +0000543 IRB.SetInsertPoint(LPI);
Derek Schufff41f67d2016-08-01 21:34:04 +0000544 SmallVector<Value *, 16> FMCArgs;
545 for (unsigned i = 0, e = LPI->getNumClauses(); i < e; ++i) {
546 Constant *Clause = LPI->getClause(i);
547 // As a temporary workaround for the lack of aggregate varargs support
548 // in the interface between JS and wasm, break out filter operands into
549 // their component elements.
550 if (LPI->isFilter(i)) {
Heejin Ahn10a70862016-09-01 00:44:37 +0000551 ArrayType *ATy = cast<ArrayType>(Clause->getType());
Derek Schufff41f67d2016-08-01 21:34:04 +0000552 for (unsigned j = 0, e = ATy->getNumElements(); j < e; ++j) {
Derek Schuffccdceda2016-08-18 15:27:25 +0000553 Value *EV = IRB.CreateExtractValue(Clause, makeArrayRef(j), "filter");
Derek Schufff41f67d2016-08-01 21:34:04 +0000554 FMCArgs.push_back(EV);
555 }
556 } else
557 FMCArgs.push_back(Clause);
558 }
559
Derek Schuff53b9af02016-08-09 00:29:55 +0000560 // Create a call to __cxa_find_matching_catch_N function
Derek Schufff41f67d2016-08-01 21:34:04 +0000561 Function *FMCF = getFindMatchingCatch(M, FMCArgs.size());
Derek Schuffccdceda2016-08-18 15:27:25 +0000562 CallInst *FMCI = IRB.CreateCall(FMCF, FMCArgs, "fmc");
Derek Schufff41f67d2016-08-01 21:34:04 +0000563 Value *Undef = UndefValue::get(LPI->getType());
Derek Schuffccdceda2016-08-18 15:27:25 +0000564 Value *Pair0 = IRB.CreateInsertValue(Undef, FMCI, 0, "pair0");
Heejin Ahn10a70862016-09-01 00:44:37 +0000565 Value *TempRet0 = IRB.CreateLoad(TempRet0GV, TempRet0GV->getName() + "val");
Derek Schuffccdceda2016-08-18 15:27:25 +0000566 Value *Pair1 = IRB.CreateInsertValue(Pair0, TempRet0, 1, "pair1");
Derek Schufff41f67d2016-08-01 21:34:04 +0000567
568 LPI->replaceAllUsesWith(Pair1);
569 ToErase.push_back(LPI);
570 }
571
572 // Erase everything we no longer need in this function
573 for (Instruction *I : ToErase)
574 I->eraseFromParent();
575
576 return Changed;
577}
Derek Schuffccdceda2016-08-18 15:27:25 +0000578
579bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
Heejin Ahn10a70862016-09-01 00:44:37 +0000580 // TODO
581 return false;
Derek Schuffccdceda2016-08-18 15:27:25 +0000582}