summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2016-08-16 09:40:59 -0700
committerGitHub <noreply@github.com>2016-08-16 09:40:59 -0700
commite5e3bf39f25ed3a2fb45a9ca1f55d6828d81a3eb (patch)
tree834f67d6ebaf295af0e1d6789bc7f52d120dff33 /src
parente268d939b86d8639d014b8036e7664d66b6a32e9 (diff)
parent7851e3a7a3bea679f422116862c5801f1938806d (diff)
downloadbinaryen-e5e3bf39f25ed3a2fb45a9ca1f55d6828d81a3eb.tar.gz
binaryen-e5e3bf39f25ed3a2fb45a9ca1f55d6828d81a3eb.tar.bz2
binaryen-e5e3bf39f25ed3a2fb45a9ca1f55d6828d81a3eb.zip
Merge pull request #668 from WebAssembly/tables_n_memories
Tables and memories
Diffstat (limited to 'src')
-rw-r--r--src/asm2wasm.h9
-rw-r--r--src/binaryen-c.cpp13
-rw-r--r--src/binaryen-c.h2
-rw-r--r--src/passes/DuplicateFunctionElimination.cpp10
-rw-r--r--src/passes/Print.cpp30
-rw-r--r--src/passes/RemoveUnusedFunctions.cpp6
-rw-r--r--src/passes/ReorderFunctions.cpp6
-rw-r--r--src/shared-constants.h3
-rw-r--r--src/shell-interface.h29
-rw-r--r--src/wasm-binary.h56
-rw-r--r--src/wasm-interpreter.h27
-rw-r--r--src/wasm-js.cpp70
-rw-r--r--src/wasm-linker.cpp34
-rw-r--r--src/wasm-linker.h8
-rw-r--r--src/wasm-s-parser.h69
-rw-r--r--src/wasm-validator.h6
-rw-r--r--src/wasm.cpp3
-rw-r--r--src/wasm.h25
18 files changed, 306 insertions, 100 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h
index c0f13ff8f..a5754f725 100644
--- a/src/asm2wasm.h
+++ b/src/asm2wasm.h
@@ -660,12 +660,17 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
// TODO: when not using aliasing function pointers, we could merge them by noticing that
// index 0 in each table is the null func, and each other index should only have one
// non-null func. However, that breaks down when function pointer casts are emulated.
- functionTableStarts[name] = wasm.table.names.size(); // this table starts here
+ if (wasm.table.segments.size() == 0) {
+ wasm.table.segments.emplace_back(wasm.allocator.alloc<Const>()->set(Literal(uint32_t(0))));
+ }
+ auto& segment = wasm.table.segments[0];
+ functionTableStarts[name] = segment.data.size(); // this table starts here
Ref contents = value[1];
for (unsigned k = 0; k < contents->size(); k++) {
IString curr = contents[k][1]->getIString();
- wasm.table.names.push_back(curr);
+ segment.data.push_back(curr);
}
+ wasm.table.initial = wasm.table.max = segment.data.size();
} else {
abort_on("invalid var element", pair);
}
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index b7fc4347a..83c626eb1 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -729,14 +729,17 @@ void BinaryenSetFunctionTable(BinaryenModuleRef module, BinaryenFunctionRef* fun
}
auto* wasm = (Module*)module;
+ Table::Segment segment(wasm->allocator.alloc<Const>()->set(Literal(int32_t(0))));
for (BinaryenIndex i = 0; i < numFuncs; i++) {
- wasm->table.names.push_back(((Function*)funcs[i])->name);
+ segment.data.push_back(((Function*)funcs[i])->name);
}
+ wasm->table.segments.push_back(segment);
+ wasm->table.initial = wasm->table.max = numFuncs;
}
// Memory. One per module
-void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, BinaryenIndex maximum, const char* exportName, const char **segments, BinaryenIndex* segmentOffsets, BinaryenIndex* segmentSizes, BinaryenIndex numSegments) {
+void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, BinaryenIndex maximum, const char* exportName, const char **segments, BinaryenExpressionRef* segmentOffsets, BinaryenIndex* segmentSizes, BinaryenIndex numSegments) {
if (tracing) {
std::cout << " {\n";
for (BinaryenIndex i = 0; i < numSegments; i++) {
@@ -754,10 +757,10 @@ void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, Binaryen
}
if (numSegments == 0) std::cout << "0"; // ensure the array is not empty, otherwise a compiler error on VS
std::cout << " };\n";
- std::cout << " BinaryenIndex segmentOffsets[] = { ";
+ std::cout << " BinaryenExpressionRef segmentOffsets[] = { ";
for (BinaryenIndex i = 0; i < numSegments; i++) {
if (i > 0) std::cout << ", ";
- std::cout << segmentOffsets[i];
+ std::cout << "expressions[" << expressions[segmentOffsets[i]] << "]";
}
if (numSegments == 0) std::cout << "0"; // ensure the array is not empty, otherwise a compiler error on VS
std::cout << " };\n";
@@ -779,7 +782,7 @@ void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, Binaryen
wasm->memory.max = maximum;
if (exportName) wasm->memory.exportName = exportName;
for (BinaryenIndex i = 0; i < numSegments; i++) {
- wasm->memory.segments.emplace_back(segmentOffsets[i], segments[i], segmentSizes[i]);
+ wasm->memory.segments.emplace_back((Expression*)segmentOffsets[i], segments[i], segmentSizes[i]);
}
}
diff --git a/src/binaryen-c.h b/src/binaryen-c.h
index b32fa566a..e2086072b 100644
--- a/src/binaryen-c.h
+++ b/src/binaryen-c.h
@@ -346,7 +346,7 @@ void BinaryenSetFunctionTable(BinaryenModuleRef module, BinaryenFunctionRef* fun
// Each segment has data in segments, a start offset in segmentOffsets, and a size in segmentSizes.
// exportName can be NULL
-void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, BinaryenIndex maximum, const char* exportName, const char **segments, BinaryenIndex* segmentOffsets, BinaryenIndex* segmentSizes, BinaryenIndex numSegments);
+void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, BinaryenIndex maximum, const char* exportName, const char **segments, BinaryenExpressionRef* segmentOffsets, BinaryenIndex* segmentSizes, BinaryenIndex numSegments);
// Start function. One per module
diff --git a/src/passes/DuplicateFunctionElimination.cpp b/src/passes/DuplicateFunctionElimination.cpp
index 961d26ba5..2b8e69b54 100644
--- a/src/passes/DuplicateFunctionElimination.cpp
+++ b/src/passes/DuplicateFunctionElimination.cpp
@@ -123,10 +123,12 @@ struct DuplicateFunctionElimination : public Pass {
replacerRunner.add<FunctionReplacer>(&replacements);
replacerRunner.run();
// replace in table
- for (auto& name : module->table.names) {
- auto iter = replacements.find(name);
- if (iter != replacements.end()) {
- name = iter->second;
+ for (auto& segment : module->table.segments) {
+ for (auto& name : segment.data) {
+ auto iter = replacements.find(name);
+ if (iter != replacements.end()) {
+ name = iter->second;
+ }
}
}
// replace in start
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 47e503fa4..5eea38bdc 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -575,12 +575,19 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
decIndent();
}
void visitTable(Table *curr) {
- printOpening(o, "table");
- for (auto name : curr->names) {
- o << ' ';
- printName(name);
+ printOpening(o, "table") << ' ' << curr->initial;
+ if (curr->max && curr->max != Table::kMaxSize) o << ' ' << curr->max;
+ o << " anyfunc)\n";
+ doIndent(o, indent);
+ for (auto& segment : curr->segments) {
+ printOpening(o, "elem ", true);
+ visit(segment.offset);
+ for (auto name : segment.data) {
+ o << ' ';
+ printName(name);
+ }
+ o << ')';
}
- o << ')';
}
void visitModule(Module *curr) {
currModule = curr;
@@ -589,9 +596,12 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
doIndent(o, indent);
printOpening(o, "memory") << ' ' << curr->memory.initial;
if (curr->memory.max && curr->memory.max != Memory::kMaxSize) o << ' ' << curr->memory.max;
+ o << ")\n";
for (auto segment : curr->memory.segments) {
- o << maybeNewLine;
- o << (minify ? "" : " ") << "(segment " << segment.offset << " \"";
+ doIndent(o, indent);
+ printOpening(o, "data ", true);
+ visit(segment.offset);
+ o << " \"";
for (size_t i = 0; i < segment.data.size(); i++) {
unsigned char c = segment.data[i];
switch (c) {
@@ -612,10 +622,8 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
}
}
}
- o << "\")";
+ o << "\")\n";
}
- o << ((curr->memory.segments.size() > 0 && !minify) ? "\n " : "") << ')';
- o << maybeNewLine;
if (curr->memory.exportName.is()) {
doIndent(o, indent);
printOpening(o, "export ");
@@ -647,7 +655,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
visitGlobal(child.get());
o << maybeNewLine;
}
- if (curr->table.names.size() > 0) {
+ if (curr->table.segments.size() > 0 || curr->table.initial > 0 || curr->table.max != Table::kMaxSize) {
doIndent(o, indent);
visitTable(&curr->table);
o << maybeNewLine;
diff --git a/src/passes/RemoveUnusedFunctions.cpp b/src/passes/RemoveUnusedFunctions.cpp
index a2941aff6..78b0f0ffc 100644
--- a/src/passes/RemoveUnusedFunctions.cpp
+++ b/src/passes/RemoveUnusedFunctions.cpp
@@ -39,8 +39,10 @@ struct RemoveUnusedFunctions : public Pass {
root.push_back(module->getFunction(curr->value));
}
// For now, all functions that can be called indirectly are marked as roots.
- for (auto& curr : module->table.names) {
- root.push_back(module->getFunction(curr));
+ for (auto& segment : module->table.segments) {
+ for (auto& curr : segment.data) {
+ root.push_back(module->getFunction(curr));
+ }
}
// Compute function reachability starting from the root set.
DirectCallGraphAnalyzer analyzer(module, root);
diff --git a/src/passes/ReorderFunctions.cpp b/src/passes/ReorderFunctions.cpp
index 38ef98afb..679fedb61 100644
--- a/src/passes/ReorderFunctions.cpp
+++ b/src/passes/ReorderFunctions.cpp
@@ -38,8 +38,10 @@ struct ReorderFunctions : public WalkerPass<PostWalker<ReorderFunctions, Visitor
for (auto& curr : module->exports) {
counts[curr->value]++;
}
- for (auto& curr : module->table.names) {
- counts[curr]++;
+ for (auto& segment : module->table.segments) {
+ for (auto& curr : segment.data) {
+ counts[curr]++;
+ }
}
std::sort(module->functions.begin(), module->functions.end(), [this](
const std::unique_ptr<Function>& a,
diff --git a/src/shared-constants.h b/src/shared-constants.h
index fb94ff11b..f0148cb67 100644
--- a/src/shared-constants.h
+++ b/src/shared-constants.h
@@ -24,10 +24,12 @@ extern Name GROW_WASM_MEMORY,
PARAM,
RESULT,
MEMORY,
+ DATA,
SEGMENT,
EXPORT,
IMPORT,
TABLE,
+ ELEM,
LOCAL,
TYPE,
CALL,
@@ -43,6 +45,7 @@ extern Name GROW_WASM_MEMORY,
NEG_NAN,
CASE,
BR,
+ ANYFUNC,
FAKE_RETURN,
ASSERT_RETURN,
ASSERT_TRAP,
diff --git a/src/shell-interface.h b/src/shell-interface.h
index f6c12e8ca..f332307ad 100644
--- a/src/shell-interface.h
+++ b/src/shell-interface.h
@@ -86,15 +86,27 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
}
} memory;
+ std::vector<Name> table;
+
ShellExternalInterface() : memory() {}
void init(Module& wasm) override {
memory.resize(wasm.memory.initial * wasm::Memory::kPageSize);
// apply memory segments
for (auto& segment : wasm.memory.segments) {
- assert(segment.offset + segment.data.size() <= wasm.memory.initial * wasm::Memory::kPageSize);
+ Address offset = ConstantExpressionRunner().visit(segment.offset).value.geti32();
+ assert(offset + segment.data.size() <= wasm.memory.initial * wasm::Memory::kPageSize);
+ for (size_t i = 0; i != segment.data.size(); ++i) {
+ 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) {
- memory.set(segment.offset + i, segment.data[i]);
+ table[offset + i] = segment.data[i];
}
}
}
@@ -114,6 +126,19 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
abort();
}
+ Literal callTable(Index index, Name type, LiteralList& arguments, ModuleInstance& instance) override {
+ if (index >= table.size()) trap("callTable overflow");
+ auto* func = instance.wasm.getFunction(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(func->name, arguments);
+ }
+
Literal load(Load* load, Address addr) override {
switch (load->type) {
case i32: {
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index aad142d44..04bebddc5 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -706,7 +706,8 @@ public:
o << U32LEB(num);
for (auto& segment : wasm->memory.segments) {
if (segment.data.size() == 0) continue;
- o << U32LEB(segment.offset);
+ writeExpression(segment.offset);
+ o << int8_t(BinaryConsts::End);
writeInlineBuffer(&segment.data[0], segment.data.size());
}
finishSection(start);
@@ -739,12 +740,19 @@ public:
}
void writeFunctionTable() {
- if (wasm->table.names.size() == 0) return;
+ if (wasm->table.segments.size() == 0) return;
if (debug) std::cerr << "== writeFunctionTable" << std::endl;
auto start = startSection(BinaryConsts::Section::FunctionTable);
- o << U32LEB(wasm->table.names.size());
- for (auto name : wasm->table.names) {
- o << U32LEB(getFunctionIndex(name));
+ o << U32LEB(wasm->table.initial);
+ o << U32LEB(wasm->table.max);
+ o << U32LEB(wasm->table.segments.size());
+ for (auto& segment : wasm->table.segments) {
+ writeExpression(segment.offset);
+ o << int8_t(BinaryConsts::End);
+ o << U32LEB(segment.data.size());
+ for (auto name : segment.data) {
+ o << U32LEB(getFunctionIndex(name));
+ }
}
finishSection(start);
}
@@ -1572,6 +1580,15 @@ public:
}
}
+ Expression* readExpression() {
+ assert(depth == 0);
+ processExpressions();
+ assert(expressionStack.size() == 1);
+ auto* ret = popExpression();
+ assert(depth == 0);
+ return ret;
+ }
+
void readGlobals() {
if (debug) std::cerr << "== readGlobals" << std::endl;
size_t num = getU32LEB();
@@ -1580,11 +1597,7 @@ public:
if (debug) std::cerr << "read one" << std::endl;
auto curr = new Global;
curr->type = getWasmType();
- assert(depth == 0);
- processExpressions();
- assert(expressionStack.size() == 1);
- curr->init = popExpression();
- assert(depth == 0);
+ curr->init = readExpression();
wasm.addGlobal(curr);
}
}
@@ -1638,9 +1651,12 @@ public:
}
}
- for (size_t index : functionTable) {
- assert(index < wasm.functions.size());
- wasm.table.names.push_back(wasm.functions[index]->name);
+ for (auto& pair : functionTable) {
+ auto i = pair.first;
+ auto& indexes = pair.second;
+ for (auto j : indexes) {
+ wasm.table.segments[i].data.push_back(wasm.functions[j]->name);
+ }
}
}
@@ -1649,7 +1665,7 @@ public:
auto num = getU32LEB();
for (size_t i = 0; i < num; i++) {
Memory::Segment curr;
- auto offset = getU32LEB();
+ auto offset = readExpression();
auto size = getU32LEB();
std::vector<char> buffer;
buffer.resize(size);
@@ -1660,14 +1676,20 @@ public:
}
}
- std::vector<size_t> functionTable;
+ std::map<Index, std::vector<Index>> functionTable;
void readFunctionTable() {
if (debug) std::cerr << "== readFunctionTable" << std::endl;
+ wasm.table.initial = getU32LEB();
+ wasm.table.max = getU32LEB();
auto num = getU32LEB();
for (size_t i = 0; i < num; i++) {
- auto index = getU32LEB();
- functionTable.push_back(index);
+ wasm.table.segments.emplace_back(readExpression());
+ auto& temporary = functionTable[i];
+ auto size = getU32LEB();
+ for (Index j = 0; j < size; j++) {
+ temporary.push_back(getU32LEB());
+ }
}
}
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 846dd896e..28a3e8e6d 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -501,8 +501,8 @@ public:
}
};
-// Execute an expression in global init
-class GlobalInitRunner : public ExpressionRunner<GlobalInitRunner> {
+// Execute an constant expression in a global init or memory offset
+class ConstantExpressionRunner : public ExpressionRunner<ConstantExpressionRunner> {
public:
Flow visitLoop(Loop* curr) { WASM_UNREACHABLE(); }
Flow visitCall(Call* curr) { WASM_UNREACHABLE(); }
@@ -537,6 +537,7 @@ public:
struct ExternalInterface {
virtual void init(Module& wasm) {}
virtual Literal callImport(Import* import, LiteralList& arguments) = 0;
+ virtual Literal callTable(Index index, Name 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;
@@ -551,7 +552,7 @@ public:
ModuleInstance(Module& wasm, ExternalInterface* externalInterface) : wasm(wasm), externalInterface(externalInterface) {
memorySize = wasm.memory.initial;
for (Index i = 0; i < wasm.globals.size(); i++) {
- globals.push_back(GlobalInitRunner().visit(wasm.globals[i]->init).value);
+ globals.push_back(ConstantExpressionRunner().visit(wasm.globals[i]->init).value);
}
externalInterface->init(wasm);
if (wasm.start.is()) {
@@ -591,8 +592,8 @@ private:
return callFunctionInternal(name, arguments);
}
-private:
- // Internal function call.
+public:
+ // Internal function call. Must be public so that callTable implementations can use it (refactor?)
Literal callFunctionInternal(IString name, LiteralList& arguments) {
class FunctionScope {
@@ -674,18 +675,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) {
@@ -804,6 +795,8 @@ private:
return ret;
}
+private:
+
Address memorySize; // in pages
template <class LS>
diff --git a/src/wasm-js.cpp b/src/wasm-js.cpp
index 33b0faa55..83956e47e 100644
--- a/src/wasm-js.cpp
+++ b/src/wasm-js.cpp
@@ -193,12 +193,25 @@ extern "C" void EMSCRIPTEN_KEEPALIVE instantiate() {
var source = Module['HEAP8'].subarray($1, $1 + $2);
var target = new Int8Array(Module['outside']['newBuffer']);
target.set(source, $0);
- }, (uint32_t)segment.offset, &segment.data[0], segment.data.size());
+ }, 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, WasmType type) {
+ switch (type) {
+ 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, import->type->result);
+ }
+
+ Literal callTable(Index index, Name type, LiteralList& arguments, ModuleInstance& instance) override {
+ void* ptr = (void*)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(func->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, instance.wasm.getFunctionType(type)->result);
}
}
diff --git a/src/wasm-linker.cpp b/src/wasm-linker.cpp
index d8bae7558..2c9a4cd4d 100644
--- a/src/wasm-linker.cpp
+++ b/src/wasm-linker.cpp
@@ -83,7 +83,7 @@ void Linker::layout() {
// Update the segments with their addresses now that they have been allocated.
for (const auto& seg : out.segments) {
Address address = staticAddresses[seg.first];
- out.wasm.memory.segments[seg.second].offset = address;
+ out.wasm.memory.segments[seg.second].offset = out.wasm.allocator.alloc<Const>()->set(Literal(uint32_t(address)));
segmentsByAddress[address] = seg.second;
}
@@ -132,7 +132,7 @@ void Linker::layout() {
// Emit the pre-assigned function names in sorted order
for (const auto& P : functionNames) {
- out.wasm.table.names.push_back(P.second);
+ getTableSegment().data.push_back(P.second);
}
for (auto& relocation : out.relocations) {
@@ -206,9 +206,11 @@ void Linker::layout() {
}
// ensure an explicit function type for indirect call targets
- for (auto& name : out.wasm.table.names) {
- auto* func = out.wasm.getFunction(name);
- func->type = ensureFunctionType(getSig(func), &out.wasm)->name;
+ for (auto& segment : out.wasm.table.segments) {
+ for (auto& name : segment.data) {
+ auto* func = out.wasm.getFunction(name);
+ func->type = ensureFunctionType(getSig(func), &out.wasm)->name;
+ }
}
// Export malloc and free whenever availble. JavsScript version of malloc has
@@ -223,6 +225,11 @@ void Linker::layout() {
if (out.symbolInfo.implementedFunctions.count("free")) {
exportFunction("free", true);
}
+
+ // finalize function table
+ if (out.wasm.table.segments.size() > 0) {
+ out.wasm.table.initial = out.wasm.table.max = getTableSegment().data.size();
+ }
}
bool Linker::linkObject(S2WasmBuilder& builder) {
@@ -382,10 +389,19 @@ void Linker::emscriptenGlue(std::ostream& o) {
o << " }\n";
}
+Table::Segment& Linker::getTableSegment() {
+ if (out.wasm.table.segments.size() == 0) {
+ out.wasm.table.segments.emplace_back(out.wasm.allocator.alloc<Const>()->set(Literal(uint32_t(0))));
+ } else {
+ assert(out.wasm.table.segments.size() == 1);
+ }
+ return out.wasm.table.segments[0];
+}
+
Index Linker::getFunctionIndex(Name name) {
if (!functionIndexes.count(name)) {
- functionIndexes[name] = out.wasm.table.names.size();
- out.wasm.table.names.push_back(name);
+ functionIndexes[name] = getTableSegment().data.size();
+ getTableSegment().data.push_back(name);
if (debug) {
std::cerr << "function index: " << name << ": "
<< functionIndexes[name] << '\n';
@@ -403,7 +419,6 @@ bool hasI64ResultOrParam(FunctionType* ft) {
}
void Linker::makeDummyFunction() {
- assert(out.wasm.table.names.empty());
bool create = false;
// Check if there are address-taken functions
for (auto& relocation : out.relocations) {
@@ -421,9 +436,10 @@ void Linker::makeDummyFunction() {
}
void Linker::makeDynCallThunks() {
+ if (out.wasm.table.segments.size() == 0) return;
std::unordered_set<std::string> sigs;
wasm::Builder wasmBuilder(out.wasm);
- for (const auto& indirectFunc : out.wasm.table.names) {
+ for (const auto& indirectFunc : getTableSegment().data) {
// Skip generating thunks for the dummy function
if (indirectFunc == dummyFunction) continue;
std::string sig(getSig(out.wasm.getFunction(indirectFunc)));
diff --git a/src/wasm-linker.h b/src/wasm-linker.h
index 3f1e8c7ac..a6f5d319a 100644
--- a/src/wasm-linker.h
+++ b/src/wasm-linker.h
@@ -114,12 +114,12 @@ class LinkerObject {
// Add an initializer segment for the named static variable.
void addSegment(Name name, const char* data, Address size) {
segments[name] = wasm.memory.segments.size();
- wasm.memory.segments.emplace_back(0, data, size);
+ wasm.memory.segments.emplace_back(wasm.allocator.alloc<Const>()->set(Literal(uint32_t(0))), data, size);
}
void addSegment(Name name, std::vector<char>& data) {
segments[name] = wasm.memory.segments.size();
- wasm.memory.segments.emplace_back(0, data);
+ wasm.memory.segments.emplace_back(wasm.allocator.alloc<Const>()->set(Literal(uint32_t(0))), data);
}
void addInitializerFunction(Name name) {
@@ -282,6 +282,10 @@ class Linker {
void ensureImport(Name target, std::string signature);
+ // Makes sure the table has a single segment, with offset 0,
+ // to which we can add content.
+ Table::Segment& getTableSegment();
+
// Retrieves (and assigns) an entry index in the indirect function table for
// a given function.
Index getFunctionIndex(Name name);
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index 750d485fb..b70f70b4e 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -367,13 +367,15 @@ private:
if (id == START) return parseStart(curr);
if (id == FUNC) return parseFunction(curr);
if (id == MEMORY) return parseMemory(curr);
+ if (id == DATA) return parseData(curr);
if (id == EXPORT) return parseExport(curr);
if (id == IMPORT) return; // already done
if (id == GLOBAL) return parseGlobal(curr);
if (id == TABLE) return parseTable(curr);
+ if (id == ELEM) return parseElem(curr);
if (id == TYPE) return; // already done
std::cerr << "bad module element " << id.str << '\n';
- throw ParseException("unknown module element");
+ throw ParseException("unknown module element", curr.line, curr.col);
}
// function parsing state
@@ -1321,19 +1323,40 @@ private:
}
while (i < s.size()) {
Element& curr = *s[i];
- assert(curr[0]->str() == SEGMENT);
- const char *input = curr[2]->c_str();
+ size_t j = 1;
+ Address offsetValue;
+ if (curr[0]->str() == DATA) {
+ offsetValue = 0;
+ } else {
+ offsetValue = atoi(curr[j++]->c_str());
+ }
+ const char *input = curr[j]->c_str();
+ auto* offset = allocator.alloc<Const>();
+ offset->type = i32;
+ offset->value = Literal(int32_t(offsetValue));
if (auto size = strlen(input)) {
std::vector<char> data;
stringToBinary(input, size, data);
- wasm.memory.segments.emplace_back(atoi(curr[1]->c_str()), data.data(), data.size());
+ wasm.memory.segments.emplace_back(offset, data.data(), data.size());
} else {
- wasm.memory.segments.emplace_back(atoi(curr[1]->c_str()), "", 0);
+ wasm.memory.segments.emplace_back(offset, "", 0);
}
i++;
}
}
+ void parseData(Element& s) {
+ auto* offset = parseExpression(s[1]);
+ const char *input = s[2]->c_str();
+ if (auto size = strlen(input)) {
+ std::vector<char> data;
+ stringToBinary(input, size, data);
+ wasm.memory.segments.emplace_back(offset, data.data(), data.size());
+ } else {
+ wasm.memory.segments.emplace_back(offset, "", 0);
+ }
+ }
+
void parseExport(Element& s) {
if (!s[2]->dollared() && !std::isdigit(s[2]->str()[0])) {
assert(s[2]->str() == MEMORY);
@@ -1402,9 +1425,41 @@ private:
}
void parseTable(Element& s) {
- for (size_t i = 1; i < s.size(); i++) {
- wasm.table.names.push_back(getFunctionName(*s[i]));
+ if (s.size() == 1) return; // empty table in old notation
+ if (!s[1]->dollared()) {
+ if (s[1]->str() == ANYFUNC) {
+ // (table type (elem ..))
+ parseElem(*s[2]);
+ wasm.table.initial = wasm.table.max = wasm.table.segments[0].data.size();
+ return;
+ }
+ // first element isn't dollared, and isn't anyfunc. this could be old syntax for (table 0 1) which means function 0 and 1, or it could be (table initial max? type), look for type
+ if (s[s.size() - 1]->str() == ANYFUNC) {
+ // (table initial max? type)
+ wasm.table.initial = atoi(s[1]->c_str());
+ wasm.table.max = atoi(s[2]->c_str());
+ return;
+ }
+ }
+ // old notation (table func1 func2 ..)
+ parseElem(s);
+ wasm.table.initial = wasm.table.max = wasm.table.segments[0].data.size();
+ }
+
+ void parseElem(Element& s) {
+ Index i = 1;
+ Expression* offset;
+ if (s[i]->isList()) {
+ // there is an init expression
+ offset = parseExpression(s[i++]);
+ } else {
+ offset = allocator.alloc<Const>()->set(Literal(int32_t(0)));
+ }
+ Table::Segment segment(offset);
+ for (; i < s.size(); i++) {
+ segment.data.push_back(getFunctionName(*s[i]));
}
+ wasm.table.segments.push_back(segment);
}
void parseType(Element& s) {
diff --git a/src/wasm-validator.h b/src/wasm-validator.h
index 90930d8f4..3d9a0e1c3 100644
--- a/src/wasm-validator.h
+++ b/src/wasm-validator.h
@@ -281,12 +281,6 @@ public:
void visitMemory(Memory *curr) {
shouldBeFalse(curr->initial > curr->max, "memory", "memory max >= initial");
shouldBeTrue(curr->max <= Memory::kMaxSize, "memory", "max memory must be <= 4GB");
- size_t top = 0;
- for (auto& segment : curr->segments) {
- shouldBeFalse(segment.offset < top, "memory", "segment offset is small enough");
- top = segment.offset + segment.data.size();
- }
- shouldBeFalse(top > curr->initial * Memory::kPageSize, "memory", "total segments must be small enough");
}
void visitModule(Module *curr) {
// exports
diff --git a/src/wasm.cpp b/src/wasm.cpp
index dbbd209b4..8cfdee759 100644
--- a/src/wasm.cpp
+++ b/src/wasm.cpp
@@ -49,10 +49,12 @@ Name GROW_WASM_MEMORY("__growWasmMemory"),
PARAM("param"),
RESULT("result"),
MEMORY("memory"),
+ DATA("data"),
SEGMENT("segment"),
EXPORT("export"),
IMPORT("import"),
TABLE("table"),
+ ELEM("elem"),
LOCAL("local"),
TYPE("type"),
CALL("call"),
@@ -68,6 +70,7 @@ Name GROW_WASM_MEMORY("__growWasmMemory"),
NEG_NAN("-nan"),
CASE("case"),
BR("br"),
+ ANYFUNC("anyfunc"),
FAKE_RETURN("fake_return_waka123"),
ASSERT_RETURN("assert_return"),
ASSERT_TRAP("assert_trap"),
diff --git a/src/wasm.h b/src/wasm.h
index 38a030e1a..68558033d 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -1432,7 +1432,23 @@ public:
class Table {
public:
- std::vector<Name> names;
+ 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<Segment> segments;
+
+ Table() : initial(0), max(kMaxSize) {}
};
class Memory {
@@ -1440,15 +1456,16 @@ 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 {
- Address offset;
+ Expression* offset;
std::vector<char> data; // TODO: optimize
Segment() {}
- Segment(Address offset, const char *init, Address size) : offset(offset) {
+ Segment(Expression* offset, const char *init, Address size) : offset(offset) {
data.resize(size);
std::copy_n(init, size, data.begin());
}
- Segment(Address offset, std::vector<char>& init) : offset(offset) {
+ Segment(Expression* offset, std::vector<char>& init) : offset(offset) {
data.swap(init);
}
};