Created
August 15, 2016 18:04
-
-
Save kripken/8b7b87030aa4c841a03667da2fde74b8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/shell-interface.h b/src/shell-interface.h | |
index 55f9b2a..db2f033 100644 | |
--- a/src/shell-interface.h | |
+++ b/src/shell-interface.h | |
@@ -86,6 +86,8 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { | |
} | |
} memory; | |
+ std::vector<Name> table; | |
+ | |
ShellExternalInterface() : memory() {} | |
void init(Module& wasm) override { | |
@@ -98,6 +100,15 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { | |
memory.set(offset + i, segment.data[i]); | |
} | |
} | |
+ | |
+ table.resize(wasm.table.initial); | |
+ for (auto& segment : wasm.table.segments) { | |
+ Address offset = ConstantExpressionRunner().visit(segment.offset).value.geti32(); | |
+ assert(offset + segment.data.size() <= wasm.table.initial); | |
+ for (size_t i = 0; i != segment.data.size(); ++i) { | |
+ table[offset + i] = segment.data[i]; | |
+ } | |
+ } | |
} | |
Literal callImport(Import *import, LiteralList& arguments) override { | |
@@ -115,6 +126,19 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { | |
abort(); | |
} | |
+ Literal callTable(Index index, FunctionType* type, LiteralList& arguments, ModuleInstance& instance) override { | |
+ if (index >= table.size()) trap("callTable overflow"); | |
+ auto* func = table[index]; | |
+ if (func->type.is() && func->type != type) trap("callIndirect: bad type"); | |
+ if (func->params.size() != arguments.size()) trap("callIndirect: bad # of arguments"); | |
+ for (size_t i = 0; i < func->params.size(); i++) { | |
+ if (func->params[i] != arguments[i].type) { | |
+ trap("callIndirect: bad argument type"); | |
+ } | |
+ } | |
+ return instance.callFunctionInternal(name, arguments); | |
+ } | |
+ | |
Literal load(Load* load, Address addr) override { | |
switch (load->type) { | |
case i32: { | |
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h | |
index 0be36a6..6fc4c59 100644 | |
--- a/src/wasm-interpreter.h | |
+++ b/src/wasm-interpreter.h | |
@@ -533,6 +533,7 @@ public: | |
struct ExternalInterface { | |
virtual void init(Module& wasm) {} | |
virtual Literal callImport(Import* import, LiteralList& arguments) = 0; | |
+ virtual Literal callTable(Index index, FunctionType* type, LiteralList& arguments, ModuleInstance& instance) = 0; | |
virtual Literal load(Load* load, Address addr) = 0; | |
virtual void store(Store* store, Address addr, Literal value) = 0; | |
virtual void growMemory(Address oldSize, Address newSize) = 0; | |
@@ -672,18 +673,8 @@ private: | |
LiteralList arguments; | |
Flow flow = generateArguments(curr->operands, arguments); | |
if (flow.breaking()) return flow; | |
- size_t index = target.value.geti32(); | |
- if (index >= instance.wasm.table.names.size()) trap("callIndirect: overflow"); | |
- Name name = instance.wasm.table.names[index]; | |
- Function *func = instance.wasm.getFunction(name); | |
- if (func->type.is() && func->type != curr->fullType) trap("callIndirect: bad type"); | |
- if (func->params.size() != arguments.size()) trap("callIndirect: bad # of arguments"); | |
- for (size_t i = 0; i < func->params.size(); i++) { | |
- if (func->params[i] != arguments[i].type) { | |
- trap("callIndirect: bad argument type"); | |
- } | |
- } | |
- return instance.callFunctionInternal(name, arguments); | |
+ Index index = target.value.geti32(); | |
+ return instance.externalInterface->callTable(index, curr->fullType, arguments, instance); | |
} | |
Flow visitGetLocal(GetLocal *curr) { | |
diff --git a/src/wasm-js.cpp b/src/wasm-js.cpp | |
index 2aacd94..742bd93 100644 | |
--- a/src/wasm-js.cpp | |
+++ b/src/wasm-js.cpp | |
@@ -195,10 +195,23 @@ extern "C" void EMSCRIPTEN_KEEPALIVE instantiate() { | |
target.set(source, $0); | |
}, ConstantExpressionRunner().visit(segment.offset).value.geti32(), &segment.data[0], segment.data.size()); | |
} | |
+ // Table support is in a JS array. If the entry is a number, it's a function pointer. If not, it's a JS method to be called directly | |
+ // TODO: make them all JS methods, wrapping a dynCall where necessary? | |
+ EM_ASM_({ | |
+ Module['outside']['wasmTable'] = new Array($0); | |
+ }, wasm.table.initial); | |
+ for (auto segment : wasm.table.segments) { | |
+ Address offset = ConstantExpressionRunner().visit(segment.offset).value.geti32(); | |
+ assert(offset + segment.data.size() <= wasm.table.initial); | |
+ for (size_t i = 0; i != segment.data.size(); ++i) { | |
+ EM_ASM_({ | |
+ Module['outside']['wasmTable'][$0] = $1; | |
+ }, offset + i, wasm.getFunction(segment.data[i])); | |
+ } | |
+ } | |
} | |
- Literal callImport(Import *import, LiteralList& arguments) override { | |
- if (wasmJSDebug) std::cout << "calling import " << import->name.str << '\n'; | |
+ void prepareTempArgments(LiteralList& arguments) { | |
EM_ASM({ | |
Module['tempArguments'] = []; | |
}); | |
@@ -213,6 +226,21 @@ extern "C" void EMSCRIPTEN_KEEPALIVE instantiate() { | |
abort(); | |
} | |
} | |
+ } | |
+ | |
+ Literal getResultFromJS(double ret) { | |
+ switch (import->type->result) { | |
+ case none: return Literal(0); | |
+ case i32: return Literal((int32_t)ret); | |
+ case f32: return Literal((float)ret); | |
+ case f64: return Literal((double)ret); | |
+ default: abort(); | |
+ } | |
+ } | |
+ | |
+ Literal callImport(Import *import, LiteralList& arguments) override { | |
+ if (wasmJSDebug) std::cout << "calling import " << import->name.str << '\n'; | |
+ prepareTempArgments(arguments); | |
double ret = EM_ASM_DOUBLE({ | |
var mod = Pointer_stringify($0); | |
var base = Pointer_stringify($1); | |
@@ -224,12 +252,36 @@ extern "C" void EMSCRIPTEN_KEEPALIVE instantiate() { | |
if (wasmJSDebug) std::cout << "calling import returning " << ret << '\n'; | |
- switch (import->type->result) { | |
- case none: return Literal(0); | |
- case i32: return Literal((int32_t)ret); | |
- case f32: return Literal((float)ret); | |
- case f64: return Literal((double)ret); | |
- default: abort(); | |
+ return getResultFromJS(ret); | |
+ } | |
+ | |
+ Literal callTable(Index index, FunctionType* type, LiteralList& arguments, ModuleInstance& instance) override { | |
+ void* ptr = EM_ASM_INT({ | |
+ var value = Module['outside']['wasmTable'][$0]; | |
+ return typeof value === "number" ? value : -1; | |
+ }, index); | |
+ if (ptr == nullptr) trap("callTable overflow"); | |
+ if (ptr != (void*)-1) { | |
+ // a Function we can call | |
+ Function* func = (Function*)ptr; | |
+ if (func->type.is() && func->type != type) trap("callIndirect: bad type"); | |
+ if (func->params.size() != arguments.size()) trap("callIndirect: bad # of arguments"); | |
+ for (size_t i = 0; i < func->params.size(); i++) { | |
+ if (func->params[i] != arguments[i].type) { | |
+ trap("callIndirect: bad argument type"); | |
+ } | |
+ } | |
+ return instance.callFunctionInternal(name, arguments); | |
+ } else { | |
+ // A JS function JS can call | |
+ prepareTempArgments(arguments); | |
+ double ret = EM_ASM_DOUBLE({ | |
+ var func = Module['outside']['wasmTable'][$0]; | |
+ var tempArguments = Module['tempArguments']; | |
+ Module['tempArguments'] = null; | |
+ return func.apply(null, tempArguments); | |
+ }, index); | |
+ return getResultFromJS(ret); | |
} | |
} | |
diff --git a/src/wasm.h b/src/wasm.h | |
index 82933bd..c41ff23 100644 | |
--- a/src/wasm.h | |
+++ b/src/wasm.h | |
@@ -1434,8 +1434,19 @@ class Table { | |
public: | |
static const Index kMaxSize = Index(-1); | |
+ struct Segment { | |
+ Expression* offset; | |
+ std::vector<Name> data; | |
+ Segment() {} | |
+ Segment(Expression* offset) : offset(offset) { | |
+ } | |
+ Segment(Expression* offset, std::vector<Name>& init) : offset(offset) { | |
+ data.swap(init); | |
+ } | |
+ }; | |
+ | |
Address initial, max; | |
- std::vector<Name> names; | |
+ std::vector<Name> segments; | |
Table() : initial(0), max(kMaxSize) {} | |
}; | |
@@ -1445,6 +1456,7 @@ public: | |
static const Address::address_t kPageSize = 64 * 1024; | |
static const Address::address_t kMaxSize = ~Address::address_t(0) / kPageSize; | |
static const Address::address_t kPageMask = ~(kPageSize - 1); | |
+ | |
struct Segment { | |
Expression* offset; | |
std::vector<char> data; // TODO: optimize | |
diff --git a/test/spec b/test/spec | |
--- a/test/spec | |
+++ b/test/spec | |
@@ -1 +1 @@ | |
-Subproject commit d4b150ce7268f903757ebdf1c7c001c99607da7a | |
+Subproject commit d4b150ce7268f903757ebdf1c7c001c99607da7a-dirty |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment