summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm')
-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
6 files changed, 380 insertions, 145 deletions
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();