blob: 8512b20770bbd5747879835442694b2669bb29b1 [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
11/// \brief This file lowers exception-related instructions in order to use
12/// Emscripten's JavaScript try and catch mechanism to handle exceptions.
13///
14/// 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:
19/// (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///
24/// This pass does following things:
25///
Derek Schuffccdceda2016-08-18 15:27:25 +000026/// 1) Create three global variables: __THREW__, __threwValue, and tempRet0.
Derek Schuff53b9af02016-08-09 00:29:55 +000027/// 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
Derek Schufff41f67d2016-08-01 21:34:04 +000029/// in JS glue code. For what invoke wrappers are, refer to 3).
30///
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) {
46/// tempRet0 = value;
47/// }
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, ...)
Derek Schufff41f67d2016-08-01 21:34:04 +000081/// %val = {%fmc, tempRet0}
82/// ... use %val ...
83/// Here N is a number calculated based on the number of clauses.
Derek Schuff53b9af02016-08-09 00:29:55 +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"
Derek Schufff41f67d2016-08-01 21:34:04 +0000104#include "llvm/IR/Module.h"
Derek Schufff41f67d2016-08-01 21:34:04 +0000105#include "llvm/Support/raw_ostream.h"
Derek Schuff53b9af02016-08-09 00:29:55 +0000106#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
131 bool DoEH; // Enable exception handling
132 bool DoSjLj; // Enable setjmp/longjmp handling
133
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);
Derek Schuff53b9af02016-08-09 00:29:55 +0000153 // Returns __cxa_find_matching_catch_N function, where N = NumClauses + 2.
Derek Schufff41f67d2016-08-01 21:34:04 +0000154 // This is because a landingpad instruction contains two more arguments,
Derek Schuff53b9af02016-08-09 00:29:55 +0000155 // a personality function and a cleanup bit, and __cxa_find_matching_catch_N
Derek Schufff41f67d2016-08-01 21:34:04 +0000156 // functions are named after the number of arguments in the original
157 // landingpad instruction.
158 Function *getFindMatchingCatch(Module &M, unsigned NumClauses);
159
160 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
Derek Schuffccdceda2016-08-18 15:27:25 +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) {
170 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
193ModulePass *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;
207 return true;
208 }
209 return true; // not a function, so an indirect call - can throw, we can't tell
210}
211
212// Returns an available name for a global value.
213// If the proposed name already exists in the module, adds '_' at the end of
214// the name until the name is available.
215static inline std::string createGlobalValueName(const Module &M,
216 const std::string &Propose) {
217 std::string Name = Propose;
218 while (M.getNamedGlobal(Name))
219 Name += "_";
220 return Name;
221}
222
223// Simple function name mangler.
224// This function simply takes LLVM's string representation of parameter types
Derek Schuff53b9af02016-08-09 00:29:55 +0000225// and concatenate them with '_'. There are non-alphanumeric characters but llc
226// is ok with it, and we need to postprocess these names after the lowering
227// phase anyway.
Derek Schufff41f67d2016-08-01 21:34:04 +0000228static std::string getSignature(FunctionType *FTy) {
229 std::string Sig;
230 raw_string_ostream OS(Sig);
231 OS << *FTy->getReturnType();
232 for (Type *ParamTy : FTy->params())
233 OS << "_" << *ParamTy;
234 if (FTy->isVarArg())
235 OS << "_...";
236 Sig = OS.str();
David Majnemerc7004902016-08-12 04:32:37 +0000237 Sig.erase(remove_if(Sig, isspace), Sig.end());
Derek Schuff53b9af02016-08-09 00:29:55 +0000238 // When s2wasm parses .s file, a comma means the end of an argument. So a
239 // mangled function name can contain any character but a comma.
240 std::replace(Sig.begin(), Sig.end(), ',', '.');
Derek Schufff41f67d2016-08-01 21:34:04 +0000241 return Sig;
242}
243
Derek Schuffccdceda2016-08-18 15:27:25 +0000244Function *
245WebAssemblyLowerEmscriptenEHSjLj::getFindMatchingCatch(Module &M,
246 unsigned NumClauses) {
Derek Schufff41f67d2016-08-01 21:34:04 +0000247 if (FindMatchingCatches.count(NumClauses))
248 return FindMatchingCatches[NumClauses];
249 PointerType *Int8PtrTy = Type::getInt8PtrTy(M.getContext());
250 SmallVector<Type *, 16> Args(NumClauses, Int8PtrTy);
251 FunctionType *FTy = FunctionType::get(Int8PtrTy, Args, false);
Derek Schuffccdceda2016-08-18 15:27:25 +0000252 Function *F =
253 Function::Create(FTy, GlobalValue::ExternalLinkage,
254 FindMatchingCatchPrefix + Twine(NumClauses + 2), &M);
Derek Schufff41f67d2016-08-01 21:34:04 +0000255 FindMatchingCatches[NumClauses] = F;
256 return F;
257}
258
Derek Schuffccdceda2016-08-18 15:27:25 +0000259Function *WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper(Module &M,
260 InvokeInst *II) {
Derek Schufff41f67d2016-08-01 21:34:04 +0000261 SmallVector<Type *, 16> ArgTys;
262 Value *Callee = II->getCalledValue();
263 FunctionType *CalleeFTy;
264 if (auto *F = dyn_cast<Function>(Callee))
265 CalleeFTy = F->getFunctionType();
266 else {
267 auto *CalleeTy = dyn_cast<PointerType>(Callee->getType())->getElementType();
268 CalleeFTy = dyn_cast<FunctionType>(CalleeTy);
269 }
270
271 std::string Sig = getSignature(CalleeFTy);
272 if (InvokeWrappers.find(Sig) != InvokeWrappers.end())
273 return InvokeWrappers[Sig];
274
275 // Put the pointer to the callee as first argument
276 ArgTys.push_back(PointerType::getUnqual(CalleeFTy));
277 // Add argument types
278 ArgTys.append(CalleeFTy->param_begin(), CalleeFTy->param_end());
279
280 FunctionType *FTy = FunctionType::get(CalleeFTy->getReturnType(), ArgTys,
281 CalleeFTy->isVarArg());
282 Function *F = Function::Create(FTy, GlobalValue::ExternalLinkage,
Derek Schuffccdceda2016-08-18 15:27:25 +0000283 InvokePrefix + Sig, &M);
Derek Schufff41f67d2016-08-01 21:34:04 +0000284 InvokeWrappers[Sig] = F;
285 return F;
286}
287
Derek Schuffccdceda2016-08-18 15:27:25 +0000288bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
Derek Schuff53b9af02016-08-09 00:29:55 +0000289 LLVMContext &C = M.getContext();
Derek Schuffccdceda2016-08-18 15:27:25 +0000290 IRBuilder<> IRB(C);
291 IntegerType *Int1Ty = IRB.getInt1Ty();
292 PointerType *Int8PtrTy = IRB.getInt8PtrTy();
293 IntegerType *Int32Ty = IRB.getInt32Ty();
294 Type *VoidTy = IRB.getVoidTy();
Derek Schufff41f67d2016-08-01 21:34:04 +0000295
Derek Schuffccdceda2016-08-18 15:27:25 +0000296 // Create global variables __THREW__, threwValue, and tempRet0, which are
297 // used in common for both exception handling and setjmp/longjmp handling
298 ThrewGV =
299 new GlobalVariable(M, Int1Ty, false, GlobalValue::ExternalLinkage,
300 IRB.getFalse(), createGlobalValueName(M, ThrewGVName));
Derek Schufff41f67d2016-08-01 21:34:04 +0000301 ThrewValueGV = new GlobalVariable(
Derek Schuffccdceda2016-08-18 15:27:25 +0000302 M, Int32Ty, false, GlobalValue::ExternalLinkage, IRB.getInt32(0),
303 createGlobalValueName(M, ThrewValueGVName));
304 TempRet0GV = new GlobalVariable(M, Int32Ty, false,
305 GlobalValue::ExternalLinkage, IRB.getInt32(0),
306 createGlobalValueName(M, TempRet0GVName));
Derek Schufff41f67d2016-08-01 21:34:04 +0000307
308 bool Changed = false;
Derek Schuffccdceda2016-08-18 15:27:25 +0000309
310 // Exception handling
311 if (DoEH) {
312 // Register __resumeException function
313 FunctionType *ResumeFTy = FunctionType::get(VoidTy, Int8PtrTy, false);
314 ResumeF = Function::Create(ResumeFTy, GlobalValue::ExternalLinkage,
315 ResumeFName, &M);
316
317 // Register llvm_eh_typeid_for function
318 FunctionType *EHTypeIDTy = FunctionType::get(Int32Ty, Int8PtrTy, false);
319 EHTypeIDF = Function::Create(EHTypeIDTy, GlobalValue::ExternalLinkage,
320 EHTypeIDFName, &M);
321
322 for (Function &F : M) {
323 if (F.isDeclaration())
324 continue;
325 Changed |= runEHOnFunction(F);
326 }
327 }
328
329 // TODO: Run CFGSimplify like the emscripten JSBackend?
330
331 // Setjmp/longjmp handling
332 if (DoSjLj) {
333 for (Function &F : M) {
334 if (F.isDeclaration())
335 continue;
336 Changed |= runSjLjOnFunction(F);
337 }
Derek Schufff41f67d2016-08-01 21:34:04 +0000338 }
339
340 if (!Changed)
341 return false;
342
Derek Schuffccdceda2016-08-18 15:27:25 +0000343 // If we have made any changes while doing exception handling or
344 // setjmp/longjmp handling, we have to create these functions for JavaScript
345 // to call.
346 assert(!M.getNamedGlobal(SetThrewFName) && "setThrew already exists");
347 assert(!M.getNamedGlobal(SetTempRet0FName) && "setTempRet0 already exists");
Derek Schufff41f67d2016-08-01 21:34:04 +0000348
349 // Create setThrew function
350 SmallVector<Type *, 2> Params = {Int1Ty, Int32Ty};
351 FunctionType *FTy = FunctionType::get(VoidTy, Params, false);
352 Function *F =
Derek Schuffccdceda2016-08-18 15:27:25 +0000353 Function::Create(FTy, GlobalValue::ExternalLinkage, SetThrewFName, &M);
Derek Schufff41f67d2016-08-01 21:34:04 +0000354 Argument *Arg1 = &*(F->arg_begin());
355 Argument *Arg2 = &*(++F->arg_begin());
356 Arg1->setName("threw");
357 Arg2->setName("value");
Derek Schuff53b9af02016-08-09 00:29:55 +0000358 BasicBlock *EntryBB = BasicBlock::Create(C, "entry", F);
359 BasicBlock *ThenBB = BasicBlock::Create(C, "if.then", F);
360 BasicBlock *EndBB = BasicBlock::Create(C, "if.end", F);
Derek Schufff41f67d2016-08-01 21:34:04 +0000361
Derek Schuffccdceda2016-08-18 15:27:25 +0000362 IRB.SetInsertPoint(EntryBB);
363 Value *Threw = IRB.CreateLoad(ThrewGV, ThrewGV->getName() + ".val");
364 Value *Cmp = IRB.CreateICmpEQ(Threw, IRB.getFalse(), "cmp");
365 IRB.CreateCondBr(Cmp, ThenBB, EndBB);
Derek Schufff41f67d2016-08-01 21:34:04 +0000366
Derek Schuffccdceda2016-08-18 15:27:25 +0000367 IRB.SetInsertPoint(ThenBB);
368 IRB.CreateStore(Arg1, ThrewGV);
369 IRB.CreateStore(Arg2, ThrewValueGV);
370 IRB.CreateBr(EndBB);
Derek Schufff41f67d2016-08-01 21:34:04 +0000371
Derek Schuffccdceda2016-08-18 15:27:25 +0000372 IRB.SetInsertPoint(EndBB);
373 IRB.CreateRetVoid();
Derek Schufff41f67d2016-08-01 21:34:04 +0000374
375 // Create setTempRet0 function
376 Params = {Int32Ty};
377 FTy = FunctionType::get(VoidTy, Params, false);
Derek Schuffccdceda2016-08-18 15:27:25 +0000378 F = Function::Create(FTy, GlobalValue::ExternalLinkage, SetTempRet0FName, &M);
Derek Schufff41f67d2016-08-01 21:34:04 +0000379 F->arg_begin()->setName("value");
Derek Schuff53b9af02016-08-09 00:29:55 +0000380 EntryBB = BasicBlock::Create(C, "entry", F);
Derek Schuffccdceda2016-08-18 15:27:25 +0000381 IRB.SetInsertPoint(EntryBB);
382 IRB.CreateStore(&*F->arg_begin(), TempRet0GV);
383 IRB.CreateRetVoid();
Derek Schufff41f67d2016-08-01 21:34:04 +0000384
385 return true;
386}
387
Derek Schuffccdceda2016-08-18 15:27:25 +0000388bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) {
Derek Schufff41f67d2016-08-01 21:34:04 +0000389 Module &M = *F.getParent();
Derek Schuff53b9af02016-08-09 00:29:55 +0000390 LLVMContext &C = F.getContext();
Derek Schuffccdceda2016-08-18 15:27:25 +0000391 IRBuilder<> IRB(C);
Derek Schufff41f67d2016-08-01 21:34:04 +0000392 bool Changed = false;
393 SmallVector<Instruction *, 64> ToErase;
394 SmallPtrSet<LandingPadInst *, 32> LandingPads;
Derek Schuff66641322016-08-09 22:37:00 +0000395 bool AllowExceptions =
Derek Schuffccdceda2016-08-18 15:27:25 +0000396 areAllExceptionsAllowed() || EHWhitelistSet.count(F.getName());
Derek Schufff41f67d2016-08-01 21:34:04 +0000397
398 for (BasicBlock &BB : F) {
399 auto *II = dyn_cast<InvokeInst>(BB.getTerminator());
400 if (!II)
401 continue;
402 Changed = true;
403 LandingPads.insert(II->getLandingPadInst());
Derek Schuffccdceda2016-08-18 15:27:25 +0000404 IRB.SetInsertPoint(II);
Derek Schufff41f67d2016-08-01 21:34:04 +0000405
Derek Schuff53b9af02016-08-09 00:29:55 +0000406 bool NeedInvoke = AllowExceptions && canThrow(II->getCalledValue());
407 if (NeedInvoke) {
Derek Schufff41f67d2016-08-01 21:34:04 +0000408 // If we are calling a function that is noreturn, we must remove that
409 // attribute. The code we insert here does expect it to return, after we
410 // catch the exception.
411 if (II->doesNotReturn()) {
412 if (auto *F = dyn_cast<Function>(II->getCalledValue()))
413 F->removeFnAttr(Attribute::NoReturn);
414 AttributeSet NewAttrs = II->getAttributes();
Derek Schuff53b9af02016-08-09 00:29:55 +0000415 NewAttrs.removeAttribute(C, AttributeSet::FunctionIndex,
Derek Schufff41f67d2016-08-01 21:34:04 +0000416 Attribute::NoReturn);
417 II->setAttributes(NewAttrs);
418 }
419
420 // Pre-invoke
421 // __THREW__ = 0;
Derek Schuffccdceda2016-08-18 15:27:25 +0000422 IRB.CreateStore(IRB.getFalse(), ThrewGV);
Derek Schufff41f67d2016-08-01 21:34:04 +0000423
424 // Invoke function wrapper in JavaScript
425 SmallVector<Value *, 16> CallArgs;
426 // Put the pointer to the callee as first argument, so it can be called
427 // within the invoke wrapper later
428 CallArgs.push_back(II->getCalledValue());
429 CallArgs.append(II->arg_begin(), II->arg_end());
Derek Schuffccdceda2016-08-18 15:27:25 +0000430 CallInst *NewCall = IRB.CreateCall(getInvokeWrapper(M, II), CallArgs);
Derek Schufff41f67d2016-08-01 21:34:04 +0000431 NewCall->takeName(II);
432 NewCall->setCallingConv(II->getCallingConv());
Derek Schufff41f67d2016-08-01 21:34:04 +0000433 NewCall->setDebugLoc(II->getDebugLoc());
Derek Schuff53b9af02016-08-09 00:29:55 +0000434
435 // Because we added the pointer to the callee as first argument, all
436 // argument attribute indices have to be incremented by one.
437 SmallVector<AttributeSet, 8> AttributesVec;
438 const AttributeSet &InvokePAL = II->getAttributes();
439 CallSite::arg_iterator AI = II->arg_begin();
440 unsigned i = 1; // Argument attribute index starts from 1
441 for (unsigned e = II->getNumArgOperands(); i <= e; ++AI, ++i) {
442 if (InvokePAL.hasAttributes(i)) {
443 AttrBuilder B(InvokePAL, i);
444 AttributesVec.push_back(AttributeSet::get(C, i + 1, B));
445 }
446 }
447 // Add any return attributes.
448 if (InvokePAL.hasAttributes(AttributeSet::ReturnIndex))
449 AttributesVec.push_back(
450 AttributeSet::get(C, InvokePAL.getRetAttributes()));
451 // Add any function attributes.
452 if (InvokePAL.hasAttributes(AttributeSet::FunctionIndex))
453 AttributesVec.push_back(
454 AttributeSet::get(C, InvokePAL.getFnAttributes()));
455 // Reconstruct the AttributesList based on the vector we constructed.
456 AttributeSet NewCallPAL = AttributeSet::get(C, AttributesVec);
457 NewCall->setAttributes(NewCallPAL);
458
Derek Schufff41f67d2016-08-01 21:34:04 +0000459 II->replaceAllUsesWith(NewCall);
460 ToErase.push_back(II);
461
462 // Post-invoke
463 // %__THREW__.val = __THREW__; __THREW__ = 0;
Derek Schuffccdceda2016-08-18 15:27:25 +0000464 Value *Threw = IRB.CreateLoad(ThrewGV, ThrewGV->getName() + ".val");
465 IRB.CreateStore(IRB.getFalse(), ThrewGV);
Derek Schufff41f67d2016-08-01 21:34:04 +0000466
467 // Insert a branch based on __THREW__ variable
Derek Schuffccdceda2016-08-18 15:27:25 +0000468 IRB.CreateCondBr(Threw, II->getUnwindDest(), II->getNormalDest());
Derek Schufff41f67d2016-08-01 21:34:04 +0000469
470 } else {
471 // This can't throw, and we don't need this invoke, just replace it with a
472 // call+branch
473 SmallVector<Value *, 16> CallArgs(II->arg_begin(), II->arg_end());
Derek Schuffccdceda2016-08-18 15:27:25 +0000474 CallInst *NewCall = IRB.CreateCall(II->getCalledValue(), CallArgs);
Derek Schufff41f67d2016-08-01 21:34:04 +0000475 NewCall->takeName(II);
476 NewCall->setCallingConv(II->getCallingConv());
Derek Schufff41f67d2016-08-01 21:34:04 +0000477 NewCall->setDebugLoc(II->getDebugLoc());
Derek Schuff53b9af02016-08-09 00:29:55 +0000478 NewCall->setAttributes(II->getAttributes());
Derek Schufff41f67d2016-08-01 21:34:04 +0000479 II->replaceAllUsesWith(NewCall);
480 ToErase.push_back(II);
481
Derek Schuffccdceda2016-08-18 15:27:25 +0000482 IRB.CreateBr(II->getNormalDest());
Derek Schufff41f67d2016-08-01 21:34:04 +0000483
484 // Remove any PHI node entries from the exception destination
485 II->getUnwindDest()->removePredecessor(&BB);
486 }
487 }
488
489 // Process resume instructions
490 for (BasicBlock &BB : F) {
491 // Scan the body of the basic block for resumes
492 for (Instruction &I : BB) {
493 auto *RI = dyn_cast<ResumeInst>(&I);
494 if (!RI)
495 continue;
496
497 // Split the input into legal values
498 Value *Input = RI->getValue();
Derek Schuffccdceda2016-08-18 15:27:25 +0000499 IRB.SetInsertPoint(RI);
500 Value *Low = IRB.CreateExtractValue(Input, 0, "low");
Derek Schufff41f67d2016-08-01 21:34:04 +0000501
Derek Schuff53b9af02016-08-09 00:29:55 +0000502 // Create a call to __resumeException function
Derek Schufff41f67d2016-08-01 21:34:04 +0000503 Value *Args[] = {Low};
Derek Schuffccdceda2016-08-18 15:27:25 +0000504 IRB.CreateCall(ResumeF, Args);
Derek Schufff41f67d2016-08-01 21:34:04 +0000505
506 // Add a terminator to the block
Derek Schuffccdceda2016-08-18 15:27:25 +0000507 IRB.CreateUnreachable();
Derek Schufff41f67d2016-08-01 21:34:04 +0000508 ToErase.push_back(RI);
509 }
510 }
511
Derek Schuff53b9af02016-08-09 00:29:55 +0000512 // Process llvm.eh.typeid.for intrinsics
513 for (BasicBlock &BB : F) {
514 for (Instruction &I : BB) {
515 auto *CI = dyn_cast<CallInst>(&I);
516 if (!CI)
517 continue;
518 const Function *Callee = CI->getCalledFunction();
519 if (!Callee)
520 continue;
521 if (Callee->getIntrinsicID() != Intrinsic::eh_typeid_for)
522 continue;
523
Derek Schuffccdceda2016-08-18 15:27:25 +0000524 IRB.SetInsertPoint(CI);
Derek Schuff53b9af02016-08-09 00:29:55 +0000525 CallInst *NewCI =
Derek Schuffccdceda2016-08-18 15:27:25 +0000526 IRB.CreateCall(EHTypeIDF, CI->getArgOperand(0), "typeid");
Derek Schuff53b9af02016-08-09 00:29:55 +0000527 CI->replaceAllUsesWith(NewCI);
528 ToErase.push_back(CI);
529 }
530 }
531
Derek Schufff41f67d2016-08-01 21:34:04 +0000532 // Look for orphan landingpads, can occur in blocks with no predecesors
533 for (BasicBlock &BB : F) {
534 Instruction *I = BB.getFirstNonPHI();
535 if (auto *LPI = dyn_cast<LandingPadInst>(I))
536 LandingPads.insert(LPI);
537 }
538
539 // Handle all the landingpad for this function together, as multiple invokes
540 // may share a single lp
541 for (LandingPadInst *LPI : LandingPads) {
Derek Schuffccdceda2016-08-18 15:27:25 +0000542 IRB.SetInsertPoint(LPI);
Derek Schufff41f67d2016-08-01 21:34:04 +0000543 SmallVector<Value *, 16> FMCArgs;
544 for (unsigned i = 0, e = LPI->getNumClauses(); i < e; ++i) {
545 Constant *Clause = LPI->getClause(i);
546 // As a temporary workaround for the lack of aggregate varargs support
547 // in the interface between JS and wasm, break out filter operands into
548 // their component elements.
549 if (LPI->isFilter(i)) {
550 ArrayType *ATy = cast<ArrayType>(Clause->getType());
551 for (unsigned j = 0, e = ATy->getNumElements(); j < e; ++j) {
Derek Schuffccdceda2016-08-18 15:27:25 +0000552 Value *EV = IRB.CreateExtractValue(Clause, makeArrayRef(j), "filter");
Derek Schufff41f67d2016-08-01 21:34:04 +0000553 FMCArgs.push_back(EV);
554 }
555 } else
556 FMCArgs.push_back(Clause);
557 }
558
Derek Schuff53b9af02016-08-09 00:29:55 +0000559 // Create a call to __cxa_find_matching_catch_N function
Derek Schufff41f67d2016-08-01 21:34:04 +0000560 Function *FMCF = getFindMatchingCatch(M, FMCArgs.size());
Derek Schuffccdceda2016-08-18 15:27:25 +0000561 CallInst *FMCI = IRB.CreateCall(FMCF, FMCArgs, "fmc");
Derek Schufff41f67d2016-08-01 21:34:04 +0000562 Value *Undef = UndefValue::get(LPI->getType());
Derek Schuffccdceda2016-08-18 15:27:25 +0000563 Value *Pair0 = IRB.CreateInsertValue(Undef, FMCI, 0, "pair0");
564 Value *TempRet0 = IRB.CreateLoad(TempRet0GV, TempRet0GV->getName() + "val");
565 Value *Pair1 = IRB.CreateInsertValue(Pair0, TempRet0, 1, "pair1");
Derek Schufff41f67d2016-08-01 21:34:04 +0000566
567 LPI->replaceAllUsesWith(Pair1);
568 ToErase.push_back(LPI);
569 }
570
571 // Erase everything we no longer need in this function
572 for (Instruction *I : ToErase)
573 I->eraseFromParent();
574
575 return Changed;
576}
Derek Schuffccdceda2016-08-18 15:27:25 +0000577
578bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
579 // TODO
580 return false;
581}