summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rw-r--r--test/dot_s/indirect-import.c27
-rw-r--r--test/dot_s/indirect-import.s55
-rw-r--r--test/dot_s/indirect-import.wast147
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": [] }