From 10a7086700780a325a6eb1f7927e81a97a7a9c7d Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Thu, 1 Sep 2016 00:44:37 +0000 Subject: [PATCH] Revert "Add asm.js-style setjmp/longjmp handling for wasm" This reverts commit r280302, it broke the integration tests. llvm-svn: 280329 --- .../WebAssemblyLowerEmscriptenEHSjLj.cpp | 927 ++++----------------- .../WebAssembly/WebAssemblyTargetMachine.cpp | 9 +- .../CodeGen/WebAssembly/lower-em-ehsjlj-options.ll | 61 -- .../WebAssembly/lower-em-exceptions-whitelist.ll | 5 +- .../CodeGen/WebAssembly/lower-em-exceptions.ll | 45 +- llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll | 213 ----- 6 files changed, 186 insertions(+), 1074 deletions(-) delete mode 100644 llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll delete mode 100644 llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 7197f28..6c84a38 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -8,62 +8,25 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief This file lowers exception-related instructions and setjmp/longjmp -/// function calls in order to use Emscripten's JavaScript try and catch -/// mechanism. +/// \brief This file lowers exception-related instructions in order to use +/// Emscripten's JavaScript try and catch mechanism to handle exceptions. /// -/// To handle exceptions and setjmp/longjmps, this scheme relies on JavaScript's -/// try and catch syntax and relevant exception-related libraries implemented -/// in JavaScript glue code that will be produced by Emscripten. This is similar -/// to the current Emscripten asm.js exception handling in fastcomp. For -/// fastcomp's EH / SjLj scheme, see these files in fastcomp LLVM branch: +/// To handle exceptions, this scheme relies on JavaScript's try and catch +/// syntax and relevant exception-related libraries implemented in JavaScript +/// glue code that will be produced by Emscripten. This is similar to the +/// current Emscripten asm.js exception handling in fastcomp. +/// For fastcomp's EH scheme, see these files in fastcomp LLVM branch: /// (Location: https://github.com/kripken/emscripten-fastcomp) /// lib/Target/JSBackend/NaCl/LowerEmExceptionsPass.cpp -/// lib/Target/JSBackend/NaCl/LowerEmSetjmp.cpp /// lib/Target/JSBackend/JSBackend.cpp /// lib/Target/JSBackend/CallHandlers.h /// -/// * Exception handling -/// This pass lowers invokes and landingpads into library functions in JS glue -/// code. Invokes are lowered into function wrappers called invoke wrappers that -/// exist in JS side, which wraps the original function call with JS try-catch. -/// If an exception occurred, cxa_throw() function in JS side sets some -/// variables (see below) so we can check whether an exception occurred from -/// wasm code and handle it appropriately. +/// This pass does following things: /// -/// * Setjmp-longjmp handling -/// This pass lowers setjmp to a reasonably-performant approach for emscripten. -/// The idea is that each block with a setjmp is broken up into two parts: the -/// part containing setjmp and the part right after the setjmp. The latter part -/// is either reached from the setjmp, or later from a longjmp. To handle the -/// longjmp, all calls that might longjmp are also called using invoke wrappers -/// and thus JS / try-catch. JS longjmp() function also sets some variables so -/// we can check / whether a longjmp occurred from wasm code. Each block with a -/// function call that might longjmp is also split up after the longjmp call. -/// After the longjmp call, we check whether a longjmp occurred, and if it did, -/// which setjmp it corresponds to, and jump to the right post-setjmp block. -/// We assume setjmp-longjmp handling always run after EH handling, which means -/// we don't expect any exception-related instructions when SjLj runs. -/// FIXME Currently this scheme does not support indirect call of setjmp, -/// because of the limitation of the scheme itself. fastcomp does not support it -/// either. -/// -/// In detail, this pass does following things: -/// -/// 1) Create three global variables: __THREW__, __threwValue, and __tempRet0. -/// __tempRet0 will be set within __cxa_find_matching_catch() function in +/// 1) Create three global variables: __THREW__, __threwValue, and tempRet0. +/// tempRet0 will be set within __cxa_find_matching_catch() function in /// JS library, and __THREW__ and __threwValue will be set in invoke wrappers -/// in JS glue code. For what invoke wrappers are, refer to 3). These -/// variables are used for both exceptions and setjmp/longjmps. -/// __THREW__ indicates whether an exception or a longjmp occurred or not. 0 -/// means nothing occurred, 1 means an exception occurred, and other numbers -/// mean a longjmp occurred. In the case of longjmp, threwValue variable -/// indicates the corresponding setjmp buffer the longjmp corresponds to. -/// In exception handling, __tempRet0 indicates the type of an exception -/// caught, and in setjmp/longjmp, it means the second argument to longjmp -/// function. -/// -/// * Exception handling +/// in JS glue code. For what invoke wrappers are, refer to 3). /// /// 2) Create setThrew and setTempRet0 functions. /// The global variables created in 1) will exist in wasm address space, @@ -80,7 +43,7 @@ /// } /// /// function setTempRet0(value) { -/// __tempRet0 = value; +/// tempRet0 = value; /// } /// /// 3) Lower @@ -115,10 +78,10 @@ /// ... use %val ... /// into /// %fmc = call @__cxa_find_matching_catch_N(c1, c2, c3, ...) -/// %val = {%fmc, __tempRet0} +/// %val = {%fmc, tempRet0} /// ... use %val ... /// Here N is a number calculated based on the number of clauses. -/// Global variable __tempRet0 is set within __cxa_find_matching_catch() in +/// Global variable tempRet0 is set within __cxa_find_matching_catch() in /// JS glue code. /// /// 5) Lower @@ -133,81 +96,14 @@ /// call @llvm_eh_typeid_for(type) /// llvm_eh_typeid_for function will be generated in JS glue code. /// -/// * Setjmp / Longjmp handling -/// -/// 7) In the function entry that calls setjmp, initialize setjmpTable and -/// sejmpTableSize as follows: -/// setjmpTableSize = 4; -/// setjmpTable = (int *) malloc(40); -/// setjmpTable[0] = 0; -/// setjmpTable and setjmpTableSize are used in saveSetjmp() function in JS -/// code. -/// -/// 8) Lower -/// setjmp(buf) -/// into -/// setjmpTable = saveSetjmp(buf, label, setjmpTable, setjmpTableSize); -/// setjmpTableSize = __tempRet0; -/// For each dynamic setjmp call, setjmpTable stores its ID (a number which -/// is incrementally assigned from 0) and its label (a unique number that -/// represents each callsite of setjmp). When we need more entries in -/// setjmpTable, it is reallocated in saveSetjmp() in JS code and it will -/// return the new table address, and assign the new table size in -/// __tempRet0. saveSetjmp also stores the setjmp's ID into the buffer buf. -/// A BB with setjmp is split into two after setjmp call in order to make the -/// post-setjmp BB the possible destination of longjmp BB. -/// -/// 9) Lower -/// longjmp(buf, value) -/// into -/// emscripten_longjmp_jmpbuf(buf, value) -/// emscripten_longjmp_jmpbuf will be lowered to emscripten_longjmp later. -/// -/// 10) Lower every call that might longjmp into -/// __THREW__ = 0; -/// call @invoke_SIG(func, arg1, arg2) -/// %__THREW__.val = __THREW__; -/// __THREW__ = 0; -/// if (%__THREW__.val != 0 & threwValue != 0) { -/// %label = testSetjmp(mem[%__THREW__.val], setjmpTable, -/// setjmpTableSize); -/// if (%label == 0) -/// emscripten_longjmp(%__THREW__.val, threwValue); -/// __tempRet0 = threwValue; -/// } else { -/// %label = -1; -/// } -/// longjmp_result = __tempRet0; -/// switch label { -/// label 1: post-setjmp BB 1 -/// label 2: post-setjmp BB 2 -/// ... -/// default: splited next BB -/// } -/// testSetjmp examines setjmpTable to see if there is a matching setjmp -/// call. After calling an invoke wrapper, if a longjmp occurred, __THREW__ -/// will be the address of matching jmp_buf buffer and threwValue be the -/// second argument to longjmp. mem[__THREW__.val] is a setjmp ID that is -/// stored in saveSetjmp. testSetjmp returns a setjmp label, a unique ID to -/// each setjmp callsite. Label 0 means this longjmp buffer does not -/// correspond to one of the setjmp callsites in this function, so in this -/// case we just chain the longjmp to the caller. (Here we call -/// emscripten_longjmp, which is different from emscripten_longjmp_jmpbuf. -/// emscripten_longjmp_jmpbuf takes jmp_buf as its first argument, while -/// emscripten_longjmp takes an int. Both of them will eventually be lowered -/// to emscripten_longjmp in s2wasm, but here we need two signatures - we -/// can't translate an int value to a jmp_buf.) -/// Label -1 means no longjmp occurred. Otherwise we jump to the right -/// post-setjmp BB based on the label. -/// ///===----------------------------------------------------------------------===// #include "WebAssembly.h" #include "llvm/IR/CallSite.h" -#include "llvm/IR/Dominators.h" #include "llvm/IR/IRBuilder.h" -#include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/Transforms/Utils/SSAUpdater.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/raw_ostream.h" +#include using namespace llvm; @@ -229,26 +125,17 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass { static const char *EHTypeIDFName; static const char *SetThrewFName; static const char *SetTempRet0FName; - static const char *EmLongjmpFName; - static const char *EmLongjmpJmpbufFName; - static const char *SaveSetjmpFName; - static const char *TestSetjmpFName; static const char *FindMatchingCatchPrefix; static const char *InvokePrefix; - bool EnableEH; // Enable exception handling - bool EnableSjLj; // Enable setjmp/longjmp handling + bool DoEH; // Enable exception handling + bool DoSjLj; // Enable setjmp/longjmp handling GlobalVariable *ThrewGV; GlobalVariable *ThrewValueGV; GlobalVariable *TempRet0GV; Function *ResumeF; Function *EHTypeIDF; - Function *EmLongjmpF; - Function *EmLongjmpJmpbufF; - Function *SaveSetjmpF; - Function *TestSetjmpF; - // __cxa_find_matching_catch_N functions. // Indexed by the number of clauses in an original landingpad instruction. DenseMap FindMatchingCatches; @@ -263,37 +150,26 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass { bool runEHOnFunction(Function &F); bool runSjLjOnFunction(Function &F); + // Returns __cxa_find_matching_catch_N function, where N = NumClauses + 2. + // This is because a landingpad instruction contains two more arguments, + // a personality function and a cleanup bit, and __cxa_find_matching_catch_N + // functions are named after the number of arguments in the original + // landingpad instruction. Function *getFindMatchingCatch(Module &M, unsigned NumClauses); - template Value *wrapInvoke(CallOrInvoke *CI); - void wrapTestSetjmp(BasicBlock *BB, Instruction *InsertPt, Value *Threw, - Value *SetjmpTable, Value *SetjmpTableSize, Value *&Label, - Value *&LongjmpResult, BasicBlock *&EndBB); - template Function *getInvokeWrapper(CallOrInvoke *CI); - + Function *getInvokeWrapper(Module &M, InvokeInst *II); bool areAllExceptionsAllowed() const { return EHWhitelistSet.empty(); } - bool canLongjmp(Module &M, const Value *Callee) const; - - void createSetThrewFunction(Module &M); - void createSetTempRet0Function(Module &M); - - void rebuildSSA(Function &F); public: static char ID; - WebAssemblyLowerEmscriptenEHSjLj(bool EnableEH = true, bool EnableSjLj = true) - : ModulePass(ID), EnableEH(EnableEH), EnableSjLj(EnableSjLj), - ThrewGV(nullptr), ThrewValueGV(nullptr), TempRet0GV(nullptr), - ResumeF(nullptr), EHTypeIDF(nullptr), EmLongjmpF(nullptr), - EmLongjmpJmpbufF(nullptr), SaveSetjmpF(nullptr), TestSetjmpF(nullptr) { + WebAssemblyLowerEmscriptenEHSjLj(bool DoEH = true, bool DoSjLj = true) + : ModulePass(ID), DoEH(DoEH), DoSjLj(DoSjLj), ThrewGV(nullptr), + ThrewValueGV(nullptr), TempRet0GV(nullptr), ResumeF(nullptr), + EHTypeIDF(nullptr) { EHWhitelistSet.insert(EHWhitelist.begin(), EHWhitelist.end()); } bool runOnModule(Module &M) override; - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.addRequired(); - } }; } // End anonymous namespace @@ -305,12 +181,6 @@ const char *WebAssemblyLowerEmscriptenEHSjLj::EHTypeIDFName = "llvm_eh_typeid_for"; const char *WebAssemblyLowerEmscriptenEHSjLj::SetThrewFName = "setThrew"; const char *WebAssemblyLowerEmscriptenEHSjLj::SetTempRet0FName = "setTempRet0"; -const char *WebAssemblyLowerEmscriptenEHSjLj::EmLongjmpFName = - "emscripten_longjmp"; -const char *WebAssemblyLowerEmscriptenEHSjLj::EmLongjmpJmpbufFName = - "emscripten_longjmp_jmpbuf"; -const char *WebAssemblyLowerEmscriptenEHSjLj::SaveSetjmpFName = "saveSetjmp"; -const char *WebAssemblyLowerEmscriptenEHSjLj::TestSetjmpFName = "testSetjmp"; const char *WebAssemblyLowerEmscriptenEHSjLj::FindMatchingCatchPrefix = "__cxa_find_matching_catch_"; const char *WebAssemblyLowerEmscriptenEHSjLj::InvokePrefix = "__invoke_"; @@ -320,9 +190,9 @@ INITIALIZE_PASS(WebAssemblyLowerEmscriptenEHSjLj, DEBUG_TYPE, "WebAssembly Lower Emscripten Exceptions / Setjmp / Longjmp", false, false) -ModulePass *llvm::createWebAssemblyLowerEmscriptenEHSjLj(bool EnableEH, - bool EnableSjLj) { - return new WebAssemblyLowerEmscriptenEHSjLj(EnableEH, EnableSjLj); +ModulePass *llvm::createWebAssemblyLowerEmscriptenEHSjLj(bool DoEH, + bool DoSjLj) { + return new WebAssemblyLowerEmscriptenEHSjLj(DoEH, DoSjLj); } static bool canThrow(const Value *V) { @@ -334,7 +204,7 @@ static bool canThrow(const Value *V) { // leave setjmp and longjmp (mostly) alone, we process them properly later if (Name == "setjmp" || Name == "longjmp") return false; - return !F->doesNotThrow(); + return true; } // not a function, so an indirect call - can throw, we can't tell return true; @@ -372,11 +242,6 @@ static std::string getSignature(FunctionType *FTy) { return Sig; } -// Returns __cxa_find_matching_catch_N function, where N = NumClauses + 2. -// This is because a landingpad instruction contains two more arguments, a -// personality function and a cleanup bit, and __cxa_find_matching_catch_N -// functions are named after the number of arguments in the original landingpad -// instruction. Function * WebAssemblyLowerEmscriptenEHSjLj::getFindMatchingCatch(Module &M, unsigned NumClauses) { @@ -392,86 +257,15 @@ WebAssemblyLowerEmscriptenEHSjLj::getFindMatchingCatch(Module &M, return F; } -// Generate invoke wrapper seqence with preamble and postamble -// Preamble: -// __THREW__ = 0; -// Postamble: -// %__THREW__.val = __THREW__; __THREW__ = 0; -// Returns %__THREW__.val, which indicates whether an exception is thrown (or -// whether longjmp occurred), for future use. -template -Value *WebAssemblyLowerEmscriptenEHSjLj::wrapInvoke(CallOrInvoke *CI) { - LLVMContext &C = CI->getModule()->getContext(); - - // If we are calling a function that is noreturn, we must remove that - // attribute. The code we insert here does expect it to return, after we - // catch the exception. - if (CI->doesNotReturn()) { - if (auto *F = dyn_cast(CI->getCalledValue())) - F->removeFnAttr(Attribute::NoReturn); - CI->removeAttribute(AttributeSet::FunctionIndex, Attribute::NoReturn); - } - - IRBuilder<> IRB(C); - IRB.SetInsertPoint(CI); - - // Pre-invoke - // __THREW__ = 0; - IRB.CreateStore(IRB.getInt32(0), ThrewGV); - - // Invoke function wrapper in JavaScript - SmallVector Args; - // Put the pointer to the callee as first argument, so it can be called - // within the invoke wrapper later - Args.push_back(CI->getCalledValue()); - Args.append(CI->arg_begin(), CI->arg_end()); - CallInst *NewCall = IRB.CreateCall(getInvokeWrapper(CI), Args); - NewCall->takeName(CI); - NewCall->setCallingConv(CI->getCallingConv()); - NewCall->setDebugLoc(CI->getDebugLoc()); - - // Because we added the pointer to the callee as first argument, all - // argument attribute indices have to be incremented by one. - SmallVector AttributesVec; - const AttributeSet &InvokePAL = CI->getAttributes(); - CallSite::arg_iterator AI = CI->arg_begin(); - unsigned i = 1; // Argument attribute index starts from 1 - for (unsigned e = CI->getNumArgOperands(); i <= e; ++AI, ++i) { - if (InvokePAL.hasAttributes(i)) { - AttrBuilder B(InvokePAL, i); - AttributesVec.push_back(AttributeSet::get(C, i + 1, B)); - } - } - // Add any return attributes. - if (InvokePAL.hasAttributes(AttributeSet::ReturnIndex)) - AttributesVec.push_back(AttributeSet::get(C, InvokePAL.getRetAttributes())); - // Add any function attributes. - if (InvokePAL.hasAttributes(AttributeSet::FunctionIndex)) - AttributesVec.push_back(AttributeSet::get(C, InvokePAL.getFnAttributes())); - // Reconstruct the AttributesList based on the vector we constructed. - AttributeSet NewCallPAL = AttributeSet::get(C, AttributesVec); - NewCall->setAttributes(NewCallPAL); - - CI->replaceAllUsesWith(NewCall); - - // Post-invoke - // %__THREW__.val = __THREW__; __THREW__ = 0; - Value *Threw = IRB.CreateLoad(ThrewGV, ThrewGV->getName() + ".val"); - IRB.CreateStore(IRB.getInt32(0), ThrewGV); - return Threw; -} - -// Get matching invoke wrapper based on callee signature -template -Function *WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper(CallOrInvoke *CI) { - Module *M = CI->getModule(); +Function *WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper(Module &M, + InvokeInst *II) { SmallVector ArgTys; - Value *Callee = CI->getCalledValue(); + Value *Callee = II->getCalledValue(); FunctionType *CalleeFTy; if (auto *F = dyn_cast(Callee)) CalleeFTy = F->getFunctionType(); else { - auto *CalleeTy = cast(Callee->getType())->getElementType(); + auto *CalleeTy = dyn_cast(Callee->getType())->getElementType(); CalleeFTy = dyn_cast(CalleeTy); } @@ -487,243 +281,42 @@ Function *WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper(CallOrInvoke *CI) { FunctionType *FTy = FunctionType::get(CalleeFTy->getReturnType(), ArgTys, CalleeFTy->isVarArg()); Function *F = Function::Create(FTy, GlobalValue::ExternalLinkage, - InvokePrefix + Sig, M); + InvokePrefix + Sig, &M); InvokeWrappers[Sig] = F; return F; } -bool WebAssemblyLowerEmscriptenEHSjLj::canLongjmp(Module &M, - const Value *Callee) const { - if (auto *CalleeF = dyn_cast(Callee)) - if (CalleeF->isIntrinsic()) - return false; - - // The reason we include malloc/free here is to exclude the malloc/free - // calls generated in setjmp prep / cleanup routines. - Function *SetjmpF = M.getFunction("setjmp"); - Function *MallocF = M.getFunction("malloc"); - Function *FreeF = M.getFunction("free"); - if (Callee == SetjmpF || Callee == MallocF || Callee == FreeF) - return false; - - // There are functions in JS glue code - if (Callee == ResumeF || Callee == EHTypeIDF || Callee == SaveSetjmpF || - Callee == TestSetjmpF) - return false; - - // __cxa_find_matching_catch_N functions cannot longjmp - if (Callee->getName().startswith(FindMatchingCatchPrefix)) - return false; - - // Exception-catching related functions - Function *BeginCatchF = M.getFunction("__cxa_begin_catch"); - Function *EndCatchF = M.getFunction("__cxa_end_catch"); - Function *AllocExceptionF = M.getFunction("__cxa_allocate_exception"); - Function *ThrowF = M.getFunction("__cxa_throw"); - Function *TerminateF = M.getFunction("__clang_call_terminate"); - if (Callee == BeginCatchF || Callee == EndCatchF || - Callee == AllocExceptionF || Callee == ThrowF || Callee == TerminateF) - return false; - - // Otherwise we don't know - return true; -} - -// Generate testSetjmp function call seqence with preamble and postamble. -// The code this generates is equivalent to the following JavaScript code: -// if (%__THREW__.val != 0 & threwValue != 0) { -// %label = _testSetjmp(mem[%__THREW__.val], setjmpTable, setjmpTableSize); -// if (%label == 0) -// emscripten_longjmp(%__THREW__.val, threwValue); -// __tempRet0 = threwValue; -// } else { -// %label = -1; -// } -// %longjmp_result = __tempRet0; -// -// As output parameters. returns %label, %longjmp_result, and the BB the last -// instruction (%longjmp_result = ...) is in. -void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp( - BasicBlock *BB, Instruction *InsertPt, Value *Threw, Value *SetjmpTable, - Value *SetjmpTableSize, Value *&Label, Value *&LongjmpResult, - BasicBlock *&EndBB) { - Function *F = BB->getParent(); - LLVMContext &C = BB->getModule()->getContext(); - IRBuilder<> IRB(C); - IRB.SetInsertPoint(InsertPt); - - // if (%__THREW__.val != 0 & threwValue != 0) - IRB.SetInsertPoint(BB); - BasicBlock *ThenBB1 = BasicBlock::Create(C, "if.then1", F); - BasicBlock *ElseBB1 = BasicBlock::Create(C, "if.else1", F); - BasicBlock *EndBB1 = BasicBlock::Create(C, "if.end", F); - Value *ThrewCmp = IRB.CreateICmpNE(Threw, IRB.getInt32(0)); - Value *ThrewValue = - IRB.CreateLoad(ThrewValueGV, ThrewValueGV->getName() + ".val"); - Value *ThrewValueCmp = IRB.CreateICmpNE(ThrewValue, IRB.getInt32(0)); - Value *Cmp1 = IRB.CreateAnd(ThrewCmp, ThrewValueCmp, "cmp1"); - IRB.CreateCondBr(Cmp1, ThenBB1, ElseBB1); - - // %label = _testSetjmp(mem[%__THREW__.val], _setjmpTable, _setjmpTableSize); - // if (%label == 0) - IRB.SetInsertPoint(ThenBB1); - BasicBlock *ThenBB2 = BasicBlock::Create(C, "if.then2", F); - BasicBlock *EndBB2 = BasicBlock::Create(C, "if.end2", F); - Value *ThrewInt = IRB.CreateIntToPtr(Threw, Type::getInt32PtrTy(C), - Threw->getName() + ".i32p"); - Value *LoadedThrew = - IRB.CreateLoad(ThrewInt, ThrewInt->getName() + ".loaded"); - Value *ThenLabel = IRB.CreateCall( - TestSetjmpF, {LoadedThrew, SetjmpTable, SetjmpTableSize}, "label"); - Value *Cmp2 = IRB.CreateICmpEQ(ThenLabel, IRB.getInt32(0)); - IRB.CreateCondBr(Cmp2, ThenBB2, EndBB2); - - // emscripten_longjmp(%__THREW__.val, threwValue); - IRB.SetInsertPoint(ThenBB2); - IRB.CreateCall(EmLongjmpF, {Threw, ThrewValue}); - IRB.CreateUnreachable(); - - // __tempRet0 = threwValue; - IRB.SetInsertPoint(EndBB2); - IRB.CreateStore(ThrewValue, TempRet0GV); - IRB.CreateBr(EndBB1); - - IRB.SetInsertPoint(ElseBB1); - IRB.CreateBr(EndBB1); - - // longjmp_result = __tempRet0; - IRB.SetInsertPoint(EndBB1); - PHINode *LabelPHI = IRB.CreatePHI(IRB.getInt32Ty(), 2, "label"); - LabelPHI->addIncoming(ThenLabel, EndBB2); - - LabelPHI->addIncoming(IRB.getInt32(-1), ElseBB1); - - // Output parameter assignment - Label = LabelPHI; - EndBB = EndBB1; - LongjmpResult = IRB.CreateLoad(TempRet0GV, "longjmp_result"); -} - -// Create setThrew function -// function setThrew(threw, value) { -// if (__THREW__ == 0) { -// __THREW__ = threw; -// __threwValue = value; -// } -// } -void WebAssemblyLowerEmscriptenEHSjLj::createSetThrewFunction(Module &M) { - LLVMContext &C = M.getContext(); - IRBuilder<> IRB(C); - - assert(!M.getNamedGlobal(SetThrewFName) && "setThrew already exists"); - Type *Params[] = {IRB.getInt32Ty(), IRB.getInt32Ty()}; - FunctionType *FTy = FunctionType::get(IRB.getVoidTy(), Params, false); - Function *F = - Function::Create(FTy, GlobalValue::ExternalLinkage, SetThrewFName, &M); - Argument *Arg1 = &*(F->arg_begin()); - Argument *Arg2 = &*(++F->arg_begin()); - Arg1->setName("threw"); - Arg2->setName("value"); - BasicBlock *EntryBB = BasicBlock::Create(C, "entry", F); - BasicBlock *ThenBB = BasicBlock::Create(C, "if.then", F); - BasicBlock *EndBB = BasicBlock::Create(C, "if.end", F); - - IRB.SetInsertPoint(EntryBB); - Value *Threw = IRB.CreateLoad(ThrewGV, ThrewGV->getName() + ".val"); - Value *Cmp = IRB.CreateICmpEQ(Threw, IRB.getInt32(0), "cmp"); - IRB.CreateCondBr(Cmp, ThenBB, EndBB); - - IRB.SetInsertPoint(ThenBB); - IRB.CreateStore(Arg1, ThrewGV); - IRB.CreateStore(Arg2, ThrewValueGV); - IRB.CreateBr(EndBB); - - IRB.SetInsertPoint(EndBB); - IRB.CreateRetVoid(); -} - -// Create setTempRet0 function -// function setTempRet0(value) { -// __tempRet0 = value; -// } -void WebAssemblyLowerEmscriptenEHSjLj::createSetTempRet0Function(Module &M) { - LLVMContext &C = M.getContext(); - IRBuilder<> IRB(C); - - assert(!M.getNamedGlobal(SetTempRet0FName) && "setTempRet0 already exists"); - Type *Params[] = {IRB.getInt32Ty()}; - FunctionType *FTy = FunctionType::get(IRB.getVoidTy(), Params, false); - Function *F = - Function::Create(FTy, GlobalValue::ExternalLinkage, SetTempRet0FName, &M); - F->arg_begin()->setName("value"); - BasicBlock *EntryBB = BasicBlock::Create(C, "entry", F); - IRB.SetInsertPoint(EntryBB); - IRB.CreateStore(&*F->arg_begin(), TempRet0GV); - IRB.CreateRetVoid(); -} - -void WebAssemblyLowerEmscriptenEHSjLj::rebuildSSA(Function &F) { - DominatorTree &DT = getAnalysis(F).getDomTree(); - DT.recalculate(F); // CFG has been changed - SSAUpdater SSA; - for (BasicBlock &BB : F) { - for (Instruction &I : BB) { - for (auto UI = I.use_begin(), UE = I.use_end(); UI != UE;) { - Use &U = *UI; - ++UI; - SSA.Initialize(I.getType(), I.getName()); - SSA.AddAvailableValue(&BB, &I); - Instruction *User = cast(U.getUser()); - if (User->getParent() == &BB) - continue; - - if (PHINode *UserPN = dyn_cast(User)) - if (UserPN->getIncomingBlock(U) == &BB) - continue; - - if (DT.dominates(&I, User)) - continue; - SSA.RewriteUseAfterInsertions(U); - } - } - } -} - bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { LLVMContext &C = M.getContext(); IRBuilder<> IRB(C); + IntegerType *Int1Ty = IRB.getInt1Ty(); + PointerType *Int8PtrTy = IRB.getInt8PtrTy(); + IntegerType *Int32Ty = IRB.getInt32Ty(); + Type *VoidTy = IRB.getVoidTy(); - Function *SetjmpF = M.getFunction("setjmp"); - Function *LongjmpF = M.getFunction("longjmp"); - bool SetjmpUsed = SetjmpF && !SetjmpF->use_empty(); - bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty(); - bool DoSjLj = EnableSjLj && (SetjmpUsed || LongjmpUsed); - - // Create global variables __THREW__, threwValue, and __tempRet0, which are + // Create global variables __THREW__, threwValue, and tempRet0, which are // used in common for both exception handling and setjmp/longjmp handling - ThrewGV = new GlobalVariable(M, IRB.getInt32Ty(), false, - GlobalValue::ExternalLinkage, IRB.getInt32(0), - createGlobalValueName(M, ThrewGVName)); + ThrewGV = + new GlobalVariable(M, Int1Ty, false, GlobalValue::ExternalLinkage, + IRB.getFalse(), createGlobalValueName(M, ThrewGVName)); ThrewValueGV = new GlobalVariable( - M, IRB.getInt32Ty(), false, GlobalValue::ExternalLinkage, IRB.getInt32(0), + M, Int32Ty, false, GlobalValue::ExternalLinkage, IRB.getInt32(0), createGlobalValueName(M, ThrewValueGVName)); - TempRet0GV = new GlobalVariable(M, IRB.getInt32Ty(), false, + TempRet0GV = new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage, IRB.getInt32(0), createGlobalValueName(M, TempRet0GVName)); bool Changed = false; // Exception handling - if (EnableEH) { + if (DoEH) { // Register __resumeException function - FunctionType *ResumeFTy = - FunctionType::get(IRB.getVoidTy(), IRB.getInt8PtrTy(), false); + FunctionType *ResumeFTy = FunctionType::get(VoidTy, Int8PtrTy, false); ResumeF = Function::Create(ResumeFTy, GlobalValue::ExternalLinkage, ResumeFName, &M); // Register llvm_eh_typeid_for function - FunctionType *EHTypeIDTy = - FunctionType::get(IRB.getInt32Ty(), IRB.getInt8PtrTy(), false); + FunctionType *EHTypeIDTy = FunctionType::get(Int32Ty, Int8PtrTy, false); EHTypeIDF = Function::Create(EHTypeIDTy, GlobalValue::ExternalLinkage, EHTypeIDFName, &M); @@ -734,80 +327,61 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { } } + // TODO: Run CFGSimplify like the emscripten JSBackend? + // Setjmp/longjmp handling if (DoSjLj) { - Changed = true; // We have setjmp or longjmp somewhere - - Function *MallocF = M.getFunction("malloc"); - Function *FreeF = M.getFunction("free"); - if (!MallocF || !FreeF) - report_fatal_error( - "malloc and free must be linked into the module if setjmp is used"); - - // Register saveSetjmp function - FunctionType *SetjmpFTy = SetjmpF->getFunctionType(); - SmallVector Params = {SetjmpFTy->getParamType(0), - IRB.getInt32Ty(), Type::getInt32PtrTy(C), - IRB.getInt32Ty()}; - FunctionType *FTy = - FunctionType::get(Type::getInt32PtrTy(C), Params, false); - SaveSetjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage, - SaveSetjmpFName, &M); - - // Register testSetjmp function - Params = {IRB.getInt32Ty(), Type::getInt32PtrTy(C), IRB.getInt32Ty()}; - FTy = FunctionType::get(IRB.getInt32Ty(), Params, false); - TestSetjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage, - TestSetjmpFName, &M); - - if (LongjmpF) { - // Replace all uses of longjmp with emscripten_longjmp_jmpbuf, which is - // defined in JS code - EmLongjmpJmpbufF = Function::Create(LongjmpF->getFunctionType(), - GlobalValue::ExternalLinkage, - EmLongjmpJmpbufFName, &M); - - LongjmpF->replaceAllUsesWith(EmLongjmpJmpbufF); - } - FTy = FunctionType::get(IRB.getVoidTy(), - {IRB.getInt32Ty(), IRB.getInt32Ty()}, false); - EmLongjmpF = - Function::Create(FTy, GlobalValue::ExternalLinkage, EmLongjmpFName, &M); - - // Only traverse functions that uses setjmp in order not to insert - // unnecessary prep / cleanup code in every function - SmallPtrSet SetjmpUsers; - for (User *U : SetjmpF->users()) { - auto *UI = cast(U); - SetjmpUsers.insert(UI->getFunction()); + for (Function &F : M) { + if (F.isDeclaration()) + continue; + Changed |= runSjLjOnFunction(F); } - for (Function *F : SetjmpUsers) - runSjLjOnFunction(*F); } - if (!Changed) { - // Delete unused global variables and functions - ThrewGV->eraseFromParent(); - ThrewValueGV->eraseFromParent(); - TempRet0GV->eraseFromParent(); - if (ResumeF) - ResumeF->eraseFromParent(); - if (EHTypeIDF) - EHTypeIDF->eraseFromParent(); - if (EmLongjmpF) - EmLongjmpF->eraseFromParent(); - if (SaveSetjmpF) - SaveSetjmpF->eraseFromParent(); - if (TestSetjmpF) - TestSetjmpF->eraseFromParent(); + if (!Changed) return false; - } // If we have made any changes while doing exception handling or // setjmp/longjmp handling, we have to create these functions for JavaScript // to call. - createSetThrewFunction(M); - createSetTempRet0Function(M); + assert(!M.getNamedGlobal(SetThrewFName) && "setThrew already exists"); + assert(!M.getNamedGlobal(SetTempRet0FName) && "setTempRet0 already exists"); + + // Create setThrew function + SmallVector Params = {Int1Ty, Int32Ty}; + FunctionType *FTy = FunctionType::get(VoidTy, Params, false); + Function *F = + Function::Create(FTy, GlobalValue::ExternalLinkage, SetThrewFName, &M); + Argument *Arg1 = &*(F->arg_begin()); + Argument *Arg2 = &*(++F->arg_begin()); + Arg1->setName("threw"); + Arg2->setName("value"); + BasicBlock *EntryBB = BasicBlock::Create(C, "entry", F); + BasicBlock *ThenBB = BasicBlock::Create(C, "if.then", F); + BasicBlock *EndBB = BasicBlock::Create(C, "if.end", F); + + IRB.SetInsertPoint(EntryBB); + Value *Threw = IRB.CreateLoad(ThrewGV, ThrewGV->getName() + ".val"); + Value *Cmp = IRB.CreateICmpEQ(Threw, IRB.getFalse(), "cmp"); + IRB.CreateCondBr(Cmp, ThenBB, EndBB); + + IRB.SetInsertPoint(ThenBB); + IRB.CreateStore(Arg1, ThrewGV); + IRB.CreateStore(Arg2, ThrewValueGV); + IRB.CreateBr(EndBB); + + IRB.SetInsertPoint(EndBB); + IRB.CreateRetVoid(); + + // Create setTempRet0 function + Params = {Int32Ty}; + FTy = FunctionType::get(VoidTy, Params, false); + F = Function::Create(FTy, GlobalValue::ExternalLinkage, SetTempRet0FName, &M); + F->arg_begin()->setName("value"); + EntryBB = BasicBlock::Create(C, "entry", F); + IRB.SetInsertPoint(EntryBB); + IRB.CreateStore(&*F->arg_begin(), TempRet0GV); + IRB.CreateRetVoid(); return true; } @@ -832,19 +406,73 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) { bool NeedInvoke = AllowExceptions && canThrow(II->getCalledValue()); if (NeedInvoke) { - // Wrap invoke with invoke wrapper and generate preamble/postamble - Value *Threw = wrapInvoke(II); + // If we are calling a function that is noreturn, we must remove that + // attribute. The code we insert here does expect it to return, after we + // catch the exception. + if (II->doesNotReturn()) { + if (auto *F = dyn_cast(II->getCalledValue())) + F->removeFnAttr(Attribute::NoReturn); + AttributeSet NewAttrs = II->getAttributes(); + NewAttrs.removeAttribute(C, AttributeSet::FunctionIndex, + Attribute::NoReturn); + II->setAttributes(NewAttrs); + } + + // Pre-invoke + // __THREW__ = 0; + IRB.CreateStore(IRB.getFalse(), ThrewGV); + + // Invoke function wrapper in JavaScript + SmallVector CallArgs; + // Put the pointer to the callee as first argument, so it can be called + // within the invoke wrapper later + CallArgs.push_back(II->getCalledValue()); + CallArgs.append(II->arg_begin(), II->arg_end()); + CallInst *NewCall = IRB.CreateCall(getInvokeWrapper(M, II), CallArgs); + NewCall->takeName(II); + NewCall->setCallingConv(II->getCallingConv()); + NewCall->setDebugLoc(II->getDebugLoc()); + + // Because we added the pointer to the callee as first argument, all + // argument attribute indices have to be incremented by one. + SmallVector AttributesVec; + const AttributeSet &InvokePAL = II->getAttributes(); + CallSite::arg_iterator AI = II->arg_begin(); + unsigned i = 1; // Argument attribute index starts from 1 + for (unsigned e = II->getNumArgOperands(); i <= e; ++AI, ++i) { + if (InvokePAL.hasAttributes(i)) { + AttrBuilder B(InvokePAL, i); + AttributesVec.push_back(AttributeSet::get(C, i + 1, B)); + } + } + // Add any return attributes. + if (InvokePAL.hasAttributes(AttributeSet::ReturnIndex)) + AttributesVec.push_back( + AttributeSet::get(C, InvokePAL.getRetAttributes())); + // Add any function attributes. + if (InvokePAL.hasAttributes(AttributeSet::FunctionIndex)) + AttributesVec.push_back( + AttributeSet::get(C, InvokePAL.getFnAttributes())); + // Reconstruct the AttributesList based on the vector we constructed. + AttributeSet NewCallPAL = AttributeSet::get(C, AttributesVec); + NewCall->setAttributes(NewCallPAL); + + II->replaceAllUsesWith(NewCall); ToErase.push_back(II); + // Post-invoke + // %__THREW__.val = __THREW__; __THREW__ = 0; + Value *Threw = IRB.CreateLoad(ThrewGV, ThrewGV->getName() + ".val"); + IRB.CreateStore(IRB.getFalse(), ThrewGV); + // Insert a branch based on __THREW__ variable - Value *Cmp = IRB.CreateICmpEQ(Threw, IRB.getInt32(1), "cmp"); - IRB.CreateCondBr(Cmp, II->getUnwindDest(), II->getNormalDest()); + IRB.CreateCondBr(Threw, II->getUnwindDest(), II->getNormalDest()); } else { // This can't throw, and we don't need this invoke, just replace it with a // call+branch - SmallVector Args(II->arg_begin(), II->arg_end()); - CallInst *NewCall = IRB.CreateCall(II->getCalledValue(), Args); + SmallVector CallArgs(II->arg_begin(), II->arg_end()); + CallInst *NewCall = IRB.CreateCall(II->getCalledValue(), CallArgs); NewCall->takeName(II); NewCall->setCallingConv(II->getCallingConv()); NewCall->setDebugLoc(II->getDebugLoc()); @@ -871,8 +499,11 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) { Value *Input = RI->getValue(); IRB.SetInsertPoint(RI); Value *Low = IRB.CreateExtractValue(Input, 0, "low"); + // Create a call to __resumeException function - IRB.CreateCall(ResumeF, {Low}); + Value *Args[] = {Low}; + IRB.CreateCall(ResumeF, Args); + // Add a terminator to the block IRB.CreateUnreachable(); ToErase.push_back(RI); @@ -917,7 +548,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) { // in the interface between JS and wasm, break out filter operands into // their component elements. if (LPI->isFilter(i)) { - auto *ATy = cast(Clause->getType()); + ArrayType *ATy = cast(Clause->getType()); for (unsigned j = 0, e = ATy->getNumElements(); j < e; ++j) { Value *EV = IRB.CreateExtractValue(Clause, makeArrayRef(j), "filter"); FMCArgs.push_back(EV); @@ -931,8 +562,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) { CallInst *FMCI = IRB.CreateCall(FMCF, FMCArgs, "fmc"); Value *Undef = UndefValue::get(LPI->getType()); Value *Pair0 = IRB.CreateInsertValue(Undef, FMCI, 0, "pair0"); - Value *TempRet0 = - IRB.CreateLoad(TempRet0GV, TempRet0GV->getName() + ".val"); + Value *TempRet0 = IRB.CreateLoad(TempRet0GV, TempRet0GV->getName() + "val"); Value *Pair1 = IRB.CreateInsertValue(Pair0, TempRet0, 1, "pair1"); LPI->replaceAllUsesWith(Pair1); @@ -947,235 +577,6 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) { } bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { - Module &M = *F.getParent(); - LLVMContext &C = F.getContext(); - IRBuilder<> IRB(C); - SmallVector ToErase; - // Vector of %setjmpTable values - std::vector SetjmpTableInsts; - // Vector of %setjmpTableSize values - std::vector SetjmpTableSizeInsts; - - // Setjmp preparation - - // This instruction effectively means %setjmpTableSize = 4. - // We create this as an instruction intentionally, and we don't want to fold - // this instruction to a constant 4, because this value will be used in - // SSAUpdater.AddAvailableValue(...) later. - BasicBlock &EntryBB = F.getEntryBlock(); - BinaryOperator *SetjmpTableSize = BinaryOperator::Create( - Instruction::Add, IRB.getInt32(4), IRB.getInt32(0), "setjmpTableSize", - &*EntryBB.getFirstInsertionPt()); - // setjmpTable = (int *) malloc(40); - Instruction *SetjmpTable = CallInst::CreateMalloc( - SetjmpTableSize, IRB.getInt32Ty(), IRB.getInt32Ty(), IRB.getInt32(40), - nullptr, nullptr, "setjmpTable"); - // setjmpTable[0] = 0; - IRB.SetInsertPoint(SetjmpTableSize); - IRB.CreateStore(IRB.getInt32(0), SetjmpTable); - SetjmpTableInsts.push_back(SetjmpTable); - SetjmpTableSizeInsts.push_back(SetjmpTableSize); - - // Setjmp transformation - std::vector SetjmpRetPHIs; - Function *SetjmpF = M.getFunction("setjmp"); - for (User *U : SetjmpF->users()) { - auto *CI = dyn_cast(U); - if (!CI) - report_fatal_error("Does not support indirect calls to setjmp"); - - BasicBlock *BB = CI->getParent(); - if (BB->getParent() != &F) // in other function - continue; - - // The tail is everything right after the call, and will be reached once - // when setjmp is called, and later when longjmp returns to the setjmp - BasicBlock *Tail = SplitBlock(BB, CI->getNextNode()); - // Add a phi to the tail, which will be the output of setjmp, which - // indicates if this is the first call or a longjmp back. The phi directly - // uses the right value based on where we arrive from - IRB.SetInsertPoint(Tail->getFirstNonPHI()); - PHINode *SetjmpRet = IRB.CreatePHI(IRB.getInt32Ty(), 2, "setjmp.ret"); - - // setjmp initial call returns 0 - SetjmpRet->addIncoming(IRB.getInt32(0), BB); - // The proper output is now this, not the setjmp call itself - CI->replaceAllUsesWith(SetjmpRet); - // longjmp returns to the setjmp will add themselves to this phi - SetjmpRetPHIs.push_back(SetjmpRet); - - // Fix call target - // Our index in the function is our place in the array + 1 to avoid index - // 0, because index 0 means the longjmp is not ours to handle. - IRB.SetInsertPoint(CI); - Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()), - SetjmpTable, SetjmpTableSize}; - Instruction *NewSetjmpTable = - IRB.CreateCall(SaveSetjmpF, Args, "setjmpTable"); - Instruction *NewSetjmpTableSize = - IRB.CreateLoad(TempRet0GV, "setjmpTableSize"); - SetjmpTableInsts.push_back(NewSetjmpTable); - SetjmpTableSizeInsts.push_back(NewSetjmpTableSize); - ToErase.push_back(CI); - } - - // Update each call that can longjmp so it can return to a setjmp where - // relevant. - - // Because we are creating new BBs while processing and don't want to make - // all these newly created BBs candidates again for longjmp processing, we - // first make the vector of candidate BBs. - std::vector BBs; - for (BasicBlock &BB : F) - BBs.push_back(&BB); - - // BBs.size() will change within the loop, so we query it every time - for (unsigned i = 0; i < BBs.size(); i++) { - BasicBlock *BB = BBs[i]; - for (Instruction &I : *BB) { - assert(!isa(&I)); - auto *CI = dyn_cast(&I); - if (!CI) - continue; - - const Value *Callee = CI->getCalledValue(); - if (!canLongjmp(M, Callee)) - continue; - - Value *Threw = nullptr; - BasicBlock *Tail; - if (Callee->getName().startswith(InvokePrefix)) { - // If invoke wrapper has already been generated for this call in - // previous EH phase, search for the load instruction - // %__THREW__.val = __THREW__; - // in postamble after the invoke wrapper call - LoadInst *ThrewLI = nullptr; - StoreInst *ThrewResetSI = nullptr; - for (auto I = std::next(BasicBlock::iterator(CI)), IE = BB->end(); - I != IE; ++I) { - if (auto *LI = dyn_cast(I)) - if (auto *GV = dyn_cast(LI->getPointerOperand())) - if (GV == ThrewGV) { - Threw = ThrewLI = LI; - break; - } - } - // Search for the store instruction after the load above - // __THREW__ = 0; - for (auto I = std::next(BasicBlock::iterator(ThrewLI)), IE = BB->end(); - I != IE; ++I) { - if (auto *SI = dyn_cast(I)) - if (auto *GV = dyn_cast(SI->getPointerOperand())) - if (GV == ThrewGV && SI->getValueOperand() == IRB.getInt32(0)) { - ThrewResetSI = SI; - break; - } - } - assert(Threw && ThrewLI && "Cannot find __THREW__ load after invoke"); - assert(ThrewResetSI && "Cannot find __THREW__ store after invoke"); - Tail = SplitBlock(BB, ThrewResetSI->getNextNode()); - - } else { - // Wrap call with invoke wrapper and generate preamble/postamble - Threw = wrapInvoke(CI); - ToErase.push_back(CI); - Tail = SplitBlock(BB, CI->getNextNode()); - } - - // We need to replace the terminator in Tail - SplitBlock makes BB go - // straight to Tail, we need to check if a longjmp occurred, and go to the - // right setjmp-tail if so - ToErase.push_back(BB->getTerminator()); - - // Generate a function call to testSetjmp function and preamble/postamble - // code to figure out (1) whether longjmp occurred (2) if longjmp - // occurred, which setjmp it corresponds to - Value *Label = nullptr; - Value *LongjmpResult = nullptr; - BasicBlock *EndBB = nullptr; - wrapTestSetjmp(BB, CI, Threw, SetjmpTable, SetjmpTableSize, Label, - LongjmpResult, EndBB); - assert(Label && LongjmpResult && EndBB); - - // Create switch instruction - IRB.SetInsertPoint(EndBB); - SwitchInst *SI = IRB.CreateSwitch(Label, Tail, SetjmpRetPHIs.size()); - // -1 means no longjmp happened, continue normally (will hit the default - // switch case). 0 means a longjmp that is not ours to handle, needs a - // rethrow. Otherwise the index is the same as the index in P+1 (to avoid - // 0). - for (unsigned i = 0; i < SetjmpRetPHIs.size(); i++) { - SI->addCase(IRB.getInt32(i + 1), SetjmpRetPHIs[i]->getParent()); - SetjmpRetPHIs[i]->addIncoming(LongjmpResult, EndBB); - } - - // We are splitting the block here, and must continue to find other calls - // in the block - which is now split. so continue to traverse in the Tail - BBs.push_back(Tail); - } - } - - // Erase everything we no longer need in this function - for (Instruction *I : ToErase) - I->eraseFromParent(); - - // Free setjmpTable buffer before each return instruction - for (BasicBlock &BB : F) { - TerminatorInst *TI = BB.getTerminator(); - if (isa(TI)) - CallInst::CreateFree(SetjmpTable, TI); - } - - // Every call to saveSetjmp can change setjmpTable and setjmpTableSize - // (when buffer reallocation occurs) - // entry: - // setjmpTableSize = 4; - // setjmpTable = (int *) malloc(40); - // setjmpTable[0] = 0; - // ... - // somebb: - // setjmpTable = saveSetjmp(buf, label, setjmpTable, setjmpTableSize); - // setjmpTableSize = __tempRet0; - // So we need to make sure the SSA for these variables is valid so that every - // saveSetjmp and testSetjmp calls have the correct arguments. - SSAUpdater SetjmpTableSSA; - SSAUpdater SetjmpTableSizeSSA; - SetjmpTableSSA.Initialize(Type::getInt32PtrTy(C), "setjmpTable"); - SetjmpTableSizeSSA.Initialize(Type::getInt32Ty(C), "setjmpTableSize"); - for (Instruction *I : SetjmpTableInsts) - SetjmpTableSSA.AddAvailableValue(I->getParent(), I); - for (Instruction *I : SetjmpTableSizeInsts) - SetjmpTableSizeSSA.AddAvailableValue(I->getParent(), I); - - for (auto UI = SetjmpTable->use_begin(), UE = SetjmpTable->use_end(); - UI != UE;) { - // Grab the use before incrementing the iterator. - Use &U = *UI; - // Increment the iterator before removing the use from the list. - ++UI; - if (Instruction *I = dyn_cast(U.getUser())) - if (I->getParent() != &EntryBB) - SetjmpTableSSA.RewriteUse(U); - } - for (auto UI = SetjmpTableSize->use_begin(), UE = SetjmpTableSize->use_end(); - UI != UE;) { - Use &U = *UI; - ++UI; - if (Instruction *I = dyn_cast(U.getUser())) - if (I->getParent() != &EntryBB) - SetjmpTableSizeSSA.RewriteUse(U); - } - - // Finally, our modifications to the cfg can break dominance of SSA variables. - // For example, in this code, - // if (x()) { .. setjmp() .. } - // if (y()) { .. longjmp() .. } - // We must split the longjmp block, and it can jump into the block splitted - // from setjmp one. But that means that when we split the setjmp block, it's - // first part no longer dominates its second part - there is a theoretically - // possible control flow path where x() is false, then y() is true and we - // reach the second part of the setjmp block, without ever reaching the first - // part. So, we rebuild SSA form here. - rebuildSSA(F); - return true; + // TODO + return false; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp index 66976a7..3aa2593 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -165,14 +165,7 @@ void WebAssemblyPassConfig::addIRPasses() { if (getOptLevel() != CodeGenOpt::None) addPass(createWebAssemblyOptimizeReturned()); - // If exception handling is not enabled, we lower invokes into calls and - // simplify CFG to delete unreachable landingpad blocks. - if (!EnableEmException) { - addPass(createLowerInvokePass()); - addPass(createCFGSimplificationPass()); - } - - // Handle exceptions and setjmp/longjmp if enabled. + // Handle exceptions. if (EnableEmException || EnableEmSjLj) addPass(createWebAssemblyLowerEmscriptenEHSjLj(EnableEmException, EnableEmSjLj)); diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll b/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll deleted file mode 100644 index 8283b49..0000000 --- a/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll +++ /dev/null @@ -1,61 +0,0 @@ -; RUN: llc < %s -enable-emscripten-cxx-exceptions | FileCheck %s --check-prefix=EH -; RUN: llc < %s -enable-emscripten-sjlj | FileCheck %s --check-prefix=SJLJ -; RUN: llc < %s | FileCheck %s --check-prefix=NONE - -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" - -%struct.__jmp_buf_tag = type { [6 x i32], i32, [32 x i32] } - -define hidden void @exception() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { -; EH-LABEL: type exception,@function -; NONE-LABEL: type exception,@function -entry: - invoke void @foo() - to label %try.cont unwind label %lpad -; EH: call __invoke_void@FUNCTION -; NONE: call foo@FUNCTION - -lpad: ; preds = %entry - %0 = landingpad { i8*, i32 } - catch i8* null - %1 = extractvalue { i8*, i32 } %0, 0 - %2 = extractvalue { i8*, i32 } %0, 1 - %3 = call i8* @__cxa_begin_catch(i8* %1) #2 - call void @__cxa_end_catch() - br label %try.cont - -try.cont: ; preds = %entry, %lpad - ret void -} - -define hidden void @setjmp_longjmp() { -; SJLJ-LABEL: type setjmp_longjmp,@function -; NONE-LABEL: type setjmp_longjmp,@function -entry: - %buf = alloca [1 x %struct.__jmp_buf_tag], align 16 - %arraydecay = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf, i32 0, i32 0 - %call = call i32 @setjmp(%struct.__jmp_buf_tag* %arraydecay) #0 - %arraydecay1 = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf, i32 0, i32 0 - call void @longjmp(%struct.__jmp_buf_tag* %arraydecay1, i32 1) #1 - unreachable -; SJLJ: i32.call ${{[a-zA-Z0-9]+}}=, saveSetjmp@FUNCTION -; SJLJ: i32.call ${{[a-zA-Z0-9]+}}=, testSetjmp@FUNCTION -; NONE: i32.call ${{[a-zA-Z0-9]+}}=, setjmp@FUNCTION -; NONE: call longjmp@FUNCTION -} - -declare void @foo() -declare i32 @__gxx_personality_v0(...) -declare i8* @__cxa_begin_catch(i8*) -declare void @__cxa_end_catch() -; Function Attrs: returns_twice -declare i32 @setjmp(%struct.__jmp_buf_tag*) #0 -; Function Attrs: noreturn -declare void @longjmp(%struct.__jmp_buf_tag*, i32) #1 -declare i8* @malloc(i32) -declare void @free(i8*) - -attributes #0 = { returns_twice } -attributes #1 = { noreturn } -attributes #2 = { nounwind } diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-exceptions-whitelist.ll b/llvm/test/CodeGen/WebAssembly/lower-em-exceptions-whitelist.ll index 5fcc399..e510bc9 100644 --- a/llvm/test/CodeGen/WebAssembly/lower-em-exceptions-whitelist.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-exceptions-whitelist.ll @@ -1,8 +1,5 @@ ; RUN: opt < %s -wasm-lower-em-ehsjlj -emscripten-cxx-exceptions-whitelist=do_catch -S | FileCheck %s -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" - define void @dont_catch() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { ; CHECK-LABEL: @dont_catch( entry: @@ -37,7 +34,7 @@ entry: invoke void @foo() to label %invoke.cont unwind label %lpad ; CHECK: entry: -; CHECK-NEXT: store i32 0, i32* +; CHECK-NEXT: store i1 false, i1* ; CHECK-NEXT: call void @__invoke_void(void ()* @foo) invoke.cont: ; preds = %entry diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll b/llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll index 60953cd..e9eb254 100644 --- a/llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll @@ -1,13 +1,10 @@ ; RUN: opt < %s -wasm-lower-em-ehsjlj -S | FileCheck %s -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" - @_ZTIi = external constant i8* @_ZTIc = external constant i8* -; CHECK-DAG: @[[__THREW__:__THREW__.*]] = global i32 0 -; CHECK-DAG: @[[THREWVALUE:__threwValue.*]] = global i32 0 -; CHECK-DAG: @[[TEMPRET0:__tempRet0.*]] = global i32 0 +; CHECK: @[[__THREW__:__THREW__.*]] = global i1 false +; CHECK: @[[THREWVALUE:__threwValue.*]] = global i32 0 +; CHECK: @[[TEMPRET0:__tempRet0.*]] = global i32 0 ; Test invoke instruction with clauses (try-catch block) define void @clause() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { @@ -16,12 +13,11 @@ entry: invoke void @foo(i32 3) to label %invoke.cont unwind label %lpad ; CHECK: entry: -; CHECK-NEXT: store i32 0, i32* @[[__THREW__]] +; CHECK-NEXT: store i1 false, i1* @[[__THREW__]] ; CHECK-NEXT: call void @__invoke_void_i32(void (i32)* @foo, i32 3) -; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i32, i32* @[[__THREW__]] -; CHECK-NEXT: store i32 0, i32* @[[__THREW__]] -; CHECK-NEXT: %cmp = icmp eq i32 %[[__THREW__VAL]], 1 -; CHECK-NEXT: br i1 %cmp, label %lpad, label %invoke.cont +; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i1, i1* @[[__THREW__]] +; CHECK-NEXT: store i1 false, i1* @[[__THREW__]] +; CHECK-NEXT: br i1 %[[__THREW__VAL]], label %lpad, label %invoke.cont invoke.cont: ; preds = %entry br label %try.cont @@ -72,12 +68,11 @@ entry: invoke void @foo(i32 3) to label %invoke.cont unwind label %lpad ; CHECK: entry: -; CHECK-NEXT: store i32 0, i32* @[[__THREW__]] +; CHECK-NEXT: store i1 false, i1* @[[__THREW__]] ; CHECK-NEXT: call void @__invoke_void_i32(void (i32)* @foo, i32 3) -; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i32, i32* @[[__THREW__]] -; CHECK-NEXT: store i32 0, i32* @[[__THREW__]] -; CHECK-NEXT: %cmp = icmp eq i32 %[[__THREW__VAL]], 1 -; CHECK-NEXT: br i1 %cmp, label %lpad, label %invoke.cont +; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i1, i1* @[[__THREW__]] +; CHECK-NEXT: store i1 false, i1* @[[__THREW__]] +; CHECK-NEXT: br i1 %[[__THREW__VAL]], label %lpad, label %invoke.cont invoke.cont: ; preds = %entry ret void @@ -123,7 +118,7 @@ entry: %0 = invoke noalias i8* @bar(i8 signext 1, i8 zeroext 2) to label %invoke.cont unwind label %lpad ; CHECK: entry: -; CHECK-NEXT: store i32 0, i32* @[[__THREW__]] +; CHECK-NEXT: store i1 false, i1* @[[__THREW__]] ; CHECK-NEXT: %0 = call noalias i8* @"__invoke_i8*_i8_i8"(i8* (i8, i8)* @bar, i8 signext 1, i8 zeroext 2) invoke.cont: ; preds = %entry @@ -167,19 +162,19 @@ declare i8* @__cxa_begin_catch(i8*) declare void @__cxa_end_catch() declare void @__cxa_call_unexpected(i8*) -; JS glue functions and invoke wrappers declaration -; CHECK-DAG: declare void @__resumeException(i8*) -; CHECK-DAG: declare void @__invoke_void_i32(void (i32)*, i32) -; CHECK-DAG: declare i8* @__cxa_find_matching_catch_4(i8*, i8*) +; JS glue functions and invoke wrappers registration +; CHECK: declare void @__resumeException(i8*) +; CHECK: declare void @__invoke_void_i32(void (i32)*, i32) +; CHECK: declare i8* @__cxa_find_matching_catch_4(i8*, i8*) ; setThrew function creation -; CHECK-LABEL: define void @setThrew(i32 %threw, i32 %value) { +; CHECK-LABEL: define void @setThrew(i1 %threw, i32 %value) { ; CHECK: entry: -; CHECK-NEXT: %[[__THREW__]].val = load i32, i32* @[[__THREW__]] -; CHECK-NEXT: %cmp = icmp eq i32 %[[__THREW__]].val, 0 +; CHECK-NEXT: %[[__THREW__]].val = load i1, i1* @[[__THREW__]] +; CHECK-NEXT: %cmp = icmp eq i1 %[[__THREW__]].val, false ; CHECK-NEXT: br i1 %cmp, label %if.then, label %if.end ; CHECK: if.then: -; CHECK-NEXT: store i32 %threw, i32* @[[__THREW__]] +; CHECK-NEXT: store i1 %threw, i1* @[[__THREW__]] ; CHECK-NEXT: store i32 %value, i32* @[[THREWVALUE]] ; CHECK-NEXT: br label %if.end ; CHECK: if.end: diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll deleted file mode 100644 index 40b9d62..0000000 --- a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll +++ /dev/null @@ -1,213 +0,0 @@ -; RUN: opt < %s -wasm-lower-em-ehsjlj -S | FileCheck %s - -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" - -%struct.__jmp_buf_tag = type { [6 x i32], i32, [32 x i32] } - -@global_var = hidden global i32 0, align 4 -; CHECK-DAG: @[[__THREW__:__THREW__.*]] = global i32 0 -; CHECK-DAG: @[[THREWVALUE:__threwValue.*]] = global i32 0 -; CHECK-DAG: @[[TEMPRET0:__tempRet0.*]] = global i32 0 - -; Test a simple setjmp - longjmp sequence -define hidden void @setjmp_longjmp() { -; CHECK-LABEL: @setjmp_longjmp -entry: - %buf = alloca [1 x %struct.__jmp_buf_tag], align 16 - %arraydecay = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf, i32 0, i32 0 - %call = call i32 @setjmp(%struct.__jmp_buf_tag* %arraydecay) #0 - %arraydecay1 = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf, i32 0, i32 0 - call void @longjmp(%struct.__jmp_buf_tag* %arraydecay1, i32 1) #1 - unreachable -; CHECK: entry: -; CHECK-NEXT: %[[MALLOCCALL:.*]] = tail call i8* @malloc(i32 40) -; CHECK-NEXT: %[[SETJMP_TABLE:.*]] = bitcast i8* %[[MALLOCCALL]] to i32* -; CHECK-NEXT: store i32 0, i32* %[[SETJMP_TABLE]] -; CHECK-NEXT: %[[SETJMP_TABLE_SIZE:.*]] = add i32 4, 0 -; CHECK-NEXT: %[[BUF:.*]] = alloca [1 x %struct.__jmp_buf_tag] -; CHECK-NEXT: %[[ARRAYDECAY:.*]] = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %[[BUF]], i32 0, i32 0 -; CHECK-NEXT: %[[SETJMP_TABLE1:.*]] = call i32* @saveSetjmp(%struct.__jmp_buf_tag* %[[ARRAYDECAY]], i32 1, i32* %[[SETJMP_TABLE]], i32 %[[SETJMP_TABLE_SIZE]]) -; CHECK-NEXT: %[[SETJMP_TABLE_SIZE1:.*]] = load i32, i32* @[[TEMPRET0]] -; CHECK-NEXT: br label %entry.split - -; CHECK: entry.split: -; CHECK-NEXT: phi i32 [ 0, %entry ], [ %[[LONGJMP_RESULT:.*]], %if.end ] -; CHECK-NEXT: %[[ARRAYDECAY1:.*]] = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %[[BUF]], i32 0, i32 0 -; CHECK-NEXT: store i32 0, i32* @[[__THREW__]] -; CHECK-NEXT: call void @"__invoke_void_%struct.__jmp_buf_tag*_i32"(void (%struct.__jmp_buf_tag*, i32)* @emscripten_longjmp_jmpbuf, %struct.__jmp_buf_tag* %[[ARRAYDECAY1]], i32 1) -; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i32, i32* @[[__THREW__]] -; CHECK-NEXT: store i32 0, i32* @[[__THREW__]] -; CHECK-NEXT: %[[CMP0:.*]] = icmp ne i32 %__THREW__.val, 0 -; CHECK-NEXT: %[[THREWVALUE_VAL:.*]] = load i32, i32* @[[THREWVALUE]] -; CHECK-NEXT: %[[CMP1:.*]] = icmp ne i32 %[[THREWVALUE_VAL]], 0 -; CHECK-NEXT: %[[CMP:.*]] = and i1 %[[CMP0]], %[[CMP1]] -; CHECK-NEXT: br i1 %[[CMP]], label %if.then1, label %if.else1 - -; CHECK: entry.split.split: -; CHECK-NEXT: unreachable - -; CHECK: if.then1: -; CHECK-NEXT: %[[__THREW__VAL_I32P:.*]] = inttoptr i32 %[[__THREW__VAL]] to i32* -; CHECK-NEXT: %[[__THREW__VAL_I32P_LOADED:.*]] = load i32, i32* %[[__THREW__VAL_I32P]] -; CHECK-NEXT: %[[LABEL:.*]] = call i32 @testSetjmp(i32 %[[__THREW__VAL_I32P_LOADED]], i32* %[[SETJMP_TABLE1]], i32 %[[SETJMP_TABLE_SIZE1]]) -; CHECK-NEXT: %[[CMP:.*]] = icmp eq i32 %[[LABEL]], 0 -; CHECK-NEXT: br i1 %[[CMP]], label %if.then2, label %if.end2 - -; CHECK: if.else1: -; CHECK-NEXT: br label %if.end - -; CHECK: if.end: -; CHECK-NEXT: %[[LABEL_PHI:.*]] = phi i32 [ %[[LABEL:.*]], %if.end2 ], [ -1, %if.else1 ] -; CHECK-NEXT: %[[LONGJMP_RESULT]] = load i32, i32* @[[TEMPRET0]] -; CHECK-NEXT: switch i32 %[[LABEL_PHI]], label %entry.split.split [ -; CHECK-NEXT: i32 1, label %entry.split -; CHECK-NEXT: ] - -; CHECK: if.then2: -; CHECK-NEXT: call void @emscripten_longjmp(i32 %[[__THREW__VAL]], i32 %[[THREWVALUE_VAL]]) -; CHECK-NEXT: unreachable - -; CHECK: if.end2: -; CHECK-NEXT: store i32 %[[THREWVALUE_VAL]], i32* @[[TEMPRET0]] -; CHECK-NEXT: br label %if.end -} - -; Test a case of a function call (which is not longjmp) after a setjmp -define hidden void @setjmp_other() { -; CHECK-LABEL: @setjmp_other -entry: - %buf = alloca [1 x %struct.__jmp_buf_tag], align 16 - %arraydecay = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf, i32 0, i32 0 - %call = call i32 @setjmp(%struct.__jmp_buf_tag* %arraydecay) #0 - call void @foo() - ret void -; CHECK: entry: -; CHECK: %[[SETJMP_TABLE:.*]] = call i32* @saveSetjmp( - -; CHECK: entry.split: -; CHECK: call void @__invoke_void(void ()* @foo) - -; CHECK: entry.split.split: -; CHECK-NEXT: %[[BUF:.*]] = bitcast i32* %[[SETJMP_TABLE]] to i8* -; CHECK-NEXT: tail call void @free(i8* %[[BUF]]) -; CHECK-NEXT: ret void -} - -; Test a case when a function call is within try-catch, after a setjmp -define hidden void @exception_and_longjmp() #3 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { -; CHECK-LABEL: @exception_and_longjmp -entry: - %buf = alloca [1 x %struct.__jmp_buf_tag], align 16 - %arraydecay = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf, i32 0, i32 0 - %call = call i32 @setjmp(%struct.__jmp_buf_tag* %arraydecay) #0 - invoke void @foo() - to label %try.cont unwind label %lpad - -; CHECK: entry.split: -; CHECK: store i32 0, i32* @[[__THREW__]] -; CHECK-NEXT: call void @__invoke_void(void ()* @foo) -; CHECK-NEXT: %[[__THREW__VAL:.*]] = load i32, i32* @[[__THREW__]] -; CHECK-NEXT: store i32 0, i32* @[[__THREW__]] -; CHECK-NEXT: %[[CMP0:.*]] = icmp ne i32 %[[__THREW__VAL]], 0 -; CHECK-NEXT: %[[THREWVALUE_VAL:.*]] = load i32, i32* @[[THREWVALUE]] -; CHECK-NEXT: %[[CMP1:.*]] = icmp ne i32 %[[THREWVALUE_VAL]], 0 -; CHECK-NEXT: %[[CMP:.*]] = and i1 %[[CMP0]], %[[CMP1]] -; CHECK-NEXT: br i1 %[[CMP]], label %if.then1, label %if.else1 - -; CHECK: entry.split.split: -; CHECK-NEXT: %[[CMP:.*]] = icmp eq i32 %[[__THREW__VAL]], 1 -; CHECK-NEXT: br i1 %[[CMP]], label %lpad, label %try.cont - -lpad: ; preds = %entry - %0 = landingpad { i8*, i32 } - catch i8* null - %1 = extractvalue { i8*, i32 } %0, 0 - %2 = extractvalue { i8*, i32 } %0, 1 - %3 = call i8* @__cxa_begin_catch(i8* %1) #2 - call void @__cxa_end_catch() - br label %try.cont - -try.cont: ; preds = %entry, %lpad - ret void -} - -; Test SSA validity -define hidden void @ssa(i32 %n) { -; CHECK-LABEL: @ssa -entry: - %buf = alloca [1 x %struct.__jmp_buf_tag], align 16 - %cmp = icmp sgt i32 %n, 5 - br i1 %cmp, label %if.then, label %if.end -; CHECK: entry: -; CHECK: %[[SETJMP_TABLE0:.*]] = bitcast i8* -; CHECK: %[[SETJMP_TABLE_SIZE0:.*]] = add i32 4, 0 - -if.then: ; preds = %entry - %0 = load i32, i32* @global_var, align 4 - %arraydecay = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf, i32 0, i32 0 - %call = call i32 @setjmp(%struct.__jmp_buf_tag* %arraydecay) #0 - store i32 %0, i32* @global_var, align 4 - br label %if.end -; CHECK: if.then: -; CHECK: %[[VAR0:.*]] = load i32, i32* @global_var, align 4 -; CHECK: %[[SETJMP_TABLE1:.*]] = call i32* @saveSetjmp( -; CHECK-NEXT: %[[SETJMP_TABLE_SIZE1:.*]] = load i32, i32* @[[TEMPRET0]] - -; CHECK: if.then.split: -; CHECK: %[[VAR1:.*]] = phi i32 [ %[[VAR0]], %if.then ], [ %[[VAR2:.*]], %if.end3 ] -; CHECK: %[[SETJMP_TABLE_SIZE2:.*]] = phi i32 [ %[[SETJMP_TABLE_SIZE1]], %if.then ], [ %[[SETJMP_TABLE_SIZE3:.*]], %if.end3 ] -; CHECK: %[[SETJMP_TABLE2:.*]] = phi i32* [ %[[SETJMP_TABLE1]], %if.then ], [ %[[SETJMP_TABLE3:.*]], %if.end3 ] -; CHECK: store i32 %[[VAR1]], i32* @global_var, align 4 - -if.end: ; preds = %if.then, %entry - %arraydecay1 = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf, i32 0, i32 0 - call void @longjmp(%struct.__jmp_buf_tag* %arraydecay1, i32 5) #1 - unreachable -; CHECK: if.end: -; CHECK: %[[VAR2]] = phi i32 [ %[[VAR1]], %if.then.split ], [ undef, %entry ] -; CHECK: %[[SETJMP_TABLE_SIZE3]] = phi i32 [ %[[SETJMP_TABLE_SIZE2]], %if.then.split ], [ %[[SETJMP_TABLE_SIZE0]], %entry ] -; CHECK: %[[SETJMP_TABLE3]] = phi i32* [ %[[SETJMP_TABLE2]], %if.then.split ], [ %[[SETJMP_TABLE0]], %entry ] -} - -; Test a case when a function only calls other functions that are neither setjmp nor longjmp -define hidden void @only_other_func() { -entry: - call void @foo() - ret void -; CHECK: call void @foo() -} - -; Test a case when a function only calls longjmp and not setjmp -define hidden void @only_longjmp() { -entry: - %buf = alloca [1 x %struct.__jmp_buf_tag], align 16 - %arraydecay = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf, i32 0, i32 0 - call void @longjmp(%struct.__jmp_buf_tag* %arraydecay, i32 5) #1 - unreachable -; CHECK: %[[ARRAYDECAY:.*]] = getelementptr inbounds -; CHECK-NEXT: call void @emscripten_longjmp_jmpbuf(%struct.__jmp_buf_tag* %[[ARRAYDECAY]], i32 5) #1 -} - -declare void @foo() -; Function Attrs: returns_twice -declare i32 @setjmp(%struct.__jmp_buf_tag*) #0 -; Function Attrs: noreturn -declare void @longjmp(%struct.__jmp_buf_tag*, i32) #1 -declare i32 @__gxx_personality_v0(...) -declare i8* @__cxa_begin_catch(i8*) -declare void @__cxa_end_catch() -declare i8* @malloc(i32) -declare void @free(i8*) - -; JS glue functions and invoke wrappers declaration -; CHECK-DAG: declare i32* @saveSetjmp(%struct.__jmp_buf_tag*, i32, i32*, i32) -; CHECK-DAG: declare i32 @testSetjmp(i32, i32*, i32) -; CHECK-DAG: declare void @emscripten_longjmp_jmpbuf(%struct.__jmp_buf_tag*, i32) -; CHECK-DAG: declare void @emscripten_longjmp(i32, i32) -; CHECK-DAG: declare void @__invoke_void(void ()*) -; CHECK-DAG: declare void @"__invoke_void_%struct.__jmp_buf_tag*_i32"(void (%struct.__jmp_buf_tag*, i32)*, %struct.__jmp_buf_tag*, i32) - -attributes #0 = { returns_twice } -attributes #1 = { noreturn } -attributes #2 = { nounwind } -- 2.7.4