diff options
-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 | ||||
-rw-r--r-- | test/dot_s/indirect-import.c | 27 | ||||
-rw-r--r-- | test/dot_s/indirect-import.s | 55 | ||||
-rw-r--r-- | test/dot_s/indirect-import.wast | 147 |
10 files changed, 324 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; diff --git a/test/dot_s/indirect-import.c b/test/dot_s/indirect-import.c new file mode 100644 index 000000000..006424d84 --- /dev/null +++ b/test/dot_s/indirect-import.c @@ -0,0 +1,27 @@ +#include <stdint.h> + +struct big { + float a; + double b; + int32_t c; +}; + +float extern_fd(double); +void extern_vj(uint64_t); +void extern_v(void); +int32_t extern_ijidf(int64_t, int32_t, double, float); +void extern_struct(struct big); +struct big extern_sret(void); + +intptr_t bar() { + float (*fd)(double) = &extern_fd; + void (*vj)(uint64_t) = &extern_vj; + vj(1ULL); + void (*v)(void) = &extern_v; + v(); + int32_t (*ijidf)(int64_t, int32_t, double, float) = &extern_ijidf; + ijidf(1LL, 2, 3.0, 4.0f); + void (*vs)(struct big) = &extern_struct; + struct big (*s)(void) = &extern_sret; + return (intptr_t)fd; +} diff --git a/test/dot_s/indirect-import.s b/test/dot_s/indirect-import.s new file mode 100644 index 000000000..5131d86f3 --- /dev/null +++ b/test/dot_s/indirect-import.s @@ -0,0 +1,55 @@ + .text + .file "indirect-import.ll" + .hidden bar + .globl bar + .type bar,@function +bar: # @bar + .result i32 + .local i32, i32 +# BB#0: # %entry + i32.const $push13=, 0 + i32.const $push10=, 0 + i32.load $push11=, __stack_pointer($pop10) + i32.const $push12=, 32 + i32.sub $push17=, $pop11, $pop12 + i32.store $push20=, __stack_pointer($pop13), $pop17 + tee_local $push19=, $0=, $pop20 + i32.const $push0=, extern_fd@FUNCTION + i32.store $drop=, 28($pop19), $pop0 + i32.const $push1=, extern_vj@FUNCTION + i32.store $drop=, 24($0), $pop1 + i64.const $push2=, 1 + call extern_vj@FUNCTION, $pop2 + i32.const $push3=, extern_v@FUNCTION + i32.store $drop=, 20($0), $pop3 + call extern_v@FUNCTION + i32.const $push4=, extern_ijidf@FUNCTION + i32.store $drop=, 16($0), $pop4 + i64.const $push18=, 1 + i32.const $push7=, 2 + f64.const $push6=, 0x1.8p1 + f32.const $push5=, 0x1p2 + i32.call $drop=, extern_ijidf@FUNCTION, $pop18, $pop7, $pop6, $pop5 + i32.const $push8=, extern_struct@FUNCTION + i32.store $drop=, 12($0), $pop8 + i32.const $push9=, extern_sret@FUNCTION + i32.store $drop=, 8($0), $pop9 + i32.load $1=, 28($0) + i32.const $push16=, 0 + i32.const $push14=, 32 + i32.add $push15=, $0, $pop14 + i32.store $drop=, __stack_pointer($pop16), $pop15 + copy_local $push21=, $1 + # fallthrough-return: $pop21 + .endfunc +.Lfunc_end0: + .size bar, .Lfunc_end0-bar + + + .ident "clang version 3.9.0 (trunk 271427) (llvm/trunk 271429)" + .functype extern_fd, f32, f64 + .functype extern_vj, void, i64 + .functype extern_v, void + .functype extern_ijidf, i32, i64, i32, f64, f32 + .functype extern_struct, void, i32 + .functype extern_sret, void, i32 diff --git a/test/dot_s/indirect-import.wast b/test/dot_s/indirect-import.wast new file mode 100644 index 000000000..8ffeea168 --- /dev/null +++ b/test/dot_s/indirect-import.wast @@ -0,0 +1,147 @@ +(module + (memory 1) + (export "memory" memory) + (type $FUNCSIG$ijidf (func (param i64 i32 f64 f32) (result i32))) + (type $FUNCSIG$v (func)) + (type $FUNCSIG$vj (func (param i64))) + (type $FUNCSIG$fd (func (param f64) (result f32))) + (type $FUNCSIG$vi (func (param i32))) + (import $extern_ijidf "env" "extern_ijidf" (param i64 i32 f64 f32) (result i32)) + (import $extern_v "env" "extern_v") + (import $extern_vj "env" "extern_vj" (param i64)) + (import $extern_fd "env" "extern_fd" (param f64) (result f32)) + (import $extern_struct "env" "extern_struct" (param i32)) + (import $extern_sret "env" "extern_sret" (param i32)) + (export "bar" $bar) + (export "dynCall_fd" $dynCall_fd) + (export "dynCall_vj" $dynCall_vj) + (export "dynCall_v" $dynCall_v) + (export "dynCall_ijidf" $dynCall_ijidf) + (export "dynCall_vi" $dynCall_vi) + (table $__importThunk_extern_fd $__importThunk_extern_vj $__importThunk_extern_v $__importThunk_extern_ijidf $__importThunk_extern_struct $__importThunk_extern_sret) + (func $bar (result i32) + (local $0 i32) + (local $1 i32) + (i32.store offset=28 + (set_local $0 + (i32.store offset=4 + (i32.const 0) + (i32.sub + (i32.load offset=4 + (i32.const 0) + ) + (i32.const 32) + ) + ) + ) + (i32.const 0) + ) + (i32.store offset=24 + (get_local $0) + (i32.const 1) + ) + (call_import $extern_vj + (i64.const 1) + ) + (i32.store offset=20 + (get_local $0) + (i32.const 2) + ) + (call_import $extern_v) + (i32.store offset=16 + (get_local $0) + (i32.const 3) + ) + (call_import $extern_ijidf + (i64.const 1) + (i32.const 2) + (f64.const 3) + (f32.const 4) + ) + (i32.store offset=12 + (get_local $0) + (i32.const 4) + ) + (i32.store offset=8 + (get_local $0) + (i32.const 5) + ) + (set_local $1 + (i32.load offset=28 + (get_local $0) + ) + ) + (i32.store offset=4 + (i32.const 0) + (i32.add + (get_local $0) + (i32.const 32) + ) + ) + (get_local $1) + ) + (func $__importThunk_extern_fd (type $FUNCSIG$fd) (param $0 f64) (result f32) + (call_import $extern_fd + (get_local $0) + ) + ) + (func $__importThunk_extern_vj (type $FUNCSIG$vj) (param $0 i64) + (call_import $extern_vj + (get_local $0) + ) + ) + (func $__importThunk_extern_v (type $FUNCSIG$v) + (call_import $extern_v) + ) + (func $__importThunk_extern_ijidf (type $FUNCSIG$ijidf) (param $0 i64) (param $1 i32) (param $2 f64) (param $3 f32) (result i32) + (call_import $extern_ijidf + (get_local $0) + (get_local $1) + (get_local $2) + (get_local $3) + ) + ) + (func $__importThunk_extern_struct (type $FUNCSIG$vi) (param $0 i32) + (call_import $extern_struct + (get_local $0) + ) + ) + (func $__importThunk_extern_sret (type $FUNCSIG$vi) (param $0 i32) + (call_import $extern_sret + (get_local $0) + ) + ) + (func $dynCall_fd (param $fptr i32) (param $0 f64) (result f32) + (call_indirect $FUNCSIG$fd + (get_local $fptr) + (get_local $0) + ) + ) + (func $dynCall_vj (param $fptr i32) (param $0 i64) + (call_indirect $FUNCSIG$vj + (get_local $fptr) + (get_local $0) + ) + ) + (func $dynCall_v (param $fptr i32) + (call_indirect $FUNCSIG$v + (get_local $fptr) + ) + ) + (func $dynCall_ijidf (param $fptr i32) (param $0 i64) (param $1 i32) (param $2 f64) (param $3 f32) (result i32) + (call_indirect $FUNCSIG$ijidf + (get_local $fptr) + (get_local $0) + (get_local $1) + (get_local $2) + (get_local $3) + ) + ) + (func $dynCall_vi (param $fptr i32) (param $0 i32) + (call_indirect $FUNCSIG$vi + (get_local $fptr) + (get_local $0) + ) + ) +) +;; METADATA: { "asmConsts": {},"staticBump": 12, "initializers": [] } |