summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/asm2wasm.h3
-rw-r--r--src/tools/s2wasm.cpp24
-rw-r--r--src/wasm-emscripten.cpp170
-rw-r--r--src/wasm-emscripten.h46
-rw-r--r--src/wasm-linker.cpp28
-rw-r--r--src/wasm-linker.h38
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)