diff options
author | Sam Clegg <sbc@chromium.org> | 2018-11-30 12:20:33 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-30 12:20:33 -0800 |
commit | 3814fd6f2dab5adb5fb81022308818e4dd3b1020 (patch) | |
tree | 00b9787fcef8599e6d8c61674c510e3dcf52391f | |
parent | 0e068c386ef1588c09e57a4d081be626d83bc31c (diff) | |
download | binaryen-3814fd6f2dab5adb5fb81022308818e4dd3b1020.tar.gz binaryen-3814fd6f2dab5adb5fb81022308818e4dd3b1020.tar.bz2 binaryen-3814fd6f2dab5adb5fb81022308818e4dd3b1020.zip |
wasm-emscripten-finalize: Remove stack pointer global from shared libs (#1791)
The wasm backend uses a wasm global (__stack_pointer) for the shadow
stack location. In order to make this work with shared libraries the
main module would have to export this global and shared libraries would
need to import it. This means we'd be relying of mutable globals which
are not yet implemented in all browsers.
This change allows is to move forward with shared libraries without
mutable global support by replacing all stack pointer access in shared
libraries with functions calls.
-rw-r--r-- | src/tools/wasm-emscripten-finalize.cpp | 21 | ||||
-rw-r--r-- | src/wasm-emscripten.h | 4 | ||||
-rw-r--r-- | src/wasm/wasm-emscripten.cpp | 71 |
3 files changed, 82 insertions, 14 deletions
diff --git a/src/tools/wasm-emscripten-finalize.cpp b/src/tools/wasm-emscripten-finalize.cpp index 6814ae032..1a3499757 100644 --- a/src/tools/wasm-emscripten-finalize.cpp +++ b/src/tools/wasm-emscripten-finalize.cpp @@ -155,11 +155,6 @@ int main(int argc, const char *argv[]) { dataSize = dataEndConst->value.geti32() - globalBase; } - std::vector<Name> initializerFunctions; - if (wasm.getFunctionOrNull("__wasm_call_ctors")) { - initializerFunctions.push_back("__wasm_call_ctors"); - } - EmscriptenGlueGenerator generator(wasm); generator.fixInvokeFunctionNames(); @@ -171,10 +166,24 @@ int main(int argc, const char *argv[]) { passRunner.run(); } - if (!isSideModule) { + std::vector<Name> initializerFunctions; + + if (isSideModule) { + generator.replaceStackPointerGlobal(); + // rename __wasm_call_ctors to __post_instantiate which is what + // emscripten expects. + // TODO(sbc): Unify these two names + if (Export* ex = wasm.getExportOrNull("__wasm_call_ctors")) { + ex->name = "__post_instantiate"; + } + } else { generator.generateRuntimeFunctions(); generator.generateMemoryGrowthFunction(); + if (wasm.getFunctionOrNull("__wasm_call_ctors")) { + initializerFunctions.push_back("__wasm_call_ctors"); + } } + generator.generateDynCallThunks(); generator.generateJSCallThunks(numReservedFunctionPointers); std::string metadata = generator.generateEmscriptenMetadata(dataSize, initializerFunctions, numReservedFunctionPointers); diff --git a/src/wasm-emscripten.h b/src/wasm-emscripten.h index aa42b86b2..c15a4b8af 100644 --- a/src/wasm-emscripten.h +++ b/src/wasm-emscripten.h @@ -41,6 +41,10 @@ public: // signature in the indirect function table. void generateDynCallThunks(); + // Convert stack pointer access from get_global/set_global to calling save + // and restore functions. + void replaceStackPointerGlobal(); + // Create thunks to support emscripten's addFunction functionality. Creates (# // of reserved function pointers) thunks for each indirectly called function // signature. diff --git a/src/wasm/wasm-emscripten.cpp b/src/wasm/wasm-emscripten.cpp index 14104c1eb..a43fc1fe7 100644 --- a/src/wasm/wasm-emscripten.cpp +++ b/src/wasm/wasm-emscripten.cpp @@ -25,6 +25,7 @@ #include "wasm-traversal.h" #include "wasm.h" #include "ir/function-type-utils.h" +#include "ir/import-utils.h" #include "ir/module-utils.h" namespace wasm { @@ -32,7 +33,10 @@ namespace wasm { cashew::IString EMSCRIPTEN_ASM_CONST("emscripten_asm_const"); cashew::IString EM_JS_PREFIX("__em_js__"); -static constexpr const char* dummyFunction = "__wasm_nullptr"; +static Name STACK_SAVE("stackSave"), + STACK_RESTORE("stackRestore"), + STACK_ALLOC("stackAlloc"), + DUMMY_FUNC("__wasm_nullptr"); void addExportedFunction(Module& wasm, Function* function) { wasm.addFunction(function); @@ -80,10 +84,9 @@ Expression* EmscriptenGlueGenerator::generateStoreStackPointer(Expression* value } void EmscriptenGlueGenerator::generateStackSaveFunction() { - Name name("stackSave"); std::vector<NameType> params { }; Function* function = builder.makeFunction( - name, std::move(params), i32, {} + STACK_SAVE, std::move(params), i32, {} ); function->body = generateLoadStackPointer(); @@ -92,10 +95,9 @@ void EmscriptenGlueGenerator::generateStackSaveFunction() { } void EmscriptenGlueGenerator::generateStackAllocFunction() { - Name name("stackAlloc"); std::vector<NameType> params { { "0", i32 } }; Function* function = builder.makeFunction( - name, std::move(params), i32, { { "1", i32 } } + STACK_ALLOC, std::move(params), i32, { { "1", i32 } } ); Expression* loadStack = generateLoadStackPointer(); GetLocal* getSizeArg = builder.makeGetLocal(0, i32); @@ -118,10 +120,9 @@ void EmscriptenGlueGenerator::generateStackAllocFunction() { } void EmscriptenGlueGenerator::generateStackRestoreFunction() { - Name name("stackRestore"); std::vector<NameType> params { { "0", i32 } }; Function* function = builder.makeFunction( - name, std::move(params), none, {} + STACK_RESTORE, std::move(params), none, {} ); GetLocal* getArg = builder.makeGetLocal(0, i32); Expression* store = generateStoreStackPointer(getArg); @@ -182,7 +183,7 @@ void EmscriptenGlueGenerator::generateDynCallThunks() { tableSegmentData = wasm.table.segments[0].data; } for (const auto& indirectFunc : tableSegmentData) { - if (indirectFunc == dummyFunction) { + if (indirectFunc == DUMMY_FUNC) { continue; } std::string sig = getSig(wasm.getFunction(indirectFunc)); @@ -207,6 +208,60 @@ void EmscriptenGlueGenerator::generateDynCallThunks() { } } +static Function* ensureFunctionImport(Module* module, Name name, std::string sig) { + // Then see if its already imported + ImportInfo info(*module); + if (Function* f = info.getImportedFunction(ENV, name)) { + return f; + } + // Failing that create a new function import. + auto import = new Function; + import->name = name; + import->module = ENV; + import->base = name; + auto* functionType = ensureFunctionType(sig, module); + import->type = functionType->name; + FunctionTypeUtils::fillFunction(import, functionType); + module->addFunction(import); + return import; +} + +struct RemoveStackPointer : public PostWalker<RemoveStackPointer> { + RemoveStackPointer(Global* StackPointer) : StackPointer(StackPointer) {} + + void visitGetGlobal(GetGlobal* curr) { + if (getModule()->getGlobalOrNull(curr->name) == StackPointer) { + ensureFunctionImport(getModule(), STACK_SAVE, "i"); + if (!builder) builder = make_unique<Builder>(*getModule()); + replaceCurrent(builder->makeCall(STACK_SAVE, {}, i32)); + } + } + + void visitSetGlobal(SetGlobal* curr) { + if (getModule()->getGlobalOrNull(curr->name) == StackPointer) { + ensureFunctionImport(getModule(), STACK_RESTORE, "vi"); + if (!builder) builder = make_unique<Builder>(*getModule()); + replaceCurrent(builder->makeCall(STACK_RESTORE, {curr->value}, none)); + } + } + +private: + std::unique_ptr<Builder> builder; + Global* StackPointer; +}; + +void EmscriptenGlueGenerator::replaceStackPointerGlobal() { + Global* stackPointer = getStackPointerGlobal(); + + // Replace all uses of stack pointer global + RemoveStackPointer walker(stackPointer); + walker.walkModule(&wasm); + + // Finally remove the stack pointer global itself. This avoids importing + // a mutable global. + wasm.removeGlobal(stackPointer->name); +} + struct JSCallWalker : public PostWalker<JSCallWalker> { Module &wasm; JSCallWalker(Module &_wasm) : wasm(_wasm) { |