summaryrefslogtreecommitdiff
path: root/src/wasm-linker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm-linker.cpp')
-rw-r--r--src/wasm-linker.cpp169
1 files changed, 31 insertions, 138 deletions
diff --git a/src/wasm-linker.cpp b/src/wasm-linker.cpp
index 04deddf4a..527ff80ee 100644
--- a/src/wasm-linker.cpp
+++ b/src/wasm-linker.cpp
@@ -20,11 +20,13 @@
#include "s2wasm.h"
#include "support/utilities.h"
#include "wasm-builder.h"
+#include "wasm-emscripten.h"
#include "wasm-printing.h"
using namespace wasm;
-cashew::IString EMSCRIPTEN_ASM_CONST("emscripten_asm_const");
+// Name of the dummy function to prevent erroneous nullptr comparisons.
+static constexpr const char* dummyFunction = "__wasm_nullptr";
void Linker::placeStackPointer(Address stackAllocation) {
// ensure this is the first allocation
@@ -138,7 +140,8 @@ void Linker::layout() {
// Emit the pre-assigned function names in sorted order
for (const auto& P : functionNames) {
- getTableSegment().data.push_back(P.second);
+ ensureTableIsPopulated();
+ getTableDataRef().push_back(P.second);
}
for (auto& relocation : out.relocations) {
@@ -236,8 +239,9 @@ void Linker::layout() {
}
// finalize function table
- if (out.wasm.table.segments.size() > 0) {
- out.wasm.table.initial = out.wasm.table.max = getTableSegment().data.size();
+ unsigned int tableSize = getTableData().size();
+ if (tableSize > 0) {
+ out.wasm.table.initial = out.wasm.table.max = tableSize;
}
}
@@ -303,115 +307,40 @@ void Linker::emscriptenGlue(std::ostream& o) {
WasmPrinter::printModule(&out.wasm, std::cerr);
}
- out.wasm.removeImport(EMSCRIPTEN_ASM_CONST); // we create _sig versions
-
- makeDynCallThunks();
-
- o << ";; METADATA: { ";
- // find asmConst calls, and emit their metadata
- struct AsmConstWalker : public PostWalker<AsmConstWalker, Visitor<AsmConstWalker>> {
- Linker* parent;
-
- std::map<std::string, std::set<std::string>> sigsForCode;
- std::map<std::string, Address> ids;
- std::set<std::string> allSigs;
+ auto functionsToThunk = getTableData();
+ std::remove(functionsToThunk.begin(), functionsToThunk.end(), dummyFunction);
+ for (auto f : emscripten::makeDynCallThunks(out.wasm, functionsToThunk)) {
+ exportFunction(f->name, true);
+ }
- void visitCallImport(CallImport* curr) {
- if (curr->target == EMSCRIPTEN_ASM_CONST) {
- auto arg = curr->operands[0]->cast<Const>();
- Address segmentIndex = parent->segmentsByAddress[arg->value.geti32()];
- std::string code = escape(&parent->out.wasm.memory.segments[segmentIndex].data[0]);
- int32_t id;
- if (ids.count(code) == 0) {
- id = ids.size();
- ids[code] = id;
- } else {
- id = ids[code];
- }
- std::string sig = getSig(curr);
- sigsForCode[code].insert(sig);
- std::string fixedTarget = EMSCRIPTEN_ASM_CONST.str + std::string("_") + sig;
- curr->target = cashew::IString(fixedTarget.c_str(), false);
- arg->value = Literal(id);
- // add import, if necessary
- if (allSigs.count(sig) == 0) {
- allSigs.insert(sig);
- auto import = new Import;
- import->name = import->base = curr->target;
- import->module = ENV;
- import->functionType = ensureFunctionType(getSig(curr), &parent->out.wasm);
- import->kind = Import::Function;
- parent->out.wasm.addImport(import);
- }
- }
- }
+ auto staticBump = nextStatic - globalBase;
+ emscripten::generateEmscriptenMetadata(o, out.wasm, segmentsByAddress, staticBump, out.initializerFunctions);
+}
- std::string escape(const char *input) {
- std::string code = input;
- // replace newlines quotes with escaped newlines
- size_t curr = 0;
- while ((curr = code.find("\\n", curr)) != std::string::npos) {
- code = code.replace(curr, 2, "\\\\n");
- curr += 3; // skip this one
- }
- // replace double quotes with escaped single quotes
- curr = 0;
- while ((curr = code.find('"', curr)) != std::string::npos) {
- if (curr == 0 || code[curr-1] != '\\') {
- code = code.replace(curr, 1, "\\" "\"");
- curr += 2; // skip this one
- } else { // already escaped, escape the slash as well
- code = code.replace(curr, 1, "\\" "\\" "\"");
- curr += 3; // skip this one
- }
- }
- return code;
- }
- };
- AsmConstWalker walker;
- walker.parent = this;
- walker.walkModule(&out.wasm);
- // print
- o << "\"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 << "]";
- }
- o << "}";
- o << ",";
- o << "\"staticBump\": " << (nextStatic - globalBase) << ", ";
-
- o << "\"initializers\": [";
- first = true;
- for (const auto& func : out.initializerFunctions) {
- if (first) first = false;
- else o << ", ";
- o << "\"" << func.c_str() << "\"";
+void Linker::ensureTableIsPopulated() {
+ if (out.wasm.table.segments.size() == 0) {
+ auto emptySegment = out.wasm.allocator.alloc<Const>()->set(Literal(uint32_t(0)));
+ out.wasm.table.segments.emplace_back(emptySegment);
}
- o << "]";
+}
- o << " }\n";
+std::vector<Name>& Linker::getTableDataRef() {
+ assert(out.wasm.table.segments.size() == 1);
+ return out.wasm.table.segments[0].data;
}
-Table::Segment& Linker::getTableSegment() {
- if (out.wasm.table.segments.size() == 0) {
- out.wasm.table.segments.emplace_back(out.wasm.allocator.alloc<Const>()->set(Literal(uint32_t(0))));
- } else {
- assert(out.wasm.table.segments.size() == 1);
+std::vector<Name> Linker::getTableData() {
+ if (out.wasm.table.segments.size() > 0) {
+ return getTableDataRef();
}
- return out.wasm.table.segments[0];
+ return {};
}
Index Linker::getFunctionIndex(Name name) {
if (!functionIndexes.count(name)) {
- functionIndexes[name] = getTableSegment().data.size();
- getTableSegment().data.push_back(name);
+ ensureTableIsPopulated();
+ functionIndexes[name] = getTableData().size();
+ getTableDataRef().push_back(name);
if (debug) {
std::cerr << "function index: " << name << ": "
<< functionIndexes[name] << '\n';
@@ -420,14 +349,6 @@ Index Linker::getFunctionIndex(Name name) {
return functionIndexes[name];
}
-bool hasI64ResultOrParam(FunctionType* ft) {
- if (ft->result == i64) return true;
- for (auto ty : ft->params) {
- if (ty == i64) return true;
- }
- return false;
-}
-
void Linker::makeDummyFunction() {
bool create = false;
// Check if there are address-taken functions
@@ -445,34 +366,6 @@ void Linker::makeDummyFunction() {
getFunctionIndex(dummy->name);
}
-void Linker::makeDynCallThunks() {
- if (out.wasm.table.segments.size() == 0) return;
- std::unordered_set<std::string> sigs;
- wasm::Builder wasmBuilder(out.wasm);
- for (const auto& indirectFunc : getTableSegment().data) {
- // Skip generating thunks for the dummy function
- if (indirectFunc == dummyFunction) continue;
- std::string sig(getSig(out.wasm.getFunction(indirectFunc)));
- auto* funcType = ensureFunctionType(sig, &out.wasm);
- if (hasI64ResultOrParam(funcType)) continue; // Can't export i64s on the web.
- if (!sigs.insert(sig).second) continue; // Sig is already in the set
- std::vector<NameType> params;
- params.emplace_back("fptr", i32); // function pointer param
- int p = 0;
- for (const auto& ty : funcType->params) params.emplace_back(std::to_string(p++), ty);
- Function* f = wasmBuilder.makeFunction(std::string("dynCall_") + sig, std::move(params), funcType->result, {});
- Expression* fptr = wasmBuilder.makeGetLocal(0, i32);
- std::vector<Expression*> args;
- for (unsigned i = 0; i < funcType->params.size(); ++i) {
- args.push_back(wasmBuilder.makeGetLocal(i + 1, funcType->params[i]));
- }
- Expression* call = wasmBuilder.makeCallIndirect(funcType, fptr, args);
- f->body = call;
- out.wasm.addFunction(f);
- exportFunction(f->name, true);
- }
-}
-
Function* Linker::getImportThunk(Name name, const FunctionType* funcType) {
Name thunkName = std::string("__importThunk_") + name.c_str();
if (Function* thunk = out.wasm.checkFunction(thunkName)) return thunk;