diff options
author | Max Graey <maxgraey@gmail.com> | 2021-10-09 05:26:08 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-08 19:26:08 -0700 |
commit | ef686a4d932b9b86edc34a3b9b15926f943f6f7b (patch) | |
tree | 38a89d5b555d4501b7d504a66c7aec409e45e9b1 | |
parent | 53c5e3e62db25fe3522a1fa615a1f53c4cefdf06 (diff) | |
download | binaryen-ef686a4d932b9b86edc34a3b9b15926f943f6f7b.tar.gz binaryen-ef686a4d932b9b86edc34a3b9b15926f943f6f7b.tar.bz2 binaryen-ef686a4d932b9b86edc34a3b9b15926f943f6f7b.zip |
Add table.size operation (#4224)
31 files changed, 301 insertions, 32 deletions
diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py index b296d85ac..22c864cee 100755 --- a/scripts/gen-s-parser.py +++ b/scripts/gen-s-parser.py @@ -523,12 +523,12 @@ instructions = [ # table instructions ("table.get", "makeTableGet(s)"), ("table.set", "makeTableSet(s)"), + ("table.size", "makeTableSize(s)"), # TODO: # table.init # table.fill # table.copy # table.grow - # table.size # # exception handling instructions ("try", "makeTry(s)"), diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index ef6c3abdd..49f453715 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -1305,6 +1305,12 @@ BinaryenExpressionRef BinaryenTableSet(BinaryenModuleRef module, .makeTableSet(name, (Expression*)index, (Expression*)value)); } +BinaryenExpressionRef BinaryenTableSize(BinaryenModuleRef module, + const char* name) { + return static_cast<Expression*>( + Builder(*(Module*)module).makeTableSize(name)); +} + BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module, const char* name, BinaryenExpressionRef body, @@ -1950,6 +1956,18 @@ void BinaryenTableSetSetValue(BinaryenExpressionRef expr, assert(valueExpr); static_cast<TableSet*>(expression)->value = (Expression*)valueExpr; } +// TableSize +const char* BinaryenTableSizeGetTable(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is<TableSize>()); + return static_cast<TableSize*>(expression)->table.c_str(); +} +void BinaryenTableSetSizeTable(BinaryenExpressionRef expr, const char* table) { + auto* expression = (Expression*)expr; + assert(expression->is<TableSize>()); + assert(table); + static_cast<TableSize*>(expression)->table = table; +} // MemoryGrow BinaryenExpressionRef BinaryenMemoryGrowGetDelta(BinaryenExpressionRef expr) { auto* expression = (Expression*)expr; diff --git a/src/binaryen-c.h b/src/binaryen-c.h index d9e1afc8e..3d57aca75 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -860,6 +860,8 @@ BinaryenTableSet(BinaryenModuleRef module, const char* name, BinaryenExpressionRef index, BinaryenExpressionRef value); +BINARYEN_API BinaryenExpressionRef BinaryenTableSize(BinaryenModuleRef module, + const char* name); // Try: name can be NULL. delegateTarget should be NULL in try-catch. BINARYEN_API BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module, @@ -1238,6 +1240,13 @@ BinaryenTableSetGetValue(BinaryenExpressionRef expr); BINARYEN_API void BinaryenTableSetSetValue(BinaryenExpressionRef expr, BinaryenExpressionRef valueExpr); +// TableSize + +// Gets the name of the table being accessed by a `table.size` expression. +BINARYEN_API const char* BinaryenTableSizeGetTable(BinaryenExpressionRef expr); +// Sets the name of the table being accessed by a `table.size` expression. +BINARYEN_API void BinaryenTableSizeSetTable(BinaryenExpressionRef expr, + const char* table); // MemoryGrow // Gets the delta of a `memory.grow` expression. diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index cca1c3445..0d61244fd 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -3046,9 +3046,17 @@ switch (op[0]) { case 'g': if (strcmp(op, "table.get") == 0) { return makeTableGet(s); } goto parse_error; - case 's': - if (strcmp(op, "table.set") == 0) { return makeTableSet(s); } - goto parse_error; + case 's': { + switch (op[7]) { + case 'e': + if (strcmp(op, "table.set") == 0) { return makeTableSet(s); } + goto parse_error; + case 'i': + if (strcmp(op, "table.size") == 0) { return makeTableSize(s); } + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index 0d107a759..a9cf8050d 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -137,6 +137,7 @@ void ReFinalize::visitRefFunc(RefFunc* curr) { void ReFinalize::visitRefEq(RefEq* curr) { curr->finalize(); } void ReFinalize::visitTableGet(TableGet* curr) { curr->finalize(); } void ReFinalize::visitTableSet(TableSet* curr) { curr->finalize(); } +void ReFinalize::visitTableSize(TableSize* 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 7cf0de926..7c18a8200 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -544,6 +544,7 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> { CostType visitTableSet(TableSet* curr) { return 2 + visit(curr->index) + visit(curr->value); } + CostType visitTableSize(TableSize* curr) { return 1; } 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 44b2cba94..3ccffb6f5 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -526,6 +526,7 @@ private: parent.writesMemory = true; parent.implicitTrap = true; } + void visitTableSize(TableSize* curr) { parent.readsTable = true; } void visitConst(Const* curr) {} void visitUnary(Unary* curr) { switch (curr->op) { diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index 8fbd2b7ec..29729c4b1 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -93,6 +93,7 @@ function initializeConstants() { 'RefEq', 'TableGet', 'TableSet', + 'TableSize', 'Try', 'Throw', 'Rethrow', @@ -671,6 +672,9 @@ function wrapModule(module, self = {}) { }, 'set'(name, index, value) { return Module['_BinaryenTableSet'](module, strToStack(name), index, value); + }, + 'size'(name) { + return Module['_BinaryenTableSize'](module, strToStack(name)); } } @@ -2825,6 +2829,12 @@ Module['getExpressionInfo'] = function(expr) { 'index': Module['_BinaryenTableSetGetIndex'](expr), 'value': Module['_BinaryenTableSetGetValue'](expr) }; + case Module['TableSizeId']: + return { + 'id': id, + 'type': type, + 'table': UTF8ToString(Module['_BinaryenTableSizeGetTable'](expr)), + }; case Module['LoadId']: return { 'id': id, @@ -3831,6 +3841,15 @@ Module['TableSet'] = makeExpressionWrapper({ } }); +Module['TableSize'] = makeExpressionWrapper({ + 'getTable'(expr) { + return UTF8ToString(Module['_BinaryenTableSizeGetTable'](expr)); + }, + 'setTable'(expr, name) { + preserveStack(() => { Module['_BinaryenTableSizeSetTable'](expr, strToStack(name)) }); + }, +}); + Module['MemorySize'] = makeExpressionWrapper({}); Module['MemoryGrow'] = makeExpressionWrapper({ diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index bea704b1d..abea90117 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -1852,6 +1852,10 @@ struct PrintExpressionContents printMedium(o, "table.set "); printName(curr->table, o); } + void visitTableSize(TableSize* curr) { + printMedium(o, "table.size "); + printName(curr->table, o); + } void visitTry(Try* curr) { printMedium(o, "try"); if (curr->name.is()) { diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 32db977ec..0f2c69e02 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -130,6 +130,7 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> { } void visitTableGet(TableGet* curr) { maybeAddTable(curr->table); } void visitTableSet(TableSet* curr) { maybeAddTable(curr->table); } + void visitTableSize(TableSize* curr) { maybeAddTable(curr->table); } void visitThrow(Throw* curr) { maybeAdd(ModuleElement(ModuleElementKind::Tag, curr->tag)); } diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index a0ae99a8b..189ce9f71 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -351,6 +351,10 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { throw FailToEvalException("table.set: TODO"); } + Literal tableSize(Name tableName) { + throw FailToEvalException("table.size: TODO"); + } + int8_t load8s(Address addr) override { return doLoad<int8_t>(addr); } uint8_t load8u(Address addr) override { return doLoad<uint8_t>(addr); } int16_t load16s(Address addr) override { return doLoad<int16_t>(addr); } diff --git a/src/wasm-binary.h b/src/wasm-binary.h index b211c536c..ae2dd4656 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1025,6 +1025,7 @@ enum ASTNodes { // reference types opcodes + TableSize = 0x10, RefNull = 0xd0, RefIsNull = 0xd1, RefFunc = 0xd2, @@ -1635,6 +1636,7 @@ public: bool maybeVisitDataDrop(Expression*& out, uint32_t code); bool maybeVisitMemoryCopy(Expression*& out, uint32_t code); bool maybeVisitMemoryFill(Expression*& out, uint32_t code); + bool maybeVisitTableSize(Expression*& out, uint32_t code); bool maybeVisitI31New(Expression*& out, uint32_t code); bool maybeVisitI31Get(Expression*& out, uint32_t code); bool maybeVisitRefTest(Expression*& out, uint32_t code); @@ -1664,6 +1666,7 @@ public: void visitRefEq(RefEq* curr); void visitTableGet(TableGet* curr); void visitTableSet(TableSet* curr); + void visitTableSize(TableSize* curr); void visitTryOrTryInBlock(Expression*& out); void visitThrow(Throw* curr); void visitRethrow(Rethrow* curr); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 467edf1bd..87167877a 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -664,6 +664,12 @@ public: ret->finalize(); return ret; } + TableSize* makeTableSize(Name table) { + auto* ret = wasm.allocator.alloc<TableSize>(); + ret->table = table; + ret->finalize(); + return ret; + } private: Try* makeTry(Name name, diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index b6828fbc2..90ab03f49 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -516,6 +516,12 @@ switch (DELEGATE_ID) { DELEGATE_END(TableSet); break; } + case Expression::Id::TableSizeId: { + DELEGATE_START(TableSize); + DELEGATE_FIELD_NAME(TableSet, table); + DELEGATE_END(TableSize); + 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 c040dd551..8d162dd90 100644 --- a/src/wasm-delegations.def +++ b/src/wasm-delegations.def @@ -60,6 +60,7 @@ DELEGATE(RefFunc); DELEGATE(RefEq); DELEGATE(TableGet); DELEGATE(TableSet); +DELEGATE(TableSize); DELEGATE(Try); DELEGATE(Throw); DELEGATE(Rethrow); diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 2952869c6..2179c4923 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1360,6 +1360,7 @@ public: } Flow visitTableGet(TableGet* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTableSet(TableSet* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitTableSize(TableSize* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTry(Try* curr) { WASM_UNREACHABLE("unimp"); } Flow visitThrow(Throw* curr) { NOTE_ENTER("Throw"); @@ -2167,6 +2168,10 @@ public: NOTE_ENTER("TableSet"); return Flow(NONCONSTANT_FLOW); } + Flow visitTableSize(TableSize* curr) { + NOTE_ENTER("TableSize"); + return Flow(NONCONSTANT_FLOW); + } Flow visitLoad(Load* curr) { NOTE_ENTER("Load"); return Flow(NONCONSTANT_FLOW); @@ -2798,7 +2803,6 @@ private: return info.interface->tableLoad(info.name, index.getSingleValue().geti32()); } - Flow visitTableSet(TableSet* curr) { NOTE_ENTER("TableSet"); Flow index = this->visit(curr->index); @@ -2815,6 +2819,13 @@ private: return Flow(); } + Flow visitTableSize(TableSize* curr) { + NOTE_ENTER("TableSize"); + auto table = instance.wasm.getTable(curr->table); + // TODO: properly handle table size when TableGrow exists + return Literal::makeFromInt32(table->initial, Type::i32); + } + Flow visitLocalGet(LocalGet* curr) { NOTE_ENTER("LocalGet"); auto index = curr->index; diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index 942eae19b..f5be7924f 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -267,6 +267,7 @@ private: Expression* makeRefEq(Element& s); Expression* makeTableGet(Element& s); Expression* makeTableSet(Element& s); + Expression* makeTableSize(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 c87b8c197..197b8bd84 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -629,6 +629,7 @@ public: RefEqId, TableGetId, TableSetId, + TableSizeId, TryId, ThrowId, RethrowId, @@ -1301,6 +1302,16 @@ public: void finalize(); }; +class TableSize : public SpecificExpression<Expression::TableSizeId> { +public: + TableSize() { type = Type::i32; } + TableSize(MixedArena& allocator) : TableSize() {} + + Name table; + + 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 7e72b056f..9aa18f270 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -2793,6 +2793,8 @@ void WasmBinaryBuilder::processNames() { get->table = getTableName(index); } else if (auto* set = ref->dynCast<TableSet>()) { set->table = getTableName(index); + } else if (auto* size = ref->dynCast<TableSize>()) { + size->table = getTableName(index); } else { WASM_UNREACHABLE("Invalid type in table references"); } @@ -3613,8 +3615,10 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { if (maybeVisitMemoryFill(curr, opcode)) { break; } - throwError("invalid code after nontrapping float-to-int prefix: " + - std::to_string(opcode)); + if (maybeVisitTableSize(curr, opcode)) { + break; + } + throwError("invalid code after misc prefix: " + std::to_string(opcode)); break; } case BinaryConsts::SIMDPrefix: { @@ -4918,6 +4922,22 @@ bool WasmBinaryBuilder::maybeVisitMemoryFill(Expression*& out, uint32_t code) { return true; } +bool WasmBinaryBuilder::maybeVisitTableSize(Expression*& out, uint32_t code) { + if (code != BinaryConsts::TableSize) { + return false; + } + Index tableIdx = getU32LEB(); + if (tableIdx >= tables.size()) { + throwError("bad table index"); + } + auto* curr = allocator.alloc<TableSize>(); + curr->finalize(); + // Defer setting the table name for later, when we know it. + tableRefs[tableIdx].push_back(curr); + out = curr; + return true; +} + bool WasmBinaryBuilder::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 854228e9f..6c41264a6 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -2462,6 +2462,15 @@ Expression* SExpressionWasmBuilder::makeTableSet(Element& s) { return Builder(wasm).makeTableSet(tableName, index, value); } +Expression* SExpressionWasmBuilder::makeTableSize(Element& s) { + auto tableName = s[1]->str(); + auto* table = wasm.getTableOrNull(tableName); + if (!table) { + throw ParseException("invalid table name in table.size", s.line, s.col); + } + return Builder(wasm).makeTableSize(tableName); +} + // 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 8f6581956..f81a0b7a0 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -1857,6 +1857,11 @@ void BinaryInstWriter::visitTableSet(TableSet* curr) { o << U32LEB(parent.getTableIndex(curr->table)); } +void BinaryInstWriter::visitTableSize(TableSize* curr) { + o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::TableSize); + 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 0e362df9c..0271884a4 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -366,6 +366,7 @@ public: void visitRefEq(RefEq* curr); void visitTableGet(TableGet* curr); void visitTableSet(TableSet* curr); + void visitTableSize(TableSize* curr); void noteDelegate(Name name, Expression* curr); void noteRethrow(Name name, Expression* curr); void visitTry(Try* curr); @@ -2063,6 +2064,14 @@ void FunctionValidator::visitTableSet(TableSet* curr) { } } +void FunctionValidator::visitTableSize(TableSize* curr) { + shouldBeTrue(getModule()->features.hasReferenceTypes(), + curr, + "table.size requires reference types to be enabled"); + auto* table = getModule()->getTableOrNull(curr->table); + shouldBeTrue(!!table, curr, "table.size table must exist"); +} + 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 4975f4597..c42a1089d 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -857,6 +857,10 @@ void TableSet::finalize() { } } +void TableSize::finalize() { + // Nothing to do - the type must have been set already during construction. +} + 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/wasm2js.h b/src/wasm2js.h index ac7e94421..03d1fd1da 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2175,6 +2175,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, unimplemented(curr); WASM_UNREACHABLE("unimp"); } + Ref visitTableSize(TableSize* 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 75ca8b5cb..09d8659a0 100644 --- a/test/binaryen.js/exception-handling.js.txt +++ b/test/binaryen.js/exception-handling.js.txt @@ -34,7 +34,7 @@ ) ) -getExpressionInfo(throw) = {"id":48,"type":1,"tag":"e"} -getExpressionInfo(rethrow) = {"id":49,"type":1,"target":"l0"} -getExpressionInfo(try_catch) = {"id":47,"type":1,"name":"l0","hasCatchAll":0,"delegateTarget":"","isDelegate":0} -getExpressionInfo(try_delegate) = {"id":47,"type":0,"name":"try_outer","hasCatchAll":1,"delegateTarget":"","isDelegate":0} +getExpressionInfo(throw) = {"id":49,"type":1,"tag":"e"} +getExpressionInfo(rethrow) = {"id":50,"type":1,"target":"l0"} +getExpressionInfo(try_catch) = {"id":48,"type":1,"name":"l0","hasCatchAll":0,"delegateTarget":"","isDelegate":0} +getExpressionInfo(try_delegate) = {"id":48,"type":0,"name":"try_outer","hasCatchAll":1,"delegateTarget":"","isDelegate":0} diff --git a/test/binaryen.js/kitchen-sink.js b/test/binaryen.js/kitchen-sink.js index f2da5d0a0..10d97cc0a 100644 --- a/test/binaryen.js/kitchen-sink.js +++ b/test/binaryen.js/kitchen-sink.js @@ -166,6 +166,7 @@ function test_ids() { console.log("RefEqId: " + binaryen.RefEqId); console.log("TableGetId: " + binaryen.TableGetId); console.log("TableSetId: " + binaryen.TableSetId); + console.log("TableSizeId: " + binaryen.TableSizeId); console.log("TryId: " + binaryen.TryId); console.log("ThrowId: " + binaryen.ThrowId); console.log("RethrowId: " + binaryen.RethrowId); diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt index 54a18cd54..f54991f0b 100644 --- a/test/binaryen.js/kitchen-sink.js.txt +++ b/test/binaryen.js/kitchen-sink.js.txt @@ -90,27 +90,28 @@ RefFuncId: 43 RefEqId: 44 TableGetId: 45 TableSetId: 46 -TryId: 47 -ThrowId: 48 -RethrowId: 49 -TupleMakeId: 50 -TupleExtractId: 51 -I31NewId: 52 -I31GetId: 53 -CallRefId: 54 -RefTestId: 55 -RefCastId: 56 -BrOnId: 57 -RttCanonId: 58 -RttSubId: 59 -StructNewId: 60 -StructGetId: 61 -StructSetId: 62 -ArrayNewId: 63 -ArrayInitId: 64 -ArrayGetId: 65 -ArraySetId: 66 -ArrayLenId: 67 +TableSizeId: 47 +TryId: 48 +ThrowId: 49 +RethrowId: 50 +TupleMakeId: 51 +TupleExtractId: 52 +I31NewId: 53 +I31GetId: 54 +CallRefId: 55 +RefTestId: 56 +RefCastId: 57 +BrOnId: 58 +RttCanonId: 59 +RttSubId: 60 +StructNewId: 61 +StructGetId: 62 +StructSetId: 63 +ArrayNewId: 64 +ArrayInitId: 65 +ArrayGetId: 66 +ArraySetId: 67 +ArrayLenId: 68 getExpressionInfo={"id":15,"type":4,"op":6} (f32.neg (f32.const -33.61199951171875) diff --git a/test/example/c-api-kitchen-sink.c b/test/example/c-api-kitchen-sink.c index 894fc1454..1e762579e 100644 --- a/test/example/c-api-kitchen-sink.c +++ b/test/example/c-api-kitchen-sink.c @@ -1026,6 +1026,8 @@ void test_core() { BinaryenExpressionPrint(funcrefExpr2); + BinaryenExpressionPrint(BinaryenTableSize(module, "0")); + // Memory. One per module const char* segments[] = {"hello, world", "I am passive"}; diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt index c28e0536d..b1afb9954 100644 --- a/test/example/c-api-kitchen-sink.txt +++ b/test/example/c-api-kitchen-sink.txt @@ -38,6 +38,7 @@ BinaryenFeatureAll: 32767 (table.get $0 (i32.const 0) ) +(table.size $0) (module (type $i32_i64_f32_f64_=>_i32 (func (param i32 i64 f32 f64) (result i32))) (type $i32_=>_none (func (param i32))) diff --git a/test/lit/table-operations.wast b/test/lit/table-operations.wast index 555708c07..dece2143a 100644 --- a/test/lit/table-operations.wast +++ b/test/lit/table-operations.wast @@ -12,9 +12,13 @@ (module ;; CHECK-BINARY: (type $none_=>_none (func)) + ;; CHECK-BINARY: (type $none_=>_i32 (func (result i32))) + ;; CHECK-BINARY: (table $table-1 1 1 funcref) ;; CHECK-TEXT: (type $none_=>_none (func)) + ;; CHECK-TEXT: (type $none_=>_i32 (func (result i32))) + ;; CHECK-TEXT: (table $table-1 1 1 funcref) (table $table-1 funcref (elem $foo) @@ -113,9 +117,21 @@ ) ) ) + + ;; CHECK-BINARY: (func $get-table-size (result i32) + ;; CHECK-BINARY-NEXT: (table.size $table-1) + ;; CHECK-BINARY-NEXT: ) + ;; CHECK-TEXT: (func $get-table-size (result i32) + ;; CHECK-TEXT-NEXT: (table.size $table-1) + ;; CHECK-TEXT-NEXT: ) + (func $get-table-size (result i32) + (table.size $table-1) + ) ) ;; CHECK-NODEBUG: (type $none_=>_none (func)) +;; CHECK-NODEBUG: (type $none_=>_i32 (func (result i32))) + ;; CHECK-NODEBUG: (table $0 1 1 funcref) ;; CHECK-NODEBUG: (table $1 3 3 funcref) @@ -152,3 +168,7 @@ ;; CHECK-NODEBUG-NEXT: ) ;; CHECK-NODEBUG-NEXT: ) ;; CHECK-NODEBUG-NEXT: ) + +;; CHECK-NODEBUG: (func $3 (result i32) +;; CHECK-NODEBUG-NEXT: (table.size $0) +;; CHECK-NODEBUG-NEXT: ) diff --git a/test/spec/table_size.wast b/test/spec/table_size.wast new file mode 100644 index 000000000..5732ca1a8 --- /dev/null +++ b/test/spec/table_size.wast @@ -0,0 +1,88 @@ +;; TODO: uncomment tests when table.grow is implemented + +(module + (table $t0 0 externref) + (table $t1 1 externref) + (table $t2 0 2 externref) + (table $t3 3 8 externref) + + (func (export "size-t0") (result i32) (table.size $t0)) + (func (export "size-t1") (result i32) (table.size $t1)) + (func (export "size-t2") (result i32) (table.size $t2)) + (func (export "size-t3") (result i32) (table.size $t3)) + + ;; (func (export "grow-t0") (param $sz i32) + ;; (drop (table.grow $t0 (ref.null extern) (local.get $sz))) + ;; ) + ;; (func (export "grow-t1") (param $sz i32) + ;; (drop (table.grow $t1 (ref.null extern) (local.get $sz))) + ;; ) + ;; (func (export "grow-t2") (param $sz i32) + ;; (drop (table.grow $t2 (ref.null extern) (local.get $sz))) + ;; ) + ;; (func (export "grow-t3") (param $sz i32) + ;; (drop (table.grow $t3 (ref.null extern) (local.get $sz))) + ;; ) +) + +(assert_return (invoke "size-t0") (i32.const 0)) +;; (assert_return (invoke "grow-t0" (i32.const 1))) +;; (assert_return (invoke "size-t0") (i32.const 1)) +;; (assert_return (invoke "grow-t0" (i32.const 4))) +;; (assert_return (invoke "size-t0") (i32.const 5)) +;; (assert_return (invoke "grow-t0" (i32.const 0))) +;; (assert_return (invoke "size-t0") (i32.const 5)) + +(assert_return (invoke "size-t1") (i32.const 1)) +;; (assert_return (invoke "grow-t1" (i32.const 1))) +;; (assert_return (invoke "size-t1") (i32.const 2)) +;; (assert_return (invoke "grow-t1" (i32.const 4))) +;; (assert_return (invoke "size-t1") (i32.const 6)) +;; (assert_return (invoke "grow-t1" (i32.const 0))) +;; (assert_return (invoke "size-t1") (i32.const 6)) + +(assert_return (invoke "size-t2") (i32.const 0)) +;; (assert_return (invoke "grow-t2" (i32.const 3))) +;; (assert_return (invoke "size-t2") (i32.const 0)) +;; (assert_return (invoke "grow-t2" (i32.const 1))) +;; (assert_return (invoke "size-t2") (i32.const 1)) +;; (assert_return (invoke "grow-t2" (i32.const 0))) +;; (assert_return (invoke "size-t2") (i32.const 1)) +;; (assert_return (invoke "grow-t2" (i32.const 4))) +;; (assert_return (invoke "size-t2") (i32.const 1)) +;; (assert_return (invoke "grow-t2" (i32.const 1))) +;; (assert_return (invoke "size-t2") (i32.const 2)) + +(assert_return (invoke "size-t3") (i32.const 3)) +;; (assert_return (invoke "grow-t3" (i32.const 1))) +;; (assert_return (invoke "size-t3") (i32.const 4)) +;; (assert_return (invoke "grow-t3" (i32.const 3))) +;; (assert_return (invoke "size-t3") (i32.const 7)) +;; (assert_return (invoke "grow-t3" (i32.const 0))) +;; (assert_return (invoke "size-t3") (i32.const 7)) +;; (assert_return (invoke "grow-t3" (i32.const 2))) +;; (assert_return (invoke "size-t3") (i32.const 7)) +;; (assert_return (invoke "grow-t3" (i32.const 1))) +;; (assert_return (invoke "size-t3") (i32.const 8)) + + +;; Type errors + +(assert_invalid + (module + (table $t 1 externref) + (func $type-result-i32-vs-empty + (table.size $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 1 externref) + (func $type-result-i32-vs-f32 (result f32) + (table.size $t) + ) + ) + "type mismatch" +) |