diff options
-rwxr-xr-x | scripts/gen-s-parser.py | 1 | ||||
-rw-r--r-- | src/gen-s-parser.inc | 10 | ||||
-rw-r--r-- | src/ir/ReFinalize.cpp | 1 | ||||
-rw-r--r-- | src/ir/cost.h | 3 | ||||
-rw-r--r-- | src/ir/effects.h | 4 | ||||
-rw-r--r-- | src/ir/possible-contents.cpp | 1 | ||||
-rw-r--r-- | src/passes/Print.cpp | 4 | ||||
-rw-r--r-- | src/wasm-binary.h | 2 | ||||
-rw-r--r-- | src/wasm-builder.h | 12 | ||||
-rw-r--r-- | src/wasm-delegations-fields.def | 9 | ||||
-rw-r--r-- | src/wasm-delegations.def | 1 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 37 | ||||
-rw-r--r-- | src/wasm-ir-builder.h | 1 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 1 | ||||
-rw-r--r-- | src/wasm.h | 14 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 20 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 12 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 5 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 19 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 9 | ||||
-rw-r--r-- | src/wasm/wat-parser.cpp | 6 | ||||
-rw-r--r-- | src/wasm2js.h | 4 | ||||
-rw-r--r-- | test/binaryen.js/exception-handling.js.txt | 8 | ||||
-rw-r--r-- | test/binaryen.js/kitchen-sink.js.txt | 68 | ||||
-rw-r--r-- | test/lit/table-operations.wast | 36 | ||||
-rw-r--r-- | test/spec/table_fill.wast | 199 |
26 files changed, 449 insertions, 38 deletions
diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py index 2fd663510..78637991b 100755 --- a/scripts/gen-s-parser.py +++ b/scripts/gen-s-parser.py @@ -547,6 +547,7 @@ instructions = [ ("table.set", "makeTableSet(s)"), ("table.size", "makeTableSize(s)"), ("table.grow", "makeTableGrow(s)"), + ("table.fill", "makeTableFill(s)"), # TODO: # table.init # table.fill diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index 506ec62ad..416349032 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -3357,6 +3357,9 @@ switch (buf[0]) { switch (buf[1]) { case 'a': { switch (buf[6]) { + case 'f': + if (op == "table.fill"sv) { return makeTableFill(s); } + goto parse_error; case 'g': { switch (buf[7]) { case 'e': @@ -9190,6 +9193,13 @@ switch (buf[0]) { switch (buf[1]) { case 'a': { switch (buf[6]) { + case 'f': + if (op == "table.fill"sv) { + auto ret = makeTableFill(ctx, pos); + CHECK_ERR(ret); + return *ret; + } + goto parse_error; case 'g': { switch (buf[7]) { case 'e': diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index b0dea7627..bfdcfe7a6 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -124,6 +124,7 @@ void ReFinalize::visitTableGet(TableGet* curr) { curr->finalize(); } void ReFinalize::visitTableSet(TableSet* curr) { curr->finalize(); } void ReFinalize::visitTableSize(TableSize* curr) { curr->finalize(); } void ReFinalize::visitTableGrow(TableGrow* curr) { curr->finalize(); } +void ReFinalize::visitTableFill(TableFill* curr) { curr->finalize(); } void ReFinalize::visitTry(Try* curr) { curr->finalize(); } void ReFinalize::visitThrow(Throw* curr) { curr->finalize(); } void ReFinalize::visitRethrow(Rethrow* curr) { curr->finalize(); } diff --git a/src/ir/cost.h b/src/ir/cost.h index e7287de77..73d027788 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -574,6 +574,9 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> { CostType visitTableGrow(TableGrow* curr) { return Unacceptable + visit(curr->value) + visit(curr->delta); } + CostType visitTableFill(TableFill* curr) { + return 6 + visit(curr->dest) + visit(curr->value) + visit(curr->size); + } CostType visitTry(Try* curr) { // We assume no exception will be thrown in most cases return visit(curr->body); diff --git a/src/ir/effects.h b/src/ir/effects.h index 171a17803..9bdf08c74 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -692,6 +692,10 @@ private: parent.readsTable = true; parent.writesTable = true; } + void visitTableFill(TableFill* curr) { + parent.writesTable = true; + parent.implicitTrap = true; + } void visitTry(Try* curr) { if (curr->delegateTarget.is()) { parent.delegateTargets.insert(curr->delegateTarget); diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index b23d8220a..27ba8c759 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -661,6 +661,7 @@ struct InfoCollector void visitTableSet(TableSet* curr) {} void visitTableSize(TableSize* curr) { addRoot(curr); } void visitTableGrow(TableGrow* curr) { addRoot(curr); } + void visitTableFill(TableFill* curr) { addRoot(curr); } void visitNop(Nop* curr) {} void visitUnreachable(Unreachable* curr) {} diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index c47f33944..a21fd79dc 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -1925,6 +1925,10 @@ struct PrintExpressionContents printMedium(o, "table.grow "); printName(curr->table, o); } + void visitTableFill(TableFill* curr) { + printMedium(o, "table.fill "); + printName(curr->table, o); + } void visitTry(Try* curr) { printMedium(o, "try"); if (curr->name.is()) { diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 8c8ae70e6..72b61223f 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1129,6 +1129,7 @@ enum ASTNodes { TableGrow = 0x0f, TableSize = 0x10, + TableFill = 0x11, RefNull = 0xd0, RefIsNull = 0xd1, RefFunc = 0xd2, @@ -1838,6 +1839,7 @@ public: bool maybeVisitMemoryFill(Expression*& out, uint32_t code); bool maybeVisitTableSize(Expression*& out, uint32_t code); bool maybeVisitTableGrow(Expression*& out, uint32_t code); + bool maybeVisitTableFill(Expression*& out, uint32_t code); bool maybeVisitRefI31(Expression*& out, uint32_t code); bool maybeVisitI31Get(Expression*& out, uint32_t code); bool maybeVisitRefTest(Expression*& out, uint32_t code); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index b9db66983..9a59b4015 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -759,6 +759,18 @@ public: ret->finalize(); return ret; } + TableFill* makeTableFill(Name table, + Expression* dest, + Expression* value, + Expression* size) { + auto* ret = wasm.allocator.alloc<TableFill>(); + ret->table = table; + ret->dest = dest; + ret->value = value; + ret->size = size; + ret->finalize(); + return ret; + } private: Try* makeTry(Name name, diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index 43cb28325..3ab112bdd 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -567,6 +567,15 @@ switch (DELEGATE_ID) { DELEGATE_END(TableGrow); break; } + case Expression::Id::TableFillId: { + DELEGATE_START(TableFill); + DELEGATE_FIELD_CHILD(TableFill, size); + DELEGATE_FIELD_CHILD(TableFill, value); + DELEGATE_FIELD_CHILD(TableFill, dest); + DELEGATE_FIELD_NAME_KIND(TableFill, table, ModuleItemKind::Table); + DELEGATE_END(TableFill); + break; + } case Expression::Id::TryId: { DELEGATE_START(Try); DELEGATE_FIELD_SCOPE_NAME_USE(Try, delegateTarget); diff --git a/src/wasm-delegations.def b/src/wasm-delegations.def index e6aa865ec..a9c9c3f75 100644 --- a/src/wasm-delegations.def +++ b/src/wasm-delegations.def @@ -62,6 +62,7 @@ DELEGATE(TableGet); DELEGATE(TableSet); DELEGATE(TableSize); DELEGATE(TableGrow); +DELEGATE(TableFill); DELEGATE(Try); DELEGATE(Throw); DELEGATE(Rethrow); diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 1d8c60883..3b451a787 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1391,6 +1391,7 @@ public: Flow visitTableSet(TableSet* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTableSize(TableSize* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTableGrow(TableGrow* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitTableFill(TableFill* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTry(Try* curr) { WASM_UNREACHABLE("unimp"); } Flow visitThrow(Throw* curr) { NOTE_ENTER("Throw"); @@ -2205,6 +2206,10 @@ public: NOTE_ENTER("TableGrow"); return Flow(NONCONSTANT_FLOW); } + Flow visitTableFill(TableFill* curr) { + NOTE_ENTER("TableFill"); + return Flow(NONCONSTANT_FLOW); + } Flow visitLoad(Load* curr) { NOTE_ENTER("Load"); return Flow(NONCONSTANT_FLOW); @@ -2989,6 +2994,38 @@ public: return ret; } + Flow visitTableFill(TableFill* curr) { + NOTE_ENTER("TableFill"); + Flow destFlow = self()->visit(curr->dest); + if (destFlow.breaking()) { + return destFlow; + } + Flow valueFlow = self()->visit(curr->value); + if (valueFlow.breaking()) { + return valueFlow; + } + Flow sizeFlow = self()->visit(curr->size); + if (sizeFlow.breaking()) { + return sizeFlow; + } + Name tableName = curr->table; + auto info = getTableInterfaceInfo(tableName); + + Index dest = destFlow.getSingleValue().geti32(); + Literal value = valueFlow.getSingleValue(); + Index size = sizeFlow.getSingleValue().geti32(); + + Index tableSize = info.interface->tableSize(tableName); + if (dest + size > tableSize) { + trap("out of bounds table access"); + } + + for (Index i = 0; i < size; ++i) { + info.interface->tableStore(info.name, dest + i, value); + } + return Flow(); + } + Flow visitLocalGet(LocalGet* curr) { NOTE_ENTER("LocalGet"); auto index = curr->index; diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h index 6c85a2f37..50c034897 100644 --- a/src/wasm-ir-builder.h +++ b/src/wasm-ir-builder.h @@ -125,6 +125,7 @@ public: // [[nodiscard]] Result<> makeTableSet(); // [[nodiscard]] Result<> makeTableSize(); // [[nodiscard]] Result<> makeTableGrow(); + // [[nodiscard]] Result<> makeTableFill(); // [[nodiscard]] Result<> makeTry(); // [[nodiscard]] Result<> makeThrow(); // [[nodiscard]] Result<> makeRethrow(); diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index ad2d83e64..4c49a7773 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -278,6 +278,7 @@ private: Expression* makeTableSet(Element& s); Expression* makeTableSize(Element& s); Expression* makeTableGrow(Element& s); + Expression* makeTableFill(Element& s); Expression* makeTry(Element& s); Expression* makeTryOrCatchBody(Element& s, Type type, bool isTry); Expression* makeThrow(Element& s); diff --git a/src/wasm.h b/src/wasm.h index ab40d5330..1792b1538 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -698,6 +698,7 @@ public: TableSetId, TableSizeId, TableGrowId, + TableFillId, TryId, ThrowId, RethrowId, @@ -1421,6 +1422,19 @@ public: void finalize(); }; +class TableFill : public SpecificExpression<Expression::TableFillId> { +public: + TableFill() = default; + TableFill(MixedArena& allocator) : TableFill() {} + + Name table; + Expression* dest; + Expression* value; + Expression* size; + + void finalize(); +}; + class Try : public SpecificExpression<Expression::TryId> { public: Try(MixedArena& allocator) : catchTags(allocator), catchBodies(allocator) {} diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 969c15aaa..0c78442a7 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -4012,6 +4012,9 @@ BinaryConsts::ASTNodes WasmBinaryReader::readExpression(Expression*& curr) { if (maybeVisitTableGrow(curr, opcode)) { break; } + if (maybeVisitTableFill(curr, opcode)) { + break; + } throwError("invalid code after misc prefix: " + std::to_string(opcode)); break; } @@ -5369,6 +5372,23 @@ bool WasmBinaryReader::maybeVisitTableGrow(Expression*& out, uint32_t code) { return true; } +bool WasmBinaryReader::maybeVisitTableFill(Expression*& out, uint32_t code) { + if (code != BinaryConsts::TableFill) { + return false; + } + Index tableIdx = getU32LEB(); + if (tableIdx >= wasm.tables.size()) { + throwError("bad table index"); + } + auto* size = popNonVoidExpression(); + auto* value = popNonVoidExpression(); + auto* dest = popNonVoidExpression(); + auto* ret = Builder(wasm).makeTableFill(Name(), dest, value, size); + tableRefs[tableIdx].push_back(&ret->table); + out = ret; + return true; +} + bool WasmBinaryReader::maybeVisitBinary(Expression*& out, uint8_t code) { Binary* curr; #define INT_TYPED_CODE(code) \ diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 7da6ae0a5..dec9249c3 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -2641,6 +2641,18 @@ Expression* SExpressionWasmBuilder::makeTableGrow(Element& s) { return Builder(wasm).makeTableGrow(tableName, value, delta); } +Expression* SExpressionWasmBuilder::makeTableFill(Element& s) { + auto tableName = s[1]->str(); + auto* table = wasm.getTableOrNull(tableName); + if (!table) { + throw ParseException("invalid table name in table.fill", s.line, s.col); + } + auto* dest = parseExpression(s[2]); + auto* value = parseExpression(s[3]); + auto* size = parseExpression(s[4]); + return Builder(wasm).makeTableFill(tableName, dest, value, size); +} + // try can be either in the form of try-catch or try-delegate. // try-catch is written in the folded wast format as // (try diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 1ddf69d41..2042189fd 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -1909,6 +1909,11 @@ void BinaryInstWriter::visitTableGrow(TableGrow* curr) { o << U32LEB(parent.getTableIndex(curr->table)); } +void BinaryInstWriter::visitTableFill(TableFill* curr) { + o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::TableFill); + o << U32LEB(parent.getTableIndex(curr->table)); +} + void BinaryInstWriter::visitTry(Try* curr) { breakStack.push_back(curr->name); o << int8_t(BinaryConsts::Try); diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index c1836a7ff..41ddd00f5 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -440,6 +440,7 @@ public: void visitTableSet(TableSet* curr); void visitTableSize(TableSize* curr); void visitTableGrow(TableGrow* curr); + void visitTableFill(TableFill* curr); void noteDelegate(Name name, Expression* curr); void noteRethrow(Name name, Expression* curr); void visitTry(Try* curr); @@ -2294,6 +2295,24 @@ void FunctionValidator::visitTableGrow(TableGrow* curr) { } } +void FunctionValidator::visitTableFill(TableFill* curr) { + shouldBeTrue( + getModule()->features.hasBulkMemory(), + curr, + "table.fill requires reference types [--enable-reference-types]"); + auto* table = getModule()->getTableOrNull(curr->table); + if (shouldBeTrue(!!table, curr, "table.fill table must exist")) { + shouldBeSubType(curr->value->type, + table->type, + curr, + "table.fill value must have right type"); + } + shouldBeEqualOrFirstIsUnreachable( + curr->dest->type, Type(Type::i32), curr, "table.fill dest must be i32"); + shouldBeEqualOrFirstIsUnreachable( + curr->size->type, Type(Type::i32), curr, "table.fill size must be i32"); +} + void FunctionValidator::noteDelegate(Name name, Expression* curr) { if (name != DELEGATE_CALLER_TARGET) { shouldBeTrue(delegateTargetNames.count(name) != 0, diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index ca7e265f8..52a09e90c 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -855,6 +855,15 @@ void TableGrow::finalize() { } } +void TableFill::finalize() { + if (dest->type == Type::unreachable || value->type == Type::unreachable || + size->type == Type::unreachable) { + type = Type::unreachable; + } else { + type = Type::none; + } +} + void Try::finalize() { // If none of the component bodies' type is a supertype of the others, assume // the current type is already correct. TODO: Calculate a proper LUB. diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp index 6a18b2f87..b31019811 100644 --- a/src/wasm/wat-parser.cpp +++ b/src/wasm/wat-parser.cpp @@ -1936,6 +1936,7 @@ template<typename Ctx> Result<typename Ctx::InstrT> makeTableGet(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeTableSet(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeTableSize(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeTableGrow(Ctx&, Index); +template<typename Ctx> Result<typename Ctx::InstrT> makeTableFill(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeTry(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> @@ -3006,6 +3007,11 @@ Result<typename Ctx::InstrT> makeTableGrow(Ctx& ctx, Index pos) { } template<typename Ctx> +Result<typename Ctx::InstrT> makeTableFill(Ctx& ctx, Index pos) { + return ctx.in.err("unimplemented instruction"); +} + +template<typename Ctx> Result<typename Ctx::InstrT> makeTry(Ctx& ctx, Index pos) { return ctx.in.err("unimplemented instruction"); } diff --git a/src/wasm2js.h b/src/wasm2js.h index 2fbde5b65..e9090d756 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2251,6 +2251,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, unimplemented(curr); WASM_UNREACHABLE("unimp"); } + Ref visitTableFill(TableFill* curr) { + unimplemented(curr); + WASM_UNREACHABLE("unimp"); + } Ref visitTry(Try* curr) { unimplemented(curr); WASM_UNREACHABLE("unimp"); diff --git a/test/binaryen.js/exception-handling.js.txt b/test/binaryen.js/exception-handling.js.txt index 905e1a3a4..b1f6dab55 100644 --- a/test/binaryen.js/exception-handling.js.txt +++ b/test/binaryen.js/exception-handling.js.txt @@ -34,7 +34,7 @@ ) ) -getExpressionInfo(throw) = {"id":50,"type":1,"tag":"e"} -getExpressionInfo(rethrow) = {"id":51,"type":1,"target":"l0"} -getExpressionInfo(try_catch) = {"id":49,"type":1,"name":"l0","hasCatchAll":0,"delegateTarget":"","isDelegate":0} -getExpressionInfo(try_delegate) = {"id":49,"type":0,"name":"try_outer","hasCatchAll":1,"delegateTarget":"","isDelegate":0} +getExpressionInfo(throw) = {"id":51,"type":1,"tag":"e"} +getExpressionInfo(rethrow) = {"id":52,"type":1,"target":"l0"} +getExpressionInfo(try_catch) = {"id":50,"type":1,"name":"l0","hasCatchAll":0,"delegateTarget":"","isDelegate":0} +getExpressionInfo(try_delegate) = {"id":50,"type":0,"name":"try_outer","hasCatchAll":1,"delegateTarget":"","isDelegate":0} diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt index e8cb7e0ab..6ae736ff0 100644 --- a/test/binaryen.js/kitchen-sink.js.txt +++ b/test/binaryen.js/kitchen-sink.js.txt @@ -82,40 +82,40 @@ TableGetId: 45 TableSetId: 46 TableSizeId: 47 TableGrowId: 48 -TryId: 49 -ThrowId: 50 -RethrowId: 51 -TupleMakeId: 52 -TupleExtractId: 53 -RefI31Id: 54 -I31GetId: 55 -CallRefId: 56 -RefTestId: 57 -RefCastId: 58 -BrOnId: 59 -StructNewId: 60 -StructGetId: 61 -StructSetId: 62 -ArrayNewId: 63 -ArrayNewFixedId: 66 -ArrayGetId: 67 -ArraySetId: 68 -ArrayLenId: 69 -ArrayCopy: 70 -RefAs: 74 -StringNew: 75 -StringConst: 76 -StringMeasure: 77 -StringEncode: 78 -StringConcat: 79 -StringEq: 80 -StringAs: 81 -StringWTF8Advance: 82 -StringWTF16Get: 83 -StringIterNext: 84 -StringIterMove: 85 -StringSliceWTF: 86 -StringSliceIter: 87 +TryId: 50 +ThrowId: 51 +RethrowId: 52 +TupleMakeId: 53 +TupleExtractId: 54 +RefI31Id: 55 +I31GetId: 56 +CallRefId: 57 +RefTestId: 58 +RefCastId: 59 +BrOnId: 60 +StructNewId: 61 +StructGetId: 62 +StructSetId: 63 +ArrayNewId: 64 +ArrayNewFixedId: 67 +ArrayGetId: 68 +ArraySetId: 69 +ArrayLenId: 70 +ArrayCopy: 71 +RefAs: 75 +StringNew: 76 +StringConst: 77 +StringMeasure: 78 +StringEncode: 79 +StringConcat: 80 +StringEq: 81 +StringAs: 82 +StringWTF8Advance: 83 +StringWTF16Get: 84 +StringIterNext: 85 +StringIterMove: 86 +StringSliceWTF: 87 +StringSliceIter: 88 getExpressionInfo={"id":15,"type":4,"op":6} (f32.neg (f32.const -33.61199951171875) diff --git a/test/lit/table-operations.wast b/test/lit/table-operations.wast index 35e5ea284..e3daa463b 100644 --- a/test/lit/table-operations.wast +++ b/test/lit/table-operations.wast @@ -16,6 +16,8 @@ ;; CHECK-BINARY: (type $2 (func (param i32) (result i32))) + ;; CHECK-BINARY: (type $3 (func (param i32 funcref i32))) + ;; CHECK-BINARY: (table $table-1 1 1 funcref) ;; CHECK-TEXT: (type $0 (func)) @@ -23,6 +25,8 @@ ;; CHECK-TEXT: (type $2 (func (param i32) (result i32))) + ;; CHECK-TEXT: (type $3 (func (param i32 funcref i32))) + ;; CHECK-TEXT: (table $table-1 1 1 funcref) (table $table-1 funcref (elem $foo) @@ -147,6 +151,28 @@ (func $table-grow (param $sz i32) (result i32) (table.grow $table-1 (ref.null func) (local.get $sz)) ) + + ;; CHECK-BINARY: (func $table-fill (type $3) (param $dest i32) (param $value funcref) (param $size i32) + ;; CHECK-BINARY-NEXT: (table.fill $table-1 + ;; CHECK-BINARY-NEXT: (local.get $dest) + ;; CHECK-BINARY-NEXT: (local.get $value) + ;; CHECK-BINARY-NEXT: (local.get $size) + ;; CHECK-BINARY-NEXT: ) + ;; CHECK-BINARY-NEXT: ) + ;; CHECK-TEXT: (func $table-fill (type $3) (param $dest i32) (param $value funcref) (param $size i32) + ;; CHECK-TEXT-NEXT: (table.fill $table-1 + ;; CHECK-TEXT-NEXT: (local.get $dest) + ;; CHECK-TEXT-NEXT: (local.get $value) + ;; CHECK-TEXT-NEXT: (local.get $size) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + (func $table-fill (param $dest i32) (param $value funcref) (param $size i32) + (table.fill $table-1 + (local.get $dest) + (local.get $value) + (local.get $size) + ) + ) ) ;; CHECK-NODEBUG: (type $0 (func)) @@ -154,6 +180,8 @@ ;; CHECK-NODEBUG: (type $2 (func (param i32) (result i32))) +;; CHECK-NODEBUG: (type $3 (func (param i32 funcref i32))) + ;; CHECK-NODEBUG: (table $0 1 1 funcref) ;; CHECK-NODEBUG: (table $1 3 3 funcref) @@ -201,3 +229,11 @@ ;; CHECK-NODEBUG-NEXT: (local.get $0) ;; CHECK-NODEBUG-NEXT: ) ;; CHECK-NODEBUG-NEXT: ) + +;; CHECK-NODEBUG: (func $5 (type $3) (param $0 i32) (param $1 funcref) (param $2 i32) +;; CHECK-NODEBUG-NEXT: (table.fill $0 +;; CHECK-NODEBUG-NEXT: (local.get $0) +;; CHECK-NODEBUG-NEXT: (local.get $1) +;; CHECK-NODEBUG-NEXT: (local.get $2) +;; CHECK-NODEBUG-NEXT: ) +;; CHECK-NODEBUG-NEXT: ) diff --git a/test/spec/table_fill.wast b/test/spec/table_fill.wast new file mode 100644 index 000000000..79b5a6a83 --- /dev/null +++ b/test/spec/table_fill.wast @@ -0,0 +1,199 @@ +(module + (type $f (func (result i32))) + + (table $t 10 funcref) + + (func $0 (result i32) + (i32.const 0) + ) + + (func $1 (result i32) + (i32.const 1) + ) + + (func $2 (result i32) + (i32.const 2) + ) + + (func $3 (result i32) + (i32.const 3) + ) + + (func $4 (result i32) + (i32.const 4) + ) + + (func (export "fill-0") (param $i i32) (param $n i32) + (table.fill $t (local.get $i) (ref.func $0) (local.get $n)) + ) + + (func (export "fill-1") (param $i i32) (param $n i32) + (table.fill $t (local.get $i) (ref.func $1) (local.get $n)) + ) + + (func (export "fill-2") (param $i i32) (param $n i32) + (table.fill $t (local.get $i) (ref.func $2) (local.get $n)) + ) + + (func (export "fill-3") (param $i i32) (param $n i32) + (table.fill $t (local.get $i) (ref.func $3) (local.get $n)) + ) + + (func (export "fill-4") (param $i i32) (param $n i32) + (table.fill $t (local.get $i) (ref.func $4) (local.get $n)) + ) + + (func (export "fill-null") (param $i i32) (param $n i32) + (table.fill $t (local.get $i) (ref.null func) (local.get $n)) + ) + + (func (export "get-null") (param $i i32) (result funcref) + (table.get $t (local.get $i)) + ) + + (func (export "get") (param $i i32) (result i32) + (call_indirect $t (type $f) (local.get $i)) + ) +) + +(assert_return (invoke "get-null" (i32.const 1)) (ref.null func)) +(assert_return (invoke "get-null" (i32.const 2)) (ref.null func)) +(assert_return (invoke "get-null" (i32.const 3)) (ref.null func)) +(assert_return (invoke "get-null" (i32.const 4)) (ref.null func)) +(assert_return (invoke "get-null" (i32.const 5)) (ref.null func)) + +(assert_return (invoke "fill-1" (i32.const 2) (i32.const 3))) +(assert_return (invoke "get-null" (i32.const 1)) (ref.null func)) +(assert_return (invoke "get" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "get" (i32.const 4)) (i32.const 1)) +(assert_return (invoke "get-null" (i32.const 5)) (ref.null func)) + +(assert_return (invoke "fill-2" (i32.const 4) (i32.const 2))) +(assert_return (invoke "get" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "get" (i32.const 4)) (i32.const 2)) +(assert_return (invoke "get" (i32.const 5)) (i32.const 2)) +(assert_return (invoke "get-null" (i32.const 6)) (ref.null func)) + +(assert_return (invoke "fill-3" (i32.const 4) (i32.const 0))) +(assert_return (invoke "get" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "get" (i32.const 4)) (i32.const 2)) +(assert_return (invoke "get" (i32.const 5)) (i32.const 2)) + +(assert_return (invoke "fill-4" (i32.const 8) (i32.const 2))) +(assert_return (invoke "get-null" (i32.const 7)) (ref.null func)) +(assert_return (invoke "get" (i32.const 8)) (i32.const 4)) +(assert_return (invoke "get" (i32.const 9)) (i32.const 4)) + +(assert_return (invoke "fill-null" (i32.const 9) (i32.const 1))) +(assert_return (invoke "get" (i32.const 8)) (i32.const 4)) +(assert_return (invoke "get-null" (i32.const 9)) (ref.null func)) + +(assert_return (invoke "fill-1" (i32.const 10) (i32.const 0))) +(assert_return (invoke "get-null" (i32.const 9)) (ref.null func)) + +(assert_trap + (invoke "fill-2" (i32.const 8) (i32.const 3)) + "out of bounds table access" +) +(assert_return (invoke "get-null" (i32.const 7)) (ref.null func)) +(assert_return (invoke "get" (i32.const 8)) (i32.const 4)) +(assert_return (invoke "get-null" (i32.const 9)) (ref.null func)) + +(assert_trap + (invoke "fill" (i32.const 11) (ref.null extern) (i32.const 0)) + "out of bounds table access" +) + +(assert_trap + (invoke "fill" (i32.const 11) (ref.null extern) (i32.const 10)) + "out of bounds table access" +) + + +;; Type errors + +(assert_invalid + (module + (table $t 10 externref) + (func $type-index-value-length-empty-vs-i32-i32 + (table.fill $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-index-empty-vs-i32 + (table.fill $t (ref.null extern) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-value-empty-vs + (table.fill $t (i32.const 1) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-length-empty-vs-i32 + (table.fill $t (i32.const 1) (ref.null extern)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 externref) + (func $type-index-f32-vs-i32 + (table.fill $t (f32.const 1) (ref.null extern) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 funcref) + (func $type-value-vs-funcref (param $r externref) + (table.fill $t (i32.const 1) (local.get $r) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 externref) + (func $type-length-f32-vs-i32 + (table.fill $t (i32.const 1) (ref.null extern) (f32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t1 1 externref) + (table $t2 1 funcref) + (func $type-value-externref-vs-funcref-multi (param $r externref) + (table.fill $t2 (i32.const 0) (local.get $r) (i32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 1 externref) + (func $type-result-empty-vs-num (result i32) + (table.fill $t (i32.const 0) (ref.null extern) (i32.const 1)) + ) + ) + "type mismatch" +) |