diff options
Diffstat (limited to 'src/wasm/wasm-emscripten.cpp')
-rw-r--r-- | src/wasm/wasm-emscripten.cpp | 142 |
1 files changed, 134 insertions, 8 deletions
diff --git a/src/wasm/wasm-emscripten.cpp b/src/wasm/wasm-emscripten.cpp index 9a393db43..5b5d557e7 100644 --- a/src/wasm/wasm-emscripten.cpp +++ b/src/wasm/wasm-emscripten.cpp @@ -508,6 +508,128 @@ void EmscriptenGlueGenerator::fixEmAsmConsts() { fixEmJsFuncsAndReturnWalker(wasm); } + +// Fixes function name hacks caused by LLVM exception & setjmp/longjmp +// handling pass for wasm. +// This does two things: +// 1. Change emscripten_longjmp_jmpbuf to emscripten_longjmp. +// In setjmp/longjmp handling pass in wasm backend, what we want to do is +// to change all function calls to longjmp to calls to emscripten_longjmp. +// Because we replace all calls to longjmp to emscripten_longjmp, the +// signature of that function should be the same as longjmp: +// emscripten_longjmp(jmp_buf, int) +// But after calling a function that might longjmp, while we test whether +// a longjmp occurred, we have to load an int address value and call +// emscripten_longjmp again with that address as the first argument. (Refer +// to lib/Target/WebAssembly/WebAssemblyEmscriptenEHSjLj.cpp in LLVM for +// details.) +// In this case we need the signature of emscripten_longjmp to be (int, +// int). So we need two different kinds of emscripten_longjmp signatures in +// LLVM IR. Both signatures will be lowered to (int, int) eventually, but +// in LLVM IR, types are not lowered yet. +// So we declare two functions in LLVM: +// emscripten_longjmp_jmpbuf(jmp_buf, int) +// emscripten_longjmp(int, int) +// And we change the name of emscripten_longjmp_jmpbuf to +// emscripten_longjmp here. +// 2. Converts invoke wrapper names. +// Refer to the comments in fixEmExceptionInvoke below. +struct FixInvokeFunctionNamesWalker : public PostWalker<FixInvokeFunctionNamesWalker> { + Module& wasm; + std::map<Name, Name> importRenames; + std::vector<Name> toRemove; + std::set<Name> newImports; + + FixInvokeFunctionNamesWalker(Module& _wasm) : wasm(_wasm) {} + + // Converts invoke wrapper names generated by LLVM backend to real invoke + // wrapper names that are expected by JavaScript glue code. + // This is required to support wasm exception handling (asm.js style). + // + // LLVM backend lowers + // invoke @func(arg1, arg2) to label %invoke.cont unwind label %lpad + // into + // ... (some code) + // call @invoke_SIG(func, arg1, arg2) + // ... (some code) + // SIG is a mangled string generated based on the LLVM IR-level function + // signature. In LLVM IR, types are not lowered yet, so this mangling scheme + // simply takes LLVM's string representtion of parameter types and concatenate + // them with '_'. For example, the name of an invoke wrapper for function + // void foo(struct mystruct*, int) will be + // "__invoke_void_%struct.mystruct*_int". + // This function converts the names of invoke wrappers based on their lowered + // argument types and a return type. In the example above, the resulting new + // wrapper name becomes "invoke_vii". + static Name fixEmExceptionInvoke(const Name& name, const std::string& sig) { + std::string nameStr = name.c_str(); + if (nameStr.front() == '"' && nameStr.back() == '"') { + nameStr = nameStr.substr(1, nameStr.size() - 2); + } + if (nameStr.find("__invoke_") != 0) { + return name; + } + std::string sigWoOrigFunc = sig.front() + sig.substr(2, sig.size() - 2); + return Name("invoke_" + sigWoOrigFunc); + } + + static Name fixEmEHSjLjNames(const Name &name, const std::string& sig) { + if (name == "emscripten_longjmp_jmpbuf") + return "emscripten_longjmp"; + return fixEmExceptionInvoke(name, sig); + } + + void visitImport(Import* curr) { + if (curr->kind != ExternalKind::Function) + return; + + FunctionType* func = wasm.getFunctionType(curr->functionType); + Name newname = fixEmEHSjLjNames(curr->name, getSig(func)); + if (newname == curr->name) + return; + + assert(importRenames.count(curr->name) == 0); + importRenames[curr->name] = newname; + // Either rename of remove the existing import + if (wasm.getImportOrNull(newname) || !newImports.insert(newname).second) { + toRemove.push_back(curr->name); + } else { + curr->base = newname; + curr->name = newname; + } + } + + void visitTable(Table* curr) { + for (auto& segment : curr->segments) { + for (size_t i = 0; i < segment.data.size(); i++) { + auto it = importRenames.find(segment.data[i]); + if (it != importRenames.end()) { + segment.data[i] = it->second; + } + } + } + } + + void visitCallImport(CallImport* curr) { + auto it = importRenames.find(curr->target); + if (it != importRenames.end()) { + curr->target = it->second; + } + } + + void visitModule(Module* curr) { + for (auto importName : toRemove) { + wasm.removeImport(importName); + } + wasm.updateMaps(); + } +}; + +void EmscriptenGlueGenerator::fixInvokeFunctionNames() { + FixInvokeFunctionNamesWalker walker(wasm); + walker.walkModule(&wasm); +} + template<class C> void printSet(std::ostream& o, C& c) { o << "["; @@ -587,15 +709,18 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata( meta << "]"; } + // We use the `base` rather than the `name` of the imports here and below + // becasue this is the externally visible name that the embedder (JS) will + // see. meta << ", \"declares\": ["; commaFirst = true; for (const auto& import : wasm.imports) { if (import->kind == ExternalKind::Function && - (emJsWalker.codeByName.count(import->name.str) == 0) && - !import->name.startsWith(EMSCRIPTEN_ASM_CONST.str) && - !import->name.startsWith("invoke_") && - !import->name.startsWith("jsCall_")) { - meta << maybeComma() << '"' << import->name.str << '"'; + (emJsWalker.codeByName.count(import->base.str) == 0) && + !import->base.startsWith(EMSCRIPTEN_ASM_CONST.str) && + !import->base.startsWith("invoke_") && + !import->base.startsWith("jsCall_")) { + meta << maybeComma() << '"' << import->base.str << '"'; } } meta << "]"; @@ -604,7 +729,7 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata( commaFirst = true; for (const auto& import : wasm.imports) { if (import->kind == ExternalKind::Global) { - meta << maybeComma() << "\"_" << import->name.str << '"'; + meta << maybeComma() << "\"_" << import->base.str << '"'; } } meta << "]"; @@ -626,8 +751,8 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata( meta << ", \"invokeFuncs\": ["; commaFirst = true; for (const auto& import : wasm.imports) { - if (import->name.startsWith("invoke_")) { - meta << maybeComma() << '"' << import->name.str << '"'; + if (import->base.startsWith("invoke_")) { + meta << maybeComma() << '"' << import->base.str << '"'; } } meta << "]"; @@ -645,6 +770,7 @@ std::string emscriptenGlue( std::vector<Name> const& initializerFunctions, unsigned numReservedFunctionPointers) { EmscriptenGlueGenerator generator(wasm, stackPointer); + generator.fixInvokeFunctionNames(); generator.generateRuntimeFunctions(); if (allowMemoryGrowth) { |