diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/binaryen-c.cpp | 49 | ||||
-rw-r--r-- | src/binaryen-c.h | 27 | ||||
-rw-r--r-- | src/gen-s-parser.inc | 14 | ||||
-rw-r--r-- | src/ir/ReFinalize.cpp | 1 | ||||
-rw-r--r-- | src/ir/cost.h | 3 | ||||
-rw-r--r-- | src/ir/effects.h | 10 | ||||
-rw-r--r-- | src/js/binaryen.js-post.js | 33 | ||||
-rw-r--r-- | src/passes/Print.cpp | 4 | ||||
-rw-r--r-- | src/passes/RemoveUnusedModuleElements.cpp | 1 | ||||
-rw-r--r-- | src/shell-interface.h | 29 | ||||
-rw-r--r-- | src/tools/wasm-ctor-eval.cpp | 24 | ||||
-rw-r--r-- | src/wasm-binary.h | 3 | ||||
-rw-r--r-- | src/wasm-builder.h | 8 | ||||
-rw-r--r-- | src/wasm-delegations-fields.def | 8 | ||||
-rw-r--r-- | src/wasm-delegations.def | 1 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 75 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 1 | ||||
-rw-r--r-- | src/wasm.h | 13 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 23 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 18 | ||||
-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 | 12 | ||||
-rw-r--r-- | src/wasm2js.h | 4 |
24 files changed, 343 insertions, 42 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index eab435caa..0a4b88a2b 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -1284,6 +1284,19 @@ BinaryenExpressionRef BinaryenTableSize(BinaryenModuleRef module, Builder(*(Module*)module).makeTableSize(name)); } +BinaryenExpressionRef BinaryenTableGrow(BinaryenModuleRef module, + const char* name, + BinaryenExpressionRef value, + BinaryenExpressionRef delta) { + if (value == nullptr) { + auto tableType = (*(Module*)module).getTableOrNull(name)->type; + value = BinaryenRefNull(module, (BinaryenType)tableType.getID()); + } + return static_cast<Expression*>( + Builder(*(Module*)module) + .makeTableGrow(name, (Expression*)value, (Expression*)delta)); +} + BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module, const char* name, BinaryenExpressionRef body, @@ -1941,6 +1954,42 @@ void BinaryenTableSizeSetTable(BinaryenExpressionRef expr, const char* table) { assert(table); static_cast<TableSize*>(expression)->table = table; } +// TableGrow +const char* BinaryenTableGrowGetTable(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is<TableGrow>()); + return static_cast<TableGrow*>(expression)->table.c_str(); +} +void BinaryenTableGrowSetTable(BinaryenExpressionRef expr, const char* table) { + auto* expression = (Expression*)expr; + assert(expression->is<TableGrow>()); + assert(table); + static_cast<TableGrow*>(expression)->table = table; +} +BinaryenExpressionRef BinaryenTableGrowGetValue(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is<TableGrow>()); + return static_cast<TableGrow*>(expression)->value; +} +void BinaryenTableGrowSetValue(BinaryenExpressionRef expr, + BinaryenExpressionRef valueExpr) { + auto* expression = (Expression*)expr; + assert(expression->is<TableGrow>()); + assert(valueExpr); + static_cast<TableGrow*>(expression)->value = (Expression*)valueExpr; +} +BinaryenExpressionRef BinaryenTableGrowGetDelta(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is<TableGrow>()); + return static_cast<TableGrow*>(expression)->delta; +} +void BinaryenTableGrowSetDelta(BinaryenExpressionRef expr, + BinaryenExpressionRef deltaExpr) { + auto* expression = (Expression*)expr; + assert(expression->is<TableGrow>()); + assert(deltaExpr); + static_cast<TableGrow*>(expression)->delta = (Expression*)deltaExpr; +} // MemoryGrow BinaryenExpressionRef BinaryenMemoryGrowGetDelta(BinaryenExpressionRef expr) { auto* expression = (Expression*)expr; diff --git a/src/binaryen-c.h b/src/binaryen-c.h index 3d57aca75..853f185e3 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -862,6 +862,11 @@ BinaryenTableSet(BinaryenModuleRef module, BinaryenExpressionRef value); BINARYEN_API BinaryenExpressionRef BinaryenTableSize(BinaryenModuleRef module, const char* name); +BINARYEN_API BinaryenExpressionRef +BinaryenTableGrow(BinaryenModuleRef module, + const char* name, + BinaryenExpressionRef value, + BinaryenExpressionRef delta); // Try: name can be NULL. delegateTarget should be NULL in try-catch. BINARYEN_API BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module, @@ -1247,6 +1252,26 @@ 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); + +// TableGrow + +// Gets the name of the table being accessed by a `table.grow` expression. +BINARYEN_API const char* BinaryenTableGrowGetTable(BinaryenExpressionRef expr); +// Sets the name of the table being accessed by a `table.grow` expression. +BINARYEN_API void BinaryenTableGrowSetTable(BinaryenExpressionRef expr, + const char* table); +// Gets the value expression of a `table.grow` expression. +BINARYEN_API BinaryenExpressionRef +BinaryenTableGrowGetValue(BinaryenExpressionRef expr); +// Sets the value expression of a `table.grow` expression. +BINARYEN_API void BinaryenTableGrowSetValue(BinaryenExpressionRef expr, + BinaryenExpressionRef valueExpr); +// Gets the delta of a `table.grow` expression. +BINARYEN_API BinaryenExpressionRef +BinaryenTableGrowGetDelta(BinaryenExpressionRef expr); +// Sets the delta of a `table.grow` expression. +BINARYEN_API void BinaryenTableGrowSetDelta(BinaryenExpressionRef expr, + BinaryenExpressionRef deltaExpr); // MemoryGrow // Gets the delta of a `memory.grow` expression. @@ -1254,7 +1279,7 @@ BINARYEN_API BinaryenExpressionRef BinaryenMemoryGrowGetDelta(BinaryenExpressionRef expr); // Sets the delta of a `memory.grow` expression. BINARYEN_API void BinaryenMemoryGrowSetDelta(BinaryenExpressionRef expr, - BinaryenExpressionRef delta); + BinaryenExpressionRef deltaExpr); // Load diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index 0d61244fd..47ba1e1df 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -3043,9 +3043,17 @@ switch (op[0]) { switch (op[1]) { case 'a': { switch (op[6]) { - case 'g': - if (strcmp(op, "table.get") == 0) { return makeTableGet(s); } - goto parse_error; + case 'g': { + switch (op[7]) { + case 'e': + if (strcmp(op, "table.get") == 0) { return makeTableGet(s); } + goto parse_error; + case 'r': + if (strcmp(op, "table.grow") == 0) { return makeTableGrow(s); } + goto parse_error; + default: goto parse_error; + } + } case 's': { switch (op[7]) { case 'e': diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index a9cf8050d..79a4c9386 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -138,6 +138,7 @@ 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::visitTableGrow(TableGrow* 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 0463d3310..bf9e48308 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -545,6 +545,9 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> { return 2 + visit(curr->index) + visit(curr->value); } CostType visitTableSize(TableSize* curr) { return 1; } + CostType visitTableGrow(TableGrow* curr) { + return 100 + visit(curr->value) + visit(curr->delta); + } 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 7074df755..e33a33818 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -534,7 +534,6 @@ 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) { @@ -592,6 +591,7 @@ private: parent.isAtomic = true; } void visitMemoryGrow(MemoryGrow* curr) { + // TODO: find out if calls is necessary here parent.calls = true; // memory.grow technically does a read-modify-write operation on the // memory size in the successful case, modifying the set of valid @@ -613,6 +613,14 @@ private: parent.writesTable = true; parent.implicitTrap = true; } + void visitTableSize(TableSize* curr) { parent.readsTable = true; } + void visitTableGrow(TableGrow* curr) { + // table.grow technically does a read-modify-write operation on the + // table size in the successful case, modifying the set of valid + // indices, and just a read operation in the failure case + parent.readsTable = true; + parent.writesTable = true; + } void visitTry(Try* curr) {} void visitThrow(Throw* curr) { if (parent.tryDepth == 0) { diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index 29729c4b1..cef5c989d 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -94,6 +94,7 @@ function initializeConstants() { 'TableGet', 'TableSet', 'TableSize', + 'TableGrow', 'Try', 'Throw', 'Rethrow', @@ -675,6 +676,9 @@ function wrapModule(module, self = {}) { }, 'size'(name) { return Module['_BinaryenTableSize'](module, strToStack(name)); + }, + 'grow'(name, value, delta) { + return Module['_BinaryenTableGrow'](module, strToStack(name), value, delta); } } @@ -2835,6 +2839,14 @@ Module['getExpressionInfo'] = function(expr) { 'type': type, 'table': UTF8ToString(Module['_BinaryenTableSizeGetTable'](expr)), }; + case Module['TableGrowId']: + return { + 'id': id, + 'type': type, + 'table': UTF8ToString(Module['_BinaryenTableGrowGetTable'](expr)), + 'value': Module['_BinaryenTableGrowGetValue'](expr), + 'delta': Module['_BinaryenTableGrowGetDelta'](expr), + }; case Module['LoadId']: return { 'id': id, @@ -3850,6 +3862,27 @@ Module['TableSize'] = makeExpressionWrapper({ }, }); +Module['TableGrow'] = makeExpressionWrapper({ + 'getTable'(expr) { + return UTF8ToString(Module['_BinaryenTableGrowGetTable'](expr)); + }, + 'setTable'(expr, name) { + preserveStack(() => { Module['_BinaryenTableGrowSetTable'](expr, strToStack(name)) }); + }, + 'getValue'(expr) { + return Module['_BinaryenTableGrowGetValue'](expr); + }, + 'setValue'(expr, valueExpr) { + Module['_BinaryenTableGrowSetValue'](expr, valueExpr); + }, + 'getDelta'(expr) { + return Module['_BinaryenTableGrowGetDelta'](expr); + }, + 'setDelta'(expr, deltaExpr) { + Module['_BinaryenTableGrowSetDelta'](expr, deltaExpr); + } +}); + Module['MemorySize'] = makeExpressionWrapper({}); Module['MemoryGrow'] = makeExpressionWrapper({ diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index fee59ce3b..96a1abe73 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -1856,6 +1856,10 @@ struct PrintExpressionContents printMedium(o, "table.size "); printName(curr->table, o); } + void visitTableGrow(TableGrow* curr) { + printMedium(o, "table.grow "); + 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 0f2c69e02..436e01885 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -131,6 +131,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 visitTableGrow(TableGrow* curr) { maybeAddTable(curr->table); } void visitThrow(Throw* curr) { maybeAdd(ModuleElement(ModuleElementKind::Tag, curr->tag)); } diff --git a/src/shell-interface.h b/src/shell-interface.h index 0c81680f5..6c6ddf7f2 100644 --- a/src/shell-interface.h +++ b/src/shell-interface.h @@ -223,27 +223,31 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { memory.set<std::array<uint8_t, 16>>(addr, value); } - void tableStore(Name tableName, Address addr, const Literal& entry) override { + Index tableSize(Name tableName) override { + return (Index)tables[tableName].size(); + } + + void tableStore(Name tableName, Index index, const Literal& entry) override { auto& table = tables[tableName]; - if (addr >= table.size()) { + if (index >= table.size()) { trap("out of bounds table access"); } else { - table[addr] = entry; + table[index] = entry; } } - Literal tableLoad(Name tableName, Address addr) override { + Literal tableLoad(Name tableName, Index index) override { auto it = tables.find(tableName); if (it == tables.end()) { trap("tableGet on non-existing table"); } auto& table = it->second; - if (addr >= table.size()) { + if (index >= table.size()) { trap("out of bounds table access"); } - return table[addr]; + return table[index]; } bool growMemory(Address /*oldSize*/, Address newSize) override { @@ -256,6 +260,19 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { return true; } + bool growTable(Name name, + const Literal& value, + Index /*oldSize*/, + Index newSize) override { + // Apply a reasonable limit on table size, 1GB, to avoid DOS on the + // interpreter. + if (newSize > 1024 * 1024 * 1024) { + return false; + } + tables[name].resize(newSize, value); + return true; + } + void trap(const char* why) override { std::cout << "[trap " << why << "]\n"; throw TrapException(); diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index 189ce9f71..54324ae5a 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -343,17 +343,16 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { std::to_string(index)); } - Literal tableGet(Name tableName, Index index) { - throw FailToEvalException("table.get: TODO"); + Index tableSize(Name tableName) override { + throw FailToEvalException("table size"); } - void tableSet(Name tableName, Index index, const Literal& value) { - throw FailToEvalException("table.set: TODO"); + Literal tableLoad(Name tableName, Index index) override { + throw FailToEvalException("table.get: TODO"); } - Literal tableSize(Name tableName) { - throw FailToEvalException("table.size: TODO"); - } + // called during initialization + void tableStore(Name tableName, Index index, const Literal& value) override {} int8_t load8s(Address addr) override { return doLoad<int8_t>(addr); } uint8_t load8u(Address addr) override { return doLoad<uint8_t>(addr); } @@ -377,12 +376,15 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { doStore<int64_t>(addr, value); } - // called during initialization, but we don't keep track of a table - void tableStore(Name tableName, Address addr, const Literal& value) override { + bool growMemory(Address /*oldSize*/, Address /*newSize*/) override { + throw FailToEvalException("grow memory"); } - bool growMemory(Address /*oldSize*/, Address newSize) override { - throw FailToEvalException("grow memory"); + bool growTable(Name /*name*/, + const Literal& /*value*/, + Index /*oldSize*/, + Index /*newSize*/) override { + throw FailToEvalException("grow table"); } void trap(const char* why) override { diff --git a/src/wasm-binary.h b/src/wasm-binary.h index ae2dd4656..e523d0994 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1025,6 +1025,7 @@ enum ASTNodes { // reference types opcodes + TableGrow = 0x0f, TableSize = 0x10, RefNull = 0xd0, RefIsNull = 0xd1, @@ -1637,6 +1638,7 @@ public: bool maybeVisitMemoryCopy(Expression*& out, uint32_t code); bool maybeVisitMemoryFill(Expression*& out, uint32_t code); bool maybeVisitTableSize(Expression*& out, uint32_t code); + bool maybeVisitTableGrow(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); @@ -1666,7 +1668,6 @@ 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 87167877a..b192afbac 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -670,6 +670,14 @@ public: ret->finalize(); return ret; } + TableGrow* makeTableGrow(Name table, Expression* value, Expression* delta) { + auto* ret = wasm.allocator.alloc<TableGrow>(); + ret->table = table; + ret->value = value; + ret->delta = delta; + ret->finalize(); + return ret; + } private: Try* makeTry(Name name, diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index dfb8aab4b..d1a2e1b83 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -522,6 +522,14 @@ switch (DELEGATE_ID) { DELEGATE_END(TableSize); break; } + case Expression::Id::TableGrowId: { + DELEGATE_START(TableGrow); + DELEGATE_FIELD_CHILD(TableGrow, delta); + DELEGATE_FIELD_CHILD(TableGrow, value); + DELEGATE_FIELD_NAME(TableGrow, table); + DELEGATE_END(TableGrow); + 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 8d162dd90..c3f04674a 100644 --- a/src/wasm-delegations.def +++ b/src/wasm-delegations.def @@ -61,6 +61,7 @@ DELEGATE(RefEq); DELEGATE(TableGet); DELEGATE(TableSet); DELEGATE(TableSize); +DELEGATE(TableGrow); DELEGATE(Try); DELEGATE(Throw); DELEGATE(Rethrow); diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 2179c4923..205c9f8cd 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1361,6 +1361,7 @@ public: Flow visitTableGet(TableGet* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTableSet(TableSet* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTableSize(TableSize* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitTableGrow(TableGrow* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTry(Try* curr) { WASM_UNREACHABLE("unimp"); } Flow visitThrow(Throw* curr) { NOTE_ENTER("Throw"); @@ -2172,6 +2173,10 @@ public: NOTE_ENTER("TableSize"); return Flow(NONCONSTANT_FLOW); } + Flow visitTableGrow(TableGrow* curr) { + NOTE_ENTER("TableGrow"); + return Flow(NONCONSTANT_FLOW); + } Flow visitLoad(Load* curr) { NOTE_ENTER("Load"); return Flow(NONCONSTANT_FLOW); @@ -2306,6 +2311,10 @@ public: Type result, SubType& instance) = 0; virtual bool growMemory(Address oldSize, Address newSize) = 0; + virtual bool growTable(Name name, + const Literal& value, + Index oldSize, + Index newSize) = 0; virtual void trap(const char* why) = 0; virtual void hostLimit(const char* why) = 0; virtual void throwException(const WasmException& exn) = 0; @@ -2452,11 +2461,12 @@ public: WASM_UNREACHABLE("unimp"); } - virtual void - tableStore(Name tableName, Address addr, const Literal& entry) { + virtual Index tableSize(Name tableName) = 0; + + virtual void tableStore(Name tableName, Index index, const Literal& entry) { WASM_UNREACHABLE("unimp"); } - virtual Literal tableLoad(Name tableName, Address addr) { + virtual Literal tableLoad(Name tableName, Index index) { WASM_UNREACHABLE("unimp"); } }; @@ -2805,25 +2815,61 @@ private: } Flow visitTableSet(TableSet* curr) { NOTE_ENTER("TableSet"); - Flow index = this->visit(curr->index); - if (index.breaking()) { - return index; + Flow indexFlow = this->visit(curr->index); + if (indexFlow.breaking()) { + return indexFlow; } - Flow flow = this->visit(curr->value); - if (flow.breaking()) { - return flow; + Flow valueFlow = this->visit(curr->value); + if (valueFlow.breaking()) { + return valueFlow; } auto info = instance.getTableInterfaceInfo(curr->table); - info.interface->tableStore( - info.name, index.getSingleValue().geti32(), flow.getSingleValue()); + info.interface->tableStore(info.name, + indexFlow.getSingleValue().geti32(), + valueFlow.getSingleValue()); 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); + auto info = instance.getTableInterfaceInfo(curr->table); + Index tableSize = info.interface->tableSize(curr->table); + return Literal::makeFromInt32(tableSize, Type::i32); + } + + Flow visitTableGrow(TableGrow* curr) { + NOTE_ENTER("TableGrow"); + Flow valueFlow = this->visit(curr->value); + if (valueFlow.breaking()) { + return valueFlow; + } + Flow deltaFlow = this->visit(curr->delta); + if (deltaFlow.breaking()) { + return deltaFlow; + } + Name tableName = curr->table; + auto info = instance.getTableInterfaceInfo(tableName); + + Index tableSize = info.interface->tableSize(tableName); + Flow ret = Literal::makeFromInt32(tableSize, Type::i32); + Flow fail = Literal::makeFromInt32(-1, Type::i32); + Index delta = deltaFlow.getSingleValue().geti32(); + + if (tableSize >= uint32_t(-1) - delta) { + return fail; + } + auto maxTableSize = instance.self()->wasm.getTable(tableName)->max; + if (uint64_t(tableSize) + uint64_t(delta) > uint64_t(maxTableSize)) { + return fail; + } + Index newSize = tableSize + delta; + if (!info.interface->growTable( + tableName, valueFlow.getSingleValue(), tableSize, newSize)) { + // We failed to grow the table in practice, even though it was valid + // to try to do so. + return fail; + } + return ret; } Flow visitLocalGet(LocalGet* curr) { @@ -3546,7 +3592,6 @@ public: protected: Address memorySize; // in pages - static const Index maxDepth = 250; void trapIfGt(uint64_t lhs, uint64_t rhs, const char* msg) { diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index f5be7924f..d6f3b2f4e 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -268,6 +268,7 @@ private: Expression* makeTableGet(Element& s); Expression* makeTableSet(Element& s); Expression* makeTableSize(Element& s); + Expression* makeTableGrow(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 197b8bd84..0c7b2a9ad 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -630,6 +630,7 @@ public: TableGetId, TableSetId, TableSizeId, + TableGrowId, TryId, ThrowId, RethrowId, @@ -1312,6 +1313,18 @@ public: void finalize(); }; +class TableGrow : public SpecificExpression<Expression::TableGrowId> { +public: + TableGrow() { type = Type::i32; } + TableGrow(MixedArena& allocator) : TableGrow() {} + + Name table; + Expression* value; + Expression* delta; + + 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 e2dacdf70..9c8b83325 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -2798,6 +2798,8 @@ void WasmBinaryBuilder::processNames() { set->table = getTableName(index); } else if (auto* size = ref->dynCast<TableSize>()) { size->table = getTableName(index); + } else if (auto* grow = ref->dynCast<TableGrow>()) { + grow->table = getTableName(index); } else { WASM_UNREACHABLE("Invalid type in table references"); } @@ -3621,6 +3623,9 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { if (maybeVisitTableSize(curr, opcode)) { break; } + if (maybeVisitTableGrow(curr, opcode)) { + break; + } throwError("invalid code after misc prefix: " + std::to_string(opcode)); break; } @@ -4941,6 +4946,24 @@ bool WasmBinaryBuilder::maybeVisitTableSize(Expression*& out, uint32_t code) { return true; } +bool WasmBinaryBuilder::maybeVisitTableGrow(Expression*& out, uint32_t code) { + if (code != BinaryConsts::TableGrow) { + return false; + } + Index tableIdx = getU32LEB(); + if (tableIdx >= tables.size()) { + throwError("bad table index"); + } + auto* curr = allocator.alloc<TableGrow>(); + curr->delta = popNonVoidExpression(); + curr->value = popNonVoidExpression(); + 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 cea666f29..1e15c1faa 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -2453,12 +2453,12 @@ Expression* SExpressionWasmBuilder::makeTableGet(Element& s) { Expression* SExpressionWasmBuilder::makeTableSet(Element& s) { auto tableName = s[1]->str(); - auto* index = parseExpression(s[2]); - auto* value = parseExpression(s[3]); auto* table = wasm.getTableOrNull(tableName); if (!table) { throw ParseException("invalid table name in table.set", s.line, s.col); } + auto* index = parseExpression(s[2]); + auto* value = parseExpression(s[3]); return Builder(wasm).makeTableSet(tableName, index, value); } @@ -2471,6 +2471,20 @@ Expression* SExpressionWasmBuilder::makeTableSize(Element& s) { return Builder(wasm).makeTableSize(tableName); } +Expression* SExpressionWasmBuilder::makeTableGrow(Element& s) { + auto tableName = s[1]->str(); + auto* table = wasm.getTableOrNull(tableName); + if (!table) { + throw ParseException("invalid table name in table.grow", s.line, s.col); + } + auto* value = parseExpression(s[2]); + if (!value->type.isRef()) { + throw ParseException("only reference types are valid for tables"); + } + auto* delta = parseExpression(s[3]); + return Builder(wasm).makeTableGrow(tableName, value, delta); +} + // 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 f81a0b7a0..f5be5d2c5 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -1862,6 +1862,11 @@ void BinaryInstWriter::visitTableSize(TableSize* curr) { o << U32LEB(parent.getTableIndex(curr->table)); } +void BinaryInstWriter::visitTableGrow(TableGrow* curr) { + o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::TableGrow); + 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 0271884a4..f8669961c 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -367,6 +367,7 @@ public: void visitTableGet(TableGet* curr); void visitTableSet(TableSet* curr); void visitTableSize(TableSize* curr); + void visitTableGrow(TableGrow* curr); void noteDelegate(Name name, Expression* curr); void noteRethrow(Name name, Expression* curr); void visitTry(Try* curr); @@ -2072,6 +2073,24 @@ void FunctionValidator::visitTableSize(TableSize* curr) { shouldBeTrue(!!table, curr, "table.size table must exist"); } +void FunctionValidator::visitTableGrow(TableGrow* curr) { + shouldBeTrue(getModule()->features.hasReferenceTypes(), + curr, + "table.grow requires reference types to be enabled"); + auto* table = getModule()->getTableOrNull(curr->table); + if (shouldBeTrue(!!table, curr, "table.grow table must exist") && + curr->type != Type::unreachable) { + shouldBeSubType(curr->value->type, + table->type, + curr, + "table.grow value must have right type"); + shouldBeEqual(curr->delta->type, + Type(Type::i32), + curr, + "table.grow must match table index type"); + } +} + 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 c42a1089d..015df26ac 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -822,9 +822,9 @@ void RefNull::finalize() {} void RefIs::finalize() { if (value->type == Type::unreachable) { type = Type::unreachable; - return; + } else { + type = Type::i32; } - type = Type::i32; } void RefFunc::finalize() { @@ -861,6 +861,14 @@ void TableSize::finalize() { // Nothing to do - the type must have been set already during construction. } +void TableGrow::finalize() { + if (delta->type == Type::unreachable || value->type == Type::unreachable) { + type = Type::unreachable; + } else { + type = Type::i32; + } +} + 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 03d1fd1da..206f945b4 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2179,6 +2179,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, unimplemented(curr); WASM_UNREACHABLE("unimp"); } + Ref visitTableGrow(TableGrow* curr) { + unimplemented(curr); + WASM_UNREACHABLE("unimp"); + } Ref visitTry(Try* curr) { unimplemented(curr); WASM_UNREACHABLE("unimp"); |