summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/asm_v_wasm.h7
-rw-r--r--src/asmjs/asm_v_wasm.cpp2
-rw-r--r--src/mixed_arena.h2
-rw-r--r--src/s2wasm.h30
-rw-r--r--src/wasm-builder.h9
-rw-r--r--src/wasm-linker.cpp51
-rw-r--r--src/wasm-linker.h16
7 files changed, 95 insertions, 22 deletions
diff --git a/src/asm_v_wasm.h b/src/asm_v_wasm.h
index 52524ecbc..7a4a0be31 100644
--- a/src/asm_v_wasm.h
+++ b/src/asm_v_wasm.h
@@ -29,12 +29,13 @@ AsmType wasmToAsmType(WasmType type);
char getSig(WasmType type);
-std::string getSig(FunctionType *type);
+std::string getSig(const FunctionType *type);
std::string getSig(Function *func);
-template<typename CallBase>
-std::string getSig(CallBase *call) {
+template<typename T,
+ typename std::enable_if<std::is_base_of<Expression, T>::value>::type* = nullptr>
+std::string getSig(T *call) {
std::string ret;
ret += getSig(call->type);
for (auto operand : call->operands) {
diff --git a/src/asmjs/asm_v_wasm.cpp b/src/asmjs/asm_v_wasm.cpp
index 14a7fbdae..648871100 100644
--- a/src/asmjs/asm_v_wasm.cpp
+++ b/src/asmjs/asm_v_wasm.cpp
@@ -53,7 +53,7 @@ char getSig(WasmType type) {
}
}
-std::string getSig(FunctionType *type) {
+std::string getSig(const FunctionType *type) {
std::string ret;
ret += getSig(type->result);
for (auto param : type->params) {
diff --git a/src/mixed_arena.h b/src/mixed_arena.h
index 77aa052c1..28b2dd462 100644
--- a/src/mixed_arena.h
+++ b/src/mixed_arena.h
@@ -217,7 +217,7 @@ public:
}
template<typename ListType>
- void set(ListType& list) {
+ void set(const ListType& list) {
size_t size = list.size();
if (allocatedElements < size) {
allocate(size);
diff --git a/src/s2wasm.h b/src/s2wasm.h
index 2228ae900..d24f5ef46 100644
--- a/src/s2wasm.h
+++ b/src/s2wasm.h
@@ -88,6 +88,11 @@ class S2WasmBuilder {
}
}
+ void skipToEOL() {
+ s = strchr(s, '\n');
+ assert(s);
+ }
+
bool skipComma() {
skipWhitespace();
if (*s != ',') return false;
@@ -397,10 +402,11 @@ class S2WasmBuilder {
else if (match("weak") || match("hidden") || match("protected") || match("internal")) getStr(); // contents are in the content that follows
else if (match("imports")) skipImports();
else if (match("data")) {}
- else if (match("ident")) {}
+ else if (match("ident")) skipToEOL();
else if (match("section")) parseToplevelSection();
- else if (match("align") || match("p2align")) s = strchr(s, '\n');
+ else if (match("align") || match("p2align")) skipToEOL();
else if (match("globl")) parseGlobl();
+ else if (match("functype")) parseFuncType();
else abort_on("process");
}
}
@@ -423,11 +429,11 @@ class S2WasmBuilder {
void parseInitializer() {
// Ignore the rest of the .section line
- s = strchr(s, '\n');
+ skipToEOL();
skipWhitespace();
// The section may start with .p2align
if (match(".p2align")) {
- s = strchr(s, '\n');
+ skipToEOL();
skipWhitespace();
}
mustMatch(".int32");
@@ -480,6 +486,22 @@ class S2WasmBuilder {
skipWhitespace();
}
+ void parseFuncType() {
+ auto decl = make_unique<FunctionType>();
+ Name name = getCommaSeparated();
+ skipComma();
+ if(match("void")) {
+ decl->result = none;
+ } else {
+ decl->result = getType();
+ }
+ while (*s && skipComma()) decl->params.push_back(getType());
+
+ FunctionType *ty = wasm->checkFunctionType(getSig(decl.get()));
+ if (!ty) ty = decl.release();
+ linkerObj->addExternType(name, ty);
+ }
+
bool parseVersionMin() {
if (match("watchos_version_min") || match("tvos_version_min") || match("ios_version_min") || match("macosx_version_min")) {
s = strchr(s, '\n');
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 4dc195d5c..86a6460d5 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -96,9 +96,14 @@ public:
// Switch
// CallBase
// Call
- // CallImport
// Also do a version which takes a sig?
- CallIndirect* makeCallIndirect(FunctionType* type, Expression* target, std::vector<Expression*>&& args) {
+ CallImport* makeCallImport(Name target, const std::vector<Expression*>& args) {
+ auto* call = allocator.alloc<CallImport>();
+ call->target = target;
+ call->operands.set(args);
+ return call;
+ }
+ CallIndirect* makeCallIndirect(FunctionType* type, Expression* target, const std::vector<Expression*>& args) {
auto* call = allocator.alloc<CallIndirect>();
call->fullType = type;
call->type = type->result;
diff --git a/src/wasm-linker.cpp b/src/wasm-linker.cpp
index aa098c2e7..1aba37b1b 100644
--- a/src/wasm-linker.cpp
+++ b/src/wasm-linker.cpp
@@ -51,19 +51,23 @@ void Linker::placeStackPointer(Address stackAllocation) {
}
}
+void Linker::ensureImport(Name target, std::string signature) {
+ if (!out.wasm.checkImport(target)) {
+ auto import = new Import;
+ import->name = import->base = target;
+ import->module = ENV;
+ import->type = ensureFunctionType(signature, &out.wasm);
+ out.wasm.addImport(import);
+ }
+}
+
void Linker::layout() {
// Convert calls to undefined functions to call_imports
for (const auto& f : out.undefinedFunctionCalls) {
Name target = f.first;
if (!out.symbolInfo.undefinedFunctions.count(target)) continue;
// Create an import for the target if necessary.
- if (!out.wasm.checkImport(target)) {
- auto import = new Import;
- import->name = import->base = target;
- import->module = ENV;
- import->type = ensureFunctionType(getSig(*f.second.begin()), &out.wasm);
- out.wasm.addImport(import);
- }
+ ensureImport(target, getSig(*f.second.begin()));
// Change each call. The target is the same since it's still the name.
// Delete and re-allocate the Expression as CallImport to avoid undefined
// behavior.
@@ -138,9 +142,17 @@ void Linker::layout() {
// function address
name = out.resolveAlias(name);
if (!out.wasm.checkFunction(name)) {
- std::cerr << "Unknown symbol: " << name << '\n';
- if (!ignoreUnknownSymbols) Fatal() << "undefined reference\n";
- *(relocation->data) = 0;
+ if (FunctionType* f = out.getExternType(name)) {
+ // Address of an imported function is taken, but imports do not have addresses in wasm.
+ // Generate a thunk to forward to the call_import.
+ Function* thunk = generateImportThunk(name, f);
+ ensureFunctionIndex(thunk->name);
+ *(relocation->data) = functionIndexes[thunk->name] + relocation->addend;
+ } else {
+ std::cerr << "Unknown symbol: " << name << '\n';
+ if (!ignoreUnknownSymbols) Fatal() << "undefined reference\n";
+ *(relocation->data) = 0;
+ }
} else {
ensureFunctionIndex(name);
*(relocation->data) = functionIndexes[name] + relocation->addend;
@@ -362,9 +374,26 @@ void Linker::makeDynCallThunks() {
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, std::move(args));
+ Expression* call = wasmBuilder.makeCallIndirect(funcType, fptr, args);
f->body = call;
out.wasm.addFunction(f);
exportFunction(f->name, true);
}
}
+
+Function* Linker::generateImportThunk(Name name, const FunctionType* funcType) {
+ wasm::Builder wasmBuilder(out.wasm);
+ std::vector<NameType> params;
+ Index p = 0;
+ for (const auto& ty : funcType->params) params.emplace_back(std::to_string(p++), ty);
+ Function *f = wasmBuilder.makeFunction(std::string("__importThunk_") + name.c_str(), std::move(params), funcType->result, {});
+ std::vector<Expression*> args;
+ for (Index i = 0; i < funcType->params.size(); ++i) {
+ args.push_back(wasmBuilder.makeGetLocal(i, funcType->params[i]));
+ }
+ Expression* call = wasmBuilder.makeCallImport(name, args);
+ f->body = call;
+ out.wasm.addFunction(f);
+ ensureImport(name, getSig(funcType));
+ return f;
+}
diff --git a/src/wasm-linker.h b/src/wasm-linker.h
index 5fe01b393..a0684d02b 100644
--- a/src/wasm-linker.h
+++ b/src/wasm-linker.h
@@ -119,6 +119,15 @@ class LinkerObject {
undefinedFunctionCalls[call->target].push_back(call);
}
+ void addExternType(Name name, FunctionType* ty) {
+ externTypesMap[name] = ty;
+ }
+ FunctionType* getExternType(Name name) {
+ auto f = externTypesMap.find(name);
+ if (f == externTypesMap.end()) return nullptr;
+ return f->second;
+ }
+
bool isEmpty() {
return wasm.functions.empty();
}
@@ -146,6 +155,9 @@ class LinkerObject {
using CallList = std::vector<Call*>;
std::map<Name, CallList> undefinedFunctionCalls;
+ // Types of functions which are declared but not defined.
+ std::unordered_map<cashew::IString, FunctionType*> externTypesMap;
+
std::map<Name, Address> segments; // name => segment index (in wasm module)
std::vector<Name> initializerFunctions;
@@ -244,6 +256,8 @@ class Linker {
o << "]";
}
+ void ensureImport(Name target, std::string signature);
+
// Create thunks for use with emscripten Runtime.dynCall. Creates one for each
// signature in the indirect function table.
void makeDynCallThunks();
@@ -263,6 +277,8 @@ class Linker {
out.wasm.addExport(exp);
}
+ Function* generateImportThunk(Name name, const FunctionType* t);
+
// The output module (linked executable)
LinkerObject out;