diff options
Diffstat (limited to 'src/wasm')
-rw-r--r-- | src/wasm/wasm-binary.cpp | 283 | ||||
-rw-r--r-- | src/wasm/wasm-emscripten.cpp | 6 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 161 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 5 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 48 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 22 |
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(); |