diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/asm2wasm.h | 3 | ||||
-rw-r--r-- | src/tools/s2wasm.cpp | 24 | ||||
-rw-r--r-- | src/wasm-emscripten.cpp | 170 | ||||
-rw-r--r-- | src/wasm-emscripten.h | 46 | ||||
-rw-r--r-- | src/wasm-linker.cpp | 28 | ||||
-rw-r--r-- | src/wasm-linker.h | 38 |
6 files changed, 170 insertions, 139 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h index 71c7447e0..6a3e15b29 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -1331,7 +1331,8 @@ void Asm2WasmBuilder::processAsm(Ref ast) { // apply memory growth, if relevant if (preprocessor.memoryGrowth) { - emscripten::generateMemoryGrowthFunction(wasm); + EmscriptenGlueGenerator generator(wasm); + generator.generateMemoryGrowthFunction(); wasm.memory.max = Memory::kMaxSize; } diff --git a/src/tools/s2wasm.cpp b/src/tools/s2wasm.cpp index 96d9ae5ce..6e7b2c05e 100644 --- a/src/tools/s2wasm.cpp +++ b/src/tools/s2wasm.cpp @@ -175,21 +175,21 @@ int main(int argc, const char *argv[]) { linker.linkArchive(lib); } - if (generateEmscriptenGlue) { - emscripten::generateRuntimeFunctions(linker.getOutput()); - } - linker.layout(); - std::stringstream meta; + std::string metadata; if (generateEmscriptenGlue) { - if (options.debug) std::cerr << "Emscripten gluing..." << std::endl; - if (allowMemoryGrowth) { - emscripten::generateMemoryGrowthFunction(linker.getOutput().wasm); + Module& wasm = linker.getOutput().wasm; + if (options.debug) { + std::cerr << "Emscripten gluing..." << std::endl; + WasmPrinter::printModule(&wasm, std::cerr); } - - // dyncall thunks - linker.emscriptenGlue(meta); + metadata = emscriptenGlue( + wasm, + allowMemoryGrowth, + linker.getStackPointerAddress(), + linker.getStaticBump(), + linker.getOutput().getInitializerFunctions()); } if (options.extra["validate"] != "none") { @@ -205,7 +205,7 @@ int main(int argc, const char *argv[]) { if (options.debug) std::cerr << "Printing..." << std::endl; Output output(options.extra["output"], Flags::Text, options.debug ? Flags::Debug : Flags::Release); WasmPrinter::printModule(&linker.getOutput().wasm, output.getStream()); - output << meta.str(); + output << metadata; if (options.debug) std::cerr << "Done." << std::endl; return 0; diff --git a/src/wasm-emscripten.cpp b/src/wasm-emscripten.cpp index 9c617a31e..14f93cff3 100644 --- a/src/wasm-emscripten.cpp +++ b/src/wasm-emscripten.cpp @@ -16,6 +16,8 @@ #include "wasm-emscripten.h" +#include <sstream> + #include "asm_v_wasm.h" #include "asmjs/shared-constants.h" #include "shared-constants.h" @@ -26,11 +28,9 @@ namespace wasm { -namespace emscripten { - cashew::IString EMSCRIPTEN_ASM_CONST("emscripten_asm_const"); -static constexpr const char* stackPointer = "__stack_pointer"; +static constexpr const char* dummyFunction = "__wasm_nullptr"; void addExportedFunction(Module& wasm, Function* function) { wasm.addFunction(function); @@ -40,82 +40,49 @@ void addExportedFunction(Module& wasm, Function* function) { wasm.addExport(export_); } -void generateMemoryGrowthFunction(Module& wasm) { - Builder builder(wasm); - Name name(GROW_WASM_MEMORY); - std::vector<NameType> params { { NEW_SIZE, i32 } }; - Function* growFunction = builder.makeFunction( - name, std::move(params), i32, {} - ); - growFunction->body = builder.makeHost( - GrowMemory, - Name(), - { builder.makeGetLocal(0, i32) } - ); - - addExportedFunction(wasm, growFunction); -} - -void addStackPointerRelocation(LinkerObject& linker, uint32_t* data) { - linker.addRelocation(new LinkerObject::Relocation( - LinkerObject::Relocation::kData, - data, - Name(stackPointer), - 0 - )); -} - -Load* generateLoadStackPointer(Builder& builder, LinkerObject& linker) { +Load* EmscriptenGlueGenerator::generateLoadStackPointer() { Load* load = builder.makeLoad( /* bytes =*/ 4, /* signed =*/ false, - /* offset =*/ 0, + /* offset =*/ stackPointerOffset, /* align =*/ 4, /* ptr =*/ builder.makeConst(Literal(0)), /* type =*/ i32 ); - addStackPointerRelocation(linker, &load->offset.addr); return load; } -Store* generateStoreStackPointer(Builder& builder, - LinkerObject& linker, - Expression* value) { +Store* EmscriptenGlueGenerator::generateStoreStackPointer(Expression* value) { Store* store = builder.makeStore( /* bytes =*/ 4, - /* offset =*/ 0, + /* offset =*/ stackPointerOffset, /* align =*/ 4, /* ptr =*/ builder.makeConst(Literal(0)), /* value =*/ value, /* type =*/ i32 ); - addStackPointerRelocation(linker, &store->offset.addr); return store; } -void generateStackSaveFunction(LinkerObject& linker) { - Module& wasm = linker.wasm; - Builder builder(wasm); +void EmscriptenGlueGenerator::generateStackSaveFunction() { Name name("stackSave"); std::vector<NameType> params { }; Function* function = builder.makeFunction( name, std::move(params), i32, {} ); - function->body = generateLoadStackPointer(builder, linker); + function->body = generateLoadStackPointer(); addExportedFunction(wasm, function); } -void generateStackAllocFunction(LinkerObject& linker) { - Module& wasm = linker.wasm; - Builder builder(wasm); +void EmscriptenGlueGenerator::generateStackAllocFunction() { Name name("stackAlloc"); std::vector<NameType> params { { "0", i32 } }; Function* function = builder.makeFunction( name, std::move(params), i32, { { "1", i32 } } ); - Load* loadStack = generateLoadStackPointer(builder, linker); + Load* loadStack = generateLoadStackPointer(); SetLocal* setStackLocal = builder.makeSetLocal(1, loadStack); GetLocal* getStackLocal = builder.makeGetLocal(1, i32); GetLocal* getSizeArg = builder.makeGetLocal(0, i32); @@ -124,7 +91,7 @@ void generateStackAllocFunction(LinkerObject& linker) { const static uint32_t bitMask = bitAlignment - 1; Const* subConst = builder.makeConst(Literal(~bitMask)); Binary* maskedSub = builder.makeBinary(AndInt32, sub, subConst); - Store* storeStack = generateStoreStackPointer(builder, linker, maskedSub); + Store* storeStack = generateStoreStackPointer(maskedSub); Block* block = builder.makeBlock(); block->list.push_back(setStackLocal); @@ -137,26 +104,39 @@ void generateStackAllocFunction(LinkerObject& linker) { addExportedFunction(wasm, function); } -void generateStackRestoreFunction(LinkerObject& linker) { - Module& wasm = linker.wasm; - Builder builder(wasm); +void EmscriptenGlueGenerator::generateStackRestoreFunction() { Name name("stackRestore"); std::vector<NameType> params { { "0", i32 } }; Function* function = builder.makeFunction( name, std::move(params), none, {} ); GetLocal* getArg = builder.makeGetLocal(0, i32); - Store* store = generateStoreStackPointer(builder, linker, getArg); + Store* store = generateStoreStackPointer(getArg); function->body = store; addExportedFunction(wasm, function); } -void generateRuntimeFunctions(LinkerObject& linker) { - generateStackSaveFunction(linker); - generateStackAllocFunction(linker); - generateStackRestoreFunction(linker); +void EmscriptenGlueGenerator::generateRuntimeFunctions() { + generateStackSaveFunction(); + generateStackAllocFunction(); + generateStackRestoreFunction(); +} + +void EmscriptenGlueGenerator::generateMemoryGrowthFunction() { + Name name(GROW_WASM_MEMORY); + std::vector<NameType> params { { NEW_SIZE, i32 } }; + Function* growFunction = builder.makeFunction( + name, std::move(params), i32, {} + ); + growFunction->body = builder.makeHost( + GrowMemory, + Name(), + { builder.makeGetLocal(0, i32) } + ); + + addExportedFunction(wasm, growFunction); } static bool hasI64ResultOrParam(FunctionType* ft) { @@ -179,13 +159,19 @@ void removeImportsWithSubstring(Module& module, Name name) { } } -std::vector<Function*> makeDynCallThunks(Module& wasm, std::vector<Name> const& tableSegmentData) { +void EmscriptenGlueGenerator::generateDynCallThunks() { removeImportsWithSubstring(wasm, EMSCRIPTEN_ASM_CONST); // we create _sig versions - std::vector<Function*> generatedFunctions; std::unordered_set<std::string> sigs; Builder builder(wasm); + std::vector<Name> tableSegmentData; + if (wasm.table.segments.size() > 0) { + tableSegmentData = wasm.table.segments[0].data; + } for (const auto& indirectFunc : tableSegmentData) { + if (indirectFunc == dummyFunction) { + continue; + } std::string sig(getSig(wasm.getFunction(indirectFunc))); auto* funcType = ensureFunctionType(sig, &wasm); if (hasI64ResultOrParam(funcType)) continue; // Can't export i64s on the web. @@ -202,10 +188,10 @@ std::vector<Function*> makeDynCallThunks(Module& wasm, std::vector<Name> const& } Expression* call = builder.makeCallIndirect(funcType, fptr, args); f->body = call; + wasm.addFunction(f); - generatedFunctions.push_back(f); + exportFunction(wasm, f->name, true); } - return generatedFunctions; } struct AsmConstWalker : public PostWalker<AsmConstWalker> { @@ -216,8 +202,13 @@ struct AsmConstWalker : public PostWalker<AsmConstWalker> { std::map<std::string, Address> ids; std::set<std::string> allSigs; - AsmConstWalker(Module& _wasm, std::unordered_map<Address, Address> _segmentsByAddress) : - wasm(_wasm), segmentsByAddress(_segmentsByAddress) { } + AsmConstWalker(Module& _wasm) : wasm(_wasm) { + for (unsigned i = 0; i < wasm.memory.segments.size(); ++i) { + Const* addrConst = wasm.memory.segments[i].offset->cast<Const>(); + auto address = addrConst->value.geti32(); + segmentsByAddress[address] = Address(i); + } + } void visitCallImport(CallImport* curr); @@ -329,43 +320,62 @@ void printSet(std::ostream& o, C& c) { o << "]"; } -void generateEmscriptenMetadata(std::ostream& o, - Module& wasm, - std::unordered_map<Address, Address> segmentsByAddress, - Address staticBump, - std::vector<Name> const& initializerFunctions) { - o << ";; METADATA: { "; +std::string EmscriptenGlueGenerator::generateEmscriptenMetadata( + Address staticBump, + std::vector<Name> const& initializerFunctions) { + std::stringstream meta; + meta << ";; METADATA: { "; + // find asmConst calls, and emit their metadata - AsmConstWalker walker(wasm, segmentsByAddress); + AsmConstWalker walker(wasm); walker.walkModule(&wasm); + // print - o << "\"asmConsts\": {"; + meta << "\"asmConsts\": {"; bool first = true; for (auto& pair : walker.sigsForCode) { auto& code = pair.first; auto& sigs = pair.second; if (first) first = false; - else o << ","; - o << '"' << walker.ids[code] << "\": [\"" << code << "\", "; - printSet(o, sigs); - o << "]"; + else meta << ","; + meta << '"' << walker.ids[code] << "\": [\"" << code << "\", "; + printSet(meta, sigs); + meta << "]"; } - o << "}"; - o << ","; - o << "\"staticBump\": " << staticBump << ", "; + meta << "}"; + meta << ","; + meta << "\"staticBump\": " << staticBump << ", "; - o << "\"initializers\": ["; + meta << "\"initializers\": ["; first = true; for (const auto& func : initializerFunctions) { if (first) first = false; - else o << ", "; - o << "\"" << func.c_str() << "\""; + else meta << ", "; + meta << "\"" << func.c_str() << "\""; } - o << "]"; + meta << "]"; + + meta << " }\n"; - o << " }\n"; + return meta.str(); } -} // namespace emscripten +std::string emscriptenGlue( + Module& wasm, + bool allowMemoryGrowth, + Address stackPointer, + Address staticBump, + std::vector<Name> const& initializerFunctions) { + EmscriptenGlueGenerator generator(wasm, stackPointer); + generator.generateRuntimeFunctions(); + + if (allowMemoryGrowth) { + generator.generateMemoryGrowthFunction(); + } + + generator.generateDynCallThunks(); + + return generator.generateEmscriptenMetadata(staticBump, initializerFunctions); +} } // namespace wasm diff --git a/src/wasm-emscripten.h b/src/wasm-emscripten.h index 7400a8b0f..90a7d9a86 100644 --- a/src/wasm-emscripten.h +++ b/src/wasm-emscripten.h @@ -18,27 +18,47 @@ #define wasm_wasm_emscripten_h #include "wasm.h" +#include "wasm-builder.h" namespace wasm { -class LinkerObject; +// Class which modifies a wasm module for use with emscripten. Generates +// runtime functions and emits metadata. +class EmscriptenGlueGenerator { +public: + EmscriptenGlueGenerator(Module& wasm, Address stackPointerOffset = Address(0)) + : wasm(wasm), + builder(wasm), + stackPointerOffset(stackPointerOffset) { } -namespace emscripten { + void generateRuntimeFunctions(); + void generateMemoryGrowthFunction(); -void generateRuntimeFunctions(LinkerObject& linker); -void generateMemoryGrowthFunction(Module&); + // Create thunks for use with emscripten Runtime.dynCall. Creates one for each + // signature in the indirect function table. + void generateDynCallThunks(); -// Create thunks for use with emscripten Runtime.dynCall. Creates one for each -// signature in the indirect function table. -std::vector<Function*> makeDynCallThunks(Module& wasm, std::vector<Name> const& tableSegmentData); + std::string generateEmscriptenMetadata( + Address staticBump, std::vector<Name> const& initializerFunctions); -void generateEmscriptenMetadata(std::ostream& o, - Module& wasm, - std::unordered_map<Address, Address> segmentsByAddress, - Address staticBump, - std::vector<Name> const& initializerFunctions); +private: + Module& wasm; + Builder builder; + Address stackPointerOffset; -} // namespace emscripten + Load* generateLoadStackPointer(); + Store* generateStoreStackPointer(Expression* value); + void generateStackSaveFunction(); + void generateStackAllocFunction(); + void generateStackRestoreFunction(); +}; + +std::string emscriptenGlue( + Module& wasm, + bool allowMemoryGrowth, + Address stackPointer, + Address staticBump, + std::vector<Name> const& initializerFunctions); } // namespace wasm diff --git a/src/wasm-linker.cpp b/src/wasm-linker.cpp index b2f337d5a..dacc3cf51 100644 --- a/src/wasm-linker.cpp +++ b/src/wasm-linker.cpp @@ -148,8 +148,8 @@ void Linker::layout() { } // XXX For now, export all functions marked .globl. - for (Name name : out.globls) exportFunction(name, false); - for (Name name : out.initializerFunctions) exportFunction(name, true); + for (Name name : out.globls) exportFunction(out.wasm, name, false); + for (Name name : out.initializerFunctions) exportFunction(out.wasm, name, true); // Pad the indirect function table with a dummy function makeDummyFunction(); @@ -212,6 +212,8 @@ void Linker::layout() { } } } + out.relocations.clear(); + if (!!startFunction) { if (out.symbolInfo.implementedFunctions.count(startFunction) == 0) { Fatal() << "Unknown start function: `" << startFunction << "`\n"; @@ -263,7 +265,7 @@ void Linker::layout() { // argument from emcc.py and export all of them. for (auto function : {"malloc", "free", "realloc", "memalign"}) { if (out.symbolInfo.implementedFunctions.count(function)) { - exportFunction(function, true); + exportFunction(out.wasm, function, true); } } @@ -331,20 +333,8 @@ bool Linker::linkArchive(Archive& archive) { return true; } -void Linker::emscriptenGlue(std::ostream& o) { - if (debug) { - WasmPrinter::printModule(&out.wasm, std::cerr); - } - - auto functionsToThunk = getTableData(); - auto removeIt = std::remove(functionsToThunk.begin(), functionsToThunk.end(), dummyFunction); - functionsToThunk.erase(removeIt, functionsToThunk.end()); - for (auto f : emscripten::makeDynCallThunks(out.wasm, functionsToThunk)) { - exportFunction(f->name, true); - } - - auto staticBump = nextStatic - globalBase; - emscripten::generateEmscriptenMetadata(o, out.wasm, segmentsByAddress, staticBump, out.initializerFunctions); +Address Linker::getStaticBump() const { + return nextStatic - globalBase; } void Linker::ensureTableSegment() { @@ -415,3 +405,7 @@ Function* Linker::getImportThunk(Name name, const FunctionType* funcType) { out.wasm.addFunction(f); return f; } + +Address Linker::getStackPointerAddress() const { + return Address(staticAddresses.at(stackPointer)); +} diff --git a/src/wasm-linker.h b/src/wasm-linker.h index 2129f4fd3..89f1cb718 100644 --- a/src/wasm-linker.h +++ b/src/wasm-linker.h @@ -33,6 +33,18 @@ namespace wasm { class S2WasmBuilder; +inline void exportFunction(Module& wasm, Name name, bool must_export) { + if (!wasm.getFunctionOrNull(name)) { + assert(!must_export); + return; + } + if (wasm.getExportOrNull(name)) return; // Already exported + auto exp = new Export; + exp->name = exp->value = name; + exp->kind = ExternalKind::Function; + wasm.addExport(exp); +} + // An "object file" for linking. Contains a wasm module, plus the associated // information needed for linking/layout. class LinkerObject { @@ -156,6 +168,10 @@ class LinkerObject { return wasm.functions.empty(); } + std::vector<Name> const& getInitializerFunctions() const { + return initializerFunctions; + } + friend class Linker; Module wasm; @@ -246,10 +262,6 @@ class Linker { // function table. void layout(); - // Support for emscripten integration: generates dyncall thunks, emits - // metadata for asmConsts, staticBump and initializer functions. - void emscriptenGlue(std::ostream& o); - // Add an object to the link by constructing it in-place with a builder. // Returns false if an error occurred. bool linkObject(S2WasmBuilder& builder); @@ -259,6 +271,12 @@ class Linker { // Returns false if an error occurred. bool linkArchive(Archive& archive); + // Returns the address of the stack pointer. + Address getStackPointerAddress() const; + + // Returns the total size of all static allocations. + Address getStaticBump() const; + private: // Allocate a static variable and return its address in linear memory Address allocateStatic(Address allocSize, Address alignment, Name name) { @@ -294,18 +312,6 @@ class Linker { return (size + Memory::kPageSize - 1) & Memory::kPageMask; } - void exportFunction(Name name, bool must_export) { - if (!out.wasm.getFunctionOrNull(name)) { - assert(!must_export); - return; - } - if (out.wasm.getExportOrNull(name)) return; // Already exported - auto exp = new Export; - exp->name = exp->value = name; - exp->kind = ExternalKind::Function; - out.wasm.addExport(exp); - } - Function* getImportThunk(Name name, const FunctionType* t); // The output module (linked executable) |