diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/binaryen-c.cpp | 86 | ||||
-rw-r--r-- | src/binaryen-c.h | 45 | ||||
-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 | 44 | ||||
-rw-r--r-- | src/js/binaryen.js-post.js | 64 | ||||
-rw-r--r-- | src/passes/Directize.cpp | 1 | ||||
-rw-r--r-- | src/passes/Print.cpp | 4 | ||||
-rw-r--r-- | src/passes/RemoveUnusedModuleElements.cpp | 1 | ||||
-rw-r--r-- | src/shell-interface.h | 3 | ||||
-rw-r--r-- | src/tools/wasm-ctor-eval.cpp | 4 | ||||
-rw-r--r-- | src/wasm-binary.h | 2 | ||||
-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 | 21 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 1 | ||||
-rw-r--r-- | src/wasm.h | 13 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 18 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 11 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 5 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 17 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 8 | ||||
-rw-r--r-- | src/wasm2js.h | 4 |
25 files changed, 369 insertions, 18 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 72598d0dc..ef6c3abdd 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -1287,6 +1287,24 @@ BinaryenExpressionRef BinaryenRefEq(BinaryenModuleRef module, Builder(*(Module*)module).makeRefEq((Expression*)left, (Expression*)right)); } +BinaryenExpressionRef BinaryenTableGet(BinaryenModuleRef module, + const char* name, + BinaryenExpressionRef index, + BinaryenType type) { + return static_cast<Expression*>( + Builder(*(Module*)module) + .makeTableGet(name, (Expression*)index, Type(type))); +} + +BinaryenExpressionRef BinaryenTableSet(BinaryenModuleRef module, + const char* name, + BinaryenExpressionRef index, + BinaryenExpressionRef value) { + return static_cast<Expression*>( + Builder(*(Module*)module) + .makeTableSet(name, (Expression*)index, (Expression*)value)); +} + BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module, const char* name, BinaryenExpressionRef body, @@ -1872,6 +1890,66 @@ void BinaryenGlobalSetSetValue(BinaryenExpressionRef expr, assert(valueExpr); static_cast<GlobalSet*>(expression)->value = (Expression*)valueExpr; } +// TableGet +const char* BinaryenTableGetGetTable(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is<TableGet>()); + return static_cast<TableGet*>(expression)->table.c_str(); +} +void BinaryenTableGetSetTable(BinaryenExpressionRef expr, const char* table) { + auto* expression = (Expression*)expr; + assert(expression->is<TableGet>()); + assert(table); + static_cast<TableGet*>(expression)->table = table; +} +BinaryenExpressionRef BinaryenTableGetGetIndex(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is<TableGet>()); + return static_cast<TableGet*>(expression)->index; +} +void BinaryenTableGetSetIndex(BinaryenExpressionRef expr, + BinaryenExpressionRef indexExpr) { + auto* expression = (Expression*)expr; + assert(expression->is<TableGet>()); + assert(indexExpr); + static_cast<TableGet*>(expression)->index = (Expression*)indexExpr; +} +// TableSet +const char* BinaryenTableSetGetTable(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is<TableSet>()); + return static_cast<TableSet*>(expression)->table.c_str(); +} +void BinaryenTableSetSetTable(BinaryenExpressionRef expr, const char* table) { + auto* expression = (Expression*)expr; + assert(expression->is<TableSet>()); + assert(table); + static_cast<TableSet*>(expression)->table = table; +} +BinaryenExpressionRef BinaryenTableSetGetIndex(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is<TableSet>()); + return static_cast<TableSet*>(expression)->index; +} +void BinaryenTableSetSetIndex(BinaryenExpressionRef expr, + BinaryenExpressionRef indexExpr) { + auto* expression = (Expression*)expr; + assert(expression->is<TableSet>()); + assert(indexExpr); + static_cast<TableSet*>(expression)->index = (Expression*)indexExpr; +} +BinaryenExpressionRef BinaryenTableSetGetValue(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is<TableSet>()); + return static_cast<TableSet*>(expression)->value; +} +void BinaryenTableSetSetValue(BinaryenExpressionRef expr, + BinaryenExpressionRef valueExpr) { + auto* expression = (Expression*)expr; + assert(expression->is<TableSet>()); + assert(valueExpr); + static_cast<TableSet*>(expression)->value = (Expression*)valueExpr; +} // MemoryGrow BinaryenExpressionRef BinaryenMemoryGrowGetDelta(BinaryenExpressionRef expr) { auto* expression = (Expression*)expr; @@ -4313,6 +4391,14 @@ BinaryenSideEffects BinaryenSideEffectWritesMemory(void) { return static_cast<BinaryenSideEffects>( EffectAnalyzer::SideEffects::WritesMemory); } +BinaryenSideEffects BinaryenSideEffectReadsTable(void) { + return static_cast<BinaryenSideEffects>( + EffectAnalyzer::SideEffects::ReadsTable); +} +BinaryenSideEffects BinaryenSideEffectWritesTable(void) { + return static_cast<BinaryenSideEffects>( + EffectAnalyzer::SideEffects::WritesTable); +} BinaryenSideEffects BinaryenSideEffectImplicitTrap(void) { return static_cast<BinaryenSideEffects>( EffectAnalyzer::SideEffects::ImplicitTrap); diff --git a/src/binaryen-c.h b/src/binaryen-c.h index f9c8719dd..d9e1afc8e 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -851,6 +851,15 @@ BINARYEN_API BinaryenExpressionRef BinaryenRefFunc(BinaryenModuleRef module, BINARYEN_API BinaryenExpressionRef BinaryenRefEq(BinaryenModuleRef module, BinaryenExpressionRef left, BinaryenExpressionRef right); +BINARYEN_API BinaryenExpressionRef BinaryenTableGet(BinaryenModuleRef module, + const char* name, + BinaryenExpressionRef index, + BinaryenType type); +BINARYEN_API BinaryenExpressionRef +BinaryenTableSet(BinaryenModuleRef module, + const char* name, + BinaryenExpressionRef index, + BinaryenExpressionRef value); // Try: name can be NULL. delegateTarget should be NULL in try-catch. BINARYEN_API BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module, @@ -1195,6 +1204,40 @@ BinaryenGlobalSetGetValue(BinaryenExpressionRef expr); BINARYEN_API void BinaryenGlobalSetSetValue(BinaryenExpressionRef expr, BinaryenExpressionRef valueExpr); +// TableGet + +// Gets the name of the table being accessed by a `table.get` expression. +BINARYEN_API const char* BinaryenTableGetGetTable(BinaryenExpressionRef expr); +// Sets the name of the table being accessed by a `table.get` expression. +BINARYEN_API void BinaryenTableGetSetTable(BinaryenExpressionRef expr, + const char* table); +// Gets the index expression of a `table.get` expression. +BINARYEN_API BinaryenExpressionRef +BinaryenTableGetGetIndex(BinaryenExpressionRef expr); +// Sets the index expression of a `table.get` expression. +BINARYEN_API void BinaryenTableGetSetIndex(BinaryenExpressionRef expr, + BinaryenExpressionRef indexExpr); + +// TableSet + +// Gets the name of the table being accessed by a `table.set` expression. +BINARYEN_API const char* BinaryenTableSetGetTable(BinaryenExpressionRef expr); +// Sets the name of the table being accessed by a `table.set` expression. +BINARYEN_API void BinaryenTableSetSetTable(BinaryenExpressionRef expr, + const char* table); +// Gets the index expression of a `table.set` expression. +BINARYEN_API BinaryenExpressionRef +BinaryenTableSetGetIndex(BinaryenExpressionRef expr); +// Sets the index expression of a `table.set` expression. +BINARYEN_API void BinaryenTableSetSetIndex(BinaryenExpressionRef expr, + BinaryenExpressionRef indexExpr); +// Gets the value expression of a `table.set` expression. +BINARYEN_API BinaryenExpressionRef +BinaryenTableSetGetValue(BinaryenExpressionRef expr); +// Sets the value expression of a `table.set` expression. +BINARYEN_API void BinaryenTableSetSetValue(BinaryenExpressionRef expr, + BinaryenExpressionRef valueExpr); + // MemoryGrow // Gets the delta of a `memory.grow` expression. @@ -2625,6 +2668,8 @@ BINARYEN_API BinaryenSideEffects BinaryenSideEffectReadsGlobal(void); BINARYEN_API BinaryenSideEffects BinaryenSideEffectWritesGlobal(void); BINARYEN_API BinaryenSideEffects BinaryenSideEffectReadsMemory(void); BINARYEN_API BinaryenSideEffects BinaryenSideEffectWritesMemory(void); +BINARYEN_API BinaryenSideEffects BinaryenSideEffectReadsTable(void); +BINARYEN_API BinaryenSideEffects BinaryenSideEffectWritesTable(void); BINARYEN_API BinaryenSideEffects BinaryenSideEffectImplicitTrap(void); BINARYEN_API BinaryenSideEffects BinaryenSideEffectTrapsNeverHappen(void); BINARYEN_API BinaryenSideEffects BinaryenSideEffectIsAtomic(void); diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index 984ebf6b2..cca1c3445 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -3041,9 +3041,17 @@ switch (op[0]) { } case 't': { switch (op[1]) { - case 'a': - if (strcmp(op, "table.get") == 0) { return makeTableGet(s); } - goto parse_error; + case 'a': { + switch (op[6]) { + 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; + default: goto parse_error; + } + } case 'h': { switch (op[2]) { case 'e': diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index 4f9c7c849..0d107a759 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -136,6 +136,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::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 12797b0b5..7cf0de926 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -541,6 +541,9 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> { return 1 + visit(curr->left) + visit(curr->right); } CostType visitTableGet(TableGet* curr) { return 1 + visit(curr->index); } + CostType visitTableSet(TableSet* curr) { + return 2 + visit(curr->index) + visit(curr->value); + } 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 22e1e8149..44b2cba94 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -75,6 +75,8 @@ public: std::set<Name> globalsWritten; bool readsMemory = false; bool writesMemory = false; + bool readsTable = false; + bool writesTable = false; // TODO: More specific type-based alias analysis, and not just at the // struct/array level. bool readsStruct = false; @@ -129,6 +131,7 @@ public: return globalsRead.size() + globalsWritten.size() > 0; } bool accessesMemory() const { return calls || readsMemory || writesMemory; } + bool accessesTable() const { return calls || readsTable || writesTable; } bool accessesStruct() const { return calls || readsStruct || writesStruct; } bool accessesArray() const { return calls || readsArray || writesArray; } // Check whether this may transfer control flow to somewhere outside of this @@ -144,12 +147,12 @@ public: // Changes something in globally-stored state. bool writesGlobalState() const { - return globalsWritten.size() || writesMemory || writesStruct || - writesArray || isAtomic || calls; + return globalsWritten.size() || writesMemory || writesTable || + writesStruct || writesArray || isAtomic || calls; } bool readsGlobalState() const { - return globalsRead.size() || readsMemory || readsStruct || readsArray || - isAtomic || calls; + return globalsRead.size() || readsMemory || readsTable || readsStruct || + readsArray || isAtomic || calls; } bool hasNonTrapSideEffects() const { @@ -184,7 +187,7 @@ public: } bool hasAnything() const { - return hasSideEffects() || accessesLocal() || readsMemory || + return hasSideEffects() || accessesLocal() || readsMemory || readsTable || accessesGlobal(); } @@ -198,6 +201,8 @@ public: (other.transfersControlFlow() && hasSideEffects()) || ((writesMemory || calls) && other.accessesMemory()) || ((other.writesMemory || other.calls) && accessesMemory()) || + ((writesTable || calls) && other.accessesTable()) || + ((other.writesTable || other.calls) && accessesTable()) || ((writesStruct || calls) && other.accessesStruct()) || ((other.writesStruct || other.calls) && accessesStruct()) || ((writesArray || calls) && other.accessesArray()) || @@ -261,6 +266,8 @@ public: calls = calls || other.calls; readsMemory = readsMemory || other.readsMemory; writesMemory = writesMemory || other.writesMemory; + readsTable = readsTable || other.readsTable; + writesTable = writesTable || other.writesTable; readsStruct = readsStruct || other.readsStruct; writesStruct = writesStruct || other.writesStruct; readsArray = readsArray || other.readsArray; @@ -590,8 +597,11 @@ private: void visitRefFunc(RefFunc* curr) {} void visitRefEq(RefEq* curr) {} void visitTableGet(TableGet* curr) { - // TODO: track readsTable/writesTable, like memory? - // Traps when the index is out of bounds for the table. + parent.readsTable = true; + parent.implicitTrap = true; + } + void visitTableSet(TableSet* curr) { + parent.writesTable = true; parent.implicitTrap = true; } void visitTry(Try* curr) {} @@ -708,12 +718,14 @@ public: WritesGlobal = 1 << 5, ReadsMemory = 1 << 6, WritesMemory = 1 << 7, - ImplicitTrap = 1 << 8, - IsAtomic = 1 << 9, - Throws = 1 << 10, - DanglingPop = 1 << 11, - TrapsNeverHappen = 1 << 12, - Any = (1 << 13) - 1 + ReadsTable = 1 << 8, + WritesTable = 1 << 9, + ImplicitTrap = 1 << 10, + IsAtomic = 1 << 11, + Throws = 1 << 12, + DanglingPop = 1 << 13, + TrapsNeverHappen = 1 << 14, + Any = (1 << 15) - 1 }; uint32_t getSideEffects() const { uint32_t effects = 0; @@ -741,6 +753,12 @@ public: if (writesMemory) { effects |= SideEffects::WritesMemory; } + if (readsTable) { + effects |= SideEffects::ReadsTable; + } + if (writesTable) { + effects |= SideEffects::WritesTable; + } if (implicitTrap) { effects |= SideEffects::ImplicitTrap; } diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index 4c5f5ed3b..8fbd2b7ec 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -91,6 +91,8 @@ function initializeConstants() { 'RefIs', 'RefFunc', 'RefEq', + 'TableGet', + 'TableSet', 'Try', 'Throw', 'Rethrow', @@ -549,6 +551,8 @@ function initializeConstants() { 'WritesGlobal', 'ReadsMemory', 'WritesMemory', + 'ReadsTable', + 'WritesTable', 'ImplicitTrap', 'IsAtomic', 'Throws', @@ -661,6 +665,15 @@ function wrapModule(module, self = {}) { } } + self['table'] = { + 'get'(name, index, type) { + return Module['_BinaryenTableGet'](module, strToStack(name), index, type); + }, + 'set'(name, index, value) { + return Module['_BinaryenTableSet'](module, strToStack(name), index, value); + } + } + self['memory'] = { 'size'() { return Module['_BinaryenMemorySize'](module); @@ -2797,6 +2810,21 @@ Module['getExpressionInfo'] = function(expr) { 'name': UTF8ToString(Module['_BinaryenGlobalSetGetName'](expr)), 'value': Module['_BinaryenGlobalSetGetValue'](expr) }; + case Module['TableGetId']: + return { + 'id': id, + 'type': type, + 'table': UTF8ToString(Module['_BinaryenTableGetGetTable'](expr)), + 'index': Module['_BinaryenTableGetGetIndex'](expr) + }; + case Module['TableSetId']: + return { + 'id': id, + 'type': type, + 'table': UTF8ToString(Module['_BinaryenTableSetGetTable'](expr)), + 'index': Module['_BinaryenTableSetGetIndex'](expr), + 'value': Module['_BinaryenTableSetGetValue'](expr) + }; case Module['LoadId']: return { 'id': id, @@ -3767,6 +3795,42 @@ Module['GlobalSet'] = makeExpressionWrapper({ } }); +Module['TableGet'] = makeExpressionWrapper({ + 'getTable'(expr) { + return UTF8ToString(Module['_BinaryenTableGetGetTable'](expr)); + }, + 'setTable'(expr, name) { + preserveStack(() => { Module['_BinaryenTableGetSetTable'](expr, strToStack(name)) }); + }, + 'getIndex'(expr) { + return Module['_BinaryenTableGetGetIndex'](expr); + }, + 'setIndex'(expr, indexExpr) { + Module['_BinaryenTableGetSetIndex'](expr, indexExpr); + } +}); + +Module['TableSet'] = makeExpressionWrapper({ + 'getTable'(expr) { + return UTF8ToString(Module['_BinaryenTableSetGetTable'](expr)); + }, + 'setTable'(expr, name) { + preserveStack(() => { Module['_BinaryenTableSetSetTable'](expr, strToStack(name)) }); + }, + 'getIndex'(expr) { + return Module['_BinaryenTableSetGetIndex'](expr); + }, + 'setIndex'(expr, indexExpr) { + Module['_BinaryenTableSetSetIndex'](expr, indexExpr); + }, + 'getValue'(expr) { + return Module['_BinaryenTableSetGetValue'](expr); + }, + 'setValue'(expr, valueExpr) { + Module['_BinaryenTableSetSetValue'](expr, valueExpr); + } +}); + Module['MemorySize'] = makeExpressionWrapper({}); Module['MemoryGrow'] = makeExpressionWrapper({ diff --git a/src/passes/Directize.cpp b/src/passes/Directize.cpp index 5a9b1b984..92953c47b 100644 --- a/src/passes/Directize.cpp +++ b/src/passes/Directize.cpp @@ -171,6 +171,7 @@ private: } }; +// TODO: handle table.get / table.set here as well struct Directize : public Pass { void run(PassRunner* runner, Module* module) override { std::unordered_map<Name, TableUtils::FlatTable> validTables; diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 4d6207b01..a5db73132 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -1846,6 +1846,10 @@ struct PrintExpressionContents printMedium(o, "table.get "); printName(curr->table, o); } + void visitTableSet(TableSet* curr) { + printMedium(o, "table.set "); + 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 7436676ef..32db977ec 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -129,6 +129,7 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> { maybeAdd(ModuleElement(ModuleElementKind::Function, curr->func)); } void visitTableGet(TableGet* curr) { maybeAddTable(curr->table); } + void visitTableSet(TableSet* 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 66608c37c..0c81680f5 100644 --- a/src/shell-interface.h +++ b/src/shell-interface.h @@ -233,7 +233,6 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { } Literal tableLoad(Name tableName, Address addr) override { - auto it = tables.find(tableName); if (it == tables.end()) { trap("tableGet on non-existing table"); @@ -241,7 +240,7 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { auto& table = it->second; if (addr >= table.size()) { - trap("tableGet overflow"); + trap("out of bounds table access"); } return table[addr]; diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index 61897dc60..a0ae99a8b 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -347,6 +347,10 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { throw FailToEvalException("table.get: TODO"); } + void tableSet(Name tableName, Index index, const Literal& value) { + throw FailToEvalException("table.set: 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 c0225c270..b211c536c 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -475,6 +475,7 @@ enum ASTNodes { GlobalSet = 0x24, TableGet = 0x25, + TableSet = 0x26, I32LoadMem = 0x28, I64LoadMem = 0x29, @@ -1662,6 +1663,7 @@ public: void visitRefFunc(RefFunc* curr); void visitRefEq(RefEq* curr); void visitTableGet(TableGet* curr); + void visitTableSet(TableSet* 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 d1cb5f16d..467edf1bd 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -656,6 +656,14 @@ public: ret->finalize(); return ret; } + TableSet* makeTableSet(Name table, Expression* index, Expression* value) { + auto* ret = wasm.allocator.alloc<TableSet>(); + ret->table = table; + ret->index = index; + ret->value = value; + ret->finalize(); + return ret; + } private: Try* makeTry(Name name, diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index c214b5b6b..b6828fbc2 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -508,6 +508,14 @@ switch (DELEGATE_ID) { DELEGATE_END(TableGet); break; } + case Expression::Id::TableSetId: { + DELEGATE_START(TableSet); + DELEGATE_FIELD_CHILD(TableSet, value); + DELEGATE_FIELD_CHILD(TableSet, index); + DELEGATE_FIELD_NAME(TableSet, table); + DELEGATE_END(TableSet); + 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 873eb2902..c040dd551 100644 --- a/src/wasm-delegations.def +++ b/src/wasm-delegations.def @@ -59,6 +59,7 @@ DELEGATE(RefIs); DELEGATE(RefFunc); DELEGATE(RefEq); DELEGATE(TableGet); +DELEGATE(TableSet); DELEGATE(Try); DELEGATE(Throw); DELEGATE(Rethrow); diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 6ea9f9c9e..2952869c6 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1359,6 +1359,7 @@ public: return Literal(int32_t(left == right)); } Flow visitTableGet(TableGet* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitTableSet(TableSet* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTry(Try* curr) { WASM_UNREACHABLE("unimp"); } Flow visitThrow(Throw* curr) { NOTE_ENTER("Throw"); @@ -2162,6 +2163,10 @@ public: NOTE_ENTER("TableGet"); return Flow(NONCONSTANT_FLOW); } + Flow visitTableSet(TableSet* curr) { + NOTE_ENTER("TableSet"); + return Flow(NONCONSTANT_FLOW); + } Flow visitLoad(Load* curr) { NOTE_ENTER("Load"); return Flow(NONCONSTANT_FLOW); @@ -2794,6 +2799,22 @@ private: index.getSingleValue().geti32()); } + Flow visitTableSet(TableSet* curr) { + NOTE_ENTER("TableSet"); + Flow index = this->visit(curr->index); + if (index.breaking()) { + return index; + } + Flow flow = this->visit(curr->value); + if (flow.breaking()) { + return flow; + } + auto info = instance.getTableInterfaceInfo(curr->table); + info.interface->tableStore( + info.name, index.getSingleValue().geti32(), flow.getSingleValue()); + return Flow(); + } + 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 693b15ea2..942eae19b 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -266,6 +266,7 @@ private: Expression* makeRefFunc(Element& s); Expression* makeRefEq(Element& s); Expression* makeTableGet(Element& s); + Expression* makeTableSet(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 697931082..fbec9247a 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -625,6 +625,7 @@ public: RefFuncId, RefEqId, TableGetId, + TableSetId, TryId, ThrowId, RethrowId, @@ -1276,6 +1277,18 @@ public: void finalize(); }; +class TableSet : public SpecificExpression<Expression::TableSetId> { +public: + TableSet(MixedArena& allocator) {} + + Name table; + + Expression* index; + Expression* value; + + 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 04dad3c9c..7e72b056f 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -2791,6 +2791,8 @@ void WasmBinaryBuilder::processNames() { callIndirect->table = getTableName(index); } else if (auto* get = ref->dynCast<TableGet>()) { get->table = getTableName(index); + } else if (auto* set = ref->dynCast<TableSet>()) { + set->table = getTableName(index); } else { WASM_UNREACHABLE("Invalid type in table references"); } @@ -3524,6 +3526,9 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { case BinaryConsts::TableGet: visitTableGet((curr = allocator.alloc<TableGet>())->cast<TableGet>()); break; + case BinaryConsts::TableSet: + visitTableSet((curr = allocator.alloc<TableSet>())->cast<TableSet>()); + break; case BinaryConsts::Try: visitTryOrTryInBlock(curr); break; @@ -6217,6 +6222,19 @@ void WasmBinaryBuilder::visitTableGet(TableGet* curr) { tableRefs[tableIdx].push_back(curr); } +void WasmBinaryBuilder::visitTableSet(TableSet* curr) { + BYN_TRACE("zz node: TableSet\n"); + Index tableIdx = getU32LEB(); + if (tableIdx >= tables.size()) { + throwError("bad table index"); + } + curr->value = popNonVoidExpression(); + curr->index = popNonVoidExpression(); + curr->finalize(); + // Defer setting the table name for later, when we know it. + tableRefs[tableIdx].push_back(curr); +} + void WasmBinaryBuilder::visitTryOrTryInBlock(Expression*& out) { BYN_TRACE("zz node: Try\n"); auto* curr = allocator.alloc<Try>(); diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 573699eae..417a0ac0e 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -2427,6 +2427,17 @@ Expression* SExpressionWasmBuilder::makeTableGet(Element& s) { return Builder(wasm).makeTableGet(tableName, index, table->type); } +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); + } + return Builder(wasm).makeTableSet(tableName, index, value); +} + // 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 58bd29f6e..b74b73ba1 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -1852,6 +1852,11 @@ void BinaryInstWriter::visitTableGet(TableGet* curr) { o << U32LEB(parent.getTableIndex(curr->table)); } +void BinaryInstWriter::visitTableSet(TableSet* curr) { + o << int8_t(BinaryConsts::TableSet); + 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 fae95980f..0e362df9c 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -365,6 +365,7 @@ public: void visitRefFunc(RefFunc* curr); void visitRefEq(RefEq* curr); void visitTableGet(TableGet* curr); + void visitTableSet(TableSet* curr); void noteDelegate(Name name, Expression* curr); void noteRethrow(Name name, Expression* curr); void visitTry(Try* curr); @@ -2046,6 +2047,22 @@ void FunctionValidator::visitTableGet(TableGet* curr) { } } +void FunctionValidator::visitTableSet(TableSet* curr) { + shouldBeTrue(getModule()->features.hasReferenceTypes(), + curr, + "table.set requires reference types to be enabled"); + shouldBeEqualOrFirstIsUnreachable( + curr->index->type, Type(Type::i32), curr, "table.set index must be an i32"); + auto* table = getModule()->getTableOrNull(curr->table); + if (shouldBeTrue(!!table, curr, "table.set table must exist") && + curr->type != Type::unreachable) { + shouldBeSubType(curr->value->type, + table->type, + curr, + "table.set value must have right 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 19ba5f265..26e6b369b 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -825,6 +825,14 @@ void TableGet::finalize() { // Otherwise, the type should have been set already. } +void TableSet::finalize() { + if (index->type == Type::unreachable || value->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/wasm2js.h b/src/wasm2js.h index ef6c9af36..ac7e94421 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2171,6 +2171,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, unimplemented(curr); WASM_UNREACHABLE("unimp"); } + Ref visitTableSet(TableSet* curr) { + unimplemented(curr); + WASM_UNREACHABLE("unimp"); + } Ref visitTry(Try* curr) { unimplemented(curr); WASM_UNREACHABLE("unimp"); |