Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 1 | //=== WebAssemblyLowerEmscriptenEHSjLj.cpp - Lower exceptions for Emscripten =// |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 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 | /// \file |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 11 | /// \brief This file lowers exception-related instructions in order to use |
| 12 | /// Emscripten's JavaScript try and catch mechanism to handle exceptions. |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 13 | /// |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 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: |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 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 | /// |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 24 | /// This pass does following things: |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 25 | /// |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 26 | /// 1) Create three global variables: __THREW__, __threwValue, and tempRet0. |
| 27 | /// tempRet0 will be set within __cxa_find_matching_catch() function in |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 28 | /// JS library, and __THREW__ and __threwValue will be set in invoke wrappers |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 29 | /// in JS glue code. For what invoke wrappers are, refer to 3). |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 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 Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 41 | /// __threwValue = value; |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 42 | /// } |
| 43 | /// } |
| 44 | /// |
| 45 | /// function setTempRet0(value) { |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 46 | /// tempRet0 = value; |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 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 Schuff | 53b9af0 | 2016-08-09 00:29:55 +0000 | [diff] [blame] | 80 | /// %fmc = call @__cxa_find_matching_catch_N(c1, c2, c3, ...) |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 81 | /// %val = {%fmc, tempRet0} |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 82 | /// ... use %val ... |
| 83 | /// Here N is a number calculated based on the number of clauses. |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 84 | /// Global variable tempRet0 is set within __cxa_find_matching_catch() in |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 85 | /// JS glue code. |
| 86 | /// |
| 87 | /// 5) Lower |
| 88 | /// resume {%a, %b} |
| 89 | /// into |
Derek Schuff | 53b9af0 | 2016-08-09 00:29:55 +0000 | [diff] [blame] | 90 | /// call @__resumeException(%a) |
| 91 | /// where __resumeException() is a function in JS glue code. |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 92 | /// |
Derek Schuff | 53b9af0 | 2016-08-09 00:29:55 +0000 | [diff] [blame] | 93 | /// 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 Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 98 | /// |
| 99 | ///===----------------------------------------------------------------------===// |
| 100 | |
| 101 | #include "WebAssembly.h" |
Derek Schuff | 53b9af0 | 2016-08-09 00:29:55 +0000 | [diff] [blame] | 102 | #include "llvm/IR/CallSite.h" |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 103 | #include "llvm/IR/IRBuilder.h" |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 104 | #include "llvm/IR/Module.h" |
| 105 | #include "llvm/Support/raw_ostream.h" |
| 106 | #include <set> |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 107 | |
| 108 | using namespace llvm; |
| 109 | |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 110 | #define DEBUG_TYPE "wasm-lower-em-ehsjlj" |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 111 | |
Derek Schuff | 6664132 | 2016-08-09 22:37:00 +0000 | [diff] [blame] | 112 | static cl::list<std::string> |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 113 | 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 Schuff | 6664132 | 2016-08-09 22:37:00 +0000 | [diff] [blame] | 118 | |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 119 | namespace { |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 120 | class 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 Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 131 | bool DoEH; // Enable exception handling |
| 132 | bool DoSjLj; // Enable setjmp/longjmp handling |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 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 Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 147 | const char *getPassName() const override { |
| 148 | return "WebAssembly Lower Emscripten Exceptions"; |
| 149 | } |
| 150 | |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 151 | bool runEHOnFunction(Function &F); |
| 152 | bool runSjLjOnFunction(Function &F); |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 153 | // 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 Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 158 | Function *getFindMatchingCatch(Module &M, unsigned NumClauses); |
| 159 | |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 160 | Function *getInvokeWrapper(Module &M, InvokeInst *II); |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 161 | bool areAllExceptionsAllowed() const { return EHWhitelistSet.empty(); } |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 162 | |
| 163 | public: |
| 164 | static char ID; |
| 165 | |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 166 | 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 Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 170 | EHWhitelistSet.insert(EHWhitelist.begin(), EHWhitelist.end()); |
Derek Schuff | 6664132 | 2016-08-09 22:37:00 +0000 | [diff] [blame] | 171 | } |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 172 | bool runOnModule(Module &M) override; |
| 173 | }; |
| 174 | } // End anonymous namespace |
| 175 | |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 176 | const char *WebAssemblyLowerEmscriptenEHSjLj::ThrewGVName = "__THREW__"; |
| 177 | const char *WebAssemblyLowerEmscriptenEHSjLj::ThrewValueGVName = "__threwValue"; |
| 178 | const char *WebAssemblyLowerEmscriptenEHSjLj::TempRet0GVName = "__tempRet0"; |
| 179 | const char *WebAssemblyLowerEmscriptenEHSjLj::ResumeFName = "__resumeException"; |
| 180 | const char *WebAssemblyLowerEmscriptenEHSjLj::EHTypeIDFName = |
| 181 | "llvm_eh_typeid_for"; |
| 182 | const char *WebAssemblyLowerEmscriptenEHSjLj::SetThrewFName = "setThrew"; |
| 183 | const char *WebAssemblyLowerEmscriptenEHSjLj::SetTempRet0FName = "setTempRet0"; |
| 184 | const char *WebAssemblyLowerEmscriptenEHSjLj::FindMatchingCatchPrefix = |
| 185 | "__cxa_find_matching_catch_"; |
| 186 | const char *WebAssemblyLowerEmscriptenEHSjLj::InvokePrefix = "__invoke_"; |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 187 | |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 188 | char WebAssemblyLowerEmscriptenEHSjLj::ID = 0; |
| 189 | INITIALIZE_PASS(WebAssemblyLowerEmscriptenEHSjLj, DEBUG_TYPE, |
| 190 | "WebAssembly Lower Emscripten Exceptions / Setjmp / Longjmp", |
| 191 | false, false) |
| 192 | |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 193 | ModulePass *llvm::createWebAssemblyLowerEmscriptenEHSjLj(bool DoEH, |
| 194 | bool DoSjLj) { |
| 195 | return new WebAssemblyLowerEmscriptenEHSjLj(DoEH, DoSjLj); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 196 | } |
| 197 | |
| 198 | static 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 Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 207 | return true; |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 208 | } |
Heejin Ahn | b6cd512 | 2016-08-24 22:53:00 +0000 | [diff] [blame] | 209 | // not a function, so an indirect call - can throw, we can't tell |
| 210 | return true; |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 211 | } |
| 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. |
| 216 | static 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 Schuff | 53b9af0 | 2016-08-09 00:29:55 +0000 | [diff] [blame] | 226 | // 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 Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 229 | static 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 Majnemer | c700490 | 2016-08-12 04:32:37 +0000 | [diff] [blame] | 238 | Sig.erase(remove_if(Sig, isspace), Sig.end()); |
Derek Schuff | 53b9af0 | 2016-08-09 00:29:55 +0000 | [diff] [blame] | 239 | // 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 Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 242 | return Sig; |
| 243 | } |
| 244 | |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 245 | Function * |
| 246 | WebAssemblyLowerEmscriptenEHSjLj::getFindMatchingCatch(Module &M, |
| 247 | unsigned NumClauses) { |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 248 | 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 Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 253 | Function *F = |
| 254 | Function::Create(FTy, GlobalValue::ExternalLinkage, |
| 255 | FindMatchingCatchPrefix + Twine(NumClauses + 2), &M); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 256 | FindMatchingCatches[NumClauses] = F; |
| 257 | return F; |
| 258 | } |
| 259 | |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 260 | Function *WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper(Module &M, |
| 261 | InvokeInst *II) { |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 262 | SmallVector<Type *, 16> ArgTys; |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 263 | Value *Callee = II->getCalledValue(); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 264 | FunctionType *CalleeFTy; |
| 265 | if (auto *F = dyn_cast<Function>(Callee)) |
| 266 | CalleeFTy = F->getFunctionType(); |
| 267 | else { |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 268 | auto *CalleeTy = dyn_cast<PointerType>(Callee->getType())->getElementType(); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 269 | 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 Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 284 | InvokePrefix + Sig, &M); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 285 | InvokeWrappers[Sig] = F; |
| 286 | return F; |
| 287 | } |
| 288 | |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 289 | bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { |
Heejin Ahn | 23d5710 | 2016-08-31 22:40:34 +0000 | [diff] [blame] | 290 | LLVMContext &C = M.getContext(); |
| 291 | IRBuilder<> IRB(C); |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 292 | IntegerType *Int1Ty = IRB.getInt1Ty(); |
| 293 | PointerType *Int8PtrTy = IRB.getInt8PtrTy(); |
| 294 | IntegerType *Int32Ty = IRB.getInt32Ty(); |
| 295 | Type *VoidTy = IRB.getVoidTy(); |
Heejin Ahn | 23d5710 | 2016-08-31 22:40:34 +0000 | [diff] [blame] | 296 | |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 297 | // 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 Ahn | 23d5710 | 2016-08-31 22:40:34 +0000 | [diff] [blame] | 347 | assert(!M.getNamedGlobal(SetThrewFName) && "setThrew already exists"); |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 348 | 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 Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 353 | Function *F = |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 354 | Function::Create(FTy, GlobalValue::ExternalLinkage, SetThrewFName, &M); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 355 | Argument *Arg1 = &*(F->arg_begin()); |
| 356 | Argument *Arg2 = &*(++F->arg_begin()); |
| 357 | Arg1->setName("threw"); |
| 358 | Arg2->setName("value"); |
Derek Schuff | 53b9af0 | 2016-08-09 00:29:55 +0000 | [diff] [blame] | 359 | 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 Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 362 | |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 363 | IRB.SetInsertPoint(EntryBB); |
| 364 | Value *Threw = IRB.CreateLoad(ThrewGV, ThrewGV->getName() + ".val"); |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 365 | Value *Cmp = IRB.CreateICmpEQ(Threw, IRB.getFalse(), "cmp"); |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 366 | IRB.CreateCondBr(Cmp, ThenBB, EndBB); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 367 | |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 368 | IRB.SetInsertPoint(ThenBB); |
| 369 | IRB.CreateStore(Arg1, ThrewGV); |
| 370 | IRB.CreateStore(Arg2, ThrewValueGV); |
| 371 | IRB.CreateBr(EndBB); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 372 | |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 373 | IRB.SetInsertPoint(EndBB); |
| 374 | IRB.CreateRetVoid(); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 375 | |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 376 | // Create setTempRet0 function |
| 377 | Params = {Int32Ty}; |
| 378 | FTy = FunctionType::get(VoidTy, Params, false); |
| 379 | F = Function::Create(FTy, GlobalValue::ExternalLinkage, SetTempRet0FName, &M); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 380 | F->arg_begin()->setName("value"); |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 381 | EntryBB = BasicBlock::Create(C, "entry", F); |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 382 | IRB.SetInsertPoint(EntryBB); |
| 383 | IRB.CreateStore(&*F->arg_begin(), TempRet0GV); |
| 384 | IRB.CreateRetVoid(); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 385 | |
| 386 | return true; |
| 387 | } |
| 388 | |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 389 | bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) { |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 390 | Module &M = *F.getParent(); |
Derek Schuff | 53b9af0 | 2016-08-09 00:29:55 +0000 | [diff] [blame] | 391 | LLVMContext &C = F.getContext(); |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 392 | IRBuilder<> IRB(C); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 393 | bool Changed = false; |
| 394 | SmallVector<Instruction *, 64> ToErase; |
| 395 | SmallPtrSet<LandingPadInst *, 32> LandingPads; |
Derek Schuff | 6664132 | 2016-08-09 22:37:00 +0000 | [diff] [blame] | 396 | bool AllowExceptions = |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 397 | areAllExceptionsAllowed() || EHWhitelistSet.count(F.getName()); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 398 | |
| 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 Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 405 | IRB.SetInsertPoint(II); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 406 | |
Derek Schuff | 53b9af0 | 2016-08-09 00:29:55 +0000 | [diff] [blame] | 407 | bool NeedInvoke = AllowExceptions && canThrow(II->getCalledValue()); |
| 408 | if (NeedInvoke) { |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 409 | // 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 Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 461 | ToErase.push_back(II); |
| 462 | |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 463 | // 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 Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 468 | // Insert a branch based on __THREW__ variable |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 469 | IRB.CreateCondBr(Threw, II->getUnwindDest(), II->getNormalDest()); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 470 | |
| 471 | } else { |
| 472 | // This can't throw, and we don't need this invoke, just replace it with a |
| 473 | // call+branch |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 474 | SmallVector<Value *, 16> CallArgs(II->arg_begin(), II->arg_end()); |
| 475 | CallInst *NewCall = IRB.CreateCall(II->getCalledValue(), CallArgs); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 476 | NewCall->takeName(II); |
| 477 | NewCall->setCallingConv(II->getCallingConv()); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 478 | NewCall->setDebugLoc(II->getDebugLoc()); |
Derek Schuff | 53b9af0 | 2016-08-09 00:29:55 +0000 | [diff] [blame] | 479 | NewCall->setAttributes(II->getAttributes()); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 480 | II->replaceAllUsesWith(NewCall); |
| 481 | ToErase.push_back(II); |
| 482 | |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 483 | IRB.CreateBr(II->getNormalDest()); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 484 | |
| 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 Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 500 | IRB.SetInsertPoint(RI); |
| 501 | Value *Low = IRB.CreateExtractValue(Input, 0, "low"); |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 502 | |
Derek Schuff | 53b9af0 | 2016-08-09 00:29:55 +0000 | [diff] [blame] | 503 | // Create a call to __resumeException function |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 504 | Value *Args[] = {Low}; |
| 505 | IRB.CreateCall(ResumeF, Args); |
| 506 | |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 507 | // Add a terminator to the block |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 508 | IRB.CreateUnreachable(); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 509 | ToErase.push_back(RI); |
| 510 | } |
| 511 | } |
| 512 | |
Derek Schuff | 53b9af0 | 2016-08-09 00:29:55 +0000 | [diff] [blame] | 513 | // 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 Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 525 | IRB.SetInsertPoint(CI); |
Derek Schuff | 53b9af0 | 2016-08-09 00:29:55 +0000 | [diff] [blame] | 526 | CallInst *NewCI = |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 527 | IRB.CreateCall(EHTypeIDF, CI->getArgOperand(0), "typeid"); |
Derek Schuff | 53b9af0 | 2016-08-09 00:29:55 +0000 | [diff] [blame] | 528 | CI->replaceAllUsesWith(NewCI); |
| 529 | ToErase.push_back(CI); |
| 530 | } |
| 531 | } |
| 532 | |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 533 | // 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 Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 543 | IRB.SetInsertPoint(LPI); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 544 | 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 Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 551 | ArrayType *ATy = cast<ArrayType>(Clause->getType()); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 552 | for (unsigned j = 0, e = ATy->getNumElements(); j < e; ++j) { |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 553 | Value *EV = IRB.CreateExtractValue(Clause, makeArrayRef(j), "filter"); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 554 | FMCArgs.push_back(EV); |
| 555 | } |
| 556 | } else |
| 557 | FMCArgs.push_back(Clause); |
| 558 | } |
| 559 | |
Derek Schuff | 53b9af0 | 2016-08-09 00:29:55 +0000 | [diff] [blame] | 560 | // Create a call to __cxa_find_matching_catch_N function |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 561 | Function *FMCF = getFindMatchingCatch(M, FMCArgs.size()); |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 562 | CallInst *FMCI = IRB.CreateCall(FMCF, FMCArgs, "fmc"); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 563 | Value *Undef = UndefValue::get(LPI->getType()); |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 564 | Value *Pair0 = IRB.CreateInsertValue(Undef, FMCI, 0, "pair0"); |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 565 | Value *TempRet0 = IRB.CreateLoad(TempRet0GV, TempRet0GV->getName() + "val"); |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 566 | Value *Pair1 = IRB.CreateInsertValue(Pair0, TempRet0, 1, "pair1"); |
Derek Schuff | f41f67d | 2016-08-01 21:34:04 +0000 | [diff] [blame] | 567 | |
| 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 Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 578 | |
| 579 | bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { |
Heejin Ahn | 10a7086 | 2016-09-01 00:44:37 +0000 | [diff] [blame] | 580 | // TODO |
| 581 | return false; |
Derek Schuff | ccdceda | 2016-08-18 15:27:25 +0000 | [diff] [blame] | 582 | } |