diff options
Diffstat (limited to 'llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp')
-rw-r--r-- | llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp | 133 |
1 files changed, 112 insertions, 21 deletions
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp index 96fa13d30729..7f1c4bb40a4c 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -49,6 +49,8 @@ using namespace llvm; #define DEBUG_TYPE "asm-printer" extern cl::opt<bool> WasmKeepRegisters; +extern cl::opt<bool> EnableEmException; +extern cl::opt<bool> EnableEmSjLj; //===----------------------------------------------------------------------===// // Helpers. @@ -81,10 +83,92 @@ WebAssemblyTargetStreamer *WebAssemblyAsmPrinter::getTargetStreamer() { return static_cast<WebAssemblyTargetStreamer *>(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::FUNCREF: + return 'F'; + case wasm::ValType::EXTERNREF: + return 'X'; + } + llvm_unreachable("Unhandled wasm::ValType enum"); +} + +// 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<MCSymbolWasm>( + GetExternalSymbolSymbol(getEmscriptenInvokeSymbolName(Sig))); + } else { + WasmSym = cast<MCSymbolWasm>(getSymbol(F)); + } + return WasmSym; +} + void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) { for (auto &It : OutContext.getSymbols()) { // Emit a .globaltype and .eventtype declaration. @@ -95,6 +179,7 @@ void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) { getTargetStreamer()->emitEventType(Sym); } + DenseSet<MCSymbol *> InvokeSymbols; for (const auto &F : M) { if (F.isIntrinsic()) continue; @@ -104,31 +189,46 @@ void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) { SmallVector<MVT, 4> Results; SmallVector<MVT, 4> Params; computeSignatureVTs(F.getFunctionType(), &F, F, TM, Params, Results); - auto *Sym = cast<MCSymbolWasm>(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 +404,6 @@ void WebAssemblyAsmPrinter::emitFunctionBodyStart() { 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. @@ -362,14 +461,6 @@ void WebAssemblyAsmPrinter::emitInstruction(const MachineInstr *MI) { // This is a compiler barrier that prevents instruction reordering during // backend compilation, and should not be emitted. break; - case WebAssembly::EXTRACT_EXCEPTION_I32: - case WebAssembly::EXTRACT_EXCEPTION_I32_S: - // These are pseudo instructions that simulates popping values from stack. - // We print these only when we have -wasm-keep-registers on for assembly - // readability. - if (!WasmKeepRegisters) - break; - LLVM_FALLTHROUGH; default: { WebAssemblyMCInstLower MCInstLowering(OutContext, *this); MCInst TmpInst; |