summaryrefslogtreecommitdiff
path: root/src/wasm/wasm-emscripten.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm/wasm-emscripten.cpp')
-rw-r--r--src/wasm/wasm-emscripten.cpp142
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) {