From 7f4bc4cf0f1842366afe11266a4d8dcf666cd811 Mon Sep 17 00:00:00 2001 From: Abbas Mashayekh Date: Mon, 29 Mar 2021 12:40:09 +0430 Subject: [PATCH 1/8] [RT] Add type to tables and elem segments This will add support for other reference types to tables and elems. --- src/binaryen-c.cpp | 2 +- src/ir/element-utils.h | 5 +- src/ir/module-splitting.cpp | 19 +- src/ir/module-utils.h | 11 +- src/passes/Print.cpp | 7 +- src/shell-interface.h | 16 +- src/tools/fuzzing.h | 2 +- src/tools/wasm-ctor-eval.cpp | 3 +- src/wasm-builder.h | 7 +- src/wasm-interpreter.h | 3 +- src/wasm.h | 13 +- src/wasm/wasm-binary.cpp | 57 ++-- src/wasm/wasm-s-parser.cpp | 34 +-- src/wasm/wasm-validator.cpp | 83 +++++- test/multi-table.wast | 14 +- test/multi-table.wast.from-wast | 6 +- test/multi-table.wast.fromBinary | 6 +- test/multi-table.wast.fromBinary.noDebugInfo | 4 + test/spec/elem_reftypes.wast | 263 +++++++++++++++++++ test/spec/table.wast | 24 ++ 20 files changed, 498 insertions(+), 81 deletions(-) create mode 100644 test/spec/elem_reftypes.wast create mode 100644 test/spec/table.wast diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index aff4ed4fcd4..78fbce7d8b6 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -3378,7 +3378,7 @@ BinaryenTableRef BinaryenAddTable(BinaryenModuleRef module, const char* name, BinaryenIndex initial, BinaryenIndex maximum) { - auto table = Builder::makeTable(name, initial, maximum); + auto table = Builder::makeTable(name, Type::funcref, initial, maximum); table->hasExplicitName = true; return ((Module*)module)->addTable(std::move(table)); } diff --git a/src/ir/element-utils.h b/src/ir/element-utils.h index adfd9955f7b..1ec6c7e20c6 100644 --- a/src/ir/element-utils.h +++ b/src/ir/element-utils.h @@ -28,7 +28,10 @@ namespace ElementUtils { template inline void iterElementSegmentFunctionNames(ElementSegment* segment, T visitor) { - // TODO(reference-types): return early if segment type is non-funcref + if (!segment->type.isFunction()) { + return; + } + for (Index i = 0; i < segment->data.size(); i++) { if (auto* get = segment->data[i]->dynCast()) { visitor(get->func, i); diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index 5b86dd494ad..0634171cea1 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -157,11 +157,16 @@ void TableSlotManager::addSlot(Name func, Slot slot) { TableSlotManager::TableSlotManager(Module& module) : module(module) { // TODO: Reject or handle passive element segments - if (module.tables.empty()) { + auto it = std::find_if(module.tables.begin(), + module.tables.end(), + [&](std::unique_ptr& table) { + return table->type == Type::funcref; + }); + if (it == module.tables.end()) { return; } - activeTable = module.tables.front().get(); + activeTable = it->get(); ModuleUtils::iterTableSegments( module, activeTable->name, [&](ElementSegment* segment) { activeTableSegments.push_back(segment); @@ -172,6 +177,7 @@ TableSlotManager::TableSlotManager(Module& module) : module(module) { // append new items at constant offsets after all existing items at constant // offsets. if (activeTableSegments.size() == 1 && + activeTableSegments[0]->type == Type::funcref && !activeTableSegments[0]->offset->is()) { assert(activeTableSegments[0]->offset->is() && "Unexpected initializer instruction"); @@ -204,7 +210,8 @@ TableSlotManager::TableSlotManager(Module& module) : module(module) { } Table* TableSlotManager::makeTable() { - return module.addTable(Builder::makeTable(Name::fromInt(0))); + return module.addTable( + Builder::makeTable(Names::getValidTableName(module, Name::fromInt(0)))); } TableSlotManager::Slot TableSlotManager::getSlot(RefFunc* entry) { @@ -533,7 +540,7 @@ void ModuleSplitter::setupTablePatching() { auto offset = ExpressionManipulator::copy(primarySeg->offset, secondary); auto secondarySeg = std::make_unique( - secondaryTable->name, offset, secondaryElems); + secondaryTable->name, offset, secondaryTable->type, secondaryElems); secondarySeg->setName(primarySeg->name, primarySeg->hasExplicitName); secondary.addElementSegment(std::move(secondarySeg)); return; @@ -545,8 +552,8 @@ void ModuleSplitter::setupTablePatching() { std::vector currData; auto finishSegment = [&]() { auto* offset = Builder(secondary).makeConst(int32_t(currBase)); - auto secondarySeg = - std::make_unique(secondaryTable->name, offset, currData); + auto secondarySeg = std::make_unique( + secondaryTable->name, offset, secondaryTable->type, currData); secondarySeg->setName(Name::fromInt(secondary.elementSegments.size()), false); secondary.addElementSegment(std::move(secondarySeg)); diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h index 666718c923c..8d778bc3cb0 100644 --- a/src/ir/module-utils.h +++ b/src/ir/module-utils.h @@ -76,6 +76,7 @@ inline ElementSegment* copyElementSegment(const ElementSegment* segment, auto copy = [&](std::unique_ptr&& ret) { ret->name = segment->name; ret->hasExplicitName = segment->hasExplicitName; + ret->type = segment->type; ret->data.reserve(segment->data.size()); for (auto* item : segment->data) { ret->data.push_back(ExpressionManipulator::copy(item, out)); @@ -92,9 +93,11 @@ inline ElementSegment* copyElementSegment(const ElementSegment* segment, } } -inline Table* copyTable(Table* table, Module& out) { +inline Table* copyTable(const Table* table, Module& out) { auto ret = std::make_unique
(); ret->name = table->name; + ret->hasExplicitName = table->hasExplicitName; + ret->type = table->type; ret->module = table->module; ret->base = table->base; @@ -510,6 +513,12 @@ inline void collectHeapTypes(Module& wasm, for (auto& curr : wasm.events) { counts.note(curr->sig); } + for (auto& curr : wasm.tables) { + counts.maybeNote(curr->type); + } + for (auto& curr : wasm.elementSegments) { + counts.maybeNote(curr->type); + } // Collect info from functions in parallel. ModuleUtils::ParallelFunctionAnalysis analysis( diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index e3ea04b5367..6de9f915c30 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2674,7 +2674,8 @@ struct PrintSExpression : public UnifiedExpressionVisitor { if (curr->hasMax()) { o << ' ' << curr->max; } - o << " funcref)"; + o << ' '; + printType(o, curr->type, currModule) << ')'; } void visitTable(Table* curr) { if (curr->imported()) { @@ -2711,9 +2712,9 @@ struct PrintSExpression : public UnifiedExpressionVisitor { }); auto printElemType = [&]() { if (allElementsRefFunc) { - TypeNamePrinter(o, currModule).print(HeapType::func); + o << "func"; } else { - TypeNamePrinter(o, currModule).print(Type::funcref); + printType(o, curr->type, currModule); } }; diff --git a/src/shell-interface.h b/src/shell-interface.h index 0ba4946dd17..eb0e9f91ce8 100644 --- a/src/shell-interface.h +++ b/src/shell-interface.h @@ -143,6 +143,13 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { wasm.memory.initial = 1; wasm.memory.max = 2; } + + ModuleUtils::iterImportedTables(wasm, [&](Table* table) { + if (table->module == SPECTEST && table->base == TABLE) { + table->initial = 10; + table->max = 20; + } + }); } Literals callImport(Function* import, LiteralList& arguments) override { @@ -234,8 +241,13 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { memory.set>(addr, value); } - void tableStore(Name tableName, Address addr, Literal entry) override { - tables[tableName][addr] = entry; + void tableStore(Name tableName, Address addr, const Literal& entry) override { + auto& table = tables[tableName]; + if (addr >= table.size()) { + trap("out of bounds table access"); + } else { + table.emplace(table.begin() + addr, entry); + } } bool growMemory(Address /*oldSize*/, Address newSize) override { diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index e6a12543aa6..39072d4bc11 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -430,7 +430,7 @@ class TranslateToFuzzReader { // needed. if (wasm.tables.empty()) { auto table = builder.makeTable( - Names::getValidTableName(wasm, "fuzzing_table"), 0, 0); + Names::getValidTableName(wasm, "fuzzing_table"), Type::funcref, 0, 0); table->hasExplicitName = true; wasm.addTable(std::move(table)); } diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index 13a522135ae..6662dd1765d 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -301,7 +301,8 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { } // called during initialization, but we don't keep track of a table - void tableStore(Name tableName, Address addr, Literal value) override {} + void tableStore(Name tableName, Address addr, const Literal& value) override { + } bool growMemory(Address /*oldSize*/, Address newSize) override { throw FailToEvalException("grow memory"); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 134d42a78cb..2a725b7b6eb 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -79,10 +79,13 @@ class Builder { return func; } - static std::unique_ptr
- makeTable(Name name, Address initial = 0, Address max = Table::kMaxSize) { + static std::unique_ptr
makeTable(Name name, + Type type = Type::funcref, + Address initial = 0, + Address max = Table::kMaxSize) { auto table = std::make_unique
(); table->name = name; + table->type = type; table->initial = initial; table->max = max; diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 61d29ada168..631b8abe68a 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -2222,7 +2222,8 @@ template class ModuleInstanceBase { WASM_UNREACHABLE("unimp"); } - virtual void tableStore(Name tableName, Address addr, Literal entry) { + virtual void + tableStore(Name tableName, Address addr, const Literal& entry) { WASM_UNREACHABLE("unimp"); } }; diff --git a/src/wasm.h b/src/wasm.h index 101f45c8169..df8375219c7 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1722,13 +1722,17 @@ class ElementSegment : public Named { public: Name table; Expression* offset; + Type type = Type::funcref; std::vector data; ElementSegment() = default; - ElementSegment(Name table, Expression* offset) - : table(table), offset(offset) {} - ElementSegment(Name table, Expression* offset, std::vector& init) - : table(table), offset(offset) { + ElementSegment(Name table, Expression* offset, Type type = Type::funcref) + : table(table), offset(offset), type(type) {} + ElementSegment(Name table, + Expression* offset, + Type type, + std::vector& init) + : table(table), offset(offset), type(type) { data.swap(init); } }; @@ -1742,6 +1746,7 @@ class Table : public Importable { Address initial = 0; Address max = kMaxSize; + Type type = Type::funcref; bool hasMax() { return max != kUnlimitedSize; } void clear() { diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 5425c1551e4..40a77b2ffc3 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -291,7 +291,7 @@ void WasmBinaryWriter::writeImports() { BYN_TRACE("write one table\n"); writeImportHeader(table); o << U32LEB(int32_t(ExternalKind::Table)); - o << S32LEB(BinaryConsts::EncodedType::funcref); + writeType(table->type); writeResizableLimits(table->initial, table->max, table->hasMax(), @@ -543,7 +543,7 @@ void WasmBinaryWriter::writeTableDeclarations() { auto num = importInfo->getNumDefinedTables(); o << U32LEB(num); ModuleUtils::iterDefinedTables(*wasm, [&](Table* table) { - o << S32LEB(BinaryConsts::EncodedType::funcref); + writeType(table->type); writeResizableLimits(table->initial, table->max, table->hasMax(), @@ -604,8 +604,8 @@ void WasmBinaryWriter::writeElementSegments() { if (isPassive || hasTableIndex) { if (usesExpressions) { - // elemType funcref - writeType(Type::funcref); + // elemType + writeType(segment->type); } else { // elemKind funcref o << U32LEB(0); @@ -1994,11 +1994,7 @@ void WasmBinaryBuilder::readImports() { 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"); - } + table->type = getType(); bool is_shared; Type indexType; @@ -2765,11 +2761,11 @@ void WasmBinaryBuilder::readTableDeclarations() { auto numTables = getU32LEB(); for (size_t i = 0; i < numTables; i++) { - auto elemType = getS32LEB(); - if (elemType != BinaryConsts::EncodedType::funcref) { - throwError("Non-funcref tables not yet supported"); + auto elemType = getType(); + if (!elemType.isRef()) { + throwError("Table type must be a reference type"); } - auto table = Builder::makeTable(Name::fromInt(i)); + auto table = Builder::makeTable(Name::fromInt(i), elemType); bool is_shared; Type indexType; getResizableLimits( @@ -2811,38 +2807,35 @@ void WasmBinaryBuilder::readElementSegments() { continue; } + auto segment = std::make_unique(); + segment->setName(Name::fromInt(i), false); + if (!isPassive) { Index tableIdx = 0; if (hasTableIdx) { tableIdx = getU32LEB(); } - auto makeActiveElem = [&](Table* table) { - auto segment = - std::make_unique(table->name, readExpression()); - segment->setName(Name::fromInt(i), false); - elementSegments.push_back(std::move(segment)); - }; - + Table* table = nullptr; auto numTableImports = tableImports.size(); if (tableIdx < numTableImports) { - makeActiveElem(tableImports[tableIdx]); + table = tableImports[tableIdx]; } else if (tableIdx - numTableImports < tables.size()) { - makeActiveElem(tables[tableIdx - numTableImports].get()); - } else { + table = tables[tableIdx - numTableImports].get(); + } + if (!table) { throwError("Table index out of range."); } - } else { - auto segment = std::make_unique(); - segment->setName(Name::fromInt(i), false); - elementSegments.push_back(std::move(segment)); + + segment->table = table->name; + segment->offset = readExpression(); } if (isPassive || hasTableIdx) { if (usesExpressions) { - auto type = getType(); - if (type != Type::funcref) { - throwError("Only funcref elem kinds are valid."); + segment->type = getType(); + if (segment->type == Type::externref) { + throwError("externref is not a valid type for element segments"); } } else { auto elemKind = getU32LEB(); @@ -2852,7 +2845,7 @@ void WasmBinaryBuilder::readElementSegments() { } } - auto& segmentData = elementSegments.back()->data; + auto& segmentData = segment->data; auto size = getU32LEB(); if (usesExpressions) { for (Index j = 0; j < size; j++) { @@ -2869,6 +2862,8 @@ void WasmBinaryBuilder::readElementSegments() { segmentData.push_back(refFunc); } } + + elementSegments.push_back(std::move(segment)); } } diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index f3ab50f0c34..fca1b2ebfd8 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -3209,7 +3209,7 @@ void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) { table->module = inner[1]->str(); table->base = inner[2]->str(); i++; - } else { + } else if (!elementStartsWith(inner, REF)) { throw ParseException("invalid table", inner.line, inner.col); } } @@ -3220,15 +3220,13 @@ void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) { table->initial = atoi(s[i++]->c_str()); hasExplicitLimit = true; } - if (s[i]->isStr() && String::isNumber(s[i]->c_str())) { table->max = atoi(s[i++]->c_str()); } - if (!s[i]->isStr() || s[i]->str() != FUNCREF) { - throw ParseException("Expected funcref"); - } else { - i += 1; + table->type = elementToType(*s[i++]); + if (!table->type.isRef()) { + throw ParseException("Only reference types are valid for tables"); } if (i < s.size() && s[i]->isList()) { @@ -3254,14 +3252,16 @@ void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) { } // parses an elem segment -// elem ::= (elem (expr) vec(funcidx)) -// | (elem (offset (expr)) func vec(funcidx)) -// | (elem (table tableidx) (offset (expr)) func vec(funcidx)) -// | (elem func vec(funcidx)) -// | (elem declare func vec(funcidx)) +// elem ::= (elem (table tableidx)? (offset (expr)) reftype vec(item (expr))) +// | (elem reftype vec(item (expr))) +// | (elem declare reftype vec(item (expr))) // // abbreviation: // (offset (expr)) ≡ (expr) +// (item (expr)) ≡ (expr) +// ϵ ≡ (table 0) +// +// funcref vec(ref.func) ≡ func vec(funcidx) // (elem (expr) vec(funcidx)) ≡ (elem (table 0) (offset (expr)) func // vec(funcidx)) // @@ -3292,7 +3292,7 @@ void SExpressionWasmBuilder::parseElem(Element& s, Table* table) { auto segment = std::make_unique(); segment->setName(name, hasExplicitName); - if (s[i]->isList()) { + if (s[i]->isList() && !elementStartsWith(s[i], REF)) { // Optional (table ) if (elementStartsWith(s[i], TABLE)) { auto& inner = *s[i++]; @@ -3315,11 +3315,15 @@ void SExpressionWasmBuilder::parseElem(Element& s, Table* table) { } else if (s[i]->isStr() && s[i]->str() == FUNC) { usesExpressions = false; i += 1; - } else if (s[i]->isStr() && s[i]->str() == FUNCREF) { + } else { + segment->type = elementToType(*s[i]); usesExpressions = true; i += 1; - } else { - throw ParseException("expected func or funcref."); + + if (segment->type == Type::externref) { + throw ParseException( + "Invalid externref type for an element segment.", s.line, s.col); + } } } diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 8bc12cadf12..a5ec614c684 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -797,6 +797,9 @@ void FunctionValidator::visitCallIndirect(CallIndirect* curr) { if (curr->target->type != Type::unreachable) { auto* table = getModule()->getTableOrNull(curr->table); shouldBeTrue(!!table, curr, "call-indirect table must exist"); + shouldBeTrue(table->type.isFunction(), + curr, + "call-indirect table must be of function type."); } validateCallParamsAndResult(curr, curr->sig); @@ -2829,6 +2832,10 @@ static void validateTables(Module& module, ValidationInfo& info) { "--enable-reference-types)"); if (!module.tables.empty()) { auto& table = module.tables.front(); + info.shouldBeTrue(table->type == Type::funcref, + "table", + "Only funcref is valid for table type (when reference " + "types are disabled)"); for (auto& segment : module.elementSegments) { info.shouldBeTrue(segment->table == table->name, "elem", @@ -2845,23 +2852,73 @@ static void validateTables(Module& module, ValidationInfo& info) { } } + for (auto& table : module.tables) { + info.shouldBeTrue(table->initial <= table->max, + "table", + "size minimum must not be greater than maximum"); + info.shouldBeTrue( + table->type.isNullable(), + "table", + "Non-nullable reference types are not yet supported for tables"); + if (!module.features.hasGC()) { + info.shouldBeTrue(table->type.isFunction() || + table->type == Type::externref, + "table", + "Only function reference types or externref are valid " + "for table type (when GC is disabled)"); + } + if (!module.features.hasTypedFunctionReferences()) { + info.shouldBeTrue(table->type == Type::funcref || + table->type == Type::externref, + "table", + "Only funcref and externref are valid for table type " + "(when typed-function references are disabled)"); + } + } + for (auto& segment : module.elementSegments) { + // Since element segment items need to be constant expressions, that leaves + // us with ref.null, ref.func and global.get. The GC proposal adds rtt.canon + // and rtt.sub to the list, but Binaryen doesn't consider RTTs as reference- + // types yet. As a result, the only possible type for element segments will + // be function references. + info.shouldBeTrue(segment->type.isFunction(), + "elem", + "element segment type must be of function type."); + info.shouldBeTrue( + segment->type.isNullable(), + "elem", + "Non-nullable reference types are not yet supported for tables"); + if (segment->table.is()) { auto table = module.getTableOrNull(segment->table); - info.shouldBeTrue( - table != nullptr, "elem", "elem segment must have a valid table name"); + info.shouldBeTrue(table != nullptr, + "elem", + "element segment must have a valid table name"); info.shouldBeTrue(!!segment->offset, "elem", "table segment offset should have an offset"); info.shouldBeEqual(segment->offset->type, Type(Type::i32), segment->offset, - "elem segment offset should be i32"); + "element segment offset should be i32"); info.shouldBeTrue(checkSegmentOffset(segment->offset, segment->data.size(), table->initial * Table::kPageSize), segment->offset, "table segment offset should be reasonable"); + if (module.features.hasTypedFunctionReferences()) { + info.shouldBeTrue( + Type::isSubType(segment->type, table->type), + "elem", + "element segment type must be a subtype of the table type"); + } else { + info.shouldBeEqual( + segment->type, + table->type, + "elem", + "element segment type must be the same as the table type"); + } validator.validate(segment->offset); } else { info.shouldBeTrue(!segment->offset, @@ -2871,10 +2928,22 @@ static void validateTables(Module& module, ValidationInfo& info) { // Avoid double checking items if (module.features.hasReferenceTypes()) { for (auto* expr : segment->data) { - info.shouldBeTrue( - expr->is() || expr->is(), - expr, - "element segment items must be either ref.func or ref.null func."); + if (auto* globalExpr = expr->dynCast()) { + auto* global = module.getGlobal(globalExpr->name); + info.shouldBeFalse( + global->mutable_, expr, "expected a constant expression"); + } else { + info.shouldBeTrue(expr->is() || expr->is() || + expr->is(), + expr, + "element segment items must be one of global.get, " + "ref.func, ref.null func"); + } + info.shouldBeSubType(expr->type, + segment->type, + expr, + "element segment item expressions must return a " + "subtype of the segment type"); validator.validate(expr); } } diff --git a/test/multi-table.wast b/test/multi-table.wast index cbfa369e0c3..a98c9e4b04d 100644 --- a/test/multi-table.wast +++ b/test/multi-table.wast @@ -1,7 +1,13 @@ (module + (type $none_=>_none (func)) + (type $A (struct)) + (global $g1 (ref null $none_=>_none) (ref.func $f)) + (global $g2 i32 (i32.const 0)) + (import "a" "b" (table $t1 1 10 funcref)) (table $t2 3 3 funcref) (table $t3 4 4 funcref) + (table $textern 0 externref) ;; add to $t1 (elem (i32.const 0) $f) @@ -10,10 +16,12 @@ (elem (table $t2) (i32.const 0) func $f) (elem $activeNonZeroOffset (table $t2) (offset (i32.const 1)) func $f $g) - (elem $e3-1 (table $t3) (i32.const 0) funcref (ref.func $f) (ref.null func)) - (elem $e3-2 (table $t3) (offset (i32.const 2)) funcref (item ref.func $f) (item (ref.func $g))) + (elem $e3-1 (table $t3) (global.get $g2) funcref (ref.func $f) (ref.null func)) + (elem $e3-2 (table $t3) (offset (i32.const 2)) (ref null $none_=>_none) (item ref.func $f) (item (ref.func $g))) - (elem $passive funcref (item ref.func $f) (item (ref.func $g)) (ref.null func)) + (elem $passive-1 func $f $g) + (elem $passive-2 funcref (item ref.func $f) (item (ref.func $g)) (ref.null func)) + (elem $passive-3 (ref null $none_=>_none) (item ref.func $f) (item (ref.func $g)) (ref.null $none_=>_none) (global.get $g1)) (elem $empty func) (elem $declarative declare func $h) diff --git a/test/multi-table.wast.from-wast b/test/multi-table.wast.from-wast index 80c081354b2..b0dde54ea11 100644 --- a/test/multi-table.wast.from-wast +++ b/test/multi-table.wast.from-wast @@ -8,8 +8,12 @@ (table $t3 4 4 funcref) (elem $e3-1 (table $t3) (i32.const 0) funcref (ref.func $f) (ref.null func)) (elem $e3-2 (table $t3) (i32.const 2) func $f $g) - (elem $passive funcref (ref.func $f) (ref.func $g) (ref.null func)) + (table $textern 0 externref) + (elem $passive-1 func $f $g) + (elem $passive-2 funcref (ref.func $f) (ref.func $g) (ref.null func)) + (elem $passive-3 (ref null $none_=>_none) (ref.func $f) (ref.func $g) (ref.null $none_=>_none) (global.get $g1)) (elem declare func $h) + (global $g1 (ref null $none_=>_none) (ref.func $f)) (func $f (drop (ref.func $h) diff --git a/test/multi-table.wast.fromBinary b/test/multi-table.wast.fromBinary index 9918b6183ec..7b878af45d4 100644 --- a/test/multi-table.wast.fromBinary +++ b/test/multi-table.wast.fromBinary @@ -8,8 +8,12 @@ (table $t3 4 4 funcref) (elem $e3-1 (table $t3) (i32.const 0) funcref (ref.func $f) (ref.null func)) (elem $e3-2 (table $t3) (i32.const 2) func $f $g) - (elem $passive funcref (ref.func $f) (ref.func $g) (ref.null func)) + (table $textern 0 externref) + (elem $passive-1 func $f $g) + (elem $passive-2 funcref (ref.func $f) (ref.func $g) (ref.null func)) + (elem $passive-3 (ref null $none_=>_none) (ref.func $f) (ref.func $g) (ref.null $none_=>_none) (global.get $g1)) (elem declare func $h) + (global $g1 (ref null $none_=>_none) (ref.func $f)) (func $f (drop (ref.func $h) diff --git a/test/multi-table.wast.fromBinary.noDebugInfo b/test/multi-table.wast.fromBinary.noDebugInfo index 638ee732904..805fa7e2808 100644 --- a/test/multi-table.wast.fromBinary.noDebugInfo +++ b/test/multi-table.wast.fromBinary.noDebugInfo @@ -8,8 +8,12 @@ (table $1 4 4 funcref) (elem (table $1) (i32.const 0) funcref (ref.func $0) (ref.null func)) (elem (table $1) (i32.const 2) func $0 $1) + (table $2 0 externref) + (elem func $0 $1) (elem funcref (ref.func $0) (ref.func $1) (ref.null func)) + (elem (ref null $none_=>_none) (ref.func $0) (ref.func $1) (ref.null $none_=>_none) (global.get $global$0)) (elem declare func $2) + (global $global$0 (ref null $none_=>_none) (ref.func $0)) (func $0 (drop (ref.func $2) diff --git a/test/spec/elem_reftypes.wast b/test/spec/elem_reftypes.wast new file mode 100644 index 00000000000..92c96c6c0ff --- /dev/null +++ b/test/spec/elem_reftypes.wast @@ -0,0 +1,263 @@ +;; Test the element section + +;; Syntax +(module + (table $t 10 funcref) + (func $f) + (func $g) + + ;; Passive + (elem funcref) + (elem funcref (ref.func $f) (item ref.func $f) (item (ref.null func)) (ref.func $g)) + (elem func) + (elem func $f $f $g $g) + + (elem $p1 funcref) + (elem $p2 funcref (ref.func $f) (ref.func $f) (ref.null func) (ref.func $g)) + (elem $p3 func) + (elem $p4 func $f $f $g $g) + + ;; Active + (elem (table $t) (i32.const 0) funcref) + (elem (table $t) (i32.const 0) funcref (ref.func $f) (ref.null func)) + (elem (table $t) (i32.const 0) func) + (elem (table $t) (i32.const 0) func $f $g) + (elem (table $t) (offset (i32.const 0)) funcref) + (elem (table $t) (offset (i32.const 0)) func $f $g) + (elem (table 0) (i32.const 0) func) + (elem (table 0x0) (i32.const 0) func $f $f) + (elem (table 0x000) (offset (i32.const 0)) func) + (elem (table 0) (offset (i32.const 0)) func $f $f) + (elem (table $t) (i32.const 0) func) + (elem (table $t) (i32.const 0) func $f $f) + (elem (table $t) (offset (i32.const 0)) func) + (elem (table $t) (offset (i32.const 0)) func $f $f) + (elem (offset (i32.const 0))) + (elem (offset (i32.const 0)) funcref (ref.func $f) (ref.null func)) + (elem (offset (i32.const 0)) func $f $f) + (elem (offset (i32.const 0)) $f $f) + (elem (i32.const 0)) + (elem (i32.const 0) funcref (ref.func $f) (ref.null func)) + (elem (i32.const 0) func $f $f) + (elem (i32.const 0) $f $f) + + (elem $a1 (table $t) (i32.const 0) funcref) + (elem $a2 (table $t) (i32.const 0) funcref (ref.func $f) (ref.null func)) + (elem $a3 (table $t) (i32.const 0) func) + (elem $a4 (table $t) (i32.const 0) func $f $g) + (elem $a9 (table $t) (offset (i32.const 0)) funcref) + (elem $a10 (table $t) (offset (i32.const 0)) func $f $g) + (elem $a11 (table 0) (i32.const 0) func) + (elem $a12 (table 0x0) (i32.const 0) func $f $f) + (elem $a13 (table 0x000) (offset (i32.const 0)) func) + (elem $a14 (table 0) (offset (i32.const 0)) func $f $f) + (elem $a15 (table $t) (i32.const 0) func) + (elem $a16 (table $t) (i32.const 0) func $f $f) + (elem $a17 (table $t) (offset (i32.const 0)) func) + (elem $a18 (table $t) (offset (i32.const 0)) func $f $f) + (elem $a19 (offset (i32.const 0))) + (elem $a20 (offset (i32.const 0)) funcref (ref.func $f) (ref.null func)) + (elem $a21 (offset (i32.const 0)) func $f $f) + (elem $a22 (offset (i32.const 0)) $f $f) + (elem $a23 (i32.const 0)) + (elem $a24 (i32.const 0) funcref (ref.func $f) (ref.null func)) + (elem $a25 (i32.const 0) func $f $f) + (elem $a26 (i32.const 0) $f $f) + + ;; Declarative + (elem declare funcref) + (elem declare funcref (ref.func $f) (ref.func $f) (ref.null func) (ref.func $g)) + (elem declare func) + (elem declare func $f $f $g $g) + + (elem $d1 declare funcref) + (elem $d2 declare funcref (ref.func $f) (ref.func $f) (ref.null func) (ref.func $g)) + (elem $d3 declare func) + (elem $d4 declare func $f $f $g $g) +) + +(module + (func $f) + (func $g) + + (table $t1 funcref (elem (ref.func $f) (ref.null func) (ref.func $g))) + (table $t2 (ref null func) (elem (ref.func $f) (ref.null func) (ref.func $g))) +) + + +;; Basic use + +(module + (table 10 funcref) + (func $f) + (elem (i32.const 0) $f) +) +(module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 0) $f) +) + +(module + (table 10 funcref) + (func $f) + (elem (i32.const 0) $f) + (elem (i32.const 3) $f) + (elem (i32.const 7) $f) + (elem (i32.const 5) $f) + (elem (i32.const 3) $f) +) +(module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 9) $f) + (elem (i32.const 3) $f) + (elem (i32.const 7) $f) + (elem (i32.const 3) $f) + (elem (i32.const 5) $f) +) + +(module + (global (import "spectest" "global_i32") i32) + (table 1000 funcref) + (func $f) + (elem (global.get 0) $f) +) + +(module + (global $g (import "spectest" "global_i32") i32) + (table 1000 funcref) + (func $f) + (elem (global.get $g) $f) +) + +(module + (type $out-i32 (func (result i32))) + (table 10 funcref) + (elem (i32.const 7) $const-i32-a) + (elem (i32.const 9) $const-i32-b) + (func $const-i32-a (type $out-i32) (i32.const 65)) + (func $const-i32-b (type $out-i32) (i32.const 66)) + (func (export "call-7") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 7)) + ) + (func (export "call-9") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 9)) + ) +) +(assert_return (invoke "call-7") (i32.const 65)) +(assert_return (invoke "call-9") (i32.const 66)) + +;; Corner cases + +(module + (table 10 funcref) + (func $f) + (elem (i32.const 9) $f) +) +(module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 9) $f) +) + +(module + (table 0 funcref) + (elem (i32.const 0)) +) +(module + (import "spectest" "table" (table 0 funcref)) + (elem (i32.const 0)) +) + +(module + (table 0 0 funcref) + (elem (i32.const 0)) +) + +(module + (table 20 funcref) + (elem (i32.const 20)) +) + +;;; We cannot enable these yet since we check table bounds at validation stage, +;;; which is incorrect for imported tables. +;; (module +;; (import "spectest" "table" (table 0 funcref)) +;; (func $f) +;; (elem (i32.const 0) $f) +;; ) + +;; (module +;; (import "spectest" "table" (table 0 100 funcref)) +;; (func $f) +;; (elem (i32.const 0) $f) +;; ) + +;; (module +;; (import "spectest" "table" (table 0 funcref)) +;; (func $f) +;; (elem (i32.const 1) $f) +;; ) + +;; (module +;; (import "spectest" "table" (table 0 30 funcref)) +;; (func $f) +;; (elem (i32.const 1) $f) +;; ) + +;; Two elements target the same slot + +(module + (type $out-i32 (func (result i32))) + (table 10 funcref) + (elem (i32.const 9) $const-i32-a) + (elem (i32.const 9) $const-i32-b) + (func $const-i32-a (type $out-i32) (i32.const 65)) + (func $const-i32-b (type $out-i32) (i32.const 66)) + (func (export "call-overwritten") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 9)) + ) +) +(assert_return (invoke "call-overwritten") (i32.const 66)) + +(module + (type $out-i32 (func (result i32))) + (import "spectest" "table" (table 10 funcref)) + (elem (i32.const 9) $const-i32-a) + (elem (i32.const 9) $const-i32-b) + (func $const-i32-a (type $out-i32) (i32.const 65)) + (func $const-i32-b (type $out-i32) (i32.const 66)) + (func (export "call-overwritten-element") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 9)) + ) +) +(assert_return (invoke "call-overwritten-element") (i32.const 66)) + +;; Element sections across multiple modules change the same table + +(module $module1 + (type $out-i32 (func (result i32))) + (table (export "shared-table") 10 funcref) + (elem (i32.const 8) $const-i32-a) + (elem (i32.const 9) $const-i32-b) + (func $const-i32-a (type $out-i32) (i32.const 65)) + (func $const-i32-b (type $out-i32) (i32.const 66)) + (func (export "call-7") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 7)) + ) + (func (export "call-8") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 8)) + ) + (func (export "call-9") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 9)) + ) +) + +(assert_invalid + (module + (type $none_=>_none (func)) + (table 0 (ref null $none_=>_none)) + (elem (i32.const 0) funcref) + ) +) \ No newline at end of file diff --git a/test/spec/table.wast b/test/spec/table.wast new file mode 100644 index 00000000000..54281a03eb0 --- /dev/null +++ b/test/spec/table.wast @@ -0,0 +1,24 @@ + +;; Test table section structure + +(module (table 0 funcref)) +(module (table 0 (ref null func))) +(module (table 1 funcref)) +(module (table 0 0 funcref)) +(module (table 0 1 funcref)) +(module (table 0 1 (ref null func))) +(module (table 1 256 funcref)) +(module (table 0 65536 externref)) +;; (module (table 0 0xffff_ffff funcref)) + +(module (table 0 funcref) (table 0 funcref)) +(module (table (import "spectest" "table") 0 funcref) (table 0 funcref)) + +(assert_invalid (module (elem (i32.const 0))) "unknown table") +(assert_invalid (module (elem (i32.const 0) $f) (func $f)) "unknown table") + + +(assert_invalid + (module (table 1 0 funcref)) + "size minimum must not be greater than maximum" +) From bde8f9443eb7cdd965e9d82e60c99c6e127653b2 Mon Sep 17 00:00:00 2001 From: Abbas Mashayekh Date: Fri, 2 Apr 2021 22:03:11 +0430 Subject: [PATCH 2/8] update tests --- test/multi-table.wast.from-wast | 5 +++-- test/multi-table.wast.fromBinary | 5 +++-- test/multi-table.wast.fromBinary.noDebugInfo | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/test/multi-table.wast.from-wast b/test/multi-table.wast.from-wast index b0dde54ea11..34900e54c13 100644 --- a/test/multi-table.wast.from-wast +++ b/test/multi-table.wast.from-wast @@ -2,18 +2,19 @@ (type $none_=>_none (func)) (import "a" "b" (table $t1 1 10 funcref)) (elem (table $t1) (i32.const 0) func $f) + (global $g1 (ref null $none_=>_none) (ref.func $f)) + (global $g2 i32 (i32.const 0)) (table $t2 3 3 funcref) (elem (table $t2) (i32.const 0) func $f) (elem $activeNonZeroOffset (table $t2) (i32.const 1) func $f $g) (table $t3 4 4 funcref) - (elem $e3-1 (table $t3) (i32.const 0) funcref (ref.func $f) (ref.null func)) + (elem $e3-1 (table $t3) (global.get $g2) funcref (ref.func $f) (ref.null func)) (elem $e3-2 (table $t3) (i32.const 2) func $f $g) (table $textern 0 externref) (elem $passive-1 func $f $g) (elem $passive-2 funcref (ref.func $f) (ref.func $g) (ref.null func)) (elem $passive-3 (ref null $none_=>_none) (ref.func $f) (ref.func $g) (ref.null $none_=>_none) (global.get $g1)) (elem declare func $h) - (global $g1 (ref null $none_=>_none) (ref.func $f)) (func $f (drop (ref.func $h) diff --git a/test/multi-table.wast.fromBinary b/test/multi-table.wast.fromBinary index 7b878af45d4..db652b8db6a 100644 --- a/test/multi-table.wast.fromBinary +++ b/test/multi-table.wast.fromBinary @@ -2,18 +2,19 @@ (type $none_=>_none (func)) (import "a" "b" (table $t1 1 10 funcref)) (elem (table $t1) (i32.const 0) func $f) + (global $g1 (ref null $none_=>_none) (ref.func $f)) + (global $g2 i32 (i32.const 0)) (table $t2 3 3 funcref) (elem (table $t2) (i32.const 0) func $f) (elem $activeNonZeroOffset (table $t2) (i32.const 1) func $f $g) (table $t3 4 4 funcref) - (elem $e3-1 (table $t3) (i32.const 0) funcref (ref.func $f) (ref.null func)) + (elem $e3-1 (table $t3) (global.get $g2) funcref (ref.func $f) (ref.null func)) (elem $e3-2 (table $t3) (i32.const 2) func $f $g) (table $textern 0 externref) (elem $passive-1 func $f $g) (elem $passive-2 funcref (ref.func $f) (ref.func $g) (ref.null func)) (elem $passive-3 (ref null $none_=>_none) (ref.func $f) (ref.func $g) (ref.null $none_=>_none) (global.get $g1)) (elem declare func $h) - (global $g1 (ref null $none_=>_none) (ref.func $f)) (func $f (drop (ref.func $h) diff --git a/test/multi-table.wast.fromBinary.noDebugInfo b/test/multi-table.wast.fromBinary.noDebugInfo index 805fa7e2808..4125f5ee875 100644 --- a/test/multi-table.wast.fromBinary.noDebugInfo +++ b/test/multi-table.wast.fromBinary.noDebugInfo @@ -2,18 +2,19 @@ (type $none_=>_none (func)) (import "a" "b" (table $timport$0 1 10 funcref)) (elem (table $timport$0) (i32.const 0) func $0) + (global $global$0 (ref null $none_=>_none) (ref.func $0)) + (global $global$1 i32 (i32.const 0)) (table $0 3 3 funcref) (elem (table $0) (i32.const 0) func $0) (elem (table $0) (i32.const 1) func $0 $1) (table $1 4 4 funcref) - (elem (table $1) (i32.const 0) funcref (ref.func $0) (ref.null func)) + (elem (table $1) (global.get $global$1) funcref (ref.func $0) (ref.null func)) (elem (table $1) (i32.const 2) func $0 $1) (table $2 0 externref) (elem func $0 $1) (elem funcref (ref.func $0) (ref.func $1) (ref.null func)) (elem (ref null $none_=>_none) (ref.func $0) (ref.func $1) (ref.null $none_=>_none) (global.get $global$0)) (elem declare func $2) - (global $global$0 (ref null $none_=>_none) (ref.func $0)) (func $0 (drop (ref.func $2) From fb2abdaf4590125e2e3220303488ec3ace513618 Mon Sep 17 00:00:00 2001 From: Abbas Mashayekh Date: Sat, 3 Apr 2021 00:14:54 +0430 Subject: [PATCH 3/8] a little bit of polish --- src/wasm/wasm-binary.cpp | 4 ++-- src/wasm/wasm-s-parser.cpp | 4 ++-- test/spec/elem_reftypes.wast | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 40a77b2ffc3..918dc9ad919 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -2834,8 +2834,8 @@ void WasmBinaryBuilder::readElementSegments() { if (isPassive || hasTableIdx) { if (usesExpressions) { segment->type = getType(); - if (segment->type == Type::externref) { - throwError("externref is not a valid type for element segments"); + if (!segment->type.isFunction()) { + throwError("Invalid type for an element segment"); } } else { auto elemKind = getU32LEB(); diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index fca1b2ebfd8..5ae07fddef3 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -3320,9 +3320,9 @@ void SExpressionWasmBuilder::parseElem(Element& s, Table* table) { usesExpressions = true; i += 1; - if (segment->type == Type::externref) { + if (!segment->type.isFunction()) { throw ParseException( - "Invalid externref type for an element segment.", s.line, s.col); + "Invalid type for an element segment.", s.line, s.col); } } } diff --git a/test/spec/elem_reftypes.wast b/test/spec/elem_reftypes.wast index 92c96c6c0ff..9efc4d59f4e 100644 --- a/test/spec/elem_reftypes.wast +++ b/test/spec/elem_reftypes.wast @@ -25,8 +25,8 @@ (elem (table $t) (offset (i32.const 0)) funcref) (elem (table $t) (offset (i32.const 0)) func $f $g) (elem (table 0) (i32.const 0) func) - (elem (table 0x0) (i32.const 0) func $f $f) - (elem (table 0x000) (offset (i32.const 0)) func) + ;; (elem (table 0x0) (i32.const 0) func $f $f) + ;; (elem (table 0x000) (offset (i32.const 0)) func) (elem (table 0) (offset (i32.const 0)) func $f $f) (elem (table $t) (i32.const 0) func) (elem (table $t) (i32.const 0) func $f $f) @@ -48,8 +48,8 @@ (elem $a9 (table $t) (offset (i32.const 0)) funcref) (elem $a10 (table $t) (offset (i32.const 0)) func $f $g) (elem $a11 (table 0) (i32.const 0) func) - (elem $a12 (table 0x0) (i32.const 0) func $f $f) - (elem $a13 (table 0x000) (offset (i32.const 0)) func) + ;; (elem $a12 (table 0x0) (i32.const 0) func $f $f) + ;; (elem $a13 (table 0x000) (offset (i32.const 0)) func) (elem $a14 (table 0) (offset (i32.const 0)) func $f $f) (elem $a15 (table $t) (i32.const 0) func) (elem $a16 (table $t) (i32.const 0) func $f $f) From a5da3d468edd10129e3b4dbfd0d2e51ba4f96cf2 Mon Sep 17 00:00:00 2001 From: Abbas Mashayekh Date: Sat, 3 Apr 2021 01:04:21 +0430 Subject: [PATCH 4/8] wasm2js: error out if there's a non-function table --- src/wasm2js.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wasm2js.h b/src/wasm2js.h index 6d3f0682ce3..8c38db7e8df 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -630,6 +630,10 @@ void Wasm2JSBuilder::addTable(Ref ast, Module* wasm) { // emit assignments separately for each index. Ref theArray = ValueBuilder::makeArray(); for (auto& table : wasm->tables) { + if (!table->type.isFunction()) { + Fatal() << "wasm2js doesn't support non-function tables\n"; + } + if (!table->imported()) { TableUtils::FlatTable flat(*wasm, *table); if (flat.valid) { From eb762586edf186c48f8826891cf41c7cb4384a6a Mon Sep 17 00:00:00 2001 From: Abbas Mashayekh Date: Sat, 3 Apr 2021 01:05:15 +0430 Subject: [PATCH 5/8] make FlatTable invalid if it's non-function type --- src/ir/table-utils.h | 2 +- src/passes/Print.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ir/table-utils.h b/src/ir/table-utils.h index 2d91f0035b2..d3151bd2cd2 100644 --- a/src/ir/table-utils.h +++ b/src/ir/table-utils.h @@ -36,7 +36,7 @@ struct FlatTable { ModuleUtils::iterTableSegments( wasm, table.name, [&](ElementSegment* segment) { auto offset = segment->offset; - if (!offset->is()) { + if (!offset->is() || !segment->type.isFunction()) { // TODO: handle some non-constant segments valid = false; return; diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 6de9f915c30..9d8e2886d73 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2727,7 +2727,6 @@ struct PrintSExpression : public UnifiedExpressionVisitor { } if (curr->table.is()) { - // TODO(reference-types): check for old-style based on the complete spec if (!allElementsRefFunc || currModule->tables.size() > 1) { // tableuse o << " (table "; From 2b07dc9918b56473195fa60f4f72644133574d00 Mon Sep 17 00:00:00 2001 From: Abbas Mashayekh Date: Tue, 6 Apr 2021 02:07:14 +0430 Subject: [PATCH 6/8] fix the fuzzer modules with more specific function type segments weren't handled correctly. We now ensure that a funcref element segments and and table exist in `setupTables`. Also when adding functions to a segment, first we find one that has a compatible type. --- src/tools/fuzzing.h | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 39072d4bc11..d8f2e10ba02 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -426,18 +426,31 @@ class TranslateToFuzzReader { // TODO(reference-types): allow the fuzzer to create multiple tables void setupTables() { - // Ensure an element segment, adding one or even adding a whole table as - // needed. - if (wasm.tables.empty()) { - auto table = builder.makeTable( + // Ensure a funcref element segment and table exist. Segments with more + // specific function types may have a smaller chance of getting functions. + Table* table = nullptr; + auto iter = + std::find_if(wasm.tables.begin(), wasm.tables.end(), [&](auto& table) { + return table->type == Type::funcref; + }); + if (iter != wasm.tables.end()) { + table = iter->get(); + } else { + auto tablePtr = builder.makeTable( Names::getValidTableName(wasm, "fuzzing_table"), Type::funcref, 0, 0); - table->hasExplicitName = true; - wasm.addTable(std::move(table)); - } - if (wasm.elementSegments.empty()) { + tablePtr->hasExplicitName = true; + table = wasm.addTable(std::move(tablePtr)); + } + bool hasFuncrefElemSegment = std::any_of( + wasm.elementSegments.begin(), + wasm.elementSegments.end(), + [&](auto& segment) { + return segment->table.is() && segment->type == Type::funcref; + }); + if (!hasFuncrefElemSegment) { // TODO: use a random table auto segment = std::make_unique( - wasm.tables[0]->name, builder.makeConst(int32_t(0))); + table->name, builder.makeConst(int32_t(0))); segment->setName(Names::getValidElementSegmentName(wasm, "elem$"), false); wasm.addElementSegment(std::move(segment)); } @@ -722,10 +735,16 @@ class TranslateToFuzzReader { } // add some to an elem segment while (oneIn(3) && !finishedInput) { - auto& randomElem = - wasm.elementSegments[upTo(wasm.elementSegments.size())]; - // FIXME: make the type NonNullable when we support it! auto type = Type(HeapType(func->sig), Nullable); + std::vector compatibleSegments; + ModuleUtils::iterActiveElementSegments( + wasm, [&](ElementSegment* segment) { + if (Type::isSubType(type, segment->type)) { + compatibleSegments.push_back(segment); + } + }); + auto& randomElem = compatibleSegments[upTo(compatibleSegments.size())]; + // FIXME: make the type NonNullable when we support it! randomElem->data.push_back(builder.makeRefFunc(func->name, type)); } numAddedFunctions++; From 8c3e93e3a2319f65f2e9a6bc5c653592bd678556 Mon Sep 17 00:00:00 2001 From: Abbas Mashayekh Date: Tue, 6 Apr 2021 02:15:30 +0430 Subject: [PATCH 7/8] trigger another build From 900b1768980180e9a7211e258d38ea8f90cd4283 Mon Sep 17 00:00:00 2001 From: Abbas Mashayekh Date: Tue, 6 Apr 2021 02:50:11 +0430 Subject: [PATCH 8/8] and another...