summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md26
-rw-r--r--src/binaryen-c.cpp145
-rw-r--r--src/binaryen-c.h38
-rw-r--r--src/ir/import-utils.h14
-rw-r--r--src/ir/module-splitting.cpp129
-rw-r--r--src/ir/module-utils.h48
-rw-r--r--src/ir/names.h4
-rw-r--r--src/js/binaryen.js-post.js52
-rw-r--r--src/passes/DeadArgumentElimination.cpp8
-rw-r--r--src/passes/Directize.cpp56
-rw-r--r--src/passes/ExtractFunction.cpp4
-rw-r--r--src/passes/FuncCastEmulation.cpp21
-rw-r--r--src/passes/GenerateDynCalls.cpp5
-rw-r--r--src/passes/I64ToI32Lowering.cpp7
-rw-r--r--src/passes/Inlining.cpp9
-rw-r--r--src/passes/LegalizeJSInterface.cpp18
-rw-r--r--src/passes/Metrics.cpp14
-rw-r--r--src/passes/PostEmscripten.cpp4
-rw-r--r--src/passes/Print.cpp39
-rw-r--r--src/passes/PrintCallGraph.cpp10
-rw-r--r--src/passes/RemoveImports.cpp4
-rw-r--r--src/passes/RemoveUnusedModuleElements.cpp67
-rw-r--r--src/passes/ReorderFunctions.cpp8
-rw-r--r--src/passes/opt-utils.h8
-rw-r--r--src/shell-interface.h24
-rw-r--r--src/tools/fuzzing.h57
-rw-r--r--src/tools/wasm-ctor-eval.cpp15
-rw-r--r--src/tools/wasm-metadce.cpp20
-rw-r--r--src/tools/wasm-reduce.cpp8
-rw-r--r--src/tools/wasm-shell.cpp28
-rw-r--r--src/tools/wasm-split.cpp15
-rw-r--r--src/tools/wasm2js.cpp5
-rw-r--r--src/wasm-binary.h41
-rw-r--r--src/wasm-builder.h14
-rw-r--r--src/wasm-delegations-fields.h1
-rw-r--r--src/wasm-interpreter.h32
-rw-r--r--src/wasm-s-parser.h7
-rw-r--r--src/wasm-traversal.h4
-rw-r--r--src/wasm.h18
-rw-r--r--src/wasm/wasm-binary.cpp283
-rw-r--r--src/wasm/wasm-emscripten.cpp6
-rw-r--r--src/wasm/wasm-s-parser.cpp161
-rw-r--r--src/wasm/wasm-stack.cpp5
-rw-r--r--src/wasm/wasm-validator.cpp48
-rw-r--r--src/wasm/wasm.cpp22
-rw-r--r--src/wasm2js.h138
-rw-r--r--test/binaryen.js/expressions.js6
-rw-r--r--test/binaryen.js/expressions.js.txt2
-rw-r--r--test/binaryen.js/kitchen-sink.js22
-rw-r--r--test/binaryen.js/kitchen-sink.js.txt8
-rw-r--r--test/binaryen.js/tail_calls.js1
-rw-r--r--test/ctor-eval/bad-indirect-call.wast.out2
-rw-r--r--test/ctor-eval/bad-indirect-call2.wast.out2
-rw-r--r--test/ctor-eval/bad-indirect-call3.wast.out2
-rw-r--r--test/example/c-api-kitchen-sink.c5
-rw-r--r--test/example/c-api-kitchen-sink.txt6
-rw-r--r--test/example/c-api-multiple-tables.c90
-rw-r--r--test/example/c-api-multiple-tables.txt20
-rw-r--r--test/example/module-splitting.txt16
-rw-r--r--test/multi-table.minified.txt1
-rw-r--r--test/multi-table.wast14
-rw-r--r--test/multi-table.wast.from-wast14
-rw-r--r--test/multi-table.wast.fromBinary15
-rw-r--r--test/multi-table.wast.fromBinary.noDebugInfo15
-rw-r--r--test/newsyntax.wast.from-wast4
-rw-r--r--test/newsyntax.wast.fromBinary4
-rw-r--r--test/newsyntax.wast.fromBinary.noDebugInfo4
-rw-r--r--test/passes/O3_low-memory-unused_metrics.txt1
-rw-r--r--test/passes/converge_O3_metrics.bin.txt2
-rw-r--r--test/passes/dae_all-features.txt2
-rw-r--r--test/passes/directize_all-features.txt52
-rw-r--r--test/passes/directize_all-features.wast46
-rw-r--r--test/passes/flatten_all-features.txt8
-rw-r--r--test/passes/func-metrics.txt6
-rw-r--r--test/passes/fuzz_metrics_noprint.bin.txt1
-rw-r--r--test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_optimize-level=3.txt4
-rw-r--r--test/passes/metrics_all-features.txt2
-rw-r--r--test/passes/metrics_strip-debug_metrics.bin.txt2
-rw-r--r--test/passes/metrics_strip-producers_metrics.bin.txt2
-rw-r--r--test/passes/print_g_metrics.bin.txt1
-rw-r--r--test/passes/remove-unused-module-elements_all-features.txt34
-rw-r--r--test/passes/remove-unused-module-elements_all-features.wast21
-rw-r--r--test/passes/remove-unused-names_merge-blocks_all-features.txt8
-rw-r--r--test/passes/remove-unused-nonfunction-module-elements_all-features.txt24
-rw-r--r--test/passes/too_much_for_liveness.bin.txt2
-rw-r--r--test/passes/translate-to-fuzz_all-features_metrics_noprint.txt1
-rw-r--r--test/polymorphic_stack.wast.from-wast2
-rw-r--r--test/reference-types.wast.from-wast34
-rw-r--r--test/reference-types.wast.fromBinary34
-rw-r--r--test/reference-types.wast.fromBinary.noDebugInfo34
-rw-r--r--test/spec/old_import.wast4
-rw-r--r--test/tail-call.wast.from-wast2
-rw-r--r--test/tail-call.wast.fromBinary2
-rw-r--r--test/tail-call.wast.fromBinary.noDebugInfo2
94 files changed, 1660 insertions, 616 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 50d018937..cafdae444 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,32 @@ Current Trunk
-------------
- `wasm-dis` now supports options to enable or disable Wasm features.
+- Reference types support has been improved by allowing multiple tables in a
+ module.
+- `call_indirect` and `return_call_indirect` now take an additional table name
+ parameter. This is necessary for reference types support.
+- New getter/setter methods have been introduced for `call_indirect` table name:
+ - `BinaryenCallIndirectGetTable`
+ - `BinaryenCallIndirectSetTable`
+ - JS API `CallIndirect.table`
+- New APIs have been added to add and manipulate multiple tables in a module:
+ - `BinaryenAddTable`
+ - `BinaryenRemoveTable`
+ - `BinaryenGetNumTables`
+ - `BinaryenGetTable`
+ - `BinaryenGetTableByIndex`
+ - `BinaryenTableGetName`
+ - `BinaryenTableGetInitial`
+ - `BinaryenTableHasMax`
+ - `BinaryenTableGetMax`
+ - `BinaryenTableImportGetModule`
+ - `BinaryenTableImportGetBase`
+ - `module.addTable`
+ - `module.removeTable`
+ - `module.getTable`
+ - `module.getTableByIndex`
+ - `module.getNumTables`
+ - `binaryen.getTableInfo`
v99
---
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index 545c4ab94..1015e41c0 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -805,6 +805,7 @@ BinaryenExpressionRef BinaryenReturnCall(BinaryenModuleRef module,
}
static BinaryenExpressionRef
makeBinaryenCallIndirect(BinaryenModuleRef module,
+ const char* table,
BinaryenExpressionRef target,
BinaryenExpressionRef* operands,
BinaryenIndex numOperands,
@@ -812,6 +813,7 @@ makeBinaryenCallIndirect(BinaryenModuleRef module,
BinaryenType results,
bool isReturn) {
auto* ret = ((Module*)module)->allocator.alloc<CallIndirect>();
+ ret->table = table;
ret->target = (Expression*)target;
for (BinaryenIndex i = 0; i < numOperands; i++) {
ret->operands.push_back((Expression*)operands[i]);
@@ -823,23 +825,25 @@ makeBinaryenCallIndirect(BinaryenModuleRef module,
return static_cast<Expression*>(ret);
}
BinaryenExpressionRef BinaryenCallIndirect(BinaryenModuleRef module,
+ const char* table,
BinaryenExpressionRef target,
BinaryenExpressionRef* operands,
BinaryenIndex numOperands,
BinaryenType params,
BinaryenType results) {
return makeBinaryenCallIndirect(
- module, target, operands, numOperands, params, results, false);
+ module, table, target, operands, numOperands, params, results, false);
}
BinaryenExpressionRef
BinaryenReturnCallIndirect(BinaryenModuleRef module,
+ const char* table,
BinaryenExpressionRef target,
BinaryenExpressionRef* operands,
BinaryenIndex numOperands,
BinaryenType params,
BinaryenType results) {
return makeBinaryenCallIndirect(
- module, target, operands, numOperands, params, results, true);
+ module, table, target, operands, numOperands, params, results, true);
}
BinaryenExpressionRef BinaryenLocalGet(BinaryenModuleRef module,
BinaryenIndex index,
@@ -1610,6 +1614,19 @@ void BinaryenCallIndirectSetTarget(BinaryenExpressionRef expr,
assert(targetExpr);
static_cast<CallIndirect*>(expression)->target = (Expression*)targetExpr;
}
+const char* BinaryenCallIndirectGetTable(BinaryenExpressionRef expr) {
+ auto* expression = (Expression*)expr;
+ assert(expression->is<CallIndirect>());
+ return static_cast<CallIndirect*>(expression)->table.c_str();
+}
+void BinaryenCallIndirectSetTable(BinaryenExpressionRef expr,
+ const char* table) {
+ Name name(table);
+ auto* expression = (Expression*)expr;
+
+ assert(expression->is<CallIndirect>());
+ static_cast<CallIndirect*>(expression)->table = name;
+}
BinaryenIndex BinaryenCallIndirectGetNumOperands(BinaryenExpressionRef expr) {
auto* expression = (Expression*)expr;
assert(expression->is<CallIndirect>());
@@ -3154,9 +3171,11 @@ void BinaryenAddTableImport(BinaryenModuleRef module,
const char* internalName,
const char* externalModuleName,
const char* externalBaseName) {
- auto& table = ((Module*)module)->table;
- table.module = externalModuleName;
- table.base = externalBaseName;
+ auto table = std::make_unique<Table>();
+ table->name = internalName;
+ table->module = externalModuleName;
+ table->base = externalBaseName;
+ ((Module*)module)->addTable(std::move(table));
}
void BinaryenAddMemoryImport(BinaryenModuleRef module,
const char* internalName,
@@ -3273,35 +3292,88 @@ BinaryenExportRef BinaryenGetExportByIndex(BinaryenModuleRef module,
return exports[index].get();
}
-// Function table. One per module
-
+// TODO(reference-types): maybe deprecate this function?
void BinaryenSetFunctionTable(BinaryenModuleRef module,
BinaryenIndex initial,
BinaryenIndex maximum,
const char** funcNames,
BinaryenIndex numFuncNames,
BinaryenExpressionRef offset) {
+ auto* wasm = (Module*)module;
+ if (wasm->tables.empty()) {
+ wasm->addTable(Builder::makeTable(Name::fromInt(0)));
+ }
+
+ auto& table = wasm->tables.front();
+ table->initial = initial;
+ table->max = maximum;
+
Table::Segment segment((Expression*)offset);
for (BinaryenIndex i = 0; i < numFuncNames; i++) {
segment.data.push_back(funcNames[i]);
}
- auto& table = ((Module*)module)->table;
- table.initial = initial;
- table.max = maximum;
- table.exists = true;
- table.segments.push_back(segment);
+ table->segments.push_back(segment);
+}
+
+BinaryenTableRef BinaryenAddTable(BinaryenModuleRef module,
+ const char* name,
+ BinaryenIndex initial,
+ BinaryenIndex maximum,
+ const char** funcNames,
+ BinaryenIndex numFuncNames,
+ BinaryenExpressionRef offset) {
+ auto table = Builder::makeTable(name, initial, maximum);
+ table->hasExplicitName = true;
+
+ Table::Segment segment((Expression*)offset);
+ for (BinaryenIndex i = 0; i < numFuncNames; i++) {
+ segment.data.push_back(funcNames[i]);
+ }
+ table->segments.push_back(segment);
+ ((Module*)module)->addTable(std::move(table));
+
+ return ((Module*)module)->getTable(name);
+}
+void BinaryenRemoveTable(BinaryenModuleRef module, const char* table) {
+ ((Module*)module)->removeTable(table);
+}
+BinaryenIndex BinaryenGetNumTables(BinaryenModuleRef module) {
+ return ((Module*)module)->tables.size();
+}
+BinaryenTableRef BinaryenGetTable(BinaryenModuleRef module, const char* name) {
+ return ((Module*)module)->getTableOrNull(name);
+}
+BinaryenTableRef BinaryenGetTableByIndex(BinaryenModuleRef module,
+ BinaryenIndex index) {
+ const auto& tables = ((Module*)module)->tables;
+ if (tables.size() <= index) {
+ Fatal() << "invalid table index.";
+ }
+ return tables[index].get();
}
int BinaryenIsFunctionTableImported(BinaryenModuleRef module) {
- return ((Module*)module)->table.imported();
+ if (((Module*)module)->tables.size() > 0) {
+ return ((Module*)module)->tables[0]->imported();
+ }
+
+ return false;
}
BinaryenIndex BinaryenGetNumFunctionTableSegments(BinaryenModuleRef module) {
- return ((Module*)module)->table.segments.size();
+ if (((Module*)module)->tables.size() > 0) {
+ return ((Module*)module)->tables[0]->segments.size();
+ }
+
+ return 0;
}
BinaryenExpressionRef
BinaryenGetFunctionTableSegmentOffset(BinaryenModuleRef module,
BinaryenIndex segmentId) {
- const auto& segments = ((Module*)module)->table.segments;
+ if (((Module*)module)->tables.empty()) {
+ Fatal() << "module has no tables.";
+ }
+
+ const auto& segments = ((Module*)module)->tables[0]->segments;
if (segments.size() <= segmentId) {
Fatal() << "invalid function table segment id.";
}
@@ -3309,7 +3381,11 @@ BinaryenGetFunctionTableSegmentOffset(BinaryenModuleRef module,
}
BinaryenIndex BinaryenGetFunctionTableSegmentLength(BinaryenModuleRef module,
BinaryenIndex segmentId) {
- const auto& segments = ((Module*)module)->table.segments;
+ if (((Module*)module)->tables.empty()) {
+ Fatal() << "module has no tables.";
+ }
+
+ const auto& segments = ((Module*)module)->tables[0]->segments;
if (segments.size() <= segmentId) {
Fatal() << "invalid function table segment id.";
}
@@ -3318,7 +3394,11 @@ BinaryenIndex BinaryenGetFunctionTableSegmentLength(BinaryenModuleRef module,
const char* BinaryenGetFunctionTableSegmentData(BinaryenModuleRef module,
BinaryenIndex segmentId,
BinaryenIndex dataId) {
- const auto& segments = ((Module*)module)->table.segments;
+ if (((Module*)module)->tables.empty()) {
+ Fatal() << "module has no tables.";
+ }
+
+ const auto& segments = ((Module*)module)->tables[0]->segments;
if (segments.size() <= segmentId ||
segments[segmentId].data.size() <= dataId) {
Fatal() << "invalid function table segment or data id.";
@@ -3789,6 +3869,21 @@ void BinaryenFunctionSetDebugLocation(BinaryenFunctionRef func,
}
//
+// =========== Table operations ===========
+//
+
+const char* BinaryenTableGetName(BinaryenTableRef table) {
+ return ((Table*)table)->name.c_str();
+}
+int BinaryenTableGetInitial(BinaryenTableRef table) {
+ return ((Table*)table)->initial;
+}
+int BinaryenTableHasMax(BinaryenTableRef table) {
+ return ((Table*)table)->hasMax();
+}
+int BinaryenTableGetMax(BinaryenTableRef table) { return ((Table*)table)->max; }
+
+//
// =========== Global operations ===========
//
@@ -3835,6 +3930,14 @@ const char* BinaryenFunctionImportGetModule(BinaryenFunctionRef import) {
return "";
}
}
+const char* BinaryenTableImportGetModule(BinaryenTableRef import) {
+ auto* table = (Table*)import;
+ if (table->imported()) {
+ return table->module.c_str();
+ } else {
+ return "";
+ }
+}
const char* BinaryenGlobalImportGetModule(BinaryenGlobalRef import) {
auto* global = (Global*)import;
if (global->imported()) {
@@ -3859,6 +3962,14 @@ const char* BinaryenFunctionImportGetBase(BinaryenFunctionRef import) {
return "";
}
}
+const char* BinaryenTableImportGetBase(BinaryenTableRef import) {
+ auto* table = (Table*)import;
+ if (table->imported()) {
+ return table->base.c_str();
+ } else {
+ return "";
+ }
+}
const char* BinaryenGlobalImportGetBase(BinaryenGlobalRef import) {
auto* global = (Global*)import;
if (global->imported()) {
diff --git a/src/binaryen-c.h b/src/binaryen-c.h
index 83424ceb6..ba61db885 100644
--- a/src/binaryen-c.h
+++ b/src/binaryen-c.h
@@ -608,6 +608,7 @@ BINARYEN_API BinaryenExpressionRef BinaryenCall(BinaryenModuleRef module,
BinaryenType returnType);
BINARYEN_API BinaryenExpressionRef
BinaryenCallIndirect(BinaryenModuleRef module,
+ const char* table,
BinaryenExpressionRef target,
BinaryenExpressionRef* operands,
BinaryenIndex numOperands,
@@ -621,6 +622,7 @@ BinaryenReturnCall(BinaryenModuleRef module,
BinaryenType returnType);
BINARYEN_API BinaryenExpressionRef
BinaryenReturnCallIndirect(BinaryenModuleRef module,
+ const char* table,
BinaryenExpressionRef target,
BinaryenExpressionRef* operands,
BinaryenIndex numOperands,
@@ -1041,6 +1043,12 @@ BinaryenCallIndirectGetTarget(BinaryenExpressionRef expr);
BINARYEN_API void
BinaryenCallIndirectSetTarget(BinaryenExpressionRef expr,
BinaryenExpressionRef targetExpr);
+// Gets the table name of a `call_indirect` expression.
+BINARYEN_API const char*
+BinaryenCallIndirectGetTable(BinaryenExpressionRef expr);
+// Sets the table name of a `call_indirect` expression.
+BINARYEN_API void BinaryenCallIndirectSetTable(BinaryenExpressionRef expr,
+ const char* table);
// Gets the number of operands of a `call_indirect` expression.
BINARYEN_API BinaryenIndex
BinaryenCallIndirectGetNumOperands(BinaryenExpressionRef expr);
@@ -2039,6 +2047,25 @@ BINARYEN_API BinaryenIndex BinaryenGetFunctionTableSegmentLength(
BINARYEN_API const char* BinaryenGetFunctionTableSegmentData(
BinaryenModuleRef module, BinaryenIndex segmentId, BinaryenIndex dataId);
+// Tables
+
+BINARYEN_REF(Table);
+
+BINARYEN_API BinaryenTableRef BinaryenAddTable(BinaryenModuleRef module,
+ const char* table,
+ BinaryenIndex initial,
+ BinaryenIndex maximum,
+ const char** funcNames,
+ BinaryenIndex numFuncNames,
+ BinaryenExpressionRef offset);
+BINARYEN_API void BinaryenRemoveTable(BinaryenModuleRef module,
+ const char* table);
+BINARYEN_API BinaryenIndex BinaryenGetNumTables(BinaryenModuleRef module);
+BINARYEN_API BinaryenTableRef BinaryenGetTable(BinaryenModuleRef module,
+ const char* name);
+BINARYEN_API BinaryenTableRef BinaryenGetTableByIndex(BinaryenModuleRef module,
+ BinaryenIndex index);
+
// Memory. One per module
// Each segment has data in segments, a start offset in segmentOffsets, and a
@@ -2330,6 +2357,15 @@ BINARYEN_API void BinaryenFunctionSetDebugLocation(BinaryenFunctionRef func,
BinaryenIndex columnNumber);
//
+// ========== Table Operations ==========
+//
+
+BINARYEN_API const char* BinaryenTableGetName(BinaryenTableRef table);
+BINARYEN_API int BinaryenTableGetInitial(BinaryenTableRef table);
+BINARYEN_API int BinaryenTableHasMax(BinaryenTableRef table);
+BINARYEN_API int BinaryenTableGetMax(BinaryenTableRef table);
+
+//
// ========== Global Operations ==========
//
@@ -2364,12 +2400,14 @@ BINARYEN_API BinaryenType BinaryenEventGetResults(BinaryenEventRef event);
// Gets the external module name of the specified import.
BINARYEN_API const char*
BinaryenFunctionImportGetModule(BinaryenFunctionRef import);
+BINARYEN_API const char* BinaryenTableImportGetModule(BinaryenTableRef import);
BINARYEN_API const char*
BinaryenGlobalImportGetModule(BinaryenGlobalRef import);
BINARYEN_API const char* BinaryenEventImportGetModule(BinaryenEventRef import);
// Gets the external base name of the specified import.
BINARYEN_API const char*
BinaryenFunctionImportGetBase(BinaryenFunctionRef import);
+BINARYEN_API const char* BinaryenTableImportGetBase(BinaryenTableRef import);
BINARYEN_API const char* BinaryenGlobalImportGetBase(BinaryenGlobalRef import);
BINARYEN_API const char* BinaryenEventImportGetBase(BinaryenEventRef import);
diff --git a/src/ir/import-utils.h b/src/ir/import-utils.h
index 3f3d27f1b..e4a656379 100644
--- a/src/ir/import-utils.h
+++ b/src/ir/import-utils.h
@@ -29,6 +29,7 @@ struct ImportInfo {
std::vector<Global*> importedGlobals;
std::vector<Function*> importedFunctions;
+ std::vector<Table*> importedTables;
std::vector<Event*> importedEvents;
ImportInfo(Module& wasm) : wasm(wasm) {
@@ -42,6 +43,11 @@ struct ImportInfo {
importedFunctions.push_back(import.get());
}
}
+ for (auto& import : wasm.tables) {
+ if (import->imported()) {
+ importedTables.push_back(import.get());
+ }
+ }
for (auto& import : wasm.events) {
if (import->imported()) {
importedEvents.push_back(import.get());
@@ -80,12 +86,14 @@ struct ImportInfo {
Index getNumImportedFunctions() { return importedFunctions.size(); }
+ Index getNumImportedTables() { return importedTables.size(); }
+
Index getNumImportedEvents() { return importedEvents.size(); }
Index getNumImports() {
return getNumImportedGlobals() + getNumImportedFunctions() +
getNumImportedEvents() + (wasm.memory.imported() ? 1 : 0) +
- (wasm.table.imported() ? 1 : 0);
+ getNumImportedTables();
}
Index getNumDefinedGlobals() {
@@ -96,6 +104,10 @@ struct ImportInfo {
return wasm.functions.size() - getNumImportedFunctions();
}
+ Index getNumDefinedTables() {
+ return wasm.tables.size() - getNumImportedTables();
+ }
+
Index getNumDefinedEvents() {
return wasm.events.size() - getNumImportedEvents();
}
diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp
index 1f33b44ac..111271063 100644
--- a/src/ir/module-splitting.cpp
+++ b/src/ir/module-splitting.cpp
@@ -99,23 +99,27 @@ namespace ModuleSplitting {
namespace {
-template<class F> void forEachElement(Table& table, F f) {
- for (auto& segment : table.segments) {
- Name base = "";
- Index offset = 0;
- if (auto* c = segment.offset->dynCast<Const>()) {
- offset = c->value.geti32();
- } else if (auto* g = segment.offset->dynCast<GlobalGet>()) {
- base = g->name;
- }
- for (size_t i = 0; i < segment.data.size(); ++i) {
- f(base, offset + i, segment.data[i]);
+template<class F> void forEachElement(Module& module, F f) {
+ for (auto& table : module.tables) {
+ for (auto& segment : table->segments) {
+ Name base = "";
+ Index offset = 0;
+ if (auto* c = segment.offset->dynCast<Const>()) {
+ offset = c->value.geti32();
+ } else if (auto* g = segment.offset->dynCast<GlobalGet>()) {
+ base = g->name;
+ }
+ for (size_t i = 0; i < segment.data.size(); ++i) {
+ f(table->name, base, offset + i, segment.data[i]);
+ }
}
}
}
struct TableSlotManager {
struct Slot {
+ Name tableName;
+
// If `global` is empty, then this slot is at a statically known index.
Name global;
Index index = 0;
@@ -124,13 +128,15 @@ struct TableSlotManager {
Expression* makeExpr(Module& module);
};
Module& module;
- Table& table;
+ Table* activeTable = nullptr;
Table::Segment* activeSegment = nullptr;
Slot activeBase;
std::map<Name, Slot> funcIndices;
TableSlotManager(Module& module);
+ Table* makeTable();
+
// Returns the table index for `func`, allocating a new index if necessary.
Slot getSlot(Name func);
void addSlot(Name func, Slot slot);
@@ -153,41 +159,54 @@ void TableSlotManager::addSlot(Name func, Slot slot) {
assert(it.second && "Function already has multiple table slots");
}
-TableSlotManager::TableSlotManager(Module& module)
- : module(module), table(module.table) {
+TableSlotManager::TableSlotManager(Module& module) : module(module) {
+ if (module.tables.empty()) {
+ return;
+ }
+ activeTable = module.tables.front().get();
// If there is exactly one table segment and that segment has a non-constant
// offset, append new items to the end of that segment. In all other cases,
// append new items at constant offsets after all existing items at constant
// offsets.
- if (table.segments.size() == 1 && !table.segments[0].offset->is<Const>()) {
- assert(table.segments[0].offset->is<GlobalGet>() &&
+ if (activeTable->segments.size() == 1 &&
+ !activeTable->segments[0].offset->is<Const>()) {
+ assert(activeTable->segments[0].offset->is<GlobalGet>() &&
"Unexpected initializer instruction");
- activeSegment = &table.segments[0];
- activeBase = {table.segments[0].offset->cast<GlobalGet>()->name, 0};
+ activeSegment = &activeTable->segments[0];
+ activeBase = {activeTable->name,
+ activeTable->segments[0].offset->cast<GlobalGet>()->name,
+ 0};
} else {
// Finds the segment with the highest occupied table slot so that new items
// can be inserted contiguously at the end of it without accidentally
// overwriting any other items. TODO: be more clever about filling gaps in
// the table, if that is ever useful.
Index maxIndex = 0;
- for (auto& segment : table.segments) {
+ for (auto& segment : activeTable->segments) {
assert(segment.offset->is<Const>() &&
"Unexpected non-const segment offset with multiple segments");
Index segmentBase = segment.offset->cast<Const>()->value.geti32();
if (segmentBase + segment.data.size() >= maxIndex) {
maxIndex = segmentBase + segment.data.size();
activeSegment = &segment;
- activeBase = {"", segmentBase};
+ activeBase = {activeTable->name, "", segmentBase};
}
}
}
+
// Initialize funcIndices with the functions already in the table.
- forEachElement(table, [&](Name base, Index offset, Name func) {
- addSlot(func, {base, offset});
+ forEachElement(module, [&](Name table, Name base, Index offset, Name func) {
+ addSlot(func, {table, base, offset});
});
}
+Table* TableSlotManager::makeTable() {
+ module.addTable(Builder::makeTable(Name::fromInt(0)));
+
+ return module.tables.front().get();
+}
+
TableSlotManager::Slot TableSlotManager::getSlot(Name func) {
auto slotIt = funcIndices.find(func);
if (slotIt != funcIndices.end()) {
@@ -196,21 +215,26 @@ TableSlotManager::Slot TableSlotManager::getSlot(Name func) {
// If there are no segments yet, allocate one.
if (activeSegment == nullptr) {
- table.exists = true;
- assert(table.segments.size() == 0);
- table.segments.emplace_back(Builder(module).makeConst(int32_t(0)));
- activeSegment = &table.segments.back();
+ if (activeTable == nullptr) {
+ activeTable = makeTable();
+ activeBase = {activeTable->name, "", 0};
+ }
+
+ assert(activeTable->segments.size() == 0);
+ activeTable->segments.emplace_back(Builder(module).makeConst(int32_t(0)));
+ activeSegment = &activeTable->segments.back();
}
- Slot newSlot = {activeBase.global,
+ Slot newSlot = {activeBase.tableName,
+ activeBase.global,
activeBase.index + Index(activeSegment->data.size())};
activeSegment->data.push_back(func);
addSlot(func, newSlot);
- if (table.initial <= newSlot.index) {
- table.initial = newSlot.index + 1;
+ if (activeTable->initial <= newSlot.index) {
+ activeTable->initial = newSlot.index + 1;
}
- if (table.max <= newSlot.index) {
- table.max = newSlot.index + 1;
+ if (activeTable->max <= newSlot.index) {
+ activeTable->max = newSlot.index + 1;
}
return newSlot;
}
@@ -358,8 +382,8 @@ void ModuleSplitter::thunkExportedSecondaryFunctions() {
for (size_t i = 0, size = func->sig.params.size(); i < size; ++i) {
args.push_back(builder.makeLocalGet(i, func->sig.params[i]));
}
- func->body =
- builder.makeCallIndirect(tableSlot.makeExpr(primary), args, func->sig);
+ func->body = builder.makeCallIndirect(
+ tableSlot.tableName, tableSlot.makeExpr(primary), args, func->sig);
primary.addFunction(std::move(func));
}
}
@@ -376,8 +400,10 @@ void ModuleSplitter::indirectCallsToSecondaryFunctions() {
if (!parent.secondaryFuncs.count(curr->target)) {
return;
}
+ auto tableSlot = parent.tableManager.getSlot(curr->target);
replaceCurrent(builder.makeCallIndirect(
- parent.tableManager.getSlot(curr->target).makeExpr(parent.primary),
+ tableSlot.tableName,
+ tableSlot.makeExpr(parent.primary),
curr->operands,
parent.secondary.getFunction(curr->target)->sig,
curr->isReturn));
@@ -425,11 +451,15 @@ void ModuleSplitter::exportImportCalledPrimaryFunctions() {
}
void ModuleSplitter::setupTablePatching() {
+ if (!tableManager.activeTable) {
+ return;
+ }
+
std::map<Index, Name> replacedElems;
// Replace table references to secondary functions with an imported
// placeholder that encodes the table index in its name:
// `importNamespace`.`index`.
- forEachElement(primary.table, [&](Name, Index index, Name& elem) {
+ forEachElement(primary, [&](Name, Name, Index index, Name& elem) {
if (secondaryFuncs.count(elem)) {
replacedElems[index] = elem;
auto* secondaryFunc = secondary.getFunction(elem);
@@ -451,10 +481,14 @@ void ModuleSplitter::setupTablePatching() {
return;
}
+ auto secondaryTable =
+ ModuleUtils::copyTableWithoutSegments(tableManager.activeTable, secondary);
+
if (tableManager.activeBase.global.size()) {
- assert(primary.table.segments.size() == 1 &&
+ assert(tableManager.activeTable->segments.size() == 1 &&
"Unexpected number of segments with non-const base");
- assert(secondary.table.segments.size() == 0);
+ assert(secondary.tables.size() == 1 &&
+ secondary.tables.front()->segments.empty());
// Since addition is not currently allowed in initializer expressions, we
// need to start the new secondary segment where the primary segment starts.
// The secondary segment will contain the same primary functions as the
@@ -463,7 +497,8 @@ void ModuleSplitter::setupTablePatching() {
// to be imported into the second module. TODO: use better strategies here,
// such as using ref.func in the start function or standardizing addition in
// initializer expressions.
- const Table::Segment& primarySeg = primary.table.segments.front();
+ const Table::Segment& primarySeg =
+ tableManager.activeTable->segments.front();
std::vector<Name> secondaryElems;
secondaryElems.reserve(primarySeg.data.size());
@@ -484,7 +519,7 @@ void ModuleSplitter::setupTablePatching() {
}
auto offset = ExpressionManipulator::copy(primarySeg.offset, secondary);
- secondary.table.segments.emplace_back(offset, secondaryElems);
+ secondaryTable->segments.emplace_back(offset, secondaryElems);
return;
}
@@ -494,7 +529,7 @@ void ModuleSplitter::setupTablePatching() {
std::vector<Name> currData;
auto finishSegment = [&]() {
auto* offset = Builder(secondary).makeConst(int32_t(currBase));
- secondary.table.segments.emplace_back(offset, currData);
+ secondaryTable->segments.emplace_back(offset, currData);
};
for (auto curr = replacedElems.begin(); curr != replacedElems.end(); ++curr) {
if (curr->first != currBase + currData.size()) {
@@ -551,12 +586,14 @@ void ModuleSplitter::shareImportableItems() {
primary.memory, secondary.memory, "memory", ExternalKind::Memory);
}
- if (primary.table.exists) {
- secondary.table.exists = true;
- secondary.table.initial = primary.table.initial;
- secondary.table.max = primary.table.max;
- makeImportExport(
- primary.table, secondary.table, "table", ExternalKind::Table);
+ for (auto& table : primary.tables) {
+ auto secondaryTable = secondary.getTableOrNull(table->name);
+ if (!secondaryTable) {
+ secondaryTable =
+ ModuleUtils::copyTableWithoutSegments(table.get(), secondary);
+ }
+
+ makeImportExport(*table, *secondaryTable, "table", ExternalKind::Table);
}
for (auto& global : primary.globals) {
diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h
index f33d41266..29ebcc2b5 100644
--- a/src/ir/module-utils.h
+++ b/src/ir/module-utils.h
@@ -70,6 +70,29 @@ inline Event* copyEvent(Event* event, Module& out) {
return ret;
}
+inline Table* copyTableWithoutSegments(Table* table, Module& out) {
+ auto ret = std::make_unique<Table>();
+ ret->name = table->name;
+ ret->module = table->module;
+ ret->base = table->base;
+
+ ret->initial = table->initial;
+ ret->max = table->max;
+
+ return out.addTable(std::move(ret));
+}
+
+inline Table* copyTable(Table* table, Module& out) {
+ auto ret = copyTableWithoutSegments(table, out);
+
+ for (auto segment : table->segments) {
+ segment.offset = ExpressionManipulator::copy(segment.offset, out);
+ ret->segments.push_back(segment);
+ }
+
+ return ret;
+}
+
inline void copyModule(const Module& in, Module& out) {
// we use names throughout, not raw pointers, so simple copying is fine
// for everything *but* expressions
@@ -85,9 +108,8 @@ inline void copyModule(const Module& in, Module& out) {
for (auto& curr : in.events) {
copyEvent(curr.get(), out);
}
- out.table = in.table;
- for (auto& segment : out.table.segments) {
- segment.offset = ExpressionManipulator::copy(segment.offset, out);
+ for (auto& curr : in.tables) {
+ copyTable(curr.get(), out);
}
out.memory = in.memory;
for (auto& segment : out.memory.segments) {
@@ -126,9 +148,11 @@ template<typename T> inline void renameFunctions(Module& wasm, T& map) {
}
};
maybeUpdate(wasm.start);
- for (auto& segment : wasm.table.segments) {
- for (auto& name : segment.data) {
- maybeUpdate(name);
+ for (auto& table : wasm.tables) {
+ for (auto& segment : table->segments) {
+ for (auto& name : segment.data) {
+ maybeUpdate(name);
+ }
}
}
for (auto& exp : wasm.exports) {
@@ -169,14 +193,18 @@ template<typename T> inline void iterDefinedMemories(Module& wasm, T visitor) {
}
template<typename T> inline void iterImportedTables(Module& wasm, T visitor) {
- if (wasm.table.exists && wasm.table.imported()) {
- visitor(&wasm.table);
+ for (auto& import : wasm.tables) {
+ if (import->imported()) {
+ visitor(import.get());
+ }
}
}
template<typename T> inline void iterDefinedTables(Module& wasm, T visitor) {
- if (wasm.table.exists && !wasm.table.imported()) {
- visitor(&wasm.table);
+ for (auto& import : wasm.tables) {
+ if (!import->imported()) {
+ visitor(import.get());
+ }
}
}
diff --git a/src/ir/names.h b/src/ir/names.h
index 99dd3cab7..6fbb987f5 100644
--- a/src/ir/names.h
+++ b/src/ir/names.h
@@ -76,6 +76,10 @@ inline Name getValidFunctionName(Module& module, Name root) {
return getValidName(
module, root, [&](Name test) { return !module.getFunctionOrNull(test); });
}
+inline Name getValidTableName(Module& module, Name root) {
+ return getValidName(
+ module, root, [&](Name test) { return !module.getTableOrNull(test); });
+}
inline Name getValidEventName(Module& module, Name root) {
return getValidName(
module, root, [&](Name test) { return !module.getEventOrNull(test); });
diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js
index fc64d8bbd..2ac2605fa 100644
--- a/src/js/binaryen.js-post.js
+++ b/src/js/binaryen.js-post.js
@@ -576,9 +576,9 @@ function wrapModule(module, self = {}) {
// 'callIndirect', 'returnCall', 'returnCallIndirect' are deprecated and may
// be removed in a future release. Please use the the snake_case names
// instead.
- self['callIndirect'] = self['call_indirect'] = function(target, operands, params, results) {
+ self['callIndirect'] = self['call_indirect'] = function(table, target, operands, params, results) {
return preserveStack(() =>
- Module['_BinaryenCallIndirect'](module, target, i32sToStack(operands), operands.length, params, results)
+ Module['_BinaryenCallIndirect'](module, strToStack(table), target, i32sToStack(operands), operands.length, params, results)
);
};
self['returnCall'] = self['return_call'] = function(name, operands, type) {
@@ -586,9 +586,9 @@ function wrapModule(module, self = {}) {
Module['_BinaryenReturnCall'](module, strToStack(name), i32sToStack(operands), operands.length, type)
);
};
- self['returnCallIndirect'] = self['return_call_indirect'] = function(target, operands, params, results) {
+ self['returnCallIndirect'] = self['return_call_indirect'] = function(table, target, operands, params, results) {
return preserveStack(() =>
- Module['_BinaryenReturnCallIndirect'](module, target, i32sToStack(operands), operands.length, params, results)
+ Module['_BinaryenReturnCallIndirect'](module, strToStack(table), target, i32sToStack(operands), operands.length, params, results)
);
};
@@ -2197,9 +2197,23 @@ function wrapModule(module, self = {}) {
self['getGlobal'] = function(name) {
return preserveStack(() => Module['_BinaryenGetGlobal'](module, strToStack(name)));
};
+ self['addTable'] = function(table, initial, maximum, funcNames, offset = self['i32']['const'](0)) {
+ return preserveStack(() => Module['_BinaryenAddTable'](module,
+ strToStack(table), initial, maximum,
+ i32sToStack(funcNames.map(strToStack)),
+ funcNames.length,
+ offset)
+ );
+ }
+ self['getTable'] = function(name) {
+ return preserveStack(() => Module['_BinaryenGetTable'](module, strToStack(name)));
+ };
self['removeGlobal'] = function(name) {
return preserveStack(() => Module['_BinaryenRemoveGlobal'](module, strToStack(name)));
}
+ self['removeTable'] = function(name) {
+ return preserveStack(() => Module['_BinaryenRemoveTable'](module, strToStack(name)));
+ }
self['addEvent'] = function(name, attribute, params, results) {
return preserveStack(() => Module['_BinaryenAddEvent'](module, strToStack(name), attribute, params, results));
};
@@ -2360,9 +2374,15 @@ function wrapModule(module, self = {}) {
self['getNumGlobals'] = function() {
return Module['_BinaryenGetNumGlobals'](module);
};
+ self['getNumTables'] = function() {
+ return Module['_BinaryenGetNumTables'](module);
+ };
self['getGlobalByIndex'] = function(index) {
return Module['_BinaryenGetGlobalByIndex'](module, index);
};
+ self['getTableByIndex'] = function(index) {
+ return Module['_BinaryenGetTableByIndex'](module, index);
+ };
self['emitText'] = function() {
const old = out;
let ret = '';
@@ -2587,6 +2607,7 @@ Module['getExpressionInfo'] = function(expr) {
'type': type,
'isReturn': Boolean(Module['_BinaryenCallIndirectIsReturn'](expr)),
'target': Module['_BinaryenCallIndirectGetTarget'](expr),
+ 'table': Module['_BinaryenCallIndirectGetTable'](expr),
'operands': getAllNested(expr, Module['_BinaryenCallIndirectGetNumOperands'], Module['_BinaryenCallIndirectGetOperandAt'])
};
case Module['LocalGetId']:
@@ -2973,6 +2994,23 @@ Module['getGlobalInfo'] = function(global) {
};
};
+// Obtains information about a 'Table'
+Module['getTableInfo'] = function(table) {
+ var hasMax = Boolean(Module['_BinaryenTableHasMax'](table));
+ var tableInfo = {
+ 'name': UTF8ToString(Module['_BinaryenTableGetName'](table)),
+ 'module': UTF8ToString(Module['_BinaryenTableImportGetModule'](table)),
+ 'base': UTF8ToString(Module['_BinaryenTableImportGetBase'](table)),
+ 'initial': Module['_BinaryenTableGetInitial'](table)
+ };
+
+ if (hasMax) {
+ tableInfo.max = Module['_BinaryenTableGetMax'](table);
+ }
+
+ return tableInfo;
+};
+
// Obtains information about a 'Event'
Module['getEventInfo'] = function(event_) {
return {
@@ -3421,6 +3459,12 @@ Module['CallIndirect'] = makeExpressionWrapper({
'setTarget'(expr, targetExpr) {
Module['_BinaryenCallIndirectSetTarget'](expr, targetExpr);
},
+ 'getTable'(expr) {
+ return UTF8ToString(Module['_BinaryenCallIndirectGetTable'](expr));
+ },
+ 'setTable'(expr, table) {
+ preserveStack(() => { Module['_BinaryenCallIndirectSetTable'](expr, strToStack(table)) });
+ },
'getNumOperands'(expr) {
return Module['_BinaryenCallIndirectGetNumOperands'](expr);
},
diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp
index f483a2295..1a404dbcf 100644
--- a/src/passes/DeadArgumentElimination.cpp
+++ b/src/passes/DeadArgumentElimination.cpp
@@ -284,9 +284,11 @@ struct DAE : public Pass {
infoMap[curr->value].hasUnseenCalls = true;
}
}
- for (auto& segment : module->table.segments) {
- for (auto name : segment.data) {
- infoMap[name].hasUnseenCalls = true;
+ for (auto& table : module->tables) {
+ for (auto& segment : table->segments) {
+ for (auto name : segment.data) {
+ infoMap[name].hasUnseenCalls = true;
+ }
}
}
// Scan all the functions.
diff --git a/src/passes/Directize.cpp b/src/passes/Directize.cpp
index f966d1a5a..6ee976b39 100644
--- a/src/passes/Directize.cpp
+++ b/src/passes/Directize.cpp
@@ -36,25 +36,31 @@ namespace {
struct FunctionDirectizer : public WalkerPass<PostWalker<FunctionDirectizer>> {
bool isFunctionParallel() override { return true; }
- Pass* create() override { return new FunctionDirectizer(flatTable); }
+ Pass* create() override { return new FunctionDirectizer(tables); }
- FunctionDirectizer(TableUtils::FlatTable* flatTable) : flatTable(flatTable) {}
+ FunctionDirectizer(
+ const std::unordered_map<Name, TableUtils::FlatTable>& tables)
+ : tables(tables) {}
void visitCallIndirect(CallIndirect* curr) {
- if (!flatTable) {
+ auto it = tables.find(curr->table);
+ if (it == tables.end()) {
return;
}
+
+ auto& flatTable = it->second;
+
if (auto* c = curr->target->dynCast<Const>()) {
Index index = c->value.geti32();
// If the index is invalid, or the type is wrong, we can
// emit an unreachable here, since in Binaryen it is ok to
// reorder/replace traps when optimizing (but never to
// remove them, at least not by default).
- if (index >= flatTable->names.size()) {
+ if (index >= flatTable.names.size()) {
replaceWithUnreachable(curr);
return;
}
- auto name = flatTable->names[index];
+ auto name = flatTable.names[index];
if (!name.is()) {
replaceWithUnreachable(curr);
return;
@@ -88,8 +94,7 @@ struct FunctionDirectizer : public WalkerPass<PostWalker<FunctionDirectizer>> {
}
private:
- // If null, then we cannot optimize call_indirects.
- TableUtils::FlatTable* flatTable;
+ const std::unordered_map<Name, TableUtils::FlatTable> tables;
bool changedTypes = false;
@@ -106,31 +111,34 @@ private:
struct Directize : public Pass {
void run(PassRunner* runner, Module* module) override {
- bool canOptimizeCallIndirect = true;
- TableUtils::FlatTable flatTable(module->table);
- if (!module->table.exists) {
- canOptimizeCallIndirect = false;
- } else if (module->table.imported()) {
- canOptimizeCallIndirect = false;
- } else {
- for (auto& ex : module->exports) {
- if (ex->kind == ExternalKind::Table) {
- canOptimizeCallIndirect = false;
+ std::unordered_map<Name, TableUtils::FlatTable> validTables;
+
+ for (auto& table : module->tables) {
+ if (!table->imported()) {
+ bool canOptimizeCallIndirect = true;
+
+ for (auto& ex : module->exports) {
+ if (ex->kind == ExternalKind::Table && ex->value == table->name) {
+ canOptimizeCallIndirect = false;
+ }
+ }
+
+ if (canOptimizeCallIndirect) {
+ TableUtils::FlatTable flatTable(*table);
+ if (flatTable.valid) {
+ validTables.emplace(table->name, flatTable);
+ }
}
- }
- if (!flatTable.valid) {
- canOptimizeCallIndirect = false;
}
}
+
// Without typed function references, all we can do is optimize table
// accesses, so if we can't do that, stop.
- if (!canOptimizeCallIndirect &&
- !module->features.hasTypedFunctionReferences()) {
+ if (validTables.empty() && !module->features.hasTypedFunctionReferences()) {
return;
}
// The table exists and is constant, so this is possible.
- FunctionDirectizer(canOptimizeCallIndirect ? &flatTable : nullptr)
- .run(runner, module);
+ FunctionDirectizer(validTables).run(runner, module);
}
};
diff --git a/src/passes/ExtractFunction.cpp b/src/passes/ExtractFunction.cpp
index 34fb449d4..ca05b6108 100644
--- a/src/passes/ExtractFunction.cpp
+++ b/src/passes/ExtractFunction.cpp
@@ -46,7 +46,9 @@ struct ExtractFunction : public Pass {
}
// clear data
module->memory.segments.clear();
- module->table.segments.clear();
+ // TODO: if the extracted function contains a call_indirect, the referenced
+ // table should not be removed.
+ module->tables.clear();
// leave just an export for the thing we want
if (!module->getExportOrNull(name)) {
module->exports.clear();
diff --git a/src/passes/FuncCastEmulation.cpp b/src/passes/FuncCastEmulation.cpp
index a9302fb65..1ac32166e 100644
--- a/src/passes/FuncCastEmulation.cpp
+++ b/src/passes/FuncCastEmulation.cpp
@@ -172,18 +172,21 @@ struct FuncCastEmulation : public Pass {
Signature ABIType(Type(std::vector<Type>(numParams, Type::i64)), Type::i64);
// Add a thunk for each function in the table, and do the call through it.
std::unordered_map<Name, Name> funcThunks;
- for (auto& segment : module->table.segments) {
- for (auto& name : segment.data) {
- auto iter = funcThunks.find(name);
- if (iter == funcThunks.end()) {
- auto thunk = makeThunk(name, module, numParams);
- funcThunks[name] = thunk;
- name = thunk;
- } else {
- name = iter->second;
+ for (auto& table : module->tables) {
+ for (auto& segment : table->segments) {
+ for (auto& name : segment.data) {
+ auto iter = funcThunks.find(name);
+ if (iter == funcThunks.end()) {
+ auto thunk = makeThunk(name, module, numParams);
+ funcThunks[name] = thunk;
+ name = thunk;
+ } else {
+ name = iter->second;
+ }
}
}
}
+
// update call_indirects
ParallelFuncCastEmulation(ABIType, numParams).run(runner, module);
}
diff --git a/src/passes/GenerateDynCalls.cpp b/src/passes/GenerateDynCalls.cpp
index df5145402..a53f1d0de 100644
--- a/src/passes/GenerateDynCalls.cpp
+++ b/src/passes/GenerateDynCalls.cpp
@@ -127,7 +127,10 @@ void GenerateDynCalls::generateDynCallThunk(Signature sig) {
for (const auto& param : sig.params) {
args.push_back(builder.makeLocalGet(++i, param));
}
- Expression* call = builder.makeCallIndirect(fptr, args, sig);
+ // FIXME: Should the existence of a table be ensured here? i.e. create one if
+ // there is none?
+ Expression* call =
+ builder.makeCallIndirect(Name::fromInt(0), fptr, args, sig);
f->body = call;
wasm->addFunction(std::move(f));
diff --git a/src/passes/I64ToI32Lowering.cpp b/src/passes/I64ToI32Lowering.cpp
index f14f1a7e2..36d0ce8f9 100644
--- a/src/passes/I64ToI32Lowering.cpp
+++ b/src/passes/I64ToI32Lowering.cpp
@@ -282,8 +282,11 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
params.push_back(param);
}
}
- return builder->makeCallIndirect(
- curr->target, args, Signature(Type(params), results), curr->isReturn);
+ return builder->makeCallIndirect(curr->table,
+ curr->target,
+ args,
+ Signature(Type(params), results),
+ curr->isReturn);
});
}
diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp
index 5a2a7137a..44ebbd54d 100644
--- a/src/passes/Inlining.cpp
+++ b/src/passes/Inlining.cpp
@@ -335,11 +335,14 @@ struct Inlining : public Pass {
infos[ex->value].usedGlobally = true;
}
}
- for (auto& segment : module->table.segments) {
- for (auto name : segment.data) {
- infos[name].usedGlobally = true;
+ for (auto& table : module->tables) {
+ for (auto& segment : table->segments) {
+ for (auto name : segment.data) {
+ infos[name].usedGlobally = true;
+ }
}
}
+
for (auto& global : module->globals) {
if (!global->imported()) {
for (auto* ref : FindAll<RefFunc>(global->init).list) {
diff --git a/src/passes/LegalizeJSInterface.cpp b/src/passes/LegalizeJSInterface.cpp
index 0e810560e..e8a604326 100644
--- a/src/passes/LegalizeJSInterface.cpp
+++ b/src/passes/LegalizeJSInterface.cpp
@@ -24,10 +24,10 @@
//
// We can also legalize in a "minimal" way, that is, only JS-specific
// components, that only JS will care about, such as dynCall methods
-// (wasm will never call them, as it can share the table directly). E.g.
+// (wasm will never call them, as it can share the tables directly). E.g.
// is dynamic linking, where we can avoid legalizing wasm=>wasm calls
// across modules, we still want to legalize dynCalls so JS can call into the
-// table even to a signature that is not legal.
+// tables even to a signature that is not legal.
//
#include "asmjs/shared-constants.h"
@@ -94,13 +94,15 @@ struct LegalizeJSInterface : public Pass {
if (im->imported() && isIllegal(im) && shouldBeLegalized(im)) {
auto funcName = makeLegalStubForCalledImport(im, module);
illegalImportsToLegal[im->name] = funcName;
- // we need to use the legalized version in the table, as the import from
- // JS is legal for JS. Our stub makes it look like a native wasm
+ // we need to use the legalized version in the tables, as the import
+ // from JS is legal for JS. Our stub makes it look like a native wasm
// function.
- for (auto& segment : module->table.segments) {
- for (auto& name : segment.data) {
- if (name == im->name) {
- name = funcName;
+ for (auto& table : module->tables) {
+ for (auto& segment : table->segments) {
+ for (auto& name : segment.data) {
+ if (name == im->name) {
+ name = funcName;
+ }
}
}
}
diff --git a/src/passes/Metrics.cpp b/src/passes/Metrics.cpp
index 3dda70d9b..09dcc5445 100644
--- a/src/passes/Metrics.cpp
+++ b/src/passes/Metrics.cpp
@@ -55,15 +55,15 @@ struct Metrics
}
ModuleUtils::iterDefinedGlobals(*module,
[&](Global* curr) { walkGlobal(curr); });
- walkTable(&module->table);
walkMemory(&module->memory);
- // add imports / funcs / globals/ exports
+ // add imports / funcs / globals / exports / tables
counts["[imports]"] = imports.getNumImports();
counts["[funcs]"] = imports.getNumDefinedFunctions();
counts["[globals]"] = imports.getNumDefinedGlobals();
counts["[events]"] = imports.getNumDefinedEvents();
counts["[exports]"] = module->exports.size();
+ counts["[tables]"] = imports.getNumDefinedTables();
// add memory and table
if (module->memory.exists) {
Index size = 0;
@@ -72,11 +72,15 @@ struct Metrics
}
counts["[memory-data]"] = size;
}
- if (module->table.exists) {
- Index size = 0;
- for (auto& segment : module->table.segments) {
+
+ Index size = 0;
+ for (auto& table : module->tables) {
+ walkTable(table.get());
+ for (auto& segment : table->segments) {
size += segment.data.size();
}
+ }
+ if (!module->tables.empty()) {
counts["[table-data]"] = size;
}
diff --git a/src/passes/PostEmscripten.cpp b/src/passes/PostEmscripten.cpp
index d57d849da..35e2b9a8b 100644
--- a/src/passes/PostEmscripten.cpp
+++ b/src/passes/PostEmscripten.cpp
@@ -61,13 +61,13 @@ struct PostEmscripten : public Pass {
hasInvokes = true;
}
}
- if (!hasInvokes) {
+ if (!hasInvokes || module->tables.empty()) {
return;
}
// Next, see if the Table is flat, which we need in order to see where
// invokes go statically. (In dynamic linking, the table is not flat,
// and we can't do this.)
- TableUtils::FlatTable flatTable(module->table);
+ TableUtils::FlatTable flatTable(*module->tables[0]);
if (!flatTable.valid) {
return;
}
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index b7b7e1320..e49735af0 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -303,10 +303,18 @@ struct PrintExpressionContents
}
void visitCallIndirect(CallIndirect* curr) {
if (curr->isReturn) {
- printMedium(o, "return_call_indirect (type ");
+ printMedium(o, "return_call_indirect ");
} else {
- printMedium(o, "call_indirect (type ");
+ printMedium(o, "call_indirect ");
}
+
+ if (features.hasReferenceTypes()) {
+ printName(curr->table, o);
+ o << ' ';
+ }
+
+ o << '(';
+ printMinor(o, "type ");
printHeapTypeName(o, curr->sig);
o << ')';
}
@@ -1936,8 +1944,8 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
const char* maybeSpace;
const char* maybeNewLine;
- bool full = false; // whether to not elide nodes in output when possible
- // (like implicit blocks) and to emit types
+ bool full = false; // whether to not elide nodes in output when possible
+ // (like implicit blocks) and to emit types
bool stackIR = false; // whether to print stack IR if it is present
// (if false, and Stack IR is there, we just
// note it exists)
@@ -2962,14 +2970,11 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
o << " funcref)";
}
void visitTable(Table* curr) {
- if (!curr->exists) {
- return;
- }
if (curr->imported()) {
doIndent(o, indent);
o << '(';
emitImportHeader(curr);
- printTableHeader(&currModule->table);
+ printTableHeader(curr);
o << ')' << maybeNewLine;
} else {
doIndent(o, indent);
@@ -2983,8 +2988,23 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
}
doIndent(o, indent);
o << '(';
- printMajor(o, "elem ");
+ printMedium(o, "elem ");
+
+ // TODO(reference-types): check for old-style based on the complete spec
+ if (currModule->tables.size() > 1) {
+ // tableuse
+ o << '(';
+ printMedium(o, "table ");
+ printName(curr->name, o);
+ o << ") ";
+ }
+
visit(segment.offset);
+
+ if (currModule->tables.size() > 1) {
+ o << " func";
+ }
+
for (auto name : segment.data) {
o << ' ';
printName(name, o);
@@ -3334,6 +3354,7 @@ printStackIR(StackIR* ir, std::ostream& o, Function* func) {
if (inst->origin->is<Pop>()) {
break;
}
+
PrintExpressionContents(func, o).visit(inst->origin);
break;
}
diff --git a/src/passes/PrintCallGraph.cpp b/src/passes/PrintCallGraph.cpp
index 6d9224a21..49e056312 100644
--- a/src/passes/PrintCallGraph.cpp
+++ b/src/passes/PrintCallGraph.cpp
@@ -96,10 +96,12 @@ struct PrintCallGraph : public Pass {
CallPrinter printer(module);
// Indirect Targets
- for (auto& segment : module->table.segments) {
- for (auto& curr : segment.data) {
- auto* func = module->getFunction(curr);
- o << " \"" << func->name << "\" [style=\"filled, rounded\"];\n";
+ for (auto& table : module->tables) {
+ for (auto& segment : table->segments) {
+ for (auto& curr : segment.data) {
+ auto* func = module->getFunction(curr);
+ o << " \"" << func->name << "\" [style=\"filled, rounded\"];\n";
+ }
}
}
diff --git a/src/passes/RemoveImports.cpp b/src/passes/RemoveImports.cpp
index 0874eac34..10a885586 100644
--- a/src/passes/RemoveImports.cpp
+++ b/src/passes/RemoveImports.cpp
@@ -49,8 +49,8 @@ struct RemoveImports : public WalkerPass<PostWalker<RemoveImports>> {
*curr, [&](Function* func) { names.push_back(func->name); });
// Do not remove names referenced in a table
std::set<Name> indirectNames;
- if (curr->table.exists) {
- for (auto& segment : curr->table.segments) {
+ for (auto& table : curr->tables) {
+ for (auto& segment : table->segments) {
for (auto& name : segment.data) {
indirectNames.insert(name);
}
diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp
index 70a326799..8401f5e1f 100644
--- a/src/passes/RemoveUnusedModuleElements.cpp
+++ b/src/passes/RemoveUnusedModuleElements.cpp
@@ -29,7 +29,7 @@
namespace wasm {
-enum class ModuleElementKind { Function, Global, Event };
+enum class ModuleElementKind { Function, Global, Event, Table };
typedef std::pair<ModuleElementKind, Name> ModuleElement;
@@ -41,7 +41,6 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> {
std::vector<ModuleElement> queue;
std::set<ModuleElement> reachable;
bool usesMemory = false;
- bool usesTable = false;
ReachabilityAnalyzer(Module* module, const std::vector<ModuleElement>& roots)
: module(module) {
@@ -52,9 +51,12 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> {
walk(segment.offset);
}
}
- for (auto& segment : module->table.segments) {
- walk(segment.offset);
+ for (auto& table : module->tables) {
+ for (auto& segment : table->segments) {
+ walk(segment.offset);
+ }
}
+
// main loop
while (queue.size()) {
auto& curr = queue.back();
@@ -73,6 +75,11 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> {
if (!global->imported()) {
walk(global->init);
}
+ } else if (curr.first == ModuleElementKind::Table) {
+ auto* table = module->getTable(curr.second);
+ for (auto& segment : table->segments) {
+ walk(segment.offset);
+ }
}
}
}
@@ -84,7 +91,14 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> {
queue.emplace_back(ModuleElementKind::Function, curr->target);
}
}
- void visitCallIndirect(CallIndirect* curr) { usesTable = true; }
+ void visitCallIndirect(CallIndirect* curr) {
+ assert(!module->tables.empty() && "call-indirect to undefined table.");
+
+ if (reachable.count(ModuleElement(ModuleElementKind::Table, curr->table)) ==
+ 0) {
+ queue.emplace_back(ModuleElementKind::Table, curr->table);
+ }
+ }
void visitGlobalGet(GlobalGet* curr) {
if (reachable.count(ModuleElement(ModuleElementKind::Global, curr->name)) ==
@@ -152,7 +166,6 @@ struct RemoveUnusedModuleElements : public Pass {
}
// Exports are roots.
bool exportsMemory = false;
- bool exportsTable = false;
for (auto& curr : module->exports) {
if (curr->kind == ExternalKind::Function) {
roots.emplace_back(ModuleElementKind::Function, curr->value);
@@ -160,25 +173,24 @@ struct RemoveUnusedModuleElements : public Pass {
roots.emplace_back(ModuleElementKind::Global, curr->value);
} else if (curr->kind == ExternalKind::Event) {
roots.emplace_back(ModuleElementKind::Event, curr->value);
+ } else if (curr->kind == ExternalKind::Table) {
+ roots.emplace_back(ModuleElementKind::Table, curr->value);
} else if (curr->kind == ExternalKind::Memory) {
exportsMemory = true;
- } else if (curr->kind == ExternalKind::Table) {
- exportsTable = true;
}
}
// Check for special imports, which are roots.
bool importsMemory = false;
- bool importsTable = false;
if (module->memory.imported()) {
importsMemory = true;
}
- if (module->table.imported()) {
- importsTable = true;
- }
// For now, all functions that can be called indirectly are marked as roots.
- for (auto& segment : module->table.segments) {
- for (auto& curr : segment.data) {
- roots.emplace_back(ModuleElementKind::Function, curr);
+ for (auto& table : module->tables) {
+ // TODO(reference-types): Check whether table's datatype is funcref.
+ for (auto& segment : table->segments) {
+ for (auto& curr : segment.data) {
+ roots.emplace_back(ModuleElementKind::Function, curr);
+ }
}
}
// Compute reachability starting from the root set.
@@ -196,6 +208,19 @@ struct RemoveUnusedModuleElements : public Pass {
return analyzer.reachable.count(
ModuleElement(ModuleElementKind::Event, curr->name)) == 0;
});
+
+ for (auto& table : module->tables) {
+ table->segments.erase(
+ std::remove_if(table->segments.begin(),
+ table->segments.end(),
+ [&](auto& seg) { return seg.data.empty(); }),
+ table->segments.end());
+ }
+ module->removeTables([&](Table* curr) {
+ return (curr->segments.empty() || !curr->imported()) &&
+ analyzer.reachable.count(
+ ModuleElement(ModuleElementKind::Table, curr->name)) == 0;
+ });
// Handle the memory and table
if (!exportsMemory && !analyzer.usesMemory) {
if (!importsMemory) {
@@ -210,18 +235,6 @@ struct RemoveUnusedModuleElements : public Pass {
module->memory.max = 0;
}
}
- if (!exportsTable && !analyzer.usesTable) {
- if (!importsTable) {
- // The table is unobservable to the outside, we can remove the contents.
- module->table.segments.clear();
- }
- if (module->table.segments.empty()) {
- module->table.exists = false;
- module->table.module = module->table.base = Name();
- module->table.initial = 0;
- module->table.max = 0;
- }
- }
}
};
diff --git a/src/passes/ReorderFunctions.cpp b/src/passes/ReorderFunctions.cpp
index 05df8cbe7..4d02616f0 100644
--- a/src/passes/ReorderFunctions.cpp
+++ b/src/passes/ReorderFunctions.cpp
@@ -70,9 +70,11 @@ struct ReorderFunctions : public Pass {
for (auto& curr : module->exports) {
counts[curr->value]++;
}
- for (auto& segment : module->table.segments) {
- for (auto& curr : segment.data) {
- counts[curr]++;
+ for (auto& table : module->tables) {
+ for (auto& segment : table->segments) {
+ for (auto& curr : segment.data) {
+ counts[curr]++;
+ }
}
}
// sort
diff --git a/src/passes/opt-utils.h b/src/passes/opt-utils.h
index cc0f40c54..0a67a7e1e 100644
--- a/src/passes/opt-utils.h
+++ b/src/passes/opt-utils.h
@@ -86,9 +86,11 @@ inline void replaceFunctions(PassRunner* runner,
// replace direct calls
FunctionRefReplacer(maybeReplace).run(runner, &module);
// replace in table
- for (auto& segment : module.table.segments) {
- for (auto& name : segment.data) {
- maybeReplace(name);
+ for (auto& table : module.tables) {
+ for (auto& segment : table->segments) {
+ for (auto& name : segment.data) {
+ maybeReplace(name);
+ }
}
}
// replace in start
diff --git a/src/shell-interface.h b/src/shell-interface.h
index aeb9cc2a9..3c7f1832a 100644
--- a/src/shell-interface.h
+++ b/src/shell-interface.h
@@ -85,14 +85,19 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
}
} memory;
- std::vector<Name> table;
+ std::unordered_map<Name, std::vector<Name>> tables;
ShellExternalInterface() : memory() {}
virtual ~ShellExternalInterface() = default;
void init(Module& wasm, ModuleInstance& instance) override {
memory.resize(wasm.memory.initial * wasm::Memory::kPageSize);
- table.resize(wasm.table.initial);
+
+ if (wasm.tables.size() > 0) {
+ for (auto& table : wasm.tables) {
+ tables[table->name].resize(table->initial);
+ }
+ }
}
void importGlobals(std::map<Name, Literals>& globals, Module& wasm) override {
@@ -154,11 +159,20 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
<< import->name.str;
}
- Literals callTable(Index index,
+ Literals callTable(Name tableName,
+ Index index,
Signature sig,
LiteralList& arguments,
Type results,
ModuleInstance& instance) override {
+
+ auto it = tables.find(tableName);
+ if (it == tables.end()) {
+ trap("callTable on non-existing table");
+ }
+
+ auto& table = it->second;
+
if (index >= table.size()) {
trap("callTable overflow");
}
@@ -216,7 +230,9 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
memory.set<std::array<uint8_t, 16>>(addr, value);
}
- void tableStore(Address addr, Name entry) override { table[addr] = entry; }
+ void tableStore(Name tableName, Address addr, Name entry) override {
+ tables[tableName][addr] = entry;
+ }
bool growMemory(Address /*oldSize*/, Address newSize) override {
// Apply a reasonable limit on memory size, 1GB, to avoid DOS on the
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index 66f175414..714e2c84b 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -423,10 +423,19 @@ private:
}
}
+ // TODO(reference-types): allow the fuzzer to create multiple tables
void setupTable() {
- wasm.table.exists = true;
- wasm.table.initial = wasm.table.max = 0;
- wasm.table.segments.emplace_back(builder.makeConst(int32_t(0)));
+ if (wasm.tables.size() > 0) {
+ auto& table = wasm.tables[0];
+ table->initial = table->max = 0;
+ table->segments.emplace_back(builder.makeConst(int32_t(0)));
+ } else {
+ auto table = builder.makeTable(
+ Names::getValidTableName(wasm, "fuzzing_table"), 0, 0);
+ table->hasExplicitName = true;
+ table->segments.emplace_back(builder.makeConst(int32_t(0)));
+ wasm.addTable(std::move(table));
+ }
}
std::map<Type, std::vector<Name>> globalsByType;
@@ -522,26 +531,27 @@ private:
}
void finalizeTable() {
- for (auto& segment : wasm.table.segments) {
- // If the offset is a global that was imported (which is ok) but no
- // longer is (not ok) we need to change that.
- if (auto* offset = segment.offset->dynCast<GlobalGet>()) {
- if (!wasm.getGlobal(offset->name)->imported()) {
- // TODO: the segments must not overlap...
- segment.offset =
- builder.makeConst(Literal::makeFromInt32(0, Type::i32));
+ for (auto& table : wasm.tables) {
+ for (auto& segment : table->segments) {
+ // If the offset is a global that was imported (which is ok) but no
+ // longer is (not ok) we need to change that.
+ if (auto* offset = segment.offset->dynCast<GlobalGet>()) {
+ if (!wasm.getGlobal(offset->name)->imported()) {
+ // TODO: the segments must not overlap...
+ segment.offset =
+ builder.makeConst(Literal::makeFromInt32(0, Type::i32));
+ }
}
+ Address maxOffset = segment.data.size();
+ if (auto* offset = segment.offset->dynCast<Const>()) {
+ maxOffset = maxOffset + offset->value.getInteger();
+ }
+ table->initial = std::max(table->initial, maxOffset);
}
- Address maxOffset = segment.data.size();
- if (auto* offset = segment.offset->dynCast<Const>()) {
- maxOffset = maxOffset + offset->value.getInteger();
- }
- wasm.table.initial = std::max(wasm.table.initial, maxOffset);
+ table->max = oneIn(2) ? Address(Table::kUnlimitedSize) : table->initial;
+ // Avoid an imported table (which the fuzz harness would need to handle).
+ table->module = table->base = Name();
}
- wasm.table.max =
- oneIn(2) ? Address(Table::kUnlimitedSize) : wasm.table.initial;
- // Avoid an imported table (which the fuzz harness would need to handle).
- wasm.table.module = wasm.table.base = Name();
}
Name HANG_LIMIT_GLOBAL;
@@ -705,7 +715,7 @@ private:
}
// add some to the table
while (oneIn(3) && !finishedInput) {
- wasm.table.segments[0].data.push_back(func->name);
+ wasm.tables[0]->segments[0].data.push_back(func->name);
}
numAddedFunctions++;
return func;
@@ -1425,7 +1435,7 @@ private:
}
Expression* makeCallIndirect(Type type) {
- auto& data = wasm.table.segments[0].data;
+ auto& data = wasm.tables[0]->segments[0].data;
if (data.empty()) {
return make(type);
}
@@ -1462,7 +1472,8 @@ private:
for (const auto& type : targetFn->sig.params) {
args.push_back(make(type));
}
- return builder.makeCallIndirect(target, args, targetFn->sig, isReturn);
+ return builder.makeCallIndirect(
+ wasm.tables[0]->name, target, args, targetFn->sig, isReturn);
}
Expression* makeCallRef(Type type) {
diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp
index 4ad9979cd..9e3915073 100644
--- a/src/tools/wasm-ctor-eval.cpp
+++ b/src/tools/wasm-ctor-eval.cpp
@@ -215,14 +215,23 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface {
extra);
}
- Literals callTable(Index index,
+ Literals callTable(Name tableName,
+ Index index,
Signature sig,
LiteralList& arguments,
Type result,
EvallingModuleInstance& instance) override {
+
+ std::unordered_map<wasm::Name, std::vector<wasm::Name>>::iterator it;
+
+ auto* table = wasm->getTableOrNull(tableName);
+ if (!table) {
+ throw FailToEvalException("callTable on non-existing table");
+ }
+
// we assume the table is not modified (hmm)
// look through the segments, try to find the function
- for (auto& segment : wasm->table.segments) {
+ for (auto& segment : table->segments) {
Index start;
// look for the index in this segment. if it has a constant offset, we
// look in the proper range. if it instead gets a global, we rely on the
@@ -282,7 +291,7 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface {
}
// called during initialization, but we don't keep track of a table
- void tableStore(Address addr, Name value) override {}
+ void tableStore(Name tableName, Address addr, Name value) override {}
bool growMemory(Address /*oldSize*/, Address newSize) override {
throw FailToEvalException("grow memory");
diff --git a/src/tools/wasm-metadce.cpp b/src/tools/wasm-metadce.cpp
index 87bb293b6..46b66c986 100644
--- a/src/tools/wasm-metadce.cpp
+++ b/src/tools/wasm-metadce.cpp
@@ -213,17 +213,19 @@ struct MetaDCEGraph {
// we can't remove segments, so root what they need
InitScanner rooter(this, Name());
rooter.setModule(&wasm);
- for (auto& segment : wasm.table.segments) {
- // TODO: currently, all functions in the table are roots, but we
- // should add an option to refine that
- for (auto& name : segment.data) {
- if (!wasm.getFunction(name)->imported()) {
- roots.insert(functionToDCENode[name]);
- } else {
- roots.insert(importIdToDCENode[getFunctionImportId(name)]);
+ for (auto& table : wasm.tables) {
+ for (auto& segment : table->segments) {
+ // TODO: currently, all functions in the table are roots, but we
+ // should add an option to refine that
+ for (auto& name : segment.data) {
+ if (!wasm.getFunction(name)->imported()) {
+ roots.insert(functionToDCENode[name]);
+ } else {
+ roots.insert(importIdToDCENode[getFunctionImportId(name)]);
+ }
}
+ rooter.walk(segment.offset);
}
- rooter.walk(segment.offset);
}
for (auto& segment : wasm.memory.segments) {
if (!segment.isPassive) {
diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp
index 3860630af..4bb4b6f9a 100644
--- a/src/tools/wasm-reduce.cpp
+++ b/src/tools/wasm-reduce.cpp
@@ -898,8 +898,14 @@ struct Reducer
}
// If we are left with a single function that is not exported or used in
// a table, that is useful as then we can change the return type.
+ bool allTablesEmpty = std::all_of(
+ module->tables.begin(), module->tables.end(), [&](auto& table) {
+ return std::all_of(table->segments.begin(),
+ table->segments.end(),
+ [&](auto& segment) { return segment.data.empty(); });
+ });
if (module->functions.size() == 1 && module->exports.empty() &&
- module->table.segments.empty()) {
+ allTablesEmpty) {
auto* func = module->functions[0].get();
// We can't remove something that might have breaks to it.
if (!func->imported() && !Properties::isNamedControlFlow(func->body)) {
diff --git a/src/tools/wasm-shell.cpp b/src/tools/wasm-shell.cpp
index 20d4b9344..b3b52d9a1 100644
--- a/src/tools/wasm-shell.cpp
+++ b/src/tools/wasm-shell.cpp
@@ -161,6 +161,7 @@ static void run_asserts(Name moduleName,
invalid = true;
};
ModuleUtils::iterImportedGlobals(wasm, reportUnknownImport);
+ ModuleUtils::iterImportedTables(wasm, reportUnknownImport);
ModuleUtils::iterImportedFunctions(wasm, [&](Importable* import) {
if (import->module == SPECTEST && import->base.startsWith(PRINT)) {
// We can handle it.
@@ -168,23 +169,22 @@ static void run_asserts(Name moduleName,
reportUnknownImport(import);
}
});
- if (wasm.memory.imported()) {
- reportUnknownImport(&wasm.memory);
- }
- if (wasm.table.imported()) {
- reportUnknownImport(&wasm.table);
- }
- for (auto& segment : wasm.table.segments) {
- for (auto name : segment.data) {
- // spec tests consider it illegal to use spectest.print in a table
- if (auto* import = wasm.getFunction(name)) {
- if (import->imported() && import->module == SPECTEST &&
- import->base.startsWith(PRINT)) {
- std::cerr << "cannot put spectest.print in table\n";
- invalid = true;
+ ModuleUtils::iterDefinedTables(wasm, [&](Table* table) {
+ for (auto& segment : table->segments) {
+ for (auto name : segment.data) {
+ // spec tests consider it illegal to use spectest.print in a table
+ if (auto* import = wasm.getFunction(name)) {
+ if (import->imported() && import->module == SPECTEST &&
+ import->base.startsWith(PRINT)) {
+ std::cerr << "cannot put spectest.print in table\n";
+ invalid = true;
+ }
}
}
}
+ });
+ if (wasm.memory.imported()) {
+ reportUnknownImport(&wasm.memory);
}
}
if (!invalid) {
diff --git a/src/tools/wasm-split.cpp b/src/tools/wasm-split.cpp
index 5b44e5f0e..171774d82 100644
--- a/src/tools/wasm-split.cpp
+++ b/src/tools/wasm-split.cpp
@@ -472,18 +472,21 @@ void adjustTableSize(Module& wasm, int initialSize) {
if (initialSize < 0) {
return;
}
- if (!wasm.table.exists) {
+ if (wasm.tables.empty()) {
Fatal() << "--initial-table used but there is no table";
}
- if ((uint64_t)initialSize < wasm.table.initial) {
+
+ auto& table = wasm.tables.front();
+
+ if ((uint64_t)initialSize < table->initial) {
Fatal() << "Specified initial table size too small, should be at least "
- << wasm.table.initial;
+ << table->initial;
}
- if ((uint64_t)initialSize > wasm.table.max) {
+ if ((uint64_t)initialSize > table->max) {
Fatal() << "Specified initial table size larger than max table size "
- << wasm.table.max;
+ << table->max;
}
- wasm.table.initial = initialSize;
+ table->initial = initialSize;
}
void instrumentModule(Module& wasm, const WasmSplitOptions& options) {
diff --git a/src/tools/wasm2js.cpp b/src/tools/wasm2js.cpp
index 1464f48f8..8659ef6e2 100644
--- a/src/tools/wasm2js.cpp
+++ b/src/tools/wasm2js.cpp
@@ -982,6 +982,11 @@ int main(int argc, const char* argv[]) {
"request for silly amounts of memory)";
}
+ // TODO: Remove this restriction when wasm2js can handle multiple tables
+ if (wasm.tables.size() > 1) {
+ Fatal() << "error: modules with multiple tables are not supported yet.";
+ }
+
if (options.passOptions.validate) {
if (!WasmValidator().validate(wasm)) {
std::cout << wasm << '\n';
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 6a409f7db..a4d41530e 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -329,7 +329,27 @@ enum Section {
Event = 13
};
-enum SegmentFlag { IsPassive = 0x01, HasMemIndex = 0x02 };
+// A passive segment is a segment that will not be automatically copied into a
+// memory or table on instantiation, and must instead be applied manually
+// using the instructions memory.init or table.init.
+// An active segment is equivalent to a passive segment, but with an implicit
+// memory.init followed by a data.drop (or table.init followed by a elem.drop)
+// that is prepended to the module's start function.
+// A declarative element segment is not available at runtime but merely serves
+// to forward-declare references that are formed in code with instructions
+// like ref.func.
+enum SegmentFlag {
+ // Bit 0: 0 = active, 1 = passive
+ IsPassive = 1 << 0,
+ // Bit 1 if passive: 0 = passive, 1 = declarative
+ IsDeclarative = 1 << 1,
+ // Bit 1 if active: 0 = index 0, 1 = index given
+ HasIndex = 1 << 1,
+ // Table element segments only:
+ // Bit 2: 0 = elemType is funcref and vector of func indexes given
+ // 1 = elemType is given and vector of ref expressions is given
+ UsesExpressions = 1 << 2
+};
enum EncodedType {
// value_type
@@ -1079,6 +1099,7 @@ class WasmBinaryWriter {
std::unordered_map<Name, Index> functionIndexes;
std::unordered_map<Name, Index> eventIndexes;
std::unordered_map<Name, Index> globalIndexes;
+ std::unordered_map<Name, Index> tableIndexes;
BinaryIndexes(Module& wasm) {
auto addIndexes = [&](auto& source, auto& indexes) {
@@ -1099,6 +1120,7 @@ class WasmBinaryWriter {
};
addIndexes(wasm.functions, functionIndexes);
addIndexes(wasm.events, eventIndexes);
+ addIndexes(wasm.tables, tableIndexes);
// Globals may have tuple types in the IR, in which case they lower to
// multiple globals, one for each tuple element, in the binary. Tuple
@@ -1171,11 +1193,12 @@ public:
void writeEvents();
uint32_t getFunctionIndex(Name name) const;
+ uint32_t getTableIndex(Name name) const;
uint32_t getGlobalIndex(Name name) const;
uint32_t getEventIndex(Name name) const;
uint32_t getTypeIndex(HeapType type) const;
- void writeFunctionTableDeclaration();
+ void writeTableDeclarations();
void writeTableElements();
void writeNames();
void writeSourceMapUrl();
@@ -1319,6 +1342,7 @@ public:
// gets a name in the combined import+defined space
Name getFunctionName(Index index);
+ Name getTableName(Index index);
Name getGlobalName(Index index);
Name getEventName(Index index);
@@ -1355,6 +1379,14 @@ public:
// function to check
Index endOfFunction = -1;
+ // we store tables here before wasm.addTable after we know their names
+ std::vector<std::unique_ptr<Table>> tables;
+ // we store table imports here before wasm.addTableImport after we know
+ // their names
+ std::vector<Table*> tableImports;
+ // at index i we have all references to the table i
+ std::map<Index, std::vector<Expression*>> tableRefs;
+
// we store globals here before wasm.addGlobal after we know their names
std::vector<std::unique_ptr<Global>> globals;
// we store global imports here before wasm.addGlobalImport after we know
@@ -1459,7 +1491,8 @@ public:
void readDataSegments();
void readDataCount();
- std::map<Index, std::vector<Index>> functionTable;
+ // A map from table indexes to the map of segment indexes to their elements
+ std::map<Index, std::map<Index, std::vector<Index>>> functionTable;
void readFunctionTableDeclaration();
void readTableElements();
@@ -1580,4 +1613,4 @@ private:
#undef DEBUG_TYPE
-#endif // wasm_wasm_binary_h
+#endif // wasm_wasm_binary_h \ No newline at end of file
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index bb37ed340..6fc5c4dda 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -78,6 +78,16 @@ public:
return func;
}
+ static std::unique_ptr<Table>
+ makeTable(Name name, Address initial = 0, Address max = Table::kMaxSize) {
+ auto table = std::make_unique<Table>();
+ table->name = name;
+ table->initial = initial;
+ table->max = max;
+
+ return table;
+ }
+
static std::unique_ptr<Export>
makeExport(Name name, Name value, ExternalKind kind) {
auto export_ = std::make_unique<Export>();
@@ -244,11 +254,13 @@ public:
return call;
}
template<typename T>
- CallIndirect* makeCallIndirect(Expression* target,
+ CallIndirect* makeCallIndirect(const Name table,
+ Expression* target,
const T& args,
Signature sig,
bool isReturn = false) {
auto* call = wasm.allocator.alloc<CallIndirect>();
+ call->table = table;
call->sig = sig;
call->type = sig.results;
call->target = target;
diff --git a/src/wasm-delegations-fields.h b/src/wasm-delegations-fields.h
index b99ebef40..033da6314 100644
--- a/src/wasm-delegations-fields.h
+++ b/src/wasm-delegations-fields.h
@@ -225,6 +225,7 @@ switch (DELEGATE_ID) {
case Expression::Id::CallIndirectId: {
DELEGATE_START(CallIndirect);
DELEGATE_FIELD_CHILD(CallIndirect, target);
+ DELEGATE_FIELD_NAME(CallIndirect, table);
DELEGATE_FIELD_CHILD_VECTOR(CallIndirect, operands);
DELEGATE_FIELD_SIGNATURE(CallIndirect, sig);
DELEGATE_FIELD_INT(CallIndirect, isReturn);
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index ba6f5d6ee..b216aa7ae 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -2069,7 +2069,8 @@ public:
virtual void init(Module& wasm, SubType& instance) {}
virtual void importGlobals(GlobalManager& globals, Module& wasm) = 0;
virtual Literals callImport(Function* import, LiteralList& arguments) = 0;
- virtual Literals callTable(Index index,
+ virtual Literals callTable(Name tableName,
+ Index index,
Signature sig,
LiteralList& arguments,
Type result,
@@ -2220,7 +2221,7 @@ public:
WASM_UNREACHABLE("unimp");
}
- virtual void tableStore(Address addr, Name entry) {
+ virtual void tableStore(Name tableName, Address addr, Name entry) {
WASM_UNREACHABLE("unimp");
}
};
@@ -2307,17 +2308,20 @@ private:
std::unordered_set<size_t> droppedSegments;
void initializeTableContents() {
- for (auto& segment : wasm.table.segments) {
- Address offset =
- (uint32_t)InitializerExpressionRunner<GlobalManager>(globals, maxDepth)
- .visit(segment.offset)
- .getSingleValue()
- .geti32();
- if (offset + segment.data.size() > wasm.table.initial) {
- externalInterface->trap("invalid offset when initializing table");
- }
- for (size_t i = 0; i != segment.data.size(); ++i) {
- externalInterface->tableStore(offset + i, segment.data[i]);
+ for (auto& table : wasm.tables) {
+ for (auto& segment : table->segments) {
+ Address offset = (uint32_t)InitializerExpressionRunner<GlobalManager>(
+ globals, maxDepth)
+ .visit(segment.offset)
+ .getSingleValue()
+ .geti32();
+ if (offset + segment.data.size() > table->initial) {
+ externalInterface->trap("invalid offset when initializing table");
+ }
+ for (size_t i = 0; i != segment.data.size(); ++i) {
+ externalInterface->tableStore(
+ table->name, offset + i, segment.data[i]);
+ }
}
}
}
@@ -2443,7 +2447,7 @@ private:
Index index = target.getSingleValue().geti32();
Type type = curr->isReturn ? scope.function->sig.results : curr->type;
Flow ret = instance.externalInterface->callTable(
- index, curr->sig, arguments, type, *instance.self());
+ curr->table, index, curr->sig, arguments, type, *instance.self());
// TODO: make this a proper tail call (return first)
if (curr->isReturn) {
ret.breakTo = RETURN_FLOW;
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index 031589894..8a92c0935 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -123,6 +123,7 @@ class SExpressionWasmBuilder {
std::unordered_map<std::string, size_t> typeIndices;
std::vector<Name> functionNames;
+ std::vector<Name> tableNames;
std::vector<Name> globalNames;
std::vector<Name> eventNames;
int functionCounter = 0;
@@ -153,6 +154,7 @@ private:
UniqueNameMapper nameMapper;
Name getFunctionName(Element& s);
+ Name getTableName(Element& s);
Name getGlobalName(Element& s);
Name getEventName(Element& s);
void parseStart(Element& s) { wasm.addStart(getFunctionName(*s[1])); }
@@ -297,7 +299,10 @@ private:
void parseGlobal(Element& s, bool preParseImport = false);
void parseTable(Element& s, bool preParseImport = false);
void parseElem(Element& s);
- void parseInnerElem(Element& s, Index i = 1, Expression* offset = nullptr);
+ void parseInnerElem(Table* table,
+ Element& s,
+ Index i = 1,
+ Expression* offset = nullptr);
// Parses something like (func ..), (array ..), (struct)
HeapType parseHeapType(Element& s);
diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h
index 4d69eaee3..803fa98aa 100644
--- a/src/wasm-traversal.h
+++ b/src/wasm-traversal.h
@@ -242,7 +242,9 @@ struct Walker : public VisitorType {
self->walkEvent(curr.get());
}
}
- self->walkTable(&module->table);
+ for (auto& curr : module->tables) {
+ self->walkTable(curr.get());
+ };
self->walkMemory(&module->memory);
}
diff --git a/src/wasm.h b/src/wasm.h
index 5e4c4668b..7fd284756 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -836,6 +836,7 @@ public:
Signature sig;
ExpressionList operands;
Expression* target;
+ Name table;
bool isReturn = false;
void finalize();
@@ -1732,18 +1733,12 @@ public:
}
};
- // Currently the wasm object always 'has' one Table. It 'exists' if it has
- // been defined or imported. The table can exist but be empty and have no
- // defined initial or max size.
- bool exists = false;
Address initial = 0;
Address max = kMaxSize;
std::vector<Segment> segments;
- Table() { name = Name::fromInt(0); }
bool hasMax() { return max != kUnlimitedSize; }
void clear() {
- exists = false;
name = "";
initial = 0;
max = kMaxSize;
@@ -1850,7 +1845,8 @@ public:
std::vector<std::unique_ptr<Global>> globals;
std::vector<std::unique_ptr<Event>> events;
- Table table;
+ std::vector<std::unique_ptr<Table>> tables;
+
Memory memory;
Name start;
@@ -1880,6 +1876,7 @@ private:
// exports map is by the *exported* name, which is unique
std::unordered_map<Name, Export*> exportsMap;
std::unordered_map<Name, Function*> functionsMap;
+ std::unordered_map<Name, Table*> tablesMap;
std::unordered_map<Name, Global*> globalsMap;
std::unordered_map<Name, Event*> eventsMap;
@@ -1888,10 +1885,12 @@ public:
Export* getExport(Name name);
Function* getFunction(Name name);
+ Table* getTable(Name name);
Global* getGlobal(Name name);
Event* getEvent(Name name);
Export* getExportOrNull(Name name);
+ Table* getTableOrNull(Name name);
Function* getFunctionOrNull(Name name);
Global* getGlobalOrNull(Name name);
Event* getEventOrNull(Name name);
@@ -1903,6 +1902,7 @@ public:
Export* addExport(std::unique_ptr<Export>&& curr);
Function* addFunction(std::unique_ptr<Function>&& curr);
+ Table* addTable(std::unique_ptr<Table>&& curr);
Global* addGlobal(std::unique_ptr<Global>&& curr);
Event* addEvent(std::unique_ptr<Event>&& curr);
@@ -1910,11 +1910,13 @@ public:
void removeExport(Name name);
void removeFunction(Name name);
+ void removeTable(Name name);
void removeGlobal(Name name);
void removeEvent(Name name);
void removeExports(std::function<bool(Export*)> pred);
void removeFunctions(std::function<bool(Function*)> pred);
+ void removeTables(std::function<bool(Table*)> pred);
void removeGlobals(std::function<bool(Global*)> pred);
void removeEvents(std::function<bool(Event*)> pred);
@@ -1939,4 +1941,4 @@ std::ostream& operator<<(std::ostream& o, wasm::StackIR& ir);
} // namespace std
-#endif // wasm_wasm_h
+#endif // wasm_wasm_h \ No newline at end of file
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 75ca5a2b1..f3ec376d1 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -48,7 +48,7 @@ void WasmBinaryWriter::write() {
writeTypes();
writeImports();
writeFunctionSignatures();
- writeFunctionTableDeclaration();
+ writeTableDeclarations();
writeMemory();
writeEvents();
writeGlobals();
@@ -285,17 +285,17 @@ void WasmBinaryWriter::writeImports() {
wasm->memory.shared,
wasm->memory.is64());
}
- if (wasm->table.imported()) {
+ ModuleUtils::iterImportedTables(*wasm, [&](Table* table) {
BYN_TRACE("write one table\n");
- writeImportHeader(&wasm->table);
+ writeImportHeader(table);
o << U32LEB(int32_t(ExternalKind::Table));
o << S32LEB(BinaryConsts::EncodedType::funcref);
- writeResizableLimits(wasm->table.initial,
- wasm->table.max,
- wasm->table.hasMax(),
+ writeResizableLimits(table->initial,
+ table->max,
+ table->hasMax(),
/*shared=*/false,
/*is64*/ false);
- }
+ });
finishSection(start);
}
@@ -493,6 +493,12 @@ uint32_t WasmBinaryWriter::getFunctionIndex(Name name) const {
return it->second;
}
+uint32_t WasmBinaryWriter::getTableIndex(Name name) const {
+ auto it = indexes.tableIndexes.find(name);
+ assert(it != indexes.tableIndexes.end());
+ return it->second;
+}
+
uint32_t WasmBinaryWriter::getGlobalIndex(Name name) const {
auto it = indexes.globalIndexes.find(name);
assert(it != indexes.globalIndexes.end());
@@ -516,38 +522,70 @@ uint32_t WasmBinaryWriter::getTypeIndex(HeapType type) const {
return it->second;
}
-void WasmBinaryWriter::writeFunctionTableDeclaration() {
- if (!wasm->table.exists || wasm->table.imported()) {
+void WasmBinaryWriter::writeTableDeclarations() {
+ if (importInfo->getNumDefinedTables() == 0) {
+ // std::cerr << std::endl << "(WasmBinaryWriter::writeTableDeclarations) No
+ // defined tables found. skipping" << std::endl;
return;
}
- BYN_TRACE("== writeFunctionTableDeclaration\n");
+ BYN_TRACE("== writeTableDeclarations\n");
auto start = startSection(BinaryConsts::Section::Table);
- o << U32LEB(1); // Declare 1 table.
- o << S32LEB(BinaryConsts::EncodedType::funcref);
- writeResizableLimits(wasm->table.initial,
- wasm->table.max,
- wasm->table.hasMax(),
- /*shared=*/false,
- /*is64*/ false);
+ auto num = importInfo->getNumDefinedTables();
+ o << U32LEB(num);
+ ModuleUtils::iterDefinedTables(*wasm, [&](Table* table) {
+ o << S32LEB(BinaryConsts::EncodedType::funcref);
+ writeResizableLimits(table->initial,
+ table->max,
+ table->hasMax(),
+ /*shared=*/false,
+ /*is64*/ false);
+ });
finishSection(start);
}
void WasmBinaryWriter::writeTableElements() {
- if (!wasm->table.exists || wasm->table.segments.size() == 0) {
+ size_t elemCount = 0;
+ for (auto& table : wasm->tables) {
+ elemCount += table->segments.size();
+ }
+ if (elemCount == 0) {
return;
}
+
BYN_TRACE("== writeTableElements\n");
auto start = startSection(BinaryConsts::Section::Element);
-
- o << U32LEB(wasm->table.segments.size());
- for (auto& segment : wasm->table.segments) {
- // Table index; 0 in the MVP (and binaryen IR only has 1 table)
- o << U32LEB(0);
- writeExpression(segment.offset);
- o << int8_t(BinaryConsts::End);
- o << U32LEB(segment.data.size());
- for (auto name : segment.data) {
- o << U32LEB(getFunctionIndex(name));
+ o << U32LEB(elemCount);
+
+ for (auto& table : wasm->tables) {
+ for (auto& segment : table->segments) {
+ Index tableIdx = getTableIndex(table->name);
+ // No support for passive element segments yet as they don't belong to a
+ // table.
+ bool isPassive = false;
+ bool isDeclarative = false;
+ bool hasTableIndex = tableIdx > 0;
+ bool usesExpressions = false;
+
+ uint32_t flags =
+ (isPassive ? BinaryConsts::IsPassive |
+ (isDeclarative ? BinaryConsts::IsDeclarative : 0)
+ : (hasTableIndex ? BinaryConsts::HasIndex : 0)) |
+ (usesExpressions ? BinaryConsts::UsesExpressions : 0);
+
+ o << U32LEB(flags);
+ if (hasTableIndex) {
+ o << U32LEB(tableIdx);
+ }
+ writeExpression(segment.offset);
+ o << int8_t(BinaryConsts::End);
+ if (!usesExpressions && (isPassive || hasTableIndex)) {
+ // elemKind funcref
+ o << U32LEB(0);
+ }
+ o << U32LEB(segment.data.size());
+ for (auto name : segment.data) {
+ o << U32LEB(getFunctionIndex(name));
+ }
}
}
finishSection(start);
@@ -648,12 +686,31 @@ void WasmBinaryWriter::writeNames() {
}
// table names
- if (wasm->table.exists && wasm->table.hasExplicitName) {
- auto substart =
- startSubsection(BinaryConsts::UserSections::Subsection::NameTable);
- o << U32LEB(1) << U32LEB(0); // currently exactly 1 table at index 0
- writeEscapedName(wasm->table.name.str);
- finishSubsection(substart);
+ {
+ std::vector<std::pair<Index, Table*>> tablesWithNames;
+ Index checked = 0;
+ auto check = [&](Table* curr) {
+ if (curr->hasExplicitName) {
+ tablesWithNames.push_back({checked, curr});
+ }
+ checked++;
+ };
+ ModuleUtils::iterImportedTables(*wasm, check);
+ ModuleUtils::iterDefinedTables(*wasm, check);
+ assert(checked == indexes.tableIndexes.size());
+
+ if (tablesWithNames.size() > 0) {
+ auto substart =
+ startSubsection(BinaryConsts::UserSections::Subsection::NameTable);
+ o << U32LEB(tablesWithNames.size());
+
+ for (auto& indexedTable : tablesWithNames) {
+ o << U32LEB(indexedTable.first);
+ writeEscapedName(indexedTable.second->name.str);
+ }
+
+ finishSubsection(substart);
+ }
}
// memory names
@@ -1626,6 +1683,13 @@ Name WasmBinaryBuilder::getFunctionName(Index index) {
return wasm.functions[index]->name;
}
+Name WasmBinaryBuilder::getTableName(Index index) {
+ if (index >= wasm.tables.size()) {
+ throwError("invalid table index");
+ }
+ return wasm.tables[index]->name;
+}
+
Name WasmBinaryBuilder::getGlobalName(Index index) {
if (index >= wasm.globals.size()) {
throwError("invalid global index");
@@ -1694,19 +1758,19 @@ void WasmBinaryBuilder::readImports() {
}
case ExternalKind::Table: {
Name name(std::string("timport$") + std::to_string(tableCounter++));
- wasm.table.module = module;
- wasm.table.base = base;
- wasm.table.name = name;
+ auto table = builder.makeTable(name);
+ table->module = module;
+ table->base = base;
auto elementType = getS32LEB();
WASM_UNUSED(elementType);
if (elementType != BinaryConsts::EncodedType::funcref) {
throwError("Imported table type is not funcref");
}
- wasm.table.exists = true;
+
bool is_shared;
Type indexType;
- getResizableLimits(wasm.table.initial,
- wasm.table.max,
+ getResizableLimits(table->initial,
+ table->max,
is_shared,
indexType,
Table::kUnlimitedSize);
@@ -1716,6 +1780,9 @@ void WasmBinaryBuilder::readImports() {
if (indexType == Type::i64) {
throwError("Tables may not be 64-bit");
}
+
+ tableImports.push_back(table.get());
+ wasm.addTable(std::move(table));
break;
}
case ExternalKind::Memory: {
@@ -2305,6 +2372,9 @@ void WasmBinaryBuilder::processNames() {
for (auto& global : globals) {
wasm.addGlobal(std::move(global));
}
+ for (auto& table : tables) {
+ wasm.addTable(std::move(table));
+ }
// now that we have names, apply things
@@ -2320,7 +2390,7 @@ void WasmBinaryBuilder::processNames() {
break;
}
case ExternalKind::Table:
- curr->value = wasm.table.name;
+ curr->value = getTableName(index);
break;
case ExternalKind::Memory:
curr->value = wasm.memory.name;
@@ -2351,11 +2421,26 @@ void WasmBinaryBuilder::processNames() {
}
}
- for (auto& pair : functionTable) {
- auto i = pair.first;
- auto& indices = pair.second;
- for (auto j : indices) {
- wasm.table.segments[i].data.push_back(getFunctionName(j));
+ for (auto& iter : tableRefs) {
+ size_t index = iter.first;
+ auto& refs = iter.second;
+ for (auto* ref : refs) {
+ if (auto* callIndirect = ref->dynCast<CallIndirect>()) {
+ callIndirect->table = getTableName(index);
+ } else {
+ WASM_UNREACHABLE("Invalid type in table references");
+ }
+ }
+ }
+
+ for (auto& table_pair : functionTable) {
+ for (auto& pair : table_pair.second) {
+ auto i = pair.first;
+ auto& indices = pair.second;
+ for (auto j : indices) {
+ wasm.tables[table_pair.first]->segments[i].data.push_back(
+ getFunctionName(j));
+ }
}
}
@@ -2395,7 +2480,7 @@ void WasmBinaryBuilder::readDataSegments() {
std::to_string(flags));
}
curr.isPassive = flags & BinaryConsts::IsPassive;
- if (flags & BinaryConsts::HasMemIndex) {
+ if (flags & BinaryConsts::HasIndex) {
auto memIndex = getU32LEB();
if (memIndex != 0) {
throwError("nonzero memory index");
@@ -2414,29 +2499,25 @@ void WasmBinaryBuilder::readDataSegments() {
void WasmBinaryBuilder::readFunctionTableDeclaration() {
BYN_TRACE("== readFunctionTableDeclaration\n");
auto numTables = getU32LEB();
- if (numTables != 1) {
- throwError("Only 1 table definition allowed in MVP");
- }
- if (wasm.table.exists) {
- throwError("Table cannot be both imported and defined");
- }
- wasm.table.exists = true;
- auto elemType = getS32LEB();
- if (elemType != BinaryConsts::EncodedType::funcref) {
- throwError("ElementType must be funcref in MVP");
- }
- bool is_shared;
- Type indexType;
- getResizableLimits(wasm.table.initial,
- wasm.table.max,
- is_shared,
- indexType,
- Table::kUnlimitedSize);
- if (is_shared) {
- throwError("Tables may not be shared");
- }
- if (indexType == Type::i64) {
- throwError("Tables may not be 64-bit");
+
+ for (size_t i = 0; i < numTables; i++) {
+ auto elemType = getS32LEB();
+ if (elemType != BinaryConsts::EncodedType::funcref) {
+ throwError("Non-funcref tables not yet supported");
+ }
+ auto table = Builder::makeTable(Name::fromInt(i));
+ bool is_shared;
+ Type indexType;
+ getResizableLimits(
+ table->initial, table->max, is_shared, indexType, Table::kUnlimitedSize);
+ if (is_shared) {
+ throwError("Tables may not be shared");
+ }
+ if (indexType == Type::i64) {
+ throwError("Tables may not be 64-bit");
+ }
+
+ tables.push_back(std::move(table));
}
}
@@ -2447,13 +2528,44 @@ void WasmBinaryBuilder::readTableElements() {
throwError("Too many segments");
}
for (size_t i = 0; i < numSegments; i++) {
- auto tableIndex = getU32LEB();
- if (tableIndex != 0) {
- throwError("Table elements must refer to table 0 in MVP");
+ auto flags = getU32LEB();
+ bool isPassive = (flags & BinaryConsts::IsPassive) != 0;
+ bool hasTableIdx = (flags & BinaryConsts::HasIndex) != 0;
+ bool usesExpressions = (flags & BinaryConsts::UsesExpressions) != 0;
+
+ if (isPassive) {
+ throwError("Only active elem segments are supported.");
+ }
+
+ if (usesExpressions) {
+ throwError("Only elem segments with function indexes are supported.");
}
- wasm.table.segments.emplace_back(readExpression());
- auto& indexSegment = functionTable[i];
+ Index tableIdx = 0;
+ if (hasTableIdx) {
+ tableIdx = getU32LEB();
+ }
+
+ auto numTableImports = tableImports.size();
+ if (tableIdx < numTableImports) {
+ auto table = tableImports[tableIdx];
+ table->segments.emplace_back(readExpression());
+ } else if (tableIdx - numTableImports < tables.size()) {
+ auto table = tables[tableIdx - numTableImports].get();
+ table->segments.emplace_back(readExpression());
+ } else {
+ throwError("Table index out of range.");
+ }
+
+ if (hasTableIdx) {
+ auto elemKind = getU32LEB();
+ if (elemKind != 0x0) {
+ throwError("Only funcref elem kinds are valid.");
+ }
+ }
+
+ size_t segmentIndex = functionTable[tableIdx].size();
+ auto& indexSegment = functionTable[tableIdx][segmentIndex];
auto size = getU32LEB();
for (Index j = 0; j < size; j++) {
indexSegment.push_back(getU32LEB());
@@ -2590,10 +2702,21 @@ void WasmBinaryBuilder::readNames(size_t payloadLen) {
} else if (nameType == BinaryConsts::UserSections::Subsection::NameTable) {
auto num = getU32LEB();
for (size_t i = 0; i < num; i++) {
+ std::unordered_set<Name> usedNames;
auto index = getU32LEB();
auto rawName = getInlineString();
- if (index == 0) {
- wasm.table.setExplicitName(escape(rawName));
+ auto name = escape(rawName);
+ // De-duplicate names by appending .1, .2, etc.
+ for (int i = 1; !usedNames.insert(name).second; ++i) {
+ name = std::string(escape(rawName).str) + std::string(".") +
+ std::to_string(i);
+ }
+
+ auto numTableImports = tableImports.size();
+ if (index < numTableImports) {
+ tableImports[index]->setExplicitName(name);
+ } else if (index - numTableImports < tables.size()) {
+ tables[index - numTableImports]->setExplicitName(name);
} else {
std::cerr << "warning: table index out of bounds in name section, "
"table subsection: "
@@ -3377,16 +3500,14 @@ void WasmBinaryBuilder::visitCallIndirect(CallIndirect* curr) {
BYN_TRACE("zz node: CallIndirect\n");
auto index = getU32LEB();
curr->sig = getSignatureByTypeIndex(index);
- auto reserved = getU32LEB();
- if (reserved != 0) {
- throwError("Invalid flags field in call_indirect");
- }
+ Index tableIdx = getU32LEB();
auto num = curr->sig.params.size();
curr->operands.resize(num);
curr->target = popNonVoidExpression();
for (size_t i = 0; i < num; i++) {
curr->operands[num - i - 1] = popNonVoidExpression();
}
+ tableRefs[tableIdx].push_back(curr);
curr->finalize();
}
diff --git a/src/wasm/wasm-emscripten.cpp b/src/wasm/wasm-emscripten.cpp
index 1fd35de29..0aabcc0b4 100644
--- a/src/wasm/wasm-emscripten.cpp
+++ b/src/wasm/wasm-emscripten.cpp
@@ -477,7 +477,11 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata() {
meta << "\n },\n";
}
- meta << " \"tableSize\": " << wasm.table.initial.addr << ",\n";
+ if (!wasm.tables.empty()) {
+ meta << " \"tableSize\": " << wasm.tables[0]->initial.addr << ",\n";
+ } else {
+ meta << " \"tableSize\": 0,\n";
+ }
// Avoid adding duplicate imports to `declares' or `invokeFuncs`. Even
// though we might import the same function multiple times (i.e. with
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 0dc121f2c..bcce3993e 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -458,6 +458,19 @@ Name SExpressionWasmBuilder::getFunctionName(Element& s) {
}
}
+Name SExpressionWasmBuilder::getTableName(Element& s) {
+ if (s.dollared()) {
+ return s.str();
+ } else {
+ // index
+ size_t offset = atoi(s.str().c_str());
+ if (offset >= tableNames.size()) {
+ throw ParseException("unknown table in getTableName", s.line, s.col);
+ }
+ return tableNames[offset];
+ }
+}
+
Name SExpressionWasmBuilder::getGlobalName(Element& s) {
if (s.dollared()) {
return s.str();
@@ -1828,11 +1841,16 @@ Expression* SExpressionWasmBuilder::makeCall(Element& s, bool isReturn) {
Expression* SExpressionWasmBuilder::makeCallIndirect(Element& s,
bool isReturn) {
- if (!wasm.table.exists) {
- throw ParseException("no table", s.line, s.col);
+ if (wasm.tables.empty()) {
+ throw ParseException("no tables", s.line, s.col);
}
Index i = 1;
auto ret = allocator.alloc<CallIndirect>();
+ if (s[i]->isStr()) {
+ ret->table = s[i++]->str();
+ } else {
+ ret->table = wasm.tables.front()->name;
+ }
i = parseTypeUse(s, i, ret->sig);
parseCallOperands(s, i, s.size() - 1, ret);
ret->target = parseExpression(s[s.size() - 1]);
@@ -2464,17 +2482,21 @@ void SExpressionWasmBuilder::parseExport(Element& s) {
ex->name = s[1]->str();
if (s[2]->isList()) {
auto& inner = *s[2];
- ex->value = inner[1]->str();
if (elementStartsWith(inner, FUNC)) {
ex->kind = ExternalKind::Function;
+ ex->value = getFunctionName(*inner[1]);
} else if (elementStartsWith(inner, MEMORY)) {
ex->kind = ExternalKind::Memory;
+ ex->value = inner[1]->str();
} else if (elementStartsWith(inner, TABLE)) {
ex->kind = ExternalKind::Table;
+ ex->value = getTableName(*inner[1]);
} else if (elementStartsWith(inner, GLOBAL)) {
ex->kind = ExternalKind::Global;
+ ex->value = getGlobalName(*inner[1]);
} else if (inner[0]->str() == EVENT) {
ex->kind = ExternalKind::Event;
+ ex->value = getEventName(*inner[1]);
} else {
throw ParseException("invalid export", inner.line, inner.col);
}
@@ -2505,10 +2527,6 @@ void SExpressionWasmBuilder::parseImport(Element& s) {
wasm.memory.exists = true;
} else if (elementStartsWith(*s[3], TABLE)) {
kind = ExternalKind::Table;
- if (wasm.table.exists) {
- throw ParseException("more than one table", s[3]->line, s[3]->col);
- }
- wasm.table.exists = true;
} else if (elementStartsWith(*s[3], GLOBAL)) {
kind = ExternalKind::Global;
} else if ((*s[3])[0]->str() == EVENT) {
@@ -2590,21 +2608,27 @@ void SExpressionWasmBuilder::parseImport(Element& s) {
global->mutable_ = mutable_;
wasm.addGlobal(global.release());
} else if (kind == ExternalKind::Table) {
- wasm.table.setName(name, hasExplicitName);
- wasm.table.module = module;
- wasm.table.base = base;
+ auto table = make_unique<Table>();
+ table->setName(name, hasExplicitName);
+ table->module = module;
+ table->base = base;
+ tableNames.push_back(name);
+
if (j < inner.size() - 1) {
auto initElem = inner[j++];
- wasm.table.initial = getAddress(initElem);
- checkAddress(wasm.table.initial, "excessive table init size", initElem);
+ table->initial = getAddress(initElem);
+ checkAddress(table->initial, "excessive table init size", initElem);
}
if (j < inner.size() - 1) {
auto maxElem = inner[j++];
- wasm.table.max = getAddress(maxElem);
- checkAddress(wasm.table.max, "excessive table max size", maxElem);
+ table->max = getAddress(maxElem);
+ checkAddress(table->max, "excessive table max size", maxElem);
} else {
- wasm.table.max = Table::kUnlimitedSize;
+ table->max = Table::kUnlimitedSize;
}
+
+ wasm.addTable(std::move(table));
+
j++; // funcref
// ends with the table element type
} else if (kind == ExternalKind::Memory) {
@@ -2728,18 +2752,20 @@ void SExpressionWasmBuilder::parseGlobal(Element& s, bool preParseImport) {
}
void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) {
- if (wasm.table.exists) {
- throw ParseException("more than one table", s.line, s.col);
- }
- wasm.table.exists = true;
+ std::unique_ptr<Table> table = make_unique<Table>();
Index i = 1;
if (i == s.size()) {
return; // empty table in old notation
}
if (s[i]->dollared()) {
- wasm.table.setExplicitName(s[i++]->str());
+ table->setExplicitName(s[i++]->str());
+ } else {
+ table->name = Name::fromInt(tableCounter++);
}
+ tableNames.push_back(table->name);
+
if (i == s.size()) {
+ wasm.addTable(std::move(table));
return;
}
Name importModule, importBase;
@@ -2748,7 +2774,7 @@ void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) {
if (elementStartsWith(inner, EXPORT)) {
auto ex = make_unique<Export>();
ex->name = inner[1]->str();
- ex->value = wasm.table.name;
+ ex->value = table->name;
ex->kind = ExternalKind::Table;
if (wasm.getExportOrNull(ex->name)) {
throw ParseException("duplicate export", inner.line, inner.col);
@@ -2759,26 +2785,27 @@ void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) {
if (!preParseImport) {
throw ParseException("!preParseImport in table", inner.line, inner.col);
}
- wasm.table.module = inner[1]->str();
- wasm.table.base = inner[2]->str();
+ table->module = inner[1]->str();
+ table->base = inner[2]->str();
i++;
} else {
throw ParseException("invalid table", inner.line, inner.col);
}
}
if (i == s.size()) {
+ wasm.addTable(std::move(table));
return;
}
if (!s[i]->dollared()) {
if (s[i]->str() == FUNCREF) {
// (table type (elem ..))
- parseInnerElem(*s[i + 1]);
- if (wasm.table.segments.size() > 0) {
- wasm.table.initial = wasm.table.max =
- wasm.table.segments[0].data.size();
+ parseInnerElem(table.get(), *s[i + 1]);
+ if (table->segments.size() > 0) {
+ table->initial = table->max = table->segments[0].data.size();
} else {
- wasm.table.initial = wasm.table.max = 0;
+ table->initial = table->max = 0;
}
+ wasm.addTable(std::move(table));
return;
}
// first element isn't dollared, and isn't funcref. this could be old syntax
@@ -2787,39 +2814,87 @@ void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) {
if (s[s.size() - 1]->str() == FUNCREF) {
// (table initial max? type)
if (i < s.size() - 1) {
- wasm.table.initial = atoi(s[i++]->c_str());
+ table->initial = atoi(s[i++]->c_str());
}
if (i < s.size() - 1) {
- wasm.table.max = atoi(s[i++]->c_str());
+ table->max = atoi(s[i++]->c_str());
}
+ wasm.addTable(std::move(table));
return;
}
}
// old notation (table func1 func2 ..)
- parseInnerElem(s, i);
- if (wasm.table.segments.size() > 0) {
- wasm.table.initial = wasm.table.max = wasm.table.segments[0].data.size();
+ parseInnerElem(table.get(), s, i);
+ if (table->segments.size() > 0) {
+ table->initial = table->max = table->segments[0].data.size();
} else {
- wasm.table.initial = wasm.table.max = 0;
+ table->initial = table->max = 0;
}
+
+ wasm.addTable(std::move(table));
}
+// parses an elem segment
+// elem ::= (elem (expr) vec(funcidx))
+// | (elem (offset (expr)) func vec(funcidx))
+// | (elem (table tableidx) (offset (expr)) func vec(funcidx))
+//
+// abbreviation:
+// (offset (expr)) ≡ (expr)
+// (elem (expr) vec(funcidx)) ≡ (elem (table 0) (offset (expr)) func
+// vec(funcidx))
+//
void SExpressionWasmBuilder::parseElem(Element& s) {
Index i = 1;
+ Table* table = nullptr;
+ Expression* offset = nullptr;
+
if (!s[i]->isList()) {
- // the table is named
- i++;
+ // optional segment id OR 'declare' OR start of elemList
+ i += 1;
+ }
+
+ // old style refers to the pre-reftypes form of (elem (expr) vec(funcidx))
+ bool oldStyle = true;
+
+ while (1) {
+ auto& inner = *s[i++];
+ if (elementStartsWith(inner, TABLE)) {
+ oldStyle = false;
+ Name tableName = getTableName(*inner[1]);
+ table = wasm.getTable(tableName);
+ } else {
+ if (elementStartsWith(inner, "offset")) {
+ offset = parseExpression(inner[1]);
+ } else {
+ offset = parseExpression(inner);
+ }
+ break;
+ }
}
- auto* offset = parseExpression(s[i++]);
- parseInnerElem(s, i, offset);
+
+ if (!oldStyle) {
+ if (strcmp(s[i]->c_str(), "func") != 0) {
+ throw ParseException(
+ "only the abbreviated form of elemList is supported.");
+ }
+ // ignore elemType for now
+ i += 1;
+ }
+
+ if (wasm.tables.empty()) {
+ throw ParseException("elem without table", s.line, s.col);
+ } else if (!table) {
+ table = wasm.tables[0].get();
+ }
+
+ parseInnerElem(table, s, i, offset);
}
-void SExpressionWasmBuilder::parseInnerElem(Element& s,
+void SExpressionWasmBuilder::parseInnerElem(Table* table,
+ Element& s,
Index i,
Expression* offset) {
- if (!wasm.table.exists) {
- throw ParseException("elem without table", s.line, s.col);
- }
if (!offset) {
offset = allocator.alloc<Const>()->set(Literal(int32_t(0)));
}
@@ -2827,7 +2902,7 @@ void SExpressionWasmBuilder::parseInnerElem(Element& s,
for (; i < s.size(); i++) {
segment.data.push_back(getFunctionName(*s[i]));
}
- wasm.table.segments.push_back(segment);
+ table->segments.push_back(segment);
}
HeapType SExpressionWasmBuilder::parseHeapType(Element& s) {
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index 95cb4a44c..00b1e5368 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -80,10 +80,11 @@ void BinaryInstWriter::visitCall(Call* curr) {
}
void BinaryInstWriter::visitCallIndirect(CallIndirect* curr) {
+ Index tableIdx = parent.getTableIndex(curr->table);
+
int8_t op =
curr->isReturn ? BinaryConsts::RetCallIndirect : BinaryConsts::CallIndirect;
- o << op << U32LEB(parent.getTypeIndex(curr->sig))
- << U32LEB(0); // Reserved flags field
+ o << op << U32LEB(parent.getTypeIndex(curr->sig)) << U32LEB(tableIdx);
}
void BinaryInstWriter::visitLocalGet(LocalGet* curr) {
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index fad78cefd..7e05bd375 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -809,6 +809,12 @@ void FunctionValidator::visitCallIndirect(CallIndirect* curr) {
Type(Type::i32),
curr,
"indirect call target must be an i32");
+
+ if (curr->target->type != Type::unreachable) {
+ auto* table = getModule()->getTableOrNull(curr->table);
+ shouldBeTrue(!!table, curr, "call-indirect table must exist");
+ }
+
validateCallParamsAndResult(curr, curr->sig);
}
@@ -2667,7 +2673,7 @@ static void validateExports(Module& module, ValidationInfo& info) {
name,
"module global exports must be found");
} else if (exp->kind == ExternalKind::Table) {
- info.shouldBeTrue(name == Name("0") || name == module.table.name,
+ info.shouldBeTrue(module.getTableOrNull(name),
name,
"module table exports must be found");
} else if (exp->kind == ExternalKind::Memory) {
@@ -2776,22 +2782,28 @@ static void validateMemory(Module& module, ValidationInfo& info) {
}
}
-static void validateTable(Module& module, ValidationInfo& info) {
- auto& curr = module.table;
- for (auto& segment : curr.segments) {
- info.shouldBeEqual(segment.offset->type,
- Type(Type::i32),
- segment.offset,
- "segment offset should be i32");
- info.shouldBeTrue(
- checkSegmentOffset(segment.offset,
- segment.data.size(),
- module.table.initial * Table::kPageSize),
- segment.offset,
- "table segment offset should be reasonable");
- for (auto name : segment.data) {
- info.shouldBeTrue(
- module.getFunctionOrNull(name), name, "segment name should be valid");
+static void validateTables(Module& module, ValidationInfo& info) {
+ if (!module.features.hasReferenceTypes()) {
+ info.shouldBeTrue(module.tables.size() <= 1,
+ "table",
+ "Only 1 table definition allowed in MVP (requires "
+ "--enable-reference-types)");
+ }
+ for (auto& curr : module.tables) {
+ for (auto& segment : curr->segments) {
+ info.shouldBeEqual(segment.offset->type,
+ Type(Type::i32),
+ segment.offset,
+ "segment offset should be i32");
+ info.shouldBeTrue(checkSegmentOffset(segment.offset,
+ segment.data.size(),
+ curr->initial * Table::kPageSize),
+ segment.offset,
+ "table segment offset should be reasonable");
+ for (auto name : segment.data) {
+ info.shouldBeTrue(
+ module.getFunctionOrNull(name), name, "segment name should be valid");
+ }
}
}
}
@@ -2865,7 +2877,7 @@ bool WasmValidator::validate(Module& module, Flags flags) {
validateExports(module, info);
validateGlobals(module, info);
validateMemory(module, info);
- validateTable(module, info);
+ validateTables(module, info);
validateEvents(module, info);
validateModule(module, info);
validateFeatures(module, info);
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index e9d2bf116..7bf9b8604 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -1164,6 +1164,10 @@ Function* Module::getFunction(Name name) {
return getModuleElement(functionsMap, name, "getFunction");
}
+Table* Module::getTable(Name name) {
+ return getModuleElement(tablesMap, name, "getTable");
+}
+
Global* Module::getGlobal(Name name) {
return getModuleElement(globalsMap, name, "getGlobal");
}
@@ -1189,6 +1193,10 @@ Function* Module::getFunctionOrNull(Name name) {
return getModuleElementOrNull(functionsMap, name);
}
+Table* Module::getTableOrNull(Name name) {
+ return getModuleElementOrNull(tablesMap, name);
+}
+
Global* Module::getGlobalOrNull(Name name) {
return getModuleElementOrNull(globalsMap, name);
}
@@ -1254,6 +1262,10 @@ Function* Module::addFunction(std::unique_ptr<Function>&& curr) {
functions, functionsMap, std::move(curr), "addFunction");
}
+Table* Module::addTable(std::unique_ptr<Table>&& curr) {
+ return addModuleElement(tables, tablesMap, std::move(curr), "addTable");
+}
+
Global* Module::addGlobal(std::unique_ptr<Global>&& curr) {
return addModuleElement(globals, globalsMap, std::move(curr), "addGlobal");
}
@@ -1281,6 +1293,9 @@ void Module::removeExport(Name name) {
void Module::removeFunction(Name name) {
removeModuleElement(functions, functionsMap, name);
}
+void Module::removeTable(Name name) {
+ removeModuleElement(tables, tablesMap, name);
+}
void Module::removeGlobal(Name name) {
removeModuleElement(globals, globalsMap, name);
}
@@ -1310,6 +1325,9 @@ void Module::removeExports(std::function<bool(Export*)> pred) {
void Module::removeFunctions(std::function<bool(Function*)> pred) {
removeModuleElements(functions, functionsMap, pred);
}
+void Module::removeTables(std::function<bool(Table*)> pred) {
+ removeModuleElements(tables, tablesMap, pred);
+}
void Module::removeGlobals(std::function<bool(Global*)> pred) {
removeModuleElements(globals, globalsMap, pred);
}
@@ -1326,6 +1344,10 @@ void Module::updateMaps() {
for (auto& curr : exports) {
exportsMap[curr->name] = curr.get();
}
+ tablesMap.clear();
+ for (auto& curr : tables) {
+ tablesMap[curr->name] = curr.get();
+ }
globalsMap.clear();
for (auto& curr : globals) {
globalsMap[curr->name] = curr.get();
diff --git a/src/wasm2js.h b/src/wasm2js.h
index db25fd472..de234b90b 100644
--- a/src/wasm2js.h
+++ b/src/wasm2js.h
@@ -81,11 +81,11 @@ void sequenceAppend(Ref& ast, Ref extra) {
}
bool isTableExported(Module& wasm) {
- if (!wasm.table.exists || wasm.table.imported()) {
+ if (wasm.tables.empty() || wasm.tables[0]->imported()) {
return false;
}
for (auto& ex : wasm.exports) {
- if (ex->kind == ExternalKind::Table && ex->value == wasm.table.name) {
+ if (ex->kind == ExternalKind::Table && ex->value == wasm.tables[0]->name) {
return true;
}
}
@@ -325,9 +325,11 @@ Ref Wasm2JSBuilder::processWasm(Module* wasm, Name funcName) {
functionsCallableFromOutside.insert(exp->value);
}
}
- for (auto& segment : wasm->table.segments) {
- for (auto name : segment.data) {
- functionsCallableFromOutside.insert(name);
+ for (auto& table : wasm->tables) {
+ for (auto& segment : table->segments) {
+ for (auto name : segment.data) {
+ functionsCallableFromOutside.insert(name);
+ }
}
}
@@ -458,15 +460,15 @@ Ref Wasm2JSBuilder::processWasm(Module* wasm, Name funcName) {
}
}
- // add table import
- if (wasm->table.exists && wasm->table.imported()) {
+ // add imported tables
+ ModuleUtils::iterImportedTables(*wasm, [&](Table* table) {
Ref theVar = ValueBuilder::makeVar();
asmFunc[3]->push_back(theVar);
ValueBuilder::appendToVar(
theVar,
FUNCTION_TABLE,
- ValueBuilder::makeDot(ValueBuilder::makeName(ENV), wasm->table.base));
- }
+ ValueBuilder::makeDot(ValueBuilder::makeName(ENV), table->base));
+ });
// create heaps, etc
addBasics(asmFunc[3], wasm);
@@ -622,7 +624,7 @@ void Wasm2JSBuilder::addGlobalImport(Ref ast, Global* import) {
}
void Wasm2JSBuilder::addTable(Ref ast, Module* wasm) {
- if (!wasm->table.exists) {
+ if (wasm->tables.size() == 0) {
return;
}
@@ -631,69 +633,73 @@ void Wasm2JSBuilder::addTable(Ref ast, Module* wasm) {
// Emit a simple flat table as a JS array literal. Otherwise,
// emit assignments separately for each index.
Ref theArray = ValueBuilder::makeArray();
- if (!wasm->table.imported()) {
- TableUtils::FlatTable flat(wasm->table);
- if (flat.valid) {
- Name null("null");
- for (auto& name : flat.names) {
- if (name.is()) {
- name = fromName(name, NameScope::Top);
- } else {
- name = null;
+ for (auto& table : wasm->tables) {
+ if (!table->imported()) {
+ TableUtils::FlatTable flat(*table);
+ if (flat.valid) {
+ Name null("null");
+ for (auto& name : flat.names) {
+ if (name.is()) {
+ name = fromName(name, NameScope::Top);
+ } else {
+ name = null;
+ }
+ ValueBuilder::appendToArray(theArray, ValueBuilder::makeName(name));
}
- ValueBuilder::appendToArray(theArray, ValueBuilder::makeName(name));
+ } else {
+ perElementInit = true;
+ Ref initial =
+ ValueBuilder::makeInt(Address::address32_t(table->initial.addr));
+ theArray = ValueBuilder::makeNew(
+ ValueBuilder::makeCall(IString("Array"), initial));
}
} else {
perElementInit = true;
- Ref initial =
- ValueBuilder::makeInt(Address::address32_t(wasm->table.initial.addr));
- theArray = ValueBuilder::makeNew(
- ValueBuilder::makeCall(IString("Array"), initial));
}
- } else {
- perElementInit = true;
- }
- if (isTableExported(*wasm)) {
- // If the table is exported use a fake WebAssembly.Table object
- // We don't handle the case where a table is both imported and exported.
- if (wasm->table.imported()) {
- Fatal() << "wasm2js doesn't support a table that is both imported and "
- "exported\n";
- }
- Ref theVar = ValueBuilder::makeVar();
- ast->push_back(theVar);
+ if (isTableExported(*wasm)) {
+ // If the table is exported use a fake WebAssembly.Table object
+ // We don't handle the case where a table is both imported and exported.
+ if (table->imported()) {
+ Fatal() << "wasm2js doesn't support a table that is both imported and "
+ "exported\n";
+ }
+ Ref theVar = ValueBuilder::makeVar();
+ ast->push_back(theVar);
- Ref table = ValueBuilder::makeCall(IString("Table"), theArray);
- ValueBuilder::appendToVar(theVar, FUNCTION_TABLE, table);
- } else if (!wasm->table.imported()) {
- // Otherwise if the table is internal (neither imported not exported).
- // Just use a plain array in this case, avoiding the Table.
- Ref theVar = ValueBuilder::makeVar();
- ast->push_back(theVar);
- ValueBuilder::appendToVar(theVar, FUNCTION_TABLE, theArray);
- }
-
- if (perElementInit) {
- // TODO: optimize for size
- for (auto& segment : wasm->table.segments) {
- auto offset = segment.offset;
- for (Index i = 0; i < segment.data.size(); i++) {
- Ref index;
- if (auto* c = offset->dynCast<Const>()) {
- index = ValueBuilder::makeInt(c->value.geti32() + i);
- } else if (auto* get = offset->dynCast<GlobalGet>()) {
- index = ValueBuilder::makeBinary(
- ValueBuilder::makeName(stringToIString(asmangle(get->name.str))),
- PLUS,
- ValueBuilder::makeNum(i));
- } else {
- WASM_UNREACHABLE("unexpected expr type");
+ Ref table = ValueBuilder::makeCall(IString("Table"), theArray);
+ ValueBuilder::appendToVar(theVar, FUNCTION_TABLE, table);
+ } else if (!table->imported()) {
+ // Otherwise if the table is internal (neither imported not exported).
+ // Just use a plain array in this case, avoiding the Table.
+ Ref theVar = ValueBuilder::makeVar();
+ ast->push_back(theVar);
+ ValueBuilder::appendToVar(theVar, FUNCTION_TABLE, theArray);
+ }
+
+ if (perElementInit) {
+ // TODO: optimize for size
+ for (auto& segment : table->segments) {
+ auto offset = segment.offset;
+ for (Index i = 0; i < segment.data.size(); i++) {
+ Ref index;
+ if (auto* c = offset->dynCast<Const>()) {
+ index = ValueBuilder::makeInt(c->value.geti32() + i);
+ } else if (auto* get = offset->dynCast<GlobalGet>()) {
+ index = ValueBuilder::makeBinary(
+ ValueBuilder::makeName(stringToIString(asmangle(get->name.str))),
+ PLUS,
+ ValueBuilder::makeNum(i));
+ } else {
+ WASM_UNREACHABLE("unexpected expr type");
+ }
+ ast->push_back(ValueBuilder::makeStatement(ValueBuilder::makeBinary(
+ ValueBuilder::makeSub(ValueBuilder::makeName(FUNCTION_TABLE),
+ index),
+ SET,
+ ValueBuilder::makeName(
+ fromName(segment.data[i], NameScope::Top)))));
}
- ast->push_back(ValueBuilder::makeStatement(ValueBuilder::makeBinary(
- ValueBuilder::makeSub(ValueBuilder::makeName(FUNCTION_TABLE), index),
- SET,
- ValueBuilder::makeName(fromName(segment.data[i], NameScope::Top)))));
}
}
}
@@ -2448,7 +2454,7 @@ void Wasm2JSGlue::emitPre() {
if (isTableExported(wasm)) {
out << "function Table(ret) {\n";
- if (wasm.table.initial == wasm.table.max) {
+ if (wasm.tables[0]->initial == wasm.tables[0]->max) {
out << " // grow method not included; table is not growable\n";
} else {
out << " ret.grow = function(by) {\n"
diff --git a/test/binaryen.js/expressions.js b/test/binaryen.js/expressions.js
index 3d2a5d138..4c97cdb90 100644
--- a/test/binaryen.js/expressions.js
+++ b/test/binaryen.js/expressions.js
@@ -295,6 +295,7 @@ console.log("# CallIndirect");
(function testCallIndirect() {
const module = new binaryen.Module();
+ var table = "0";
var target = module.i32.const(42);
var params = binaryen.none;
var results = binaryen.none;
@@ -302,9 +303,10 @@ console.log("# CallIndirect");
module.i32.const(1),
module.i32.const(2)
];
- const theCallIndirect = binaryen.CallIndirect(module.call_indirect(target, operands, params, results));
+ const theCallIndirect = binaryen.CallIndirect(module.call_indirect(table, target, operands, params, results));
assert(theCallIndirect instanceof binaryen.CallIndirect);
assert(theCallIndirect instanceof binaryen.Expression);
+ assert(theCallIndirect.table === table);
assert(theCallIndirect.target === target);
assertDeepEqual(theCallIndirect.operands, operands);
assert(theCallIndirect.params === params);
@@ -346,7 +348,7 @@ console.log("# CallIndirect");
assert(
theCallIndirect.toText()
==
- "(call_indirect (type $i32_i32_=>_i32)\n (i32.const 7)\n (i32.const 6)\n (i32.const 9000)\n)\n"
+ "(call_indirect $0 (type $i32_i32_=>_i32)\n (i32.const 7)\n (i32.const 6)\n (i32.const 9000)\n)\n"
);
module.dispose();
diff --git a/test/binaryen.js/expressions.js.txt b/test/binaryen.js/expressions.js.txt
index 5a0353c27..80a642af8 100644
--- a/test/binaryen.js/expressions.js.txt
+++ b/test/binaryen.js/expressions.js.txt
@@ -42,7 +42,7 @@
)
# CallIndirect
-(call_indirect (type $i32_i32_=>_i32)
+(call_indirect $0 (type $i32_i32_=>_i32)
(i32.const 7)
(i32.const 6)
(i32.const 9000)
diff --git a/test/binaryen.js/kitchen-sink.js b/test/binaryen.js/kitchen-sink.js
index 39154ef7d..327cf0534 100644
--- a/test/binaryen.js/kitchen-sink.js
+++ b/test/binaryen.js/kitchen-sink.js
@@ -517,7 +517,7 @@ function test_core() {
)
),
module.i32.eqz( // check the output type of the call node
- module.call_indirect(makeInt32(2449), [ makeInt32(13), makeInt64(37, 0), makeFloat32(1.3), makeFloat64(3.7) ], iIfF, binaryen.i32)
+ module.call_indirect("0", makeInt32(2449), [ makeInt32(13), makeInt64(37, 0), makeFloat32(1.3), makeFloat64(3.7) ], iIfF, binaryen.i32)
),
module.drop(module.local.get(0, binaryen.i32)),
module.local.set(0, makeInt32(101)),
@@ -532,7 +532,7 @@ function test_core() {
module.return(makeInt32(1337)),
// Tail Call
module.return_call("kitchen()sinker", [ makeInt32(13), makeInt64(37, 0), makeFloat32(1.3), makeFloat64(3.7) ], binaryen.i32),
- module.return_call_indirect(makeInt32(2449), [ makeInt32(13), makeInt64(37, 0), makeFloat32(1.3), makeFloat64(3.7) ], iIfF, binaryen.i32),
+ module.return_call_indirect("0", makeInt32(2449), [ makeInt32(13), makeInt64(37, 0), makeFloat32(1.3), makeFloat64(3.7) ], iIfF, binaryen.i32),
// Reference types
module.ref.is_null(module.ref.null(binaryen.externref)),
@@ -661,9 +661,25 @@ function test_core() {
module.addGlobalExport("a-global", "a-global-exp");
module.addEventExport("a-event", "a-event-exp");
- // Function table. One per module
+ // Tables
+ module.addTable("t1", 0, 2, []);
+ var tablePtr = module.getTable("t1");
+ assert(tablePtr !== 0);
+ assert(tablePtr === module.getTableByIndex(0));
+ var table = binaryen.getTableInfo(tablePtr);
+ assert(table.name === "t1");
+ assert(table.module === "");
+ assert(table.base === "");
+ assert(table.initial === 0);
+ assert(table.max === 2);
+
+ module.removeTable("t1");
+ assert(module.getNumTables() === 0);
+
+ // Legacy
module.setFunctionTable(1, 0xffffffff, [ binaryen.getFunctionInfo(sinker).name ]);
+ assert(module.getNumTables() === 1);
// Memory. One per module
diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt
index 9b45d9a01..4b9de93ab 100644
--- a/test/binaryen.js/kitchen-sink.js.txt
+++ b/test/binaryen.js/kitchen-sink.js.txt
@@ -1758,7 +1758,7 @@ getExpressionInfo(tuple[3])={"id":14,"type":5,"value":3.7}
)
(drop
(i32.eqz
- (call_indirect (type $i32_i64_f32_f64_=>_i32)
+ (call_indirect $0 (type $i32_i64_f32_f64_=>_i32)
(i32.const 13)
(i64.const 37)
(f32.const 1.2999999523162842)
@@ -1822,7 +1822,7 @@ getExpressionInfo(tuple[3])={"id":14,"type":5,"value":3.7}
(f32.const 1.2999999523162842)
(f64.const 3.7)
)
- (return_call_indirect (type $i32_i64_f32_f64_=>_i32)
+ (return_call_indirect $0 (type $i32_i64_f32_f64_=>_i32)
(i32.const 13)
(i64.const 37)
(f32.const 1.2999999523162842)
@@ -3620,7 +3620,7 @@ getExpressionInfo(tuple[3])={"id":14,"type":5,"value":3.7}
)
(drop
(i32.eqz
- (call_indirect (type $i32_i64_f32_f64_=>_i32)
+ (call_indirect $0 (type $i32_i64_f32_f64_=>_i32)
(i32.const 13)
(i64.const 37)
(f32.const 1.2999999523162842)
@@ -3684,7 +3684,7 @@ getExpressionInfo(tuple[3])={"id":14,"type":5,"value":3.7}
(f32.const 1.2999999523162842)
(f64.const 3.7)
)
- (return_call_indirect (type $i32_i64_f32_f64_=>_i32)
+ (return_call_indirect $0 (type $i32_i64_f32_f64_=>_i32)
(i32.const 13)
(i64.const 37)
(f32.const 1.2999999523162842)
diff --git a/test/binaryen.js/tail_calls.js b/test/binaryen.js/tail_calls.js
index 1b993be91..61fe5d829 100644
--- a/test/binaryen.js/tail_calls.js
+++ b/test/binaryen.js/tail_calls.js
@@ -18,6 +18,7 @@ var bar = module.addFunction(
binaryen.none,
[],
module.return_call_indirect(
+ "0",
module.i32.const(0),
[],
binaryen.none,
diff --git a/test/ctor-eval/bad-indirect-call.wast.out b/test/ctor-eval/bad-indirect-call.wast.out
index 4c33e46ca..6629e6bbb 100644
--- a/test/ctor-eval/bad-indirect-call.wast.out
+++ b/test/ctor-eval/bad-indirect-call.wast.out
@@ -6,7 +6,7 @@
(elem (i32.const 0) $call-indirect)
(export "test1" (func $test1))
(func $test1
- (call_indirect (type $none_=>_none)
+ (call_indirect $0 (type $none_=>_none)
(i32.const 1)
)
(i32.store8
diff --git a/test/ctor-eval/bad-indirect-call2.wast.out b/test/ctor-eval/bad-indirect-call2.wast.out
index ed7546981..9a9186a78 100644
--- a/test/ctor-eval/bad-indirect-call2.wast.out
+++ b/test/ctor-eval/bad-indirect-call2.wast.out
@@ -7,7 +7,7 @@
(elem (i32.const 0) $_abort $call-indirect)
(export "test1" (func $test1))
(func $test1
- (call_indirect (type $none_=>_none)
+ (call_indirect $0 (type $none_=>_none)
(i32.const 0)
)
(i32.store8
diff --git a/test/ctor-eval/bad-indirect-call3.wast.out b/test/ctor-eval/bad-indirect-call3.wast.out
index 5edfb58b1..422360974 100644
--- a/test/ctor-eval/bad-indirect-call3.wast.out
+++ b/test/ctor-eval/bad-indirect-call3.wast.out
@@ -14,7 +14,7 @@
)
)
(func $sig_mismatch
- (call_indirect (type $funcref_=>_none)
+ (call_indirect $0 (type $funcref_=>_none)
(ref.null func)
(i32.const 0)
)
diff --git a/test/example/c-api-kitchen-sink.c b/test/example/c-api-kitchen-sink.c
index 907ab58c9..b45039c69 100644
--- a/test/example/c-api-kitchen-sink.c
+++ b/test/example/c-api-kitchen-sink.c
@@ -318,6 +318,8 @@ void test_core() {
BinaryenAddEvent(
module, "a-event", 0, BinaryenTypeInt32(), BinaryenTypeNone());
+ BinaryenAddTable(module, "tab", 0, 100, NULL, 0, makeInt32(module, 0));
+
// Exception handling
// (try
@@ -680,6 +682,7 @@ void test_core() {
BinaryenUnary(module,
BinaryenEqZInt32(), // check the output type of the call node
BinaryenCallIndirect(module,
+ "tab",
makeInt32(module, 2449),
callOperands4b,
4,
@@ -704,6 +707,7 @@ void test_core() {
BinaryenReturnCall(
module, "kitchen()sinker", callOperands4, 4, BinaryenTypeInt32()),
BinaryenReturnCallIndirect(module,
+ "tab",
makeInt32(module, 2449),
callOperands4b,
4,
@@ -840,6 +844,7 @@ void test_core() {
void test_unreachable() {
BinaryenModuleRef module = BinaryenModuleCreate();
BinaryenExpressionRef body = BinaryenCallIndirect(module,
+ "invalid-table",
BinaryenUnreachable(module),
NULL,
0,
diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt
index 8b06c1109..f03e4ec6c 100644
--- a/test/example/c-api-kitchen-sink.txt
+++ b/test/example/c-api-kitchen-sink.txt
@@ -38,7 +38,7 @@ BinaryenFeatureAll: 8191
(memory $0 (shared 1 256))
(data (i32.const 10) "hello, world")
(data passive "I am passive")
- (table $0 1 1 funcref)
+ (table $tab 1 1 funcref)
(elem (i32.const 0) "$kitchen()sinker")
(global $a-global i32 (i32.const 7))
(global $a-mutable-global (mut f32) (f32.const 7.5))
@@ -1666,7 +1666,7 @@ BinaryenFeatureAll: 8191
)
(drop
(i32.eqz
- (call_indirect (type $i32_i64_f32_f64_=>_i32)
+ (call_indirect $tab (type $i32_i64_f32_f64_=>_i32)
(i32.const 13)
(i64.const 37)
(f32.const 1.2999999523162842)
@@ -1730,7 +1730,7 @@ BinaryenFeatureAll: 8191
(f32.const 1.2999999523162842)
(f64.const 3.7)
)
- (return_call_indirect (type $i32_i64_f32_f64_=>_i32)
+ (return_call_indirect $tab (type $i32_i64_f32_f64_=>_i32)
(i32.const 13)
(i64.const 37)
(f32.const 1.2999999523162842)
diff --git a/test/example/c-api-multiple-tables.c b/test/example/c-api-multiple-tables.c
new file mode 100644
index 000000000..4ba612658
--- /dev/null
+++ b/test/example/c-api-multiple-tables.c
@@ -0,0 +1,90 @@
+#include <assert.h>
+#include <string.h>
+#include <binaryen-c.h>
+
+// "hello world" type example: create a function that adds two i32s and returns
+// the result
+
+int main() {
+ BinaryenModuleRef module = BinaryenModuleCreate();
+ BinaryenModuleSetFeatures(module, BinaryenFeatureReferenceTypes());
+
+ // Create a function type for i32 (i32, i32)
+ BinaryenType ii[2] = {BinaryenTypeInt32(), BinaryenTypeInt32()};
+ BinaryenType params = BinaryenTypeCreate(ii, 2);
+ BinaryenType results = BinaryenTypeInt32();
+
+ assert(BinaryenGetNumTables(module) == 0);
+
+ {
+ // Get the 0 and 1 arguments, and add them
+ BinaryenExpressionRef x = BinaryenLocalGet(module, 0, BinaryenTypeInt32()),
+ y = BinaryenLocalGet(module, 1, BinaryenTypeInt32());
+ BinaryenExpressionRef add =
+ BinaryenBinary(module, BinaryenAddInt32(), x, y);
+
+ // Create the add function
+ // Note: no additional local variables
+ // Note: no basic blocks here, we are an AST. The function body is just an
+ // expression node.
+ BinaryenFunctionRef adder =
+ BinaryenAddFunction(module, "adder", params, results, NULL, 0, add);
+
+ const char* funcNames[] = {"adder"};
+ BinaryenAddTable(module,
+ "tab",
+ 1,
+ 1,
+ funcNames,
+ 1,
+ BinaryenConst(module, BinaryenLiteralInt32(0)));
+ assert(BinaryenGetTable(module, "tab") != NULL);
+
+ BinaryenAddTable(module,
+ "t2",
+ 1,
+ 1,
+ funcNames,
+ 1,
+ BinaryenConst(module, BinaryenLiteralInt32(0)));
+ BinaryenTableRef t2 = BinaryenGetTableByIndex(module, 1);
+ assert(t2 != NULL);
+
+ assert(strcmp(BinaryenTableGetName(t2), "t2") == 0);
+ assert(BinaryenTableGetInitial(t2) == 1);
+ assert(BinaryenTableHasMax(t2) == 1);
+ assert(BinaryenTableGetMax(t2) == 1);
+ assert(strcmp(BinaryenTableImportGetModule(t2), "") == 0);
+ assert(strcmp(BinaryenTableImportGetBase(t2), "") == 0);
+
+ assert(BinaryenGetNumTables(module) == 2);
+ }
+
+ {
+ // Get the 0 and 1 arguments, and add them
+ BinaryenExpressionRef operands[] = {
+ BinaryenLocalGet(module, 0, BinaryenTypeInt32()),
+ BinaryenLocalGet(module, 1, BinaryenTypeInt32())};
+
+ BinaryenExpressionRef add_indirect =
+ BinaryenCallIndirect(module,
+ "tab",
+ BinaryenConst(module, BinaryenLiteralInt32(0)),
+ operands,
+ 2,
+ params,
+ results);
+ BinaryenCallIndirectSetTable(add_indirect, "t2");
+
+ BinaryenFunctionRef call_adder_indirectly = BinaryenAddFunction(
+ module, "call_adder_indirect", params, results, NULL, 0, add_indirect);
+ }
+
+ // Print it out
+ BinaryenModulePrint(module);
+
+ // Clean up the module, which owns all the objects we created above
+ BinaryenModuleDispose(module);
+
+ return 0;
+}
diff --git a/test/example/c-api-multiple-tables.txt b/test/example/c-api-multiple-tables.txt
new file mode 100644
index 000000000..c0f2da53b
--- /dev/null
+++ b/test/example/c-api-multiple-tables.txt
@@ -0,0 +1,20 @@
+(module
+ (type $i32_i32_=>_i32 (func (param i32 i32) (result i32)))
+ (table $tab 1 1 funcref)
+ (elem (table $tab) (i32.const 0) func $adder)
+ (table $t2 1 1 funcref)
+ (elem (table $t2) (i32.const 0) func $adder)
+ (func $adder (param $0 i32) (param $1 i32) (result i32)
+ (i32.add
+ (local.get $0)
+ (local.get $1)
+ )
+ )
+ (func $call_adder_indirect (param $0 i32) (param $1 i32) (result i32)
+ (call_indirect $t2 (type $i32_i32_=>_i32)
+ (local.get $0)
+ (local.get $1)
+ (i32.const 0)
+ )
+ )
+)
diff --git a/test/example/module-splitting.txt b/test/example/module-splitting.txt
index 32d579c2c..93a766652 100644
--- a/test/example/module-splitting.txt
+++ b/test/example/module-splitting.txt
@@ -311,7 +311,7 @@ After:
(export "foo" (func $foo))
(export "%table" (table $0))
(func $foo (param $0 i32) (result i32)
- (call_indirect (type $i32_=>_i32)
+ (call_indirect $0 (type $i32_=>_i32)
(local.get $0)
(i32.const 0)
)
@@ -377,7 +377,7 @@ After:
(export "foo" (func $foo))
(export "%table" (table $table))
(func $foo (param $0 i32) (result i32)
- (call_indirect (type $i32_=>_i32)
+ (call_indirect $table (type $i32_=>_i32)
(local.get $0)
(i32.const 42)
)
@@ -417,7 +417,7 @@ After:
(export "%table" (table $table))
(export "%global" (global $base))
(func $foo (param $0 i32) (result i32)
- (call_indirect (type $i32_=>_i32)
+ (call_indirect $table (type $i32_=>_i32)
(local.get $0)
(global.get $base)
)
@@ -467,7 +467,7 @@ After:
(nop)
)
(func $foo (param $0 i32) (result i32)
- (call_indirect (type $i32_=>_i32)
+ (call_indirect $table (type $i32_=>_i32)
(local.get $0)
(i32.add
(global.get $base)
@@ -564,7 +564,7 @@ After:
(elem (i32.const 0) $placeholder_0)
(export "%table" (table $0))
(func $foo
- (call_indirect (type $none_=>_none)
+ (call_indirect $0 (type $none_=>_none)
(i32.const 0)
)
)
@@ -631,7 +631,7 @@ After:
(nop)
)
(func $bar
- (call_indirect (type $none_=>_none)
+ (call_indirect $0 (type $none_=>_none)
(i32.const 0)
)
)
@@ -923,7 +923,7 @@ After:
(export "%foo" (func $foo))
(export "%table" (table $table))
(func $foo (param $0 i32) (result i32)
- (call_indirect (type $i32_=>_i32)
+ (call_indirect $table (type $i32_=>_i32)
(i32.const 0)
(i32.const 1)
)
@@ -963,7 +963,7 @@ After:
(export "foo2" (func $foo))
(export "%table" (table $0))
(func $foo
- (call_indirect (type $none_=>_none)
+ (call_indirect $0 (type $none_=>_none)
(i32.const 0)
)
)
diff --git a/test/multi-table.minified.txt b/test/multi-table.minified.txt
new file mode 100644
index 000000000..6222ea303
--- /dev/null
+++ b/test/multi-table.minified.txt
@@ -0,0 +1 @@
+(module(type $none_=>_none (func))(import "a" "b" (table $t1 1 10 funcref))(elem (table $t1) (i32.const 0) func $f)(table $t2 3 3 funcref)(elem (table $t2) (i32.const 0) func $f)(elem (table $t2) (i32.const 1) func $f $g)(func $f(nop))(func $g(nop))) \ No newline at end of file
diff --git a/test/multi-table.wast b/test/multi-table.wast
new file mode 100644
index 000000000..07c1e0077
--- /dev/null
+++ b/test/multi-table.wast
@@ -0,0 +1,14 @@
+(module
+ (import "a" "b" (table $t1 1 10 funcref))
+ (table $t2 3 3 funcref)
+
+ ;; add to $t1
+ (elem (i32.const 0) $f)
+
+ ;; add to $t2
+ (elem (table $t2) (i32.const 0) func $f)
+ (elem (table $t2) (offset (i32.const 1)) func $f $g)
+
+ (func $f)
+ (func $g)
+) \ No newline at end of file
diff --git a/test/multi-table.wast.from-wast b/test/multi-table.wast.from-wast
new file mode 100644
index 000000000..9d1842b70
--- /dev/null
+++ b/test/multi-table.wast.from-wast
@@ -0,0 +1,14 @@
+(module
+ (type $none_=>_none (func))
+ (import "a" "b" (table $t1 1 10 funcref))
+ (elem (table $t1) (i32.const 0) func $f)
+ (table $t2 3 3 funcref)
+ (elem (table $t2) (i32.const 0) func $f)
+ (elem (table $t2) (i32.const 1) func $f $g)
+ (func $f
+ (nop)
+ )
+ (func $g
+ (nop)
+ )
+)
diff --git a/test/multi-table.wast.fromBinary b/test/multi-table.wast.fromBinary
new file mode 100644
index 000000000..1bfd568da
--- /dev/null
+++ b/test/multi-table.wast.fromBinary
@@ -0,0 +1,15 @@
+(module
+ (type $none_=>_none (func))
+ (import "a" "b" (table $t1 1 10 funcref))
+ (elem (table $t1) (i32.const 0) func $f)
+ (table $t2 3 3 funcref)
+ (elem (table $t2) (i32.const 0) func $f)
+ (elem (table $t2) (i32.const 1) func $f $g)
+ (func $f
+ (nop)
+ )
+ (func $g
+ (nop)
+ )
+)
+
diff --git a/test/multi-table.wast.fromBinary.noDebugInfo b/test/multi-table.wast.fromBinary.noDebugInfo
new file mode 100644
index 000000000..00c3a64cb
--- /dev/null
+++ b/test/multi-table.wast.fromBinary.noDebugInfo
@@ -0,0 +1,15 @@
+(module
+ (type $none_=>_none (func))
+ (import "a" "b" (table $timport$0 1 10 funcref))
+ (elem (table $timport$0) (i32.const 0) func $0)
+ (table $0 3 3 funcref)
+ (elem (table $0) (i32.const 0) func $0)
+ (elem (table $0) (i32.const 1) func $0 $1)
+ (func $0
+ (nop)
+ )
+ (func $1
+ (nop)
+ )
+)
+
diff --git a/test/newsyntax.wast.from-wast b/test/newsyntax.wast.from-wast
index edf54b5b9..7b8e2b2a6 100644
--- a/test/newsyntax.wast.from-wast
+++ b/test/newsyntax.wast.from-wast
@@ -5,13 +5,13 @@
(export "call_indirect" (func $0))
(func $0
(drop
- (call_indirect (type $i32_f64_=>_i32)
+ (call_indirect $timport$0 (type $i32_f64_=>_i32)
(i32.const 10)
(f64.const 20)
(i32.const 30)
)
)
- (call_indirect (type $none_=>_none)
+ (call_indirect $timport$0 (type $none_=>_none)
(i32.const 1)
)
)
diff --git a/test/newsyntax.wast.fromBinary b/test/newsyntax.wast.fromBinary
index ea3b1ac9a..5309fb3eb 100644
--- a/test/newsyntax.wast.fromBinary
+++ b/test/newsyntax.wast.fromBinary
@@ -5,13 +5,13 @@
(export "call_indirect" (func $0))
(func $0
(drop
- (call_indirect (type $i32_f64_=>_i32)
+ (call_indirect $timport$0 (type $i32_f64_=>_i32)
(i32.const 10)
(f64.const 20)
(i32.const 30)
)
)
- (call_indirect (type $none_=>_none)
+ (call_indirect $timport$0 (type $none_=>_none)
(i32.const 1)
)
)
diff --git a/test/newsyntax.wast.fromBinary.noDebugInfo b/test/newsyntax.wast.fromBinary.noDebugInfo
index ea3b1ac9a..5309fb3eb 100644
--- a/test/newsyntax.wast.fromBinary.noDebugInfo
+++ b/test/newsyntax.wast.fromBinary.noDebugInfo
@@ -5,13 +5,13 @@
(export "call_indirect" (func $0))
(func $0
(drop
- (call_indirect (type $i32_f64_=>_i32)
+ (call_indirect $timport$0 (type $i32_f64_=>_i32)
(i32.const 10)
(f64.const 20)
(i32.const 30)
)
)
- (call_indirect (type $none_=>_none)
+ (call_indirect $timport$0 (type $none_=>_none)
(i32.const 1)
)
)
diff --git a/test/passes/O3_low-memory-unused_metrics.txt b/test/passes/O3_low-memory-unused_metrics.txt
index 1ec56ef2c..db50f97a8 100644
--- a/test/passes/O3_low-memory-unused_metrics.txt
+++ b/test/passes/O3_low-memory-unused_metrics.txt
@@ -6,6 +6,7 @@ total
[imports] : 10
[memory-data] : 0
[table-data] : 0
+ [tables] : 0
[total] : 1964
[vars] : 9
Binary : 240
diff --git a/test/passes/converge_O3_metrics.bin.txt b/test/passes/converge_O3_metrics.bin.txt
index 1764458f6..2df4a4b0b 100644
--- a/test/passes/converge_O3_metrics.bin.txt
+++ b/test/passes/converge_O3_metrics.bin.txt
@@ -6,6 +6,7 @@ total
[imports] : 3
[memory-data] : 28
[table-data] : 429
+ [tables] : 0
[total] : 129
[vars] : 4
Binary : 12
@@ -247,6 +248,7 @@ total
[imports] : 3
[memory-data] : 28
[table-data] : 429
+ [tables] : 0
[total] : 129
[vars] : 4
Binary : 12
diff --git a/test/passes/dae_all-features.txt b/test/passes/dae_all-features.txt
index 9a1a4d89a..1c8c9f799 100644
--- a/test/passes/dae_all-features.txt
+++ b/test/passes/dae_all-features.txt
@@ -268,7 +268,7 @@
(i32.const 42)
)
(drop
- (return_call_indirect (type $none_=>_i32)
+ (return_call_indirect $0 (type $none_=>_i32)
(i32.const 0)
)
)
diff --git a/test/passes/directize_all-features.txt b/test/passes/directize_all-features.txt
index 4b6934f06..6b3d10b53 100644
--- a/test/passes/directize_all-features.txt
+++ b/test/passes/directize_all-features.txt
@@ -14,8 +14,29 @@
)
(module
(type $i32_i32_=>_none (func (param i32 i32)))
+ (type $i32_=>_i32 (func (param i32) (result i32)))
(table $0 5 5 funcref)
- (elem (i32.const 4) $foo)
+ (elem (table $0) (i32.const 1) func $dummy)
+ (table $1 5 5 funcref)
+ (elem (table $1) (i32.const 1) func $f)
+ (func $dummy (param $0 i32) (result i32)
+ (local.get $0)
+ )
+ (func $f (param $0 i32) (param $1 i32)
+ (unreachable)
+ )
+ (func $g (param $x i32) (param $y i32)
+ (call $f
+ (local.get $x)
+ (local.get $y)
+ )
+ )
+)
+(module
+ (type $i32_i32_=>_none (func (param i32 i32)))
+ (table $0 5 5 funcref)
+ (table $1 5 5 funcref)
+ (elem (table $1) (i32.const 4) func $foo)
(func $foo (param $0 i32) (param $1 i32)
(unreachable)
)
@@ -43,7 +64,9 @@
(module
(type $i32_i32_=>_none (func (param i32 i32)))
(table $0 5 5 funcref)
- (elem (i32.const 0) $foo $foo $foo $foo $foo)
+ (elem (table $0) (i32.const 0) func $foo $foo $foo $foo $foo)
+ (table $1 5 5 funcref)
+ (elem (table $1) (i32.const 0) func $foo $foo $foo $foo $foo)
(func $foo (param $0 i32) (param $1 i32)
(unreachable)
)
@@ -62,7 +85,7 @@
(unreachable)
)
(func $bar (param $x i32) (param $y i32)
- (call_indirect (type $i32_i32_=>_none)
+ (call_indirect $table (type $i32_i32_=>_none)
(local.get $x)
(local.get $y)
(i32.const 1)
@@ -78,7 +101,7 @@
(unreachable)
)
(func $bar (param $x i32) (param $y i32)
- (call_indirect (type $i32_i32_=>_none)
+ (call_indirect $0 (type $i32_i32_=>_none)
(local.get $x)
(local.get $y)
(i32.const 1)
@@ -94,7 +117,24 @@
(unreachable)
)
(func $bar (param $x i32) (param $y i32)
- (call_indirect (type $i32_i32_=>_none)
+ (call_indirect $0 (type $i32_i32_=>_none)
+ (local.get $x)
+ (local.get $y)
+ (i32.const 1)
+ )
+ )
+)
+(module
+ (type $i32_i32_=>_none (func (param i32 i32)))
+ (table $0 5 5 funcref)
+ (table $1 5 5 funcref)
+ (elem (table $1) (global.get $g) func $foo)
+ (global $g (mut i32) (i32.const 1))
+ (func $foo (param $0 i32) (param $1 i32)
+ (unreachable)
+ )
+ (func $bar (param $x i32) (param $y i32)
+ (call_indirect $1 (type $i32_i32_=>_none)
(local.get $x)
(local.get $y)
(i32.const 1)
@@ -110,7 +150,7 @@
(unreachable)
)
(func $bar (param $x i32) (param $y i32) (param $z i32)
- (call_indirect (type $i32_i32_=>_none)
+ (call_indirect $0 (type $i32_i32_=>_none)
(local.get $x)
(local.get $y)
(local.get $z)
diff --git a/test/passes/directize_all-features.wast b/test/passes/directize_all-features.wast
index b113af396..d07b2d191 100644
--- a/test/passes/directize_all-features.wast
+++ b/test/passes/directize_all-features.wast
@@ -13,16 +13,37 @@
)
)
)
+(module
+ (type $ii (func (param i32 i32)))
+ (table $0 5 5 funcref)
+ (table $1 5 5 funcref)
+ (elem (table $0) (i32.const 1) func $dummy)
+ (elem (table $1) (i32.const 1) func $f)
+ (func $dummy (param i32) (result i32)
+ (local.get 0)
+ )
+ (func $f (param i32) (param i32)
+ (unreachable)
+ )
+ (func $g (param $x i32) (param $y i32)
+ (call_indirect $1 (type $ii)
+ (local.get $x)
+ (local.get $y)
+ (i32.const 1)
+ )
+ )
+)
;; at table edges
(module
(type $ii (func (param i32 i32)))
(table $0 5 5 funcref)
- (elem (i32.const 4) $foo)
+ (table $1 5 5 funcref)
+ (elem (table $1) (i32.const 4) func $foo)
(func $foo (param i32) (param i32)
(unreachable)
)
(func $bar (param $x i32) (param $y i32)
- (call_indirect (type $ii)
+ (call_indirect $1 (type $ii)
(local.get $x)
(local.get $y)
(i32.const 4)
@@ -47,12 +68,14 @@
(module
(type $ii (func (param i32 i32)))
(table $0 5 5 funcref)
+ (table $1 5 5 funcref)
(elem (i32.const 0) $foo $foo $foo $foo $foo)
+ (elem (table $1) (i32.const 0) func $foo $foo $foo $foo $foo)
(func $foo (param i32) (param i32)
(unreachable)
)
(func $bar (param $x i32) (param $y i32)
- (call_indirect (type $ii)
+ (call_indirect $1 (type $ii)
(local.get $x)
(local.get $y)
(i32.const 2)
@@ -109,6 +132,23 @@
)
)
)
+(module
+ (type $ii (func (param i32 i32)))
+ (table $0 5 5 funcref)
+ (table $1 5 5 funcref)
+ (global $g (mut i32) (i32.const 1))
+ (elem (table $1) (global.get $g) func $foo)
+ (func $foo (param i32) (param i32)
+ (unreachable)
+ )
+ (func $bar (param $x i32) (param $y i32)
+ (call_indirect $1 (type $ii)
+ (local.get $x)
+ (local.get $y)
+ (i32.const 1)
+ )
+ )
+)
;; non-constant call index
(module
(type $ii (func (param i32 i32)))
diff --git a/test/passes/flatten_all-features.txt b/test/passes/flatten_all-features.txt
index e3d2ca55e..192c9ad82 100644
--- a/test/passes/flatten_all-features.txt
+++ b/test/passes/flatten_all-features.txt
@@ -1050,7 +1050,7 @@
(i32.const -1)
(block
(unreachable)
- (call_indirect (type $i32_i32_=>_none)
+ (call_indirect $0 (type $i32_i32_=>_none)
(i32.const 123)
(i32.const 456)
(unreachable)
@@ -1062,7 +1062,7 @@
(i32.const -2)
(block
(unreachable)
- (call_indirect (type $i32_i32_=>_none)
+ (call_indirect $0 (type $i32_i32_=>_none)
(i32.const 139)
(unreachable)
(i32.const 0)
@@ -1075,7 +1075,7 @@
(block
(unreachable)
(unreachable)
- (call_indirect (type $i32_i32_=>_none)
+ (call_indirect $0 (type $i32_i32_=>_none)
(i32.const 246)
(unreachable)
(unreachable)
@@ -1089,7 +1089,7 @@
(unreachable)
(unreachable)
(unreachable)
- (call_indirect (type $i32_i32_=>_none)
+ (call_indirect $0 (type $i32_i32_=>_none)
(unreachable)
(unreachable)
(unreachable)
diff --git a/test/passes/func-metrics.txt b/test/passes/func-metrics.txt
index 117d9baf7..75ff9dcf2 100644
--- a/test/passes/func-metrics.txt
+++ b/test/passes/func-metrics.txt
@@ -6,6 +6,7 @@ global
[imports] : 0
[memory-data] : 9
[table-data] : 3
+ [tables] : 1
[total] : 3
Const : 3
func: empty
@@ -95,6 +96,7 @@ global
[funcs] : 0
[globals] : 0
[imports] : 0
+ [tables] : 0
[total] : 0
(module
)
@@ -104,6 +106,7 @@ global
[funcs] : 3
[globals] : 0
[imports] : 1
+ [tables] : 0
[total] : 0
func: func_a
[binary-bytes] : 16
@@ -179,6 +182,7 @@ global
[funcs] : 1
[globals] : 0
[imports] : 1
+ [tables] : 0
[total] : 0
func: func_a
[binary-bytes] : 12
@@ -211,6 +215,7 @@ global
[funcs] : 1
[globals] : 0
[imports] : 1
+ [tables] : 0
[total] : 0
func: func_a
[binary-bytes] : 12
@@ -239,6 +244,7 @@ global
[funcs] : 1
[globals] : 1
[imports] : 1
+ [tables] : 0
[total] : 1
GlobalGet : 1
func: 0
diff --git a/test/passes/fuzz_metrics_noprint.bin.txt b/test/passes/fuzz_metrics_noprint.bin.txt
index fd335ce6f..d8c708f3c 100644
--- a/test/passes/fuzz_metrics_noprint.bin.txt
+++ b/test/passes/fuzz_metrics_noprint.bin.txt
@@ -6,6 +6,7 @@ total
[imports] : 4
[memory-data] : 4
[table-data] : 16
+ [tables] : 1
[total] : 9206
[vars] : 186
Binary : 715
diff --git a/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_optimize-level=3.txt b/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_optimize-level=3.txt
index c3dfec2dc..9a0ec41ec 100644
--- a/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_optimize-level=3.txt
+++ b/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_optimize-level=3.txt
@@ -276,7 +276,7 @@
i32.and
i32.const 8
i32.add
- call_indirect (type $f32_=>_none)
+ call_indirect $0 (type $f32_=>_none)
)
(func $cneg (param $x f32)
local.get $x
@@ -285,7 +285,7 @@
i32.and
i32.const 8
i32.add
- call_indirect (type $f32_=>_none)
+ call_indirect $0 (type $f32_=>_none)
)
(func $___syscall_ret
(local $$0 i32)
diff --git a/test/passes/metrics_all-features.txt b/test/passes/metrics_all-features.txt
index 79381a62d..0a22ac945 100644
--- a/test/passes/metrics_all-features.txt
+++ b/test/passes/metrics_all-features.txt
@@ -6,6 +6,7 @@ total
[imports] : 0
[memory-data] : 9
[table-data] : 3
+ [tables] : 1
[total] : 27
[vars] : 1
Binary : 1
@@ -69,6 +70,7 @@ total
[funcs] : 0
[globals] : 0
[imports] : 0
+ [tables] : 0
[total] : 0
[vars] : 0
(module
diff --git a/test/passes/metrics_strip-debug_metrics.bin.txt b/test/passes/metrics_strip-debug_metrics.bin.txt
index f873992bb..5df5c0772 100644
--- a/test/passes/metrics_strip-debug_metrics.bin.txt
+++ b/test/passes/metrics_strip-debug_metrics.bin.txt
@@ -4,6 +4,7 @@ total
[funcs] : 1
[globals] : 0
[imports] : 0
+ [tables] : 0
[total] : 1
[vars] : 0
Nop : 1
@@ -13,6 +14,7 @@ total
[funcs] : 1
[globals] : 0
[imports] : 0
+ [tables] : 0
[total] : 1
[vars] : 0
Nop : 1
diff --git a/test/passes/metrics_strip-producers_metrics.bin.txt b/test/passes/metrics_strip-producers_metrics.bin.txt
index f20d10e0b..a7ae38f9a 100644
--- a/test/passes/metrics_strip-producers_metrics.bin.txt
+++ b/test/passes/metrics_strip-producers_metrics.bin.txt
@@ -4,6 +4,7 @@ total
[funcs] : 1
[globals] : 0
[imports] : 0
+ [tables] : 0
[total] : 1
[vars] : 0
Nop : 1
@@ -13,6 +14,7 @@ total
[funcs] : 1
[globals] : 0
[imports] : 0
+ [tables] : 0
[total] : 1
[vars] : 0
Nop : 1
diff --git a/test/passes/print_g_metrics.bin.txt b/test/passes/print_g_metrics.bin.txt
index 57a1d2745..ca0eeaa58 100644
--- a/test/passes/print_g_metrics.bin.txt
+++ b/test/passes/print_g_metrics.bin.txt
@@ -70,6 +70,7 @@ total
[funcs] : 3
[globals] : 1
[imports] : 0
+ [tables] : 0
[total] : 37
[vars] : 0
Binary : 11
diff --git a/test/passes/remove-unused-module-elements_all-features.txt b/test/passes/remove-unused-module-elements_all-features.txt
index 0873cfee4..22751f9f5 100644
--- a/test/passes/remove-unused-module-elements_all-features.txt
+++ b/test/passes/remove-unused-module-elements_all-features.txt
@@ -36,40 +36,40 @@
(call $called3)
)
(func $other1 (param $0 i32)
- (call_indirect (type $none_=>_none)
+ (call_indirect $0 (type $none_=>_none)
(i32.const 0)
)
- (call_indirect (type $none_=>_none)
+ (call_indirect $0 (type $none_=>_none)
(i32.const 0)
)
- (call_indirect (type $none_=>_none)
+ (call_indirect $0 (type $none_=>_none)
(i32.const 0)
)
- (call_indirect (type $none_=>_none)
+ (call_indirect $0 (type $none_=>_none)
(i32.const 0)
)
- (call_indirect (type $i32_=>_none)
+ (call_indirect $0 (type $i32_=>_none)
(i32.const 0)
(i32.const 0)
)
- (call_indirect (type $i32_=>_none)
+ (call_indirect $0 (type $i32_=>_none)
(i32.const 0)
(i32.const 0)
)
(drop
- (call_indirect (type $i32_=>_i32)
+ (call_indirect $0 (type $i32_=>_i32)
(i32.const 0)
(i32.const 0)
)
)
(drop
- (call_indirect (type $i32_=>_i32)
+ (call_indirect $0 (type $i32_=>_i32)
(i32.const 0)
(i32.const 0)
)
)
(drop
- (call_indirect (type $i32_=>_i32)
+ (call_indirect $0 (type $i32_=>_i32)
(i32.const 0)
(i32.const 0)
)
@@ -84,10 +84,22 @@
(module
)
(module
+ (type $none_=>_none (func))
+ (import "env" "table2" (table $1 1 1 funcref))
+ (elem (i32.const 0) $f)
+ (func $f
+ (nop)
+ )
+)
+(module
+)
+(module
+)
+(module
(import "env" "memory" (memory $0 256))
(import "env" "table" (table $timport$0 1 funcref))
(export "mem" (memory $0))
- (export "tab" (table $0))
+ (export "tab" (table $timport$0))
)
(module
(type $none_=>_none (func))
@@ -110,7 +122,7 @@
(i32.const 0)
)
)
- (call_indirect (type $none_=>_none)
+ (call_indirect $timport$0 (type $none_=>_none)
(i32.const 0)
)
)
diff --git a/test/passes/remove-unused-module-elements_all-features.wast b/test/passes/remove-unused-module-elements_all-features.wast
index c70dc1495..8837f91a9 100644
--- a/test/passes/remove-unused-module-elements_all-features.wast
+++ b/test/passes/remove-unused-module-elements_all-features.wast
@@ -73,10 +73,31 @@
(import "env" "memory" (memory $0 256))
(import "env" "table" (table 0 funcref))
)
+(module ;; remove all tables and the memory
+ (import "env" "memory" (memory $0 256))
+ (import "env" "table" (table 0 funcref))
+ (import "env" "table2" (table $1 1 2 funcref))
+ (elem (table $1) (offset (i32.const 0)) func)
+ (elem (table $1) (offset (i32.const 1)) func)
+)
+(module ;; remove the first table and memory, but not the second one
+ (import "env" "memory" (memory $0 256))
+ (import "env" "table" (table 0 funcref))
+ (import "env" "table2" (table $1 1 1 funcref))
+ (elem (table $1) (offset (i32.const 0)) func)
+ (elem (table $1) (offset (i32.const 0)) func $f)
+ (func $f)
+)
(module ;; also when not imported
(memory 256)
(table 1 funcref)
)
+(module ;; also with multiple tables
+ (memory 256)
+ (table $0 1 funcref)
+ (table $1 1 funcref)
+ (elem (table $1) (i32.const 0) func)
+)
(module ;; but not when exported
(import "env" "memory" (memory $0 256))
(import "env" "table" (table 1 funcref))
diff --git a/test/passes/remove-unused-names_merge-blocks_all-features.txt b/test/passes/remove-unused-names_merge-blocks_all-features.txt
index 4efda13ca..774715e02 100644
--- a/test/passes/remove-unused-names_merge-blocks_all-features.txt
+++ b/test/passes/remove-unused-names_merge-blocks_all-features.txt
@@ -694,12 +694,12 @@
(drop
(i32.const 50)
)
- (call_indirect (type $i32_i32_=>_none)
+ (call_indirect $0 (type $i32_i32_=>_none)
(i32.const 20)
(i32.const 40)
(i32.const 60)
)
- (call_indirect (type $i32_i32_=>_none)
+ (call_indirect $0 (type $i32_i32_=>_none)
(unreachable)
(block (result i32)
(drop
@@ -717,7 +717,7 @@
(drop
(i32.const 31)
)
- (call_indirect (type $i32_i32_=>_none)
+ (call_indirect $0 (type $i32_i32_=>_none)
(i32.const 41)
(unreachable)
(block (result i32)
@@ -733,7 +733,7 @@
(drop
(i32.const 52)
)
- (call_indirect (type $i32_i32_=>_none)
+ (call_indirect $0 (type $i32_i32_=>_none)
(i32.const 42)
(i32.const 62)
(unreachable)
diff --git a/test/passes/remove-unused-nonfunction-module-elements_all-features.txt b/test/passes/remove-unused-nonfunction-module-elements_all-features.txt
index 5a5406f78..76d388b23 100644
--- a/test/passes/remove-unused-nonfunction-module-elements_all-features.txt
+++ b/test/passes/remove-unused-nonfunction-module-elements_all-features.txt
@@ -51,40 +51,40 @@
(call $remove3)
)
(func $other1 (param $0 i32)
- (call_indirect (type $none_=>_none)
+ (call_indirect $0 (type $none_=>_none)
(i32.const 0)
)
- (call_indirect (type $none_=>_none)
+ (call_indirect $0 (type $none_=>_none)
(i32.const 0)
)
- (call_indirect (type $none_=>_none)
+ (call_indirect $0 (type $none_=>_none)
(i32.const 0)
)
- (call_indirect (type $none_=>_none)
+ (call_indirect $0 (type $none_=>_none)
(i32.const 0)
)
- (call_indirect (type $i32_=>_none)
+ (call_indirect $0 (type $i32_=>_none)
(i32.const 0)
(i32.const 0)
)
- (call_indirect (type $i32_=>_none)
+ (call_indirect $0 (type $i32_=>_none)
(i32.const 0)
(i32.const 0)
)
(drop
- (call_indirect (type $i32_=>_i32)
+ (call_indirect $0 (type $i32_=>_i32)
(i32.const 0)
(i32.const 0)
)
)
(drop
- (call_indirect (type $i32_=>_i32)
+ (call_indirect $0 (type $i32_=>_i32)
(i32.const 0)
(i32.const 0)
)
)
(drop
- (call_indirect (type $i32_=>_i32)
+ (call_indirect $0 (type $i32_=>_i32)
(i32.const 0)
(i32.const 0)
)
@@ -102,7 +102,7 @@
(import "env" "memory" (memory $0 256))
(import "env" "table" (table $timport$0 1 funcref))
(export "mem" (memory $0))
- (export "tab" (table $0))
+ (export "tab" (table $timport$0))
)
(module
(type $none_=>_none (func))
@@ -125,7 +125,7 @@
(i32.const 0)
)
)
- (call_indirect (type $none_=>_none)
+ (call_indirect $timport$0 (type $none_=>_none)
(i32.const 0)
)
)
@@ -306,7 +306,7 @@
(f64.const 1)
(f64.const 1)
)
- (call_indirect (type $f64_=>_f64)
+ (call_indirect $0 (type $f64_=>_f64)
(f64.const 1)
(i32.const 0)
)
diff --git a/test/passes/too_much_for_liveness.bin.txt b/test/passes/too_much_for_liveness.bin.txt
index 1d0e2df2d..6a9afb9bd 100644
--- a/test/passes/too_much_for_liveness.bin.txt
+++ b/test/passes/too_much_for_liveness.bin.txt
@@ -4,6 +4,7 @@ total
[funcs] : 1
[globals] : 0
[imports] : 0
+ [tables] : 0
[total] : 4
[vars] : 65536
Block : 1
@@ -16,6 +17,7 @@ total
[funcs] : 1
[globals] : 0
[imports] : 0
+ [tables] : 0
[total] : 4
[vars] : 65536
Block : 1
diff --git a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
index 158c9b49a..36ed6b87e 100644
--- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
+++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
@@ -6,6 +6,7 @@ total
[imports] : 5
[memory-data] : 22
[table-data] : 1
+ [tables] : 1
[total] : 850
[vars] : 29
AtomicFence : 1
diff --git a/test/polymorphic_stack.wast.from-wast b/test/polymorphic_stack.wast.from-wast
index 18b991df3..be9cb82bd 100644
--- a/test/polymorphic_stack.wast.from-wast
+++ b/test/polymorphic_stack.wast.from-wast
@@ -36,7 +36,7 @@
)
(drop
(i64.eqz
- (call_indirect (type $i32_=>_i32)
+ (call_indirect $timport$0 (type $i32_=>_i32)
(unreachable)
(unreachable)
)
diff --git a/test/reference-types.wast.from-wast b/test/reference-types.wast.from-wast
index 3d8e70c23..29aab9bea 100644
--- a/test/reference-types.wast.from-wast
+++ b/test/reference-types.wast.from-wast
@@ -191,71 +191,71 @@
(call $take_anyref
(ref.func $foo)
)
- (call_indirect (type $externref_=>_none)
+ (call_indirect $0 (type $externref_=>_none)
(local.get $local_externref)
(i32.const 0)
)
- (call_indirect (type $externref_=>_none)
+ (call_indirect $0 (type $externref_=>_none)
(global.get $global_externref)
(i32.const 0)
)
- (call_indirect (type $externref_=>_none)
+ (call_indirect $0 (type $externref_=>_none)
(ref.null extern)
(i32.const 0)
)
- (call_indirect (type $funcref_=>_none)
+ (call_indirect $0 (type $funcref_=>_none)
(local.get $local_funcref)
(i32.const 1)
)
- (call_indirect (type $funcref_=>_none)
+ (call_indirect $0 (type $funcref_=>_none)
(global.get $global_funcref)
(i32.const 1)
)
- (call_indirect (type $funcref_=>_none)
+ (call_indirect $0 (type $funcref_=>_none)
(ref.null func)
(i32.const 1)
)
- (call_indirect (type $funcref_=>_none)
+ (call_indirect $0 (type $funcref_=>_none)
(ref.func $foo)
(i32.const 1)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(local.get $local_anyref)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(global.get $global_anyref)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(ref.null any)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(local.get $local_externref)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(global.get $global_externref)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(ref.null extern)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(local.get $local_funcref)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(global.get $global_funcref)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(ref.null func)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(ref.func $foo)
(i32.const 3)
)
diff --git a/test/reference-types.wast.fromBinary b/test/reference-types.wast.fromBinary
index ae54cfcd8..5e51309ec 100644
--- a/test/reference-types.wast.fromBinary
+++ b/test/reference-types.wast.fromBinary
@@ -191,71 +191,71 @@
(call $take_anyref
(ref.func $foo)
)
- (call_indirect (type $externref_=>_none)
+ (call_indirect $0 (type $externref_=>_none)
(local.get $local_funcref)
(i32.const 0)
)
- (call_indirect (type $externref_=>_none)
+ (call_indirect $0 (type $externref_=>_none)
(global.get $global_externref)
(i32.const 0)
)
- (call_indirect (type $externref_=>_none)
+ (call_indirect $0 (type $externref_=>_none)
(ref.null extern)
(i32.const 0)
)
- (call_indirect (type $funcref_=>_none)
+ (call_indirect $0 (type $funcref_=>_none)
(local.get $local_externref)
(i32.const 1)
)
- (call_indirect (type $funcref_=>_none)
+ (call_indirect $0 (type $funcref_=>_none)
(global.get $global_funcref)
(i32.const 1)
)
- (call_indirect (type $funcref_=>_none)
+ (call_indirect $0 (type $funcref_=>_none)
(ref.null func)
(i32.const 1)
)
- (call_indirect (type $funcref_=>_none)
+ (call_indirect $0 (type $funcref_=>_none)
(ref.func $foo)
(i32.const 1)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(local.get $local_anyref)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(global.get $global_anyref)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(ref.null any)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(local.get $local_funcref)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(global.get $global_externref)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(ref.null extern)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(local.get $local_externref)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(global.get $global_funcref)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(ref.null func)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(ref.func $foo)
(i32.const 3)
)
diff --git a/test/reference-types.wast.fromBinary.noDebugInfo b/test/reference-types.wast.fromBinary.noDebugInfo
index 08186f32b..23c2bb03b 100644
--- a/test/reference-types.wast.fromBinary.noDebugInfo
+++ b/test/reference-types.wast.fromBinary.noDebugInfo
@@ -191,71 +191,71 @@
(call $2
(ref.func $3)
)
- (call_indirect (type $externref_=>_none)
+ (call_indirect $0 (type $externref_=>_none)
(local.get $1)
(i32.const 0)
)
- (call_indirect (type $externref_=>_none)
+ (call_indirect $0 (type $externref_=>_none)
(global.get $global$0)
(i32.const 0)
)
- (call_indirect (type $externref_=>_none)
+ (call_indirect $0 (type $externref_=>_none)
(ref.null extern)
(i32.const 0)
)
- (call_indirect (type $funcref_=>_none)
+ (call_indirect $0 (type $funcref_=>_none)
(local.get $0)
(i32.const 1)
)
- (call_indirect (type $funcref_=>_none)
+ (call_indirect $0 (type $funcref_=>_none)
(global.get $global$1)
(i32.const 1)
)
- (call_indirect (type $funcref_=>_none)
+ (call_indirect $0 (type $funcref_=>_none)
(ref.null func)
(i32.const 1)
)
- (call_indirect (type $funcref_=>_none)
+ (call_indirect $0 (type $funcref_=>_none)
(ref.func $3)
(i32.const 1)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(local.get $2)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(global.get $global$3)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(ref.null any)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(local.get $1)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(global.get $global$0)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(ref.null extern)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(local.get $0)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(global.get $global$1)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(ref.null func)
(i32.const 3)
)
- (call_indirect (type $anyref_=>_none)
+ (call_indirect $0 (type $anyref_=>_none)
(ref.func $3)
(i32.const 3)
)
diff --git a/test/spec/old_import.wast b/test/spec/old_import.wast
index 36638a994..6c5e9dcdb 100644
--- a/test/spec/old_import.wast
+++ b/test/spec/old_import.wast
@@ -135,10 +135,6 @@
(module (import "" "" (table 10 funcref)) (table 10 funcref))
"multiple tables"
)
-(assert_invalid
- (module (table 10 funcref) (table 10 funcref))
- "multiple tables"
-)
(assert_unlinkable
(module (import "spectest" "unknown" (table 10 funcref)))
diff --git a/test/tail-call.wast.from-wast b/test/tail-call.wast.from-wast
index 2a08be62e..480f47a4f 100644
--- a/test/tail-call.wast.from-wast
+++ b/test/tail-call.wast.from-wast
@@ -6,7 +6,7 @@
(return_call $bar)
)
(func $bar
- (return_call_indirect (type $none_=>_none)
+ (return_call_indirect $0 (type $none_=>_none)
(i32.const 0)
)
)
diff --git a/test/tail-call.wast.fromBinary b/test/tail-call.wast.fromBinary
index 70438c907..58fad5d61 100644
--- a/test/tail-call.wast.fromBinary
+++ b/test/tail-call.wast.fromBinary
@@ -6,7 +6,7 @@
(return_call $bar)
)
(func $bar
- (return_call_indirect (type $none_=>_none)
+ (return_call_indirect $0 (type $none_=>_none)
(i32.const 0)
)
)
diff --git a/test/tail-call.wast.fromBinary.noDebugInfo b/test/tail-call.wast.fromBinary.noDebugInfo
index e7a1ab752..919626330 100644
--- a/test/tail-call.wast.fromBinary.noDebugInfo
+++ b/test/tail-call.wast.fromBinary.noDebugInfo
@@ -6,7 +6,7 @@
(return_call $1)
)
(func $1
- (return_call_indirect (type $none_=>_none)
+ (return_call_indirect $0 (type $none_=>_none)
(i32.const 0)
)
)