aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp')
-rw-r--r--contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp133
1 files changed, 112 insertions, 21 deletions
diff --git a/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
index 96fa13d30729..7f1c4bb40a4c 100644
--- a/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
+++ b/contrib/llvm-project/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;