diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h @@ -77,6 +77,9 @@ MVT getRegType(unsigned RegNo) const; std::string regToString(const MachineOperand &MO); WebAssemblyTargetStreamer *getTargetStreamer(); + MCSymbolWasm *getMCSymbolForFunction(const Function *F, bool EnableEmEH, + wasm::WasmSignature *Sig, + bool &InvokeDetected); }; } // end namespace llvm diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -49,6 +49,8 @@ #define DEBUG_TYPE "asm-printer" extern cl::opt WasmKeepRegisters; +extern cl::opt EnableEmException; +extern cl::opt EnableEmSjLj; //===----------------------------------------------------------------------===// // Helpers. @@ -81,10 +83,91 @@ return static_cast(TS); } +// Emscripten exception handling helpers +// +// This converts invoke names generated by LowerEmscriptenEHSjLj to real names +// that are expected by JavaScript glue code. The invoke names generated by +// Emscripten JS glue code are based on their argument and return types; for +// example, for a function that takes an i32 and returns nothing, it is +// 'invoke_vi'. But the format of invoke generated by LowerEmscriptenEHSjLj pass +// contains a mangled string generated from their IR types, for example, +// "__invoke_void_%struct.mystruct*_int", because final wasm types are not +// available in the IR pass. So we convert those names to the form that +// Emscripten JS code expects. +// +// Refer to LowerEmscriptenEHSjLj pass for more details. + +// Returns true if the given function name is an invoke name generated by +// LowerEmscriptenEHSjLj pass. +static bool isEmscriptenInvokeName(StringRef Name) { + if (Name.front() == '"' && Name.back() == '"') + Name = Name.substr(1, Name.size() - 2); + return Name.startswith("__invoke_"); +} + +// Returns a character that represents the given wasm value type in invoke +// signatures. +static char getInvokeSig(wasm::ValType VT) { + switch (VT) { + case wasm::ValType::I32: + return 'i'; + case wasm::ValType::I64: + return 'j'; + case wasm::ValType::F32: + return 'f'; + case wasm::ValType::F64: + return 'd'; + case wasm::ValType::V128: + return 'V'; + case wasm::ValType::EXNREF: + return 'E'; + case wasm::ValType::EXTERNREF: + return 'X'; + } +} + +// Given the wasm signature, generate the invoke name in the format JS glue code +// expects. +static std::string getEmscriptenInvokeSymbolName(wasm::WasmSignature *Sig) { + assert(Sig->Returns.size() <= 1); + std::string Ret = "invoke_"; + if (!Sig->Returns.empty()) + for (auto VT : Sig->Returns) + Ret += getInvokeSig(VT); + else + Ret += 'v'; + // Invokes' first argument is a pointer to the original function, so skip it + for (unsigned I = 1, E = Sig->Params.size(); I < E; I++) + Ret += getInvokeSig(Sig->Params[I]); + return Ret; +} + //===----------------------------------------------------------------------===// // WebAssemblyAsmPrinter Implementation. //===----------------------------------------------------------------------===// +MCSymbolWasm *WebAssemblyAsmPrinter::getMCSymbolForFunction( + const Function *F, bool EnableEmEH, wasm::WasmSignature *Sig, + bool &InvokeDetected) { + MCSymbolWasm *WasmSym = nullptr; + if (EnableEmEH && isEmscriptenInvokeName(F->getName())) { + assert(Sig); + InvokeDetected = true; + if (Sig->Returns.size() > 1) { + std::string Msg = + "Emscripten EH/SjLj does not support multivalue returns: " + + std::string(F->getName()) + ": " + + WebAssembly::signatureToString(Sig); + report_fatal_error(Msg); + } + WasmSym = cast( + GetExternalSymbolSymbol(getEmscriptenInvokeSymbolName(Sig))); + } else { + WasmSym = cast(getSymbol(F)); + } + return WasmSym; +} + void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) { for (auto &It : OutContext.getSymbols()) { // Emit a .globaltype and .eventtype declaration. @@ -95,6 +178,7 @@ getTargetStreamer()->emitEventType(Sym); } + DenseSet InvokeSymbols; for (const auto &F : M) { if (F.isIntrinsic()) continue; @@ -104,31 +188,46 @@ SmallVector Results; SmallVector Params; computeSignatureVTs(F.getFunctionType(), &F, F, TM, Params, Results); - auto *Sym = cast(getSymbol(&F)); + // At this point these MCSymbols may or may not have been created already + // and thus also contain a signature, but we need to get the signature + // anyway here in case it is an invoke that has not yet been created. We + // will discard it later if it turns out not to be necessary. + auto Signature = signatureFromMVTs(Results, Params); + bool InvokeDetected = false; + auto *Sym = getMCSymbolForFunction(&F, EnableEmException || EnableEmSjLj, + Signature.get(), InvokeDetected); + + // Multiple functions can be mapped to the same invoke symbol. For + // example, two IR functions '__invoke_void_i8*' and '__invoke_void_i32' + // are both mapped to '__invoke_vi'. We keep them in a set once we emit an + // Emscripten EH symbol so we don't emit the same symbol twice. + if (InvokeDetected && !InvokeSymbols.insert(Sym).second) + continue; + Sym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); if (!Sym->getSignature()) { - auto Signature = signatureFromMVTs(Results, Params); Sym->setSignature(Signature.get()); addSignature(std::move(Signature)); + } else { + // This symbol has already been created and had a signature. Discard it. + Signature.reset(); } - // FIXME: this was originally intended for post-linking and was only used - // for imports that were only called indirectly (i.e. s2wasm could not - // infer the type from a call). With object files it applies to all - // imports. so fix the names and the tests, or rethink how import - // delcarations work in asm files. + getTargetStreamer()->emitFunctionType(Sym); - if (TM.getTargetTriple().isOSBinFormatWasm() && - F.hasFnAttribute("wasm-import-module")) { + if (F.hasFnAttribute("wasm-import-module")) { StringRef Name = F.getFnAttribute("wasm-import-module").getValueAsString(); Sym->setImportModule(storeName(Name)); getTargetStreamer()->emitImportModule(Sym, Name); } - if (TM.getTargetTriple().isOSBinFormatWasm() && - F.hasFnAttribute("wasm-import-name")) { + if (F.hasFnAttribute("wasm-import-name")) { + // If this is a converted Emscripten EH/SjLj symbol, we shouldn't use + // the original function name but the converted symbol name. StringRef Name = - F.getFnAttribute("wasm-import-name").getValueAsString(); + InvokeDetected + ? Sym->getName() + : F.getFnAttribute("wasm-import-name").getValueAsString(); Sym->setImportName(storeName(Name)); getTargetStreamer()->emitImportName(Sym, Name); } @@ -304,7 +403,6 @@ addSignature(std::move(Signature)); WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); - // FIXME: clean up how params and results are emitted (use signatures) getTargetStreamer()->emitFunctionType(WasmSym); // Emit the function index. diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -140,8 +140,7 @@ /// 1) Lower /// longjmp(buf, value) /// into -/// emscripten_longjmp_jmpbuf(buf, value) -/// emscripten_longjmp_jmpbuf will be lowered to emscripten_longjmp later. +/// emscripten_longjmp(buf, value) /// /// In case calls to setjmp() exists /// @@ -196,14 +195,9 @@ /// 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. +/// case we just chain the longjmp to the caller. Label -1 means no longjmp +/// occurred. Otherwise we jump to the right post-setjmp BB based on the +/// label. /// ///===----------------------------------------------------------------------===// @@ -241,7 +235,6 @@ Function *ResumeF = nullptr; Function *EHTypeIDF = nullptr; Function *EmLongjmpF = nullptr; - Function *EmLongjmpJmpbufF = nullptr; Function *SaveSetjmpF = nullptr; Function *TestSetjmpF = nullptr; @@ -642,6 +635,30 @@ } } +// Replace uses of longjmp with emscripten_longjmp. emscripten_longjmp takes +// arguments of type {i32, i32} and longjmp takes {jmp_buf*, i32}, so we need a +// ptrtoint instruction here to make the type match. jmp_buf* will eventually be +// lowered to i32 in the wasm backend. +static void replaceLongjmpWithEmscriptenLongjmp(Function *LongjmpF, + Function *EmLongjmpF) { + SmallVector ToErase; + LLVMContext &C = LongjmpF->getParent()->getContext(); + IRBuilder<> IRB(C); + for (User *U : LongjmpF->users()) { + auto *CI = dyn_cast(U); + if (!CI) + report_fatal_error("Does not support indirect calls to longjmp"); + IRB.SetInsertPoint(CI); + Value *Jmpbuf = + IRB.CreatePtrToInt(CI->getArgOperand(0), IRB.getInt32Ty(), "jmpbuf"); + IRB.CreateCall(EmLongjmpF, {Jmpbuf, CI->getArgOperand(1)}); + ToErase.push_back(CI); + } + + for (auto *I : ToErase) + I->eraseFromParent(); +} + bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { LLVM_DEBUG(dbgs() << "********** Lower Emscripten EH & SjLj **********\n"); @@ -654,6 +671,10 @@ bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty(); bool DoSjLj = EnableSjLj && (SetjmpUsed || LongjmpUsed); + if ((EnableEH || DoSjLj) && + Triple(M.getTargetTriple()).getArch() == Triple::wasm64) + report_fatal_error("Emscripten EH/SjLj is not supported with wasm64 yet"); + auto *TPC = getAnalysisIfAvailable(); assert(TPC && "Expected a TargetPassConfig"); auto &TM = TPC->getTM(); @@ -696,22 +717,21 @@ if (DoSjLj) { Changed = true; // We have setjmp or longjmp somewhere - if (LongjmpF) { - // Replace all uses of longjmp with emscripten_longjmp_jmpbuf, which is - // defined in JS code - EmLongjmpJmpbufF = getEmscriptenFunction(LongjmpF->getFunctionType(), - "emscripten_longjmp_jmpbuf", &M); - LongjmpF->replaceAllUsesWith(EmLongjmpJmpbufF); - } + // Register emscripten_longjmp function + FunctionType *FTy = FunctionType::get( + IRB.getVoidTy(), {IRB.getInt32Ty(), IRB.getInt32Ty()}, false); + EmLongjmpF = getEmscriptenFunction(FTy, "emscripten_longjmp", &M); + + if (LongjmpF) + replaceLongjmpWithEmscriptenLongjmp(LongjmpF, EmLongjmpF); if (SetjmpF) { // Register saveSetjmp function FunctionType *SetjmpFTy = SetjmpF->getFunctionType(); - FunctionType *FTy = - FunctionType::get(Type::getInt32PtrTy(C), - {SetjmpFTy->getParamType(0), IRB.getInt32Ty(), - Type::getInt32PtrTy(C), IRB.getInt32Ty()}, - false); + FTy = FunctionType::get(Type::getInt32PtrTy(C), + {SetjmpFTy->getParamType(0), IRB.getInt32Ty(), + Type::getInt32PtrTy(C), IRB.getInt32Ty()}, + false); SaveSetjmpF = getEmscriptenFunction(FTy, "saveSetjmp", &M); // Register testSetjmp function @@ -720,10 +740,6 @@ {IRB.getInt32Ty(), Type::getInt32PtrTy(C), IRB.getInt32Ty()}, false); TestSetjmpF = getEmscriptenFunction(FTy, "testSetjmp", &M); - FTy = FunctionType::get(IRB.getVoidTy(), - {IRB.getInt32Ty(), IRB.getInt32Ty()}, false); - EmLongjmpF = getEmscriptenFunction(FTy, "emscripten_longjmp", &M); - // Only traverse functions that uses setjmp in order not to insert // unnecessary prep / cleanup code in every function SmallPtrSet SetjmpUsers; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -38,29 +38,34 @@ " instruction output for test purposes only."), cl::init(false)); +extern cl::opt EnableEmException; +extern cl::opt EnableEmSjLj; + static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI); MCSymbol * WebAssemblyMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const { const GlobalValue *Global = MO.getGlobal(); - auto *WasmSym = cast(Printer.getSymbol(Global)); - - if (const auto *FuncTy = dyn_cast(Global->getValueType())) { - const MachineFunction &MF = *MO.getParent()->getParent()->getParent(); - const TargetMachine &TM = MF.getTarget(); - const Function &CurrentFunc = MF.getFunction(); - - SmallVector ResultMVTs; - SmallVector ParamMVTs; - const auto *const F = dyn_cast(Global); - computeSignatureVTs(FuncTy, F, CurrentFunc, TM, ParamMVTs, ResultMVTs); - - auto Signature = signatureFromMVTs(ResultMVTs, ParamMVTs); - WasmSym->setSignature(Signature.get()); - Printer.addSignature(std::move(Signature)); - WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); - } - + if (!isa(Global)) + return cast(Printer.getSymbol(Global)); + + const auto *FuncTy = cast(Global->getValueType()); + const MachineFunction &MF = *MO.getParent()->getParent()->getParent(); + const TargetMachine &TM = MF.getTarget(); + const Function &CurrentFunc = MF.getFunction(); + + SmallVector ResultMVTs; + SmallVector ParamMVTs; + const auto *const F = dyn_cast(Global); + computeSignatureVTs(FuncTy, F, CurrentFunc, TM, ParamMVTs, ResultMVTs); + auto Signature = signatureFromMVTs(ResultMVTs, ParamMVTs); + + bool InvokeDetected = false; + auto *WasmSym = Printer.getMCSymbolForFunction( + F, EnableEmException || EnableEmSjLj, Signature.get(), InvokeDetected); + WasmSym->setSignature(Signature.get()); + Printer.addSignature(std::move(Signature)); + WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); return WasmSym; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -34,13 +34,13 @@ #define DEBUG_TYPE "wasm" // Emscripten's asm.js-style exception handling -static cl::opt EnableEmException( +cl::opt EnableEmException( "enable-emscripten-cxx-exceptions", cl::desc("WebAssembly Emscripten-style exception handling"), cl::init(false)); // Emscripten's asm.js-style setjmp/longjmp handling -static cl::opt EnableEmSjLj( +cl::opt EnableEmSjLj( "enable-emscripten-sjlj", cl::desc("WebAssembly Emscripten-style setjmp/longjmp handling"), cl::init(false)); diff --git a/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll b/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll --- a/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll +++ b/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll @@ -153,12 +153,12 @@ ; CHECK-LABEL: test_invoke: ; CHECK: i32.const $push[[L1:[0-9]+]]=, call_func{{$}} ; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, has_i32_ret{{$}} -; CHECK-NEXT: call "__invoke_void_i32()*", $pop[[L1]], $pop[[L0]]{{$}} +; CHECK-NEXT: call invoke_vi, $pop[[L1]], $pop[[L0]]{{$}} ; CHECK: i32.const $push[[L3:[0-9]+]]=, call_func{{$}} ; CHECK-NEXT: i32.const $push[[L2:[0-9]+]]=, has_i32_arg{{$}} -; CHECK-NEXT: call "__invoke_void_i32()*", $pop[[L3]], $pop[[L2]]{{$}} +; CHECK-NEXT: call invoke_vi, $pop[[L3]], $pop[[L2]]{{$}} ; CHECK: i32.const $push[[L4:[0-9]+]]=, .Lhas_i32_arg_bitcast.2{{$}} -; CHECK-NEXT: call __invoke_void, $pop[[L4]]{{$}} +; CHECK-NEXT: call invoke_v, $pop[[L4]]{{$}} declare i32 @personality(...) define void @test_invoke() personality i32 (...)* @personality { entry: diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll b/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-multi-return.ll copy from llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll copy to llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-multi-return.ll --- a/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-multi-return.ll @@ -1,27 +1,25 @@ -; 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 +; RUN: not --crash llc < %s -enable-emscripten-cxx-exceptions -mattr=+multivalue 2>&1 | FileCheck %s --check-prefix=EH +; RUN: not --crash llc < %s -enable-emscripten-sjlj -mattr=+multivalue 2>&1 | FileCheck %s --check-prefix=SJLJ + +; Currently multivalue returning functions are not supported in Emscripten EH / +; SjLj. Make sure they error out. 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 +define void @exception() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { entry: - invoke void @foo() + invoke {i32, i32} @foo(i32 3) to label %try.cont unwind label %lpad -; EH: call __invoke_void -; NONE: call foo lpad: ; preds = %entry - %0 = landingpad { i8*, i32 } + %1 = 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 + %2 = extractvalue { i8*, i32 } %1, 0 + %3 = extractvalue { i8*, i32 } %1, 1 + %4 = call i8* @__cxa_begin_catch(i8* %2) #2 call void @__cxa_end_catch() br label %try.cont @@ -29,23 +27,18 @@ ret void } -define hidden void @setjmp_longjmp() { -; SJLJ-LABEL: type setjmp_longjmp,@function -; NONE-LABEL: type setjmp_longjmp,@function +define void @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 {i32, i32} @foo(i32 3) call void @longjmp(%struct.__jmp_buf_tag* %arraydecay1, i32 1) #1 unreachable -; SJLJ: call saveSetjmp -; SJLJ: call testSetjmp -; NONE: call setjmp -; NONE: call longjmp } -declare void @foo() +declare {i32, i32} @foo(i32) declare i32 @__gxx_personality_v0(...) declare i8* @__cxa_begin_catch(i8*) declare void @__cxa_end_catch() @@ -59,3 +52,6 @@ attributes #0 = { returns_twice } attributes #1 = { noreturn } attributes #2 = { nounwind } + +; EH: LLVM ERROR: Emscripten EH/SjLj does not support multivalue returns +; SJLJ: LLVM ERROR: Emscripten EH/SjLj does not support multivalue returns diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll b/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll --- a/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll @@ -1,20 +1,30 @@ ; 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 +; RUN: not --crash llc < %s -enable-emscripten-cxx-exceptions -mtriple=wasm64-unknown-unknown 2>&1 | FileCheck %s --check-prefix=WASM64-EH +; RUN: not --crash llc < %s -enable-emscripten-sjlj -mtriple=wasm64-unknown-unknown 2>&1 | FileCheck %s --check-prefix=WASM64-SJLJ 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*) { +define 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() + invoke void @foo(i32 3) + to label %invoke.cont unwind label %lpad +; EH: call invoke_vi +; EH-NOT: call __invoke_void_i32 +; NONE: call foo + +invoke.cont: + invoke void @bar() to label %try.cont unwind label %lpad -; EH: call __invoke_void -; NONE: call foo +; EH: call invoke_v +; EH-NOT: call __invoke_void +; NONE: call bar lpad: ; preds = %entry %0 = landingpad { i8*, i32 } @@ -29,7 +39,7 @@ ret void } -define hidden void @setjmp_longjmp() { +define void @setjmp_longjmp() { ; SJLJ-LABEL: type setjmp_longjmp,@function ; NONE-LABEL: type setjmp_longjmp,@function entry: @@ -40,12 +50,30 @@ call void @longjmp(%struct.__jmp_buf_tag* %arraydecay1, i32 1) #1 unreachable ; SJLJ: call saveSetjmp +; SJLJ: i32.const emscripten_longjmp +; SJLJ-NOT: i32.const emscripten_longjmp_jmpbuf +; SJLJ: call invoke_vii +; SJLJ-NOT: call "__invoke_void_%struct.__jmp_buf_tag*_i32" ; SJLJ: call testSetjmp + ; NONE: call setjmp ; NONE: call longjmp } -declare void @foo() +; Tests whether a user function with 'invoke_' prefix can be used +declare void @invoke_ignoreme() +define void @test_invoke_ignoreme() { +; EH-LABEL: type test_invoke_ignoreme,@function +; SJLJ-LABEL: type test_invoke_ignoreme,@function +entry: + call void @invoke_ignoreme() +; EH: call invoke_ignoreme +; SJLJ: call invoke_ignoreme + ret void +} + +declare void @foo(i32) +declare void @bar() declare i32 @__gxx_personality_v0(...) declare i8* @__cxa_begin_catch(i8*) declare void @__cxa_end_catch() @@ -59,3 +87,20 @@ attributes #0 = { returns_twice } attributes #1 = { noreturn } attributes #2 = { nounwind } + +; EH: .functype invoke_vi (i32, i32) -> () +; EH: .import_module invoke_vi, env +; EH: .import_name invoke_vi, invoke_vi +; EH-NOT: .functype __invoke_void_i32 +; EH-NOT: .import_module __invoke_void_i32 +; EH-NOT: .import_name __invoke_void_i32 + +; SJLJ: .functype emscripten_longjmp (i32, i32) -> () +; SJLJ: .import_module emscripten_longjmp, env +; SJLJ: .import_name emscripten_longjmp, emscripten_longjmp +; SJLJ-NOT: .functype emscripten_longjmp_jmpbuf +; SJLJ-NOT: .import_module emscripten_longjmp_jmpbuf +; SJLJ-NOT: .import_name emscripten_longjmp_jmpbuf + +; WASM64-EH: LLVM ERROR: Emscripten EH/SjLj is not supported with wasm64 yet +; WASM64-SJLJ: LLVM ERROR: Emscripten EH/SjLj is not supported with wasm64 yet diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-sret.ll b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-sret.ll --- a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-sret.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj-sret.ll @@ -19,7 +19,7 @@ ; It needs to be the first argument (that's what we're testing here) ; CHECK: i32.const $push[[FPTR:[0-9]+]]=, returns_struct ; This is the sret stack region (as an offset from the stack pointer local) - ; CHECK: call "__invoke_{i32.i32}", $pop[[FPTR]] + ; CHECK: call invoke_vi, $pop[[FPTR]] %ret = call {i32, i32} @returns_struct() ret {i32, i32} %ret } diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll --- a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll @@ -36,8 +36,9 @@ ; 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: %[[JMPBUF:.*]] = ptrtoint %struct.__jmp_buf_tag* %[[ARRAYDECAY1]] to i32 ; CHECK-NEXT: store i32 0, i32* @__THREW__ -; CHECK-NEXT: call cc{{.*}} 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: call cc{{.*}} void @__invoke_void_i32_i32(void (i32, i32)* @emscripten_longjmp, i32 %[[JMPBUF]], 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 @@ -187,8 +188,8 @@ %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) +; CHECK: %[[JMPBUF:.*]] = ptrtoint +; CHECK-NEXT: call void @emscripten_longjmp(i32 %[[JMPBUF]], i32 5) } ; Test inline asm handling @@ -224,7 +225,7 @@ @buffer = global [1 x %struct.__jmp_buf_tag] zeroinitializer, align 16 define void @longjmp_only() { entry: - ; CHECK: call void @emscripten_longjmp_jmpbuf + ; CHECK: call void @emscripten_longjmp call void @longjmp(%struct.__jmp_buf_tag* getelementptr inbounds ([1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* @buffer, i32 0, i32 0), i32 1) #1 unreachable } @@ -265,28 +266,24 @@ ; CHECK-DAG: declare void @setTempRet0(i32) ; 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 } attributes #3 = { allocsize(0) } -; CHECK: attributes #{{[0-9]+}} = { nounwind "wasm-import-module"="env" "wasm-import-name"="getTempRet0" } -; CHECK: attributes #{{[0-9]+}} = { nounwind "wasm-import-module"="env" "wasm-import-name"="setTempRet0" } -; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__resumeException" } -; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="llvm_eh_typeid_for" } -; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__invoke_void" } -; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__cxa_find_matching_catch_3" } -; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="emscripten_longjmp_jmpbuf" } -; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="saveSetjmp" } -; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="testSetjmp" } -; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="emscripten_longjmp" } -; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__invoke_i8*_i32_%struct.__jmp_buf_tag*" } -; CHECK: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__invoke_void_%struct.__jmp_buf_tag*_i32" } -; CHECK: attributes #[[ALLOCSIZE_ATTR]] = { allocsize(1) } +; CHECK-DAG: attributes #{{[0-9]+}} = { nounwind "wasm-import-module"="env" "wasm-import-name"="getTempRet0" } +; CHECK-DAG: attributes #{{[0-9]+}} = { nounwind "wasm-import-module"="env" "wasm-import-name"="setTempRet0" } +; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__resumeException" } +; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="llvm_eh_typeid_for" } +; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__invoke_void" } +; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__cxa_find_matching_catch_3" } +; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="saveSetjmp" } +; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="testSetjmp" } +; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="emscripten_longjmp" } +; CHECK-DAG: attributes #{{[0-9]+}} = { "wasm-import-module"="env" "wasm-import-name"="__invoke_i8*_i32_%struct.__jmp_buf_tag*" } +; CHECK-DAG: attributes #[[ALLOCSIZE_ATTR]] = { allocsize(1) } !llvm.dbg.cu = !{!2} !llvm.module.flags = !{!0}