1 //=== WebAssemblyLowerEmscriptenEHSjLj.cpp - Lower exceptions for Emscripten =// 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 11 /// This file lowers exception-related instructions and setjmp/longjmp 12 /// function calls in order to use Emscripten's JavaScript try and catch 13 /// mechanism. 14 /// 15 /// To handle exceptions and setjmp/longjmps, this scheme relies on JavaScript's 16 /// try and catch syntax and relevant exception-related libraries implemented 17 /// in JavaScript glue code that will be produced by Emscripten. This is similar 18 /// to the current Emscripten asm.js exception handling in fastcomp. For 19 /// fastcomp's EH / SjLj scheme, see these files in fastcomp LLVM branch: 20 /// (Location: https://github.com/kripken/emscripten-fastcomp) 21 /// lib/Target/JSBackend/NaCl/LowerEmExceptionsPass.cpp 22 /// lib/Target/JSBackend/NaCl/LowerEmSetjmp.cpp 23 /// lib/Target/JSBackend/JSBackend.cpp 24 /// lib/Target/JSBackend/CallHandlers.h 25 /// 26 /// * Exception handling 27 /// This pass lowers invokes and landingpads into library functions in JS glue 28 /// code. Invokes are lowered into function wrappers called invoke wrappers that 29 /// exist in JS side, which wraps the original function call with JS try-catch. 30 /// If an exception occurred, cxa_throw() function in JS side sets some 31 /// variables (see below) so we can check whether an exception occurred from 32 /// wasm code and handle it appropriately. 33 /// 34 /// * Setjmp-longjmp handling 35 /// This pass lowers setjmp to a reasonably-performant approach for emscripten. 36 /// The idea is that each block with a setjmp is broken up into two parts: the 37 /// part containing setjmp and the part right after the setjmp. The latter part 38 /// is either reached from the setjmp, or later from a longjmp. To handle the 39 /// longjmp, all calls that might longjmp are also called using invoke wrappers 40 /// and thus JS / try-catch. JS longjmp() function also sets some variables so 41 /// we can check / whether a longjmp occurred from wasm code. Each block with a 42 /// function call that might longjmp is also split up after the longjmp call. 43 /// After the longjmp call, we check whether a longjmp occurred, and if it did, 44 /// which setjmp it corresponds to, and jump to the right post-setjmp block. 45 /// We assume setjmp-longjmp handling always run after EH handling, which means 46 /// we don't expect any exception-related instructions when SjLj runs. 47 /// FIXME Currently this scheme does not support indirect call of setjmp, 48 /// because of the limitation of the scheme itself. fastcomp does not support it 49 /// either. 50 /// 51 /// In detail, this pass does following things: 52 /// 53 /// 1) Assumes the existence of global variables: __THREW__, __threwValue, and 54 /// __tempRet0. 55 /// __tempRet0 will be set within __cxa_find_matching_catch() function in 56 /// JS library, and __THREW__ and __threwValue will be set in invoke wrappers 57 /// in JS glue code. For what invoke wrappers are, refer to 3). These 58 /// variables are used for both exceptions and setjmp/longjmps. 59 /// __THREW__ indicates whether an exception or a longjmp occurred or not. 0 60 /// means nothing occurred, 1 means an exception occurred, and other numbers 61 /// mean a longjmp occurred. In the case of longjmp, __threwValue variable 62 /// indicates the corresponding setjmp buffer the longjmp corresponds to. 63 /// In exception handling, __tempRet0 indicates the type of an exception 64 /// caught, and in setjmp/longjmp, it means the second argument to longjmp 65 /// function. 66 /// 67 /// * Exception handling 68 /// 69 /// 2) We assume the existence of setThrew and setTempRet0 functions at link 70 /// time. 71 /// The global variables in 1) will exist in wasm address space, 72 /// but their values should be set in JS code, so these functions 73 /// as interfaces to JS glue code. These functions are equivalent to the 74 /// following JS functions, which actually exist in asm.js version of JS 75 /// library. 76 /// 77 /// function setThrew(threw, value) { 78 /// if (__THREW__ == 0) { 79 /// __THREW__ = threw; 80 /// __threwValue = value; 81 /// } 82 /// } 83 /// 84 /// function setTempRet0(value) { 85 /// __tempRet0 = value; 86 /// } 87 /// 88 /// 3) Lower 89 /// invoke @func(arg1, arg2) to label %invoke.cont unwind label %lpad 90 /// into 91 /// __THREW__ = 0; 92 /// call @__invoke_SIG(func, arg1, arg2) 93 /// %__THREW__.val = __THREW__; 94 /// __THREW__ = 0; 95 /// if (%__THREW__.val == 1) 96 /// goto %lpad 97 /// else 98 /// goto %invoke.cont 99 /// SIG is a mangled string generated based on the LLVM IR-level function 100 /// signature. After LLVM IR types are lowered to the target wasm types, 101 /// the names for these wrappers will change based on wasm types as well, 102 /// as in invoke_vi (function takes an int and returns void). The bodies of 103 /// these wrappers will be generated in JS glue code, and inside those 104 /// wrappers we use JS try-catch to generate actual exception effects. It 105 /// also calls the original callee function. An example wrapper in JS code 106 /// would look like this: 107 /// function invoke_vi(index,a1) { 108 /// try { 109 /// Module["dynCall_vi"](index,a1); // This calls original callee 110 /// } catch(e) { 111 /// if (typeof e !== 'number' && e !== 'longjmp') throw e; 112 /// asm["setThrew"](1, 0); // setThrew is called here 113 /// } 114 /// } 115 /// If an exception is thrown, __THREW__ will be set to true in a wrapper, 116 /// so we can jump to the right BB based on this value. 117 /// 118 /// 4) Lower 119 /// %val = landingpad catch c1 catch c2 catch c3 ... 120 /// ... use %val ... 121 /// into 122 /// %fmc = call @__cxa_find_matching_catch_N(c1, c2, c3, ...) 123 /// %val = {%fmc, __tempRet0} 124 /// ... use %val ... 125 /// Here N is a number calculated based on the number of clauses. 126 /// Global variable __tempRet0 is set within __cxa_find_matching_catch() in 127 /// JS glue code. 128 /// 129 /// 5) Lower 130 /// resume {%a, %b} 131 /// into 132 /// call @__resumeException(%a) 133 /// where __resumeException() is a function in JS glue code. 134 /// 135 /// 6) Lower 136 /// call @llvm.eh.typeid.for(type) (intrinsic) 137 /// into 138 /// call @llvm_eh_typeid_for(type) 139 /// llvm_eh_typeid_for function will be generated in JS glue code. 140 /// 141 /// * Setjmp / Longjmp handling 142 /// 143 /// In case calls to longjmp() exists 144 /// 145 /// 1) Lower 146 /// longjmp(buf, value) 147 /// into 148 /// emscripten_longjmp_jmpbuf(buf, value) 149 /// emscripten_longjmp_jmpbuf will be lowered to emscripten_longjmp later. 150 /// 151 /// In case calls to setjmp() exists 152 /// 153 /// 2) In the function entry that calls setjmp, initialize setjmpTable and 154 /// sejmpTableSize as follows: 155 /// setjmpTableSize = 4; 156 /// setjmpTable = (int *) malloc(40); 157 /// setjmpTable[0] = 0; 158 /// setjmpTable and setjmpTableSize are used in saveSetjmp() function in JS 159 /// code. 160 /// 161 /// 3) Lower 162 /// setjmp(buf) 163 /// into 164 /// setjmpTable = saveSetjmp(buf, label, setjmpTable, setjmpTableSize); 165 /// setjmpTableSize = __tempRet0; 166 /// For each dynamic setjmp call, setjmpTable stores its ID (a number which 167 /// is incrementally assigned from 0) and its label (a unique number that 168 /// represents each callsite of setjmp). When we need more entries in 169 /// setjmpTable, it is reallocated in saveSetjmp() in JS code and it will 170 /// return the new table address, and assign the new table size in 171 /// __tempRet0. saveSetjmp also stores the setjmp's ID into the buffer buf. 172 /// A BB with setjmp is split into two after setjmp call in order to make the 173 /// post-setjmp BB the possible destination of longjmp BB. 174 /// 175 /// 176 /// 4) Lower every call that might longjmp into 177 /// __THREW__ = 0; 178 /// call @__invoke_SIG(func, arg1, arg2) 179 /// %__THREW__.val = __THREW__; 180 /// __THREW__ = 0; 181 /// if (%__THREW__.val != 0 & __threwValue != 0) { 182 /// %label = testSetjmp(mem[%__THREW__.val], setjmpTable, 183 /// setjmpTableSize); 184 /// if (%label == 0) 185 /// emscripten_longjmp(%__THREW__.val, __threwValue); 186 /// __tempRet0 = __threwValue; 187 /// } else { 188 /// %label = -1; 189 /// } 190 /// longjmp_result = __tempRet0; 191 /// switch label { 192 /// label 1: goto post-setjmp BB 1 193 /// label 2: goto post-setjmp BB 2 194 /// ... 195 /// default: goto splitted next BB 196 /// } 197 /// testSetjmp examines setjmpTable to see if there is a matching setjmp 198 /// call. After calling an invoke wrapper, if a longjmp occurred, __THREW__ 199 /// will be the address of matching jmp_buf buffer and __threwValue be the 200 /// second argument to longjmp. mem[__THREW__.val] is a setjmp ID that is 201 /// stored in saveSetjmp. testSetjmp returns a setjmp label, a unique ID to 202 /// each setjmp callsite. Label 0 means this longjmp buffer does not 203 /// correspond to one of the setjmp callsites in this function, so in this 204 /// case we just chain the longjmp to the caller. (Here we call 205 /// emscripten_longjmp, which is different from emscripten_longjmp_jmpbuf. 206 /// emscripten_longjmp_jmpbuf takes jmp_buf as its first argument, while 207 /// emscripten_longjmp takes an int. Both of them will eventually be lowered 208 /// to emscripten_longjmp in s2wasm, but here we need two signatures - we 209 /// can't translate an int value to a jmp_buf.) 210 /// Label -1 means no longjmp occurred. Otherwise we jump to the right 211 /// post-setjmp BB based on the label. 212 /// 213 ///===----------------------------------------------------------------------===// 214 215 #include "WebAssembly.h" 216 #include "llvm/IR/CallSite.h" 217 #include "llvm/IR/Dominators.h" 218 #include "llvm/IR/IRBuilder.h" 219 #include "llvm/Transforms/Utils/BasicBlockUtils.h" 220 #include "llvm/Transforms/Utils/SSAUpdater.h" 221 222 using namespace llvm; 223 224 #define DEBUG_TYPE "wasm-lower-em-ehsjlj" 225 226 static cl::list<std::string> 227 EHWhitelist("emscripten-cxx-exceptions-whitelist", 228 cl::desc("The list of function names in which Emscripten-style " 229 "exception handling is enabled (see emscripten " 230 "EMSCRIPTEN_CATCHING_WHITELIST options)"), 231 cl::CommaSeparated); 232 233 namespace { 234 class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass { 235 static const char *ResumeFName; 236 static const char *EHTypeIDFName; 237 static const char *EmLongjmpFName; 238 static const char *EmLongjmpJmpbufFName; 239 static const char *SaveSetjmpFName; 240 static const char *TestSetjmpFName; 241 static const char *FindMatchingCatchPrefix; 242 static const char *InvokePrefix; 243 244 bool EnableEH; // Enable exception handling 245 bool EnableSjLj; // Enable setjmp/longjmp handling 246 247 GlobalVariable *ThrewGV; 248 GlobalVariable *ThrewValueGV; 249 GlobalVariable *TempRet0GV; 250 Function *ResumeF; 251 Function *EHTypeIDF; 252 Function *EmLongjmpF; 253 Function *EmLongjmpJmpbufF; 254 Function *SaveSetjmpF; 255 Function *TestSetjmpF; 256 257 // __cxa_find_matching_catch_N functions. 258 // Indexed by the number of clauses in an original landingpad instruction. 259 DenseMap<int, Function *> FindMatchingCatches; 260 // Map of <function signature string, invoke_ wrappers> 261 StringMap<Function *> InvokeWrappers; 262 // Set of whitelisted function names for exception handling 263 std::set<std::string> EHWhitelistSet; 264 265 StringRef getPassName() const override { 266 return "WebAssembly Lower Emscripten Exceptions"; 267 } 268 269 bool runEHOnFunction(Function &F); 270 bool runSjLjOnFunction(Function &F); 271 Function *getFindMatchingCatch(Module &M, unsigned NumClauses); 272 273 template <typename CallOrInvoke> Value *wrapInvoke(CallOrInvoke *CI); 274 void wrapTestSetjmp(BasicBlock *BB, Instruction *InsertPt, Value *Threw, 275 Value *SetjmpTable, Value *SetjmpTableSize, Value *&Label, 276 Value *&LongjmpResult, BasicBlock *&EndBB); 277 template <typename CallOrInvoke> Function *getInvokeWrapper(CallOrInvoke *CI); 278 279 bool areAllExceptionsAllowed() const { return EHWhitelistSet.empty(); } 280 bool canLongjmp(Module &M, const Value *Callee) const; 281 282 void rebuildSSA(Function &F); 283 284 public: 285 static char ID; 286 287 WebAssemblyLowerEmscriptenEHSjLj(bool EnableEH = true, bool EnableSjLj = true) 288 : ModulePass(ID), EnableEH(EnableEH), EnableSjLj(EnableSjLj), 289 ThrewGV(nullptr), ThrewValueGV(nullptr), TempRet0GV(nullptr), 290 ResumeF(nullptr), EHTypeIDF(nullptr), EmLongjmpF(nullptr), 291 EmLongjmpJmpbufF(nullptr), SaveSetjmpF(nullptr), TestSetjmpF(nullptr) { 292 EHWhitelistSet.insert(EHWhitelist.begin(), EHWhitelist.end()); 293 } 294 bool runOnModule(Module &M) override; 295 296 void getAnalysisUsage(AnalysisUsage &AU) const override { 297 AU.addRequired<DominatorTreeWrapperPass>(); 298 } 299 }; 300 } // End anonymous namespace 301 302 const char *WebAssemblyLowerEmscriptenEHSjLj::ResumeFName = "__resumeException"; 303 const char *WebAssemblyLowerEmscriptenEHSjLj::EHTypeIDFName = 304 "llvm_eh_typeid_for"; 305 const char *WebAssemblyLowerEmscriptenEHSjLj::EmLongjmpFName = 306 "emscripten_longjmp"; 307 const char *WebAssemblyLowerEmscriptenEHSjLj::EmLongjmpJmpbufFName = 308 "emscripten_longjmp_jmpbuf"; 309 const char *WebAssemblyLowerEmscriptenEHSjLj::SaveSetjmpFName = "saveSetjmp"; 310 const char *WebAssemblyLowerEmscriptenEHSjLj::TestSetjmpFName = "testSetjmp"; 311 const char *WebAssemblyLowerEmscriptenEHSjLj::FindMatchingCatchPrefix = 312 "__cxa_find_matching_catch_"; 313 const char *WebAssemblyLowerEmscriptenEHSjLj::InvokePrefix = "__invoke_"; 314 315 char WebAssemblyLowerEmscriptenEHSjLj::ID = 0; 316 INITIALIZE_PASS(WebAssemblyLowerEmscriptenEHSjLj, DEBUG_TYPE, 317 "WebAssembly Lower Emscripten Exceptions / Setjmp / Longjmp", 318 false, false) 319 320 ModulePass *llvm::createWebAssemblyLowerEmscriptenEHSjLj(bool EnableEH, 321 bool EnableSjLj) { 322 return new WebAssemblyLowerEmscriptenEHSjLj(EnableEH, EnableSjLj); 323 } 324 325 static bool canThrow(const Value *V) { 326 if (const auto *F = dyn_cast<const Function>(V)) { 327 // Intrinsics cannot throw 328 if (F->isIntrinsic()) 329 return false; 330 StringRef Name = F->getName(); 331 // leave setjmp and longjmp (mostly) alone, we process them properly later 332 if (Name == "setjmp" || Name == "longjmp") 333 return false; 334 return !F->doesNotThrow(); 335 } 336 // not a function, so an indirect call - can throw, we can't tell 337 return true; 338 } 339 340 // Get a global variable with the given name. If it doesn't exist declare it, 341 // which will generate an import and asssumes that it will exist at link time. 342 static GlobalVariable *getGlobalVariableI32(Module &M, IRBuilder<> &IRB, 343 const char *Name) { 344 if (M.getNamedGlobal(Name)) 345 report_fatal_error(Twine("variable name is reserved: ") + Name); 346 347 return new GlobalVariable(M, IRB.getInt32Ty(), false, 348 GlobalValue::ExternalLinkage, nullptr, Name); 349 } 350 351 // Simple function name mangler. 352 // This function simply takes LLVM's string representation of parameter types 353 // and concatenate them with '_'. There are non-alphanumeric characters but llc 354 // is ok with it, and we need to postprocess these names after the lowering 355 // phase anyway. 356 static std::string getSignature(FunctionType *FTy) { 357 std::string Sig; 358 raw_string_ostream OS(Sig); 359 OS << *FTy->getReturnType(); 360 for (Type *ParamTy : FTy->params()) 361 OS << "_" << *ParamTy; 362 if (FTy->isVarArg()) 363 OS << "_..."; 364 Sig = OS.str(); 365 Sig.erase(remove_if(Sig, isspace), Sig.end()); 366 // When s2wasm parses .s file, a comma means the end of an argument. So a 367 // mangled function name can contain any character but a comma. 368 std::replace(Sig.begin(), Sig.end(), ',', '.'); 369 return Sig; 370 } 371 372 // Returns __cxa_find_matching_catch_N function, where N = NumClauses + 2. 373 // This is because a landingpad instruction contains two more arguments, a 374 // personality function and a cleanup bit, and __cxa_find_matching_catch_N 375 // functions are named after the number of arguments in the original landingpad 376 // instruction. 377 Function * 378 WebAssemblyLowerEmscriptenEHSjLj::getFindMatchingCatch(Module &M, 379 unsigned NumClauses) { 380 if (FindMatchingCatches.count(NumClauses)) 381 return FindMatchingCatches[NumClauses]; 382 PointerType *Int8PtrTy = Type::getInt8PtrTy(M.getContext()); 383 SmallVector<Type *, 16> Args(NumClauses, Int8PtrTy); 384 FunctionType *FTy = FunctionType::get(Int8PtrTy, Args, false); 385 Function *F = 386 Function::Create(FTy, GlobalValue::ExternalLinkage, 387 FindMatchingCatchPrefix + Twine(NumClauses + 2), &M); 388 FindMatchingCatches[NumClauses] = F; 389 return F; 390 } 391 392 // Generate invoke wrapper seqence with preamble and postamble 393 // Preamble: 394 // __THREW__ = 0; 395 // Postamble: 396 // %__THREW__.val = __THREW__; __THREW__ = 0; 397 // Returns %__THREW__.val, which indicates whether an exception is thrown (or 398 // whether longjmp occurred), for future use. 399 template <typename CallOrInvoke> 400 Value *WebAssemblyLowerEmscriptenEHSjLj::wrapInvoke(CallOrInvoke *CI) { 401 LLVMContext &C = CI->getModule()->getContext(); 402 403 // If we are calling a function that is noreturn, we must remove that 404 // attribute. The code we insert here does expect it to return, after we 405 // catch the exception. 406 if (CI->doesNotReturn()) { 407 if (auto *F = dyn_cast<Function>(CI->getCalledValue())) 408 F->removeFnAttr(Attribute::NoReturn); 409 CI->removeAttribute(AttributeList::FunctionIndex, Attribute::NoReturn); 410 } 411 412 IRBuilder<> IRB(C); 413 IRB.SetInsertPoint(CI); 414 415 // Pre-invoke 416 // __THREW__ = 0; 417 IRB.CreateStore(IRB.getInt32(0), ThrewGV); 418 419 // Invoke function wrapper in JavaScript 420 SmallVector<Value *, 16> Args; 421 // Put the pointer to the callee as first argument, so it can be called 422 // within the invoke wrapper later 423 Args.push_back(CI->getCalledValue()); 424 Args.append(CI->arg_begin(), CI->arg_end()); 425 CallInst *NewCall = IRB.CreateCall(getInvokeWrapper(CI), Args); 426 NewCall->takeName(CI); 427 NewCall->setCallingConv(CI->getCallingConv()); 428 NewCall->setDebugLoc(CI->getDebugLoc()); 429 430 // Because we added the pointer to the callee as first argument, all 431 // argument attribute indices have to be incremented by one. 432 SmallVector<AttributeSet, 8> ArgAttributes; 433 const AttributeList &InvokeAL = CI->getAttributes(); 434 435 // No attributes for the callee pointer. 436 ArgAttributes.push_back(AttributeSet()); 437 // Copy the argument attributes from the original 438 for (unsigned i = 0, e = CI->getNumArgOperands(); i < e; ++i) 439 ArgAttributes.push_back(InvokeAL.getParamAttributes(i)); 440 441 // Reconstruct the AttributesList based on the vector we constructed. 442 AttributeList NewCallAL = 443 AttributeList::get(C, InvokeAL.getFnAttributes(), 444 InvokeAL.getRetAttributes(), ArgAttributes); 445 NewCall->setAttributes(NewCallAL); 446 447 CI->replaceAllUsesWith(NewCall); 448 449 // Post-invoke 450 // %__THREW__.val = __THREW__; __THREW__ = 0; 451 Value *Threw = IRB.CreateLoad(ThrewGV, ThrewGV->getName() + ".val"); 452 IRB.CreateStore(IRB.getInt32(0), ThrewGV); 453 return Threw; 454 } 455 456 // Get matching invoke wrapper based on callee signature 457 template <typename CallOrInvoke> 458 Function *WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper(CallOrInvoke *CI) { 459 Module *M = CI->getModule(); 460 SmallVector<Type *, 16> ArgTys; 461 Value *Callee = CI->getCalledValue(); 462 FunctionType *CalleeFTy; 463 if (auto *F = dyn_cast<Function>(Callee)) 464 CalleeFTy = F->getFunctionType(); 465 else { 466 auto *CalleeTy = cast<PointerType>(Callee->getType())->getElementType(); 467 CalleeFTy = dyn_cast<FunctionType>(CalleeTy); 468 } 469 470 std::string Sig = getSignature(CalleeFTy); 471 if (InvokeWrappers.find(Sig) != InvokeWrappers.end()) 472 return InvokeWrappers[Sig]; 473 474 // Put the pointer to the callee as first argument 475 ArgTys.push_back(PointerType::getUnqual(CalleeFTy)); 476 // Add argument types 477 ArgTys.append(CalleeFTy->param_begin(), CalleeFTy->param_end()); 478 479 FunctionType *FTy = FunctionType::get(CalleeFTy->getReturnType(), ArgTys, 480 CalleeFTy->isVarArg()); 481 Function *F = Function::Create(FTy, GlobalValue::ExternalLinkage, 482 InvokePrefix + Sig, M); 483 InvokeWrappers[Sig] = F; 484 return F; 485 } 486 487 bool WebAssemblyLowerEmscriptenEHSjLj::canLongjmp(Module &M, 488 const Value *Callee) const { 489 if (auto *CalleeF = dyn_cast<Function>(Callee)) 490 if (CalleeF->isIntrinsic()) 491 return false; 492 493 // The reason we include malloc/free here is to exclude the malloc/free 494 // calls generated in setjmp prep / cleanup routines. 495 Function *SetjmpF = M.getFunction("setjmp"); 496 Function *MallocF = M.getFunction("malloc"); 497 Function *FreeF = M.getFunction("free"); 498 if (Callee == SetjmpF || Callee == MallocF || Callee == FreeF) 499 return false; 500 501 // There are functions in JS glue code 502 if (Callee == ResumeF || Callee == EHTypeIDF || Callee == SaveSetjmpF || 503 Callee == TestSetjmpF) 504 return false; 505 506 // __cxa_find_matching_catch_N functions cannot longjmp 507 if (Callee->getName().startswith(FindMatchingCatchPrefix)) 508 return false; 509 510 // Exception-catching related functions 511 Function *BeginCatchF = M.getFunction("__cxa_begin_catch"); 512 Function *EndCatchF = M.getFunction("__cxa_end_catch"); 513 Function *AllocExceptionF = M.getFunction("__cxa_allocate_exception"); 514 Function *ThrowF = M.getFunction("__cxa_throw"); 515 Function *TerminateF = M.getFunction("__clang_call_terminate"); 516 if (Callee == BeginCatchF || Callee == EndCatchF || 517 Callee == AllocExceptionF || Callee == ThrowF || Callee == TerminateF) 518 return false; 519 520 // Otherwise we don't know 521 return true; 522 } 523 524 // Generate testSetjmp function call seqence with preamble and postamble. 525 // The code this generates is equivalent to the following JavaScript code: 526 // if (%__THREW__.val != 0 & threwValue != 0) { 527 // %label = _testSetjmp(mem[%__THREW__.val], setjmpTable, setjmpTableSize); 528 // if (%label == 0) 529 // emscripten_longjmp(%__THREW__.val, threwValue); 530 // __tempRet0 = threwValue; 531 // } else { 532 // %label = -1; 533 // } 534 // %longjmp_result = __tempRet0; 535 // 536 // As output parameters. returns %label, %longjmp_result, and the BB the last 537 // instruction (%longjmp_result = ...) is in. 538 void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp( 539 BasicBlock *BB, Instruction *InsertPt, Value *Threw, Value *SetjmpTable, 540 Value *SetjmpTableSize, Value *&Label, Value *&LongjmpResult, 541 BasicBlock *&EndBB) { 542 Function *F = BB->getParent(); 543 LLVMContext &C = BB->getModule()->getContext(); 544 IRBuilder<> IRB(C); 545 IRB.SetInsertPoint(InsertPt); 546 547 // if (%__THREW__.val != 0 & threwValue != 0) 548 IRB.SetInsertPoint(BB); 549 BasicBlock *ThenBB1 = BasicBlock::Create(C, "if.then1", F); 550 BasicBlock *ElseBB1 = BasicBlock::Create(C, "if.else1", F); 551 BasicBlock *EndBB1 = BasicBlock::Create(C, "if.end", F); 552 Value *ThrewCmp = IRB.CreateICmpNE(Threw, IRB.getInt32(0)); 553 Value *ThrewValue = 554 IRB.CreateLoad(ThrewValueGV, ThrewValueGV->getName() + ".val"); 555 Value *ThrewValueCmp = IRB.CreateICmpNE(ThrewValue, IRB.getInt32(0)); 556 Value *Cmp1 = IRB.CreateAnd(ThrewCmp, ThrewValueCmp, "cmp1"); 557 IRB.CreateCondBr(Cmp1, ThenBB1, ElseBB1); 558 559 // %label = _testSetjmp(mem[%__THREW__.val], _setjmpTable, _setjmpTableSize); 560 // if (%label == 0) 561 IRB.SetInsertPoint(ThenBB1); 562 BasicBlock *ThenBB2 = BasicBlock::Create(C, "if.then2", F); 563 BasicBlock *EndBB2 = BasicBlock::Create(C, "if.end2", F); 564 Value *ThrewInt = IRB.CreateIntToPtr(Threw, Type::getInt32PtrTy(C), 565 Threw->getName() + ".i32p"); 566 Value *LoadedThrew = 567 IRB.CreateLoad(ThrewInt, ThrewInt->getName() + ".loaded"); 568 Value *ThenLabel = IRB.CreateCall( 569 TestSetjmpF, {LoadedThrew, SetjmpTable, SetjmpTableSize}, "label"); 570 Value *Cmp2 = IRB.CreateICmpEQ(ThenLabel, IRB.getInt32(0)); 571 IRB.CreateCondBr(Cmp2, ThenBB2, EndBB2); 572 573 // emscripten_longjmp(%__THREW__.val, threwValue); 574 IRB.SetInsertPoint(ThenBB2); 575 IRB.CreateCall(EmLongjmpF, {Threw, ThrewValue}); 576 IRB.CreateUnreachable(); 577 578 // __tempRet0 = threwValue; 579 IRB.SetInsertPoint(EndBB2); 580 IRB.CreateStore(ThrewValue, TempRet0GV); 581 IRB.CreateBr(EndBB1); 582 583 IRB.SetInsertPoint(ElseBB1); 584 IRB.CreateBr(EndBB1); 585 586 // longjmp_result = __tempRet0; 587 IRB.SetInsertPoint(EndBB1); 588 PHINode *LabelPHI = IRB.CreatePHI(IRB.getInt32Ty(), 2, "label"); 589 LabelPHI->addIncoming(ThenLabel, EndBB2); 590 591 LabelPHI->addIncoming(IRB.getInt32(-1), ElseBB1); 592 593 // Output parameter assignment 594 Label = LabelPHI; 595 EndBB = EndBB1; 596 LongjmpResult = IRB.CreateLoad(TempRet0GV, "longjmp_result"); 597 } 598 599 void WebAssemblyLowerEmscriptenEHSjLj::rebuildSSA(Function &F) { 600 DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>(F).getDomTree(); 601 DT.recalculate(F); // CFG has been changed 602 SSAUpdater SSA; 603 for (BasicBlock &BB : F) { 604 for (Instruction &I : BB) { 605 for (auto UI = I.use_begin(), UE = I.use_end(); UI != UE;) { 606 Use &U = *UI; 607 ++UI; 608 SSA.Initialize(I.getType(), I.getName()); 609 SSA.AddAvailableValue(&BB, &I); 610 Instruction *User = cast<Instruction>(U.getUser()); 611 if (User->getParent() == &BB) 612 continue; 613 614 if (PHINode *UserPN = dyn_cast<PHINode>(User)) 615 if (UserPN->getIncomingBlock(U) == &BB) 616 continue; 617 618 if (DT.dominates(&I, User)) 619 continue; 620 SSA.RewriteUseAfterInsertions(U); 621 } 622 } 623 } 624 } 625 626 bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { 627 LLVMContext &C = M.getContext(); 628 IRBuilder<> IRB(C); 629 630 Function *SetjmpF = M.getFunction("setjmp"); 631 Function *LongjmpF = M.getFunction("longjmp"); 632 bool SetjmpUsed = SetjmpF && !SetjmpF->use_empty(); 633 bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty(); 634 bool DoSjLj = EnableSjLj && (SetjmpUsed || LongjmpUsed); 635 636 // Declare (or get) global variables __THREW__, __threwValue, and __tempRet0, 637 // which are used in common for both exception handling and setjmp/longjmp 638 // handling 639 ThrewGV = getGlobalVariableI32(M, IRB, "__THREW__"); 640 ThrewValueGV = getGlobalVariableI32(M, IRB, "__threwValue"); 641 TempRet0GV = getGlobalVariableI32(M, IRB, "__tempRet0"); 642 643 bool Changed = false; 644 645 // Exception handling 646 if (EnableEH) { 647 // Register __resumeException function 648 FunctionType *ResumeFTy = 649 FunctionType::get(IRB.getVoidTy(), IRB.getInt8PtrTy(), false); 650 ResumeF = Function::Create(ResumeFTy, GlobalValue::ExternalLinkage, 651 ResumeFName, &M); 652 653 // Register llvm_eh_typeid_for function 654 FunctionType *EHTypeIDTy = 655 FunctionType::get(IRB.getInt32Ty(), IRB.getInt8PtrTy(), false); 656 EHTypeIDF = Function::Create(EHTypeIDTy, GlobalValue::ExternalLinkage, 657 EHTypeIDFName, &M); 658 659 for (Function &F : M) { 660 if (F.isDeclaration()) 661 continue; 662 Changed |= runEHOnFunction(F); 663 } 664 } 665 666 // Setjmp/longjmp handling 667 if (DoSjLj) { 668 Changed = true; // We have setjmp or longjmp somewhere 669 670 if (LongjmpF) { 671 // Replace all uses of longjmp with emscripten_longjmp_jmpbuf, which is 672 // defined in JS code 673 EmLongjmpJmpbufF = Function::Create(LongjmpF->getFunctionType(), 674 GlobalValue::ExternalLinkage, 675 EmLongjmpJmpbufFName, &M); 676 677 LongjmpF->replaceAllUsesWith(EmLongjmpJmpbufF); 678 } 679 680 if (SetjmpF) { 681 // Register saveSetjmp function 682 FunctionType *SetjmpFTy = SetjmpF->getFunctionType(); 683 SmallVector<Type *, 4> Params = {SetjmpFTy->getParamType(0), 684 IRB.getInt32Ty(), Type::getInt32PtrTy(C), 685 IRB.getInt32Ty()}; 686 FunctionType *FTy = 687 FunctionType::get(Type::getInt32PtrTy(C), Params, false); 688 SaveSetjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage, 689 SaveSetjmpFName, &M); 690 691 // Register testSetjmp function 692 Params = {IRB.getInt32Ty(), Type::getInt32PtrTy(C), IRB.getInt32Ty()}; 693 FTy = FunctionType::get(IRB.getInt32Ty(), Params, false); 694 TestSetjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage, 695 TestSetjmpFName, &M); 696 697 FTy = FunctionType::get(IRB.getVoidTy(), 698 {IRB.getInt32Ty(), IRB.getInt32Ty()}, false); 699 EmLongjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage, 700 EmLongjmpFName, &M); 701 702 // Only traverse functions that uses setjmp in order not to insert 703 // unnecessary prep / cleanup code in every function 704 SmallPtrSet<Function *, 8> SetjmpUsers; 705 for (User *U : SetjmpF->users()) { 706 auto *UI = cast<Instruction>(U); 707 SetjmpUsers.insert(UI->getFunction()); 708 } 709 for (Function *F : SetjmpUsers) 710 runSjLjOnFunction(*F); 711 } 712 } 713 714 if (!Changed) { 715 // Delete unused global variables and functions 716 if (ResumeF) 717 ResumeF->eraseFromParent(); 718 if (EHTypeIDF) 719 EHTypeIDF->eraseFromParent(); 720 if (EmLongjmpF) 721 EmLongjmpF->eraseFromParent(); 722 if (SaveSetjmpF) 723 SaveSetjmpF->eraseFromParent(); 724 if (TestSetjmpF) 725 TestSetjmpF->eraseFromParent(); 726 return false; 727 } 728 729 return true; 730 } 731 732 bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) { 733 Module &M = *F.getParent(); 734 LLVMContext &C = F.getContext(); 735 IRBuilder<> IRB(C); 736 bool Changed = false; 737 SmallVector<Instruction *, 64> ToErase; 738 SmallPtrSet<LandingPadInst *, 32> LandingPads; 739 bool AllowExceptions = 740 areAllExceptionsAllowed() || EHWhitelistSet.count(F.getName()); 741 742 for (BasicBlock &BB : F) { 743 auto *II = dyn_cast<InvokeInst>(BB.getTerminator()); 744 if (!II) 745 continue; 746 Changed = true; 747 LandingPads.insert(II->getLandingPadInst()); 748 IRB.SetInsertPoint(II); 749 750 bool NeedInvoke = AllowExceptions && canThrow(II->getCalledValue()); 751 if (NeedInvoke) { 752 // Wrap invoke with invoke wrapper and generate preamble/postamble 753 Value *Threw = wrapInvoke(II); 754 ToErase.push_back(II); 755 756 // Insert a branch based on __THREW__ variable 757 Value *Cmp = IRB.CreateICmpEQ(Threw, IRB.getInt32(1), "cmp"); 758 IRB.CreateCondBr(Cmp, II->getUnwindDest(), II->getNormalDest()); 759 760 } else { 761 // This can't throw, and we don't need this invoke, just replace it with a 762 // call+branch 763 SmallVector<Value *, 16> Args(II->arg_begin(), II->arg_end()); 764 CallInst *NewCall = IRB.CreateCall(II->getCalledValue(), Args); 765 NewCall->takeName(II); 766 NewCall->setCallingConv(II->getCallingConv()); 767 NewCall->setDebugLoc(II->getDebugLoc()); 768 NewCall->setAttributes(II->getAttributes()); 769 II->replaceAllUsesWith(NewCall); 770 ToErase.push_back(II); 771 772 IRB.CreateBr(II->getNormalDest()); 773 774 // Remove any PHI node entries from the exception destination 775 II->getUnwindDest()->removePredecessor(&BB); 776 } 777 } 778 779 // Process resume instructions 780 for (BasicBlock &BB : F) { 781 // Scan the body of the basic block for resumes 782 for (Instruction &I : BB) { 783 auto *RI = dyn_cast<ResumeInst>(&I); 784 if (!RI) 785 continue; 786 787 // Split the input into legal values 788 Value *Input = RI->getValue(); 789 IRB.SetInsertPoint(RI); 790 Value *Low = IRB.CreateExtractValue(Input, 0, "low"); 791 // Create a call to __resumeException function 792 IRB.CreateCall(ResumeF, {Low}); 793 // Add a terminator to the block 794 IRB.CreateUnreachable(); 795 ToErase.push_back(RI); 796 } 797 } 798 799 // Process llvm.eh.typeid.for intrinsics 800 for (BasicBlock &BB : F) { 801 for (Instruction &I : BB) { 802 auto *CI = dyn_cast<CallInst>(&I); 803 if (!CI) 804 continue; 805 const Function *Callee = CI->getCalledFunction(); 806 if (!Callee) 807 continue; 808 if (Callee->getIntrinsicID() != Intrinsic::eh_typeid_for) 809 continue; 810 811 IRB.SetInsertPoint(CI); 812 CallInst *NewCI = 813 IRB.CreateCall(EHTypeIDF, CI->getArgOperand(0), "typeid"); 814 CI->replaceAllUsesWith(NewCI); 815 ToErase.push_back(CI); 816 } 817 } 818 819 // Look for orphan landingpads, can occur in blocks with no predecessors 820 for (BasicBlock &BB : F) { 821 Instruction *I = BB.getFirstNonPHI(); 822 if (auto *LPI = dyn_cast<LandingPadInst>(I)) 823 LandingPads.insert(LPI); 824 } 825 826 // Handle all the landingpad for this function together, as multiple invokes 827 // may share a single lp 828 for (LandingPadInst *LPI : LandingPads) { 829 IRB.SetInsertPoint(LPI); 830 SmallVector<Value *, 16> FMCArgs; 831 for (unsigned i = 0, e = LPI->getNumClauses(); i < e; ++i) { 832 Constant *Clause = LPI->getClause(i); 833 // As a temporary workaround for the lack of aggregate varargs support 834 // in the interface between JS and wasm, break out filter operands into 835 // their component elements. 836 if (LPI->isFilter(i)) { 837 auto *ATy = cast<ArrayType>(Clause->getType()); 838 for (unsigned j = 0, e = ATy->getNumElements(); j < e; ++j) { 839 Value *EV = IRB.CreateExtractValue(Clause, makeArrayRef(j), "filter"); 840 FMCArgs.push_back(EV); 841 } 842 } else 843 FMCArgs.push_back(Clause); 844 } 845 846 // Create a call to __cxa_find_matching_catch_N function 847 Function *FMCF = getFindMatchingCatch(M, FMCArgs.size()); 848 CallInst *FMCI = IRB.CreateCall(FMCF, FMCArgs, "fmc"); 849 Value *Undef = UndefValue::get(LPI->getType()); 850 Value *Pair0 = IRB.CreateInsertValue(Undef, FMCI, 0, "pair0"); 851 Value *TempRet0 = 852 IRB.CreateLoad(TempRet0GV, TempRet0GV->getName() + ".val"); 853 Value *Pair1 = IRB.CreateInsertValue(Pair0, TempRet0, 1, "pair1"); 854 855 LPI->replaceAllUsesWith(Pair1); 856 ToErase.push_back(LPI); 857 } 858 859 // Erase everything we no longer need in this function 860 for (Instruction *I : ToErase) 861 I->eraseFromParent(); 862 863 return Changed; 864 } 865 866 bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { 867 Module &M = *F.getParent(); 868 LLVMContext &C = F.getContext(); 869 IRBuilder<> IRB(C); 870 SmallVector<Instruction *, 64> ToErase; 871 // Vector of %setjmpTable values 872 std::vector<Instruction *> SetjmpTableInsts; 873 // Vector of %setjmpTableSize values 874 std::vector<Instruction *> SetjmpTableSizeInsts; 875 876 // Setjmp preparation 877 878 // This instruction effectively means %setjmpTableSize = 4. 879 // We create this as an instruction intentionally, and we don't want to fold 880 // this instruction to a constant 4, because this value will be used in 881 // SSAUpdater.AddAvailableValue(...) later. 882 BasicBlock &EntryBB = F.getEntryBlock(); 883 BinaryOperator *SetjmpTableSize = BinaryOperator::Create( 884 Instruction::Add, IRB.getInt32(4), IRB.getInt32(0), "setjmpTableSize", 885 &*EntryBB.getFirstInsertionPt()); 886 // setjmpTable = (int *) malloc(40); 887 Instruction *SetjmpTable = CallInst::CreateMalloc( 888 SetjmpTableSize, IRB.getInt32Ty(), IRB.getInt32Ty(), IRB.getInt32(40), 889 nullptr, nullptr, "setjmpTable"); 890 // setjmpTable[0] = 0; 891 IRB.SetInsertPoint(SetjmpTableSize); 892 IRB.CreateStore(IRB.getInt32(0), SetjmpTable); 893 SetjmpTableInsts.push_back(SetjmpTable); 894 SetjmpTableSizeInsts.push_back(SetjmpTableSize); 895 896 // Setjmp transformation 897 std::vector<PHINode *> SetjmpRetPHIs; 898 Function *SetjmpF = M.getFunction("setjmp"); 899 for (User *U : SetjmpF->users()) { 900 auto *CI = dyn_cast<CallInst>(U); 901 if (!CI) 902 report_fatal_error("Does not support indirect calls to setjmp"); 903 904 BasicBlock *BB = CI->getParent(); 905 if (BB->getParent() != &F) // in other function 906 continue; 907 908 // The tail is everything right after the call, and will be reached once 909 // when setjmp is called, and later when longjmp returns to the setjmp 910 BasicBlock *Tail = SplitBlock(BB, CI->getNextNode()); 911 // Add a phi to the tail, which will be the output of setjmp, which 912 // indicates if this is the first call or a longjmp back. The phi directly 913 // uses the right value based on where we arrive from 914 IRB.SetInsertPoint(Tail->getFirstNonPHI()); 915 PHINode *SetjmpRet = IRB.CreatePHI(IRB.getInt32Ty(), 2, "setjmp.ret"); 916 917 // setjmp initial call returns 0 918 SetjmpRet->addIncoming(IRB.getInt32(0), BB); 919 // The proper output is now this, not the setjmp call itself 920 CI->replaceAllUsesWith(SetjmpRet); 921 // longjmp returns to the setjmp will add themselves to this phi 922 SetjmpRetPHIs.push_back(SetjmpRet); 923 924 // Fix call target 925 // Our index in the function is our place in the array + 1 to avoid index 926 // 0, because index 0 means the longjmp is not ours to handle. 927 IRB.SetInsertPoint(CI); 928 Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()), 929 SetjmpTable, SetjmpTableSize}; 930 Instruction *NewSetjmpTable = 931 IRB.CreateCall(SaveSetjmpF, Args, "setjmpTable"); 932 Instruction *NewSetjmpTableSize = 933 IRB.CreateLoad(TempRet0GV, "setjmpTableSize"); 934 SetjmpTableInsts.push_back(NewSetjmpTable); 935 SetjmpTableSizeInsts.push_back(NewSetjmpTableSize); 936 ToErase.push_back(CI); 937 } 938 939 // Update each call that can longjmp so it can return to a setjmp where 940 // relevant. 941 942 // Because we are creating new BBs while processing and don't want to make 943 // all these newly created BBs candidates again for longjmp processing, we 944 // first make the vector of candidate BBs. 945 std::vector<BasicBlock *> BBs; 946 for (BasicBlock &BB : F) 947 BBs.push_back(&BB); 948 949 // BBs.size() will change within the loop, so we query it every time 950 for (unsigned i = 0; i < BBs.size(); i++) { 951 BasicBlock *BB = BBs[i]; 952 for (Instruction &I : *BB) { 953 assert(!isa<InvokeInst>(&I)); 954 auto *CI = dyn_cast<CallInst>(&I); 955 if (!CI) 956 continue; 957 958 const Value *Callee = CI->getCalledValue(); 959 if (!canLongjmp(M, Callee)) 960 continue; 961 962 Value *Threw = nullptr; 963 BasicBlock *Tail; 964 if (Callee->getName().startswith(InvokePrefix)) { 965 // If invoke wrapper has already been generated for this call in 966 // previous EH phase, search for the load instruction 967 // %__THREW__.val = __THREW__; 968 // in postamble after the invoke wrapper call 969 LoadInst *ThrewLI = nullptr; 970 StoreInst *ThrewResetSI = nullptr; 971 for (auto I = std::next(BasicBlock::iterator(CI)), IE = BB->end(); 972 I != IE; ++I) { 973 if (auto *LI = dyn_cast<LoadInst>(I)) 974 if (auto *GV = dyn_cast<GlobalVariable>(LI->getPointerOperand())) 975 if (GV == ThrewGV) { 976 Threw = ThrewLI = LI; 977 break; 978 } 979 } 980 // Search for the store instruction after the load above 981 // __THREW__ = 0; 982 for (auto I = std::next(BasicBlock::iterator(ThrewLI)), IE = BB->end(); 983 I != IE; ++I) { 984 if (auto *SI = dyn_cast<StoreInst>(I)) 985 if (auto *GV = dyn_cast<GlobalVariable>(SI->getPointerOperand())) 986 if (GV == ThrewGV && SI->getValueOperand() == IRB.getInt32(0)) { 987 ThrewResetSI = SI; 988 break; 989 } 990 } 991 assert(Threw && ThrewLI && "Cannot find __THREW__ load after invoke"); 992 assert(ThrewResetSI && "Cannot find __THREW__ store after invoke"); 993 Tail = SplitBlock(BB, ThrewResetSI->getNextNode()); 994 995 } else { 996 // Wrap call with invoke wrapper and generate preamble/postamble 997 Threw = wrapInvoke(CI); 998 ToErase.push_back(CI); 999 Tail = SplitBlock(BB, CI->getNextNode()); 1000 } 1001 1002 // We need to replace the terminator in Tail - SplitBlock makes BB go 1003 // straight to Tail, we need to check if a longjmp occurred, and go to the 1004 // right setjmp-tail if so 1005 ToErase.push_back(BB->getTerminator()); 1006 1007 // Generate a function call to testSetjmp function and preamble/postamble 1008 // code to figure out (1) whether longjmp occurred (2) if longjmp 1009 // occurred, which setjmp it corresponds to 1010 Value *Label = nullptr; 1011 Value *LongjmpResult = nullptr; 1012 BasicBlock *EndBB = nullptr; 1013 wrapTestSetjmp(BB, CI, Threw, SetjmpTable, SetjmpTableSize, Label, 1014 LongjmpResult, EndBB); 1015 assert(Label && LongjmpResult && EndBB); 1016 1017 // Create switch instruction 1018 IRB.SetInsertPoint(EndBB); 1019 SwitchInst *SI = IRB.CreateSwitch(Label, Tail, SetjmpRetPHIs.size()); 1020 // -1 means no longjmp happened, continue normally (will hit the default 1021 // switch case). 0 means a longjmp that is not ours to handle, needs a 1022 // rethrow. Otherwise the index is the same as the index in P+1 (to avoid 1023 // 0). 1024 for (unsigned i = 0; i < SetjmpRetPHIs.size(); i++) { 1025 SI->addCase(IRB.getInt32(i + 1), SetjmpRetPHIs[i]->getParent()); 1026 SetjmpRetPHIs[i]->addIncoming(LongjmpResult, EndBB); 1027 } 1028 1029 // We are splitting the block here, and must continue to find other calls 1030 // in the block - which is now split. so continue to traverse in the Tail 1031 BBs.push_back(Tail); 1032 } 1033 } 1034 1035 // Erase everything we no longer need in this function 1036 for (Instruction *I : ToErase) 1037 I->eraseFromParent(); 1038 1039 // Free setjmpTable buffer before each return instruction 1040 for (BasicBlock &BB : F) { 1041 Instruction *TI = BB.getTerminator(); 1042 if (isa<ReturnInst>(TI)) 1043 CallInst::CreateFree(SetjmpTable, TI); 1044 } 1045 1046 // Every call to saveSetjmp can change setjmpTable and setjmpTableSize 1047 // (when buffer reallocation occurs) 1048 // entry: 1049 // setjmpTableSize = 4; 1050 // setjmpTable = (int *) malloc(40); 1051 // setjmpTable[0] = 0; 1052 // ... 1053 // somebb: 1054 // setjmpTable = saveSetjmp(buf, label, setjmpTable, setjmpTableSize); 1055 // setjmpTableSize = __tempRet0; 1056 // So we need to make sure the SSA for these variables is valid so that every 1057 // saveSetjmp and testSetjmp calls have the correct arguments. 1058 SSAUpdater SetjmpTableSSA; 1059 SSAUpdater SetjmpTableSizeSSA; 1060 SetjmpTableSSA.Initialize(Type::getInt32PtrTy(C), "setjmpTable"); 1061 SetjmpTableSizeSSA.Initialize(Type::getInt32Ty(C), "setjmpTableSize"); 1062 for (Instruction *I : SetjmpTableInsts) 1063 SetjmpTableSSA.AddAvailableValue(I->getParent(), I); 1064 for (Instruction *I : SetjmpTableSizeInsts) 1065 SetjmpTableSizeSSA.AddAvailableValue(I->getParent(), I); 1066 1067 for (auto UI = SetjmpTable->use_begin(), UE = SetjmpTable->use_end(); 1068 UI != UE;) { 1069 // Grab the use before incrementing the iterator. 1070 Use &U = *UI; 1071 // Increment the iterator before removing the use from the list. 1072 ++UI; 1073 if (Instruction *I = dyn_cast<Instruction>(U.getUser())) 1074 if (I->getParent() != &EntryBB) 1075 SetjmpTableSSA.RewriteUse(U); 1076 } 1077 for (auto UI = SetjmpTableSize->use_begin(), UE = SetjmpTableSize->use_end(); 1078 UI != UE;) { 1079 Use &U = *UI; 1080 ++UI; 1081 if (Instruction *I = dyn_cast<Instruction>(U.getUser())) 1082 if (I->getParent() != &EntryBB) 1083 SetjmpTableSizeSSA.RewriteUse(U); 1084 } 1085 1086 // Finally, our modifications to the cfg can break dominance of SSA variables. 1087 // For example, in this code, 1088 // if (x()) { .. setjmp() .. } 1089 // if (y()) { .. longjmp() .. } 1090 // We must split the longjmp block, and it can jump into the block splitted 1091 // from setjmp one. But that means that when we split the setjmp block, it's 1092 // first part no longer dominates its second part - there is a theoretically 1093 // possible control flow path where x() is false, then y() is true and we 1094 // reach the second part of the setjmp block, without ever reaching the first 1095 // part. So, we rebuild SSA form here. 1096 rebuildSSA(F); 1097 return true; 1098 } 1099