diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/asm_v_wasm.h | 7 | ||||
-rw-r--r-- | src/asmjs/asm_v_wasm.cpp | 2 | ||||
-rw-r--r-- | src/mixed_arena.h | 2 | ||||
-rw-r--r-- | src/s2wasm.h | 30 | ||||
-rw-r--r-- | src/wasm-builder.h | 9 | ||||
-rw-r--r-- | src/wasm-linker.cpp | 51 | ||||
-rw-r--r-- | src/wasm-linker.h | 16 |
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; |