diff options
author | Alon Zakai <azakai@google.com> | 2024-08-16 12:53:52 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-16 12:53:52 -0700 |
commit | 958ff4115e542ef1d0ae712f4961e342386efe54 (patch) | |
tree | 7805d641d9a73b32650a053931c4bd8c3814d804 /src | |
parent | 7209629bec3961fcc12b150ba6df546d3997b6c2 (diff) | |
download | binaryen-958ff4115e542ef1d0ae712f4961e342386efe54.tar.gz binaryen-958ff4115e542ef1d0ae712f4961e342386efe54.tar.bz2 binaryen-958ff4115e542ef1d0ae712f4961e342386efe54.zip |
Implement table.init (#6827)
Also use TableInit in the interpreter to initialize module's table
state, which will now handle traps properly, fixing #6431
Diffstat (limited to 'src')
-rw-r--r-- | src/gen-s-parser.inc | 6 | ||||
-rw-r--r-- | src/ir/ReFinalize.cpp | 1 | ||||
-rw-r--r-- | src/ir/child-typer.h | 6 | ||||
-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/ir/subtype-exprs.h | 5 | ||||
-rw-r--r-- | src/parser/contexts.h | 13 | ||||
-rw-r--r-- | src/parser/parsers.h | 12 | ||||
-rw-r--r-- | src/passes/Directize.cpp | 3 | ||||
-rw-r--r-- | src/passes/Print.cpp | 6 | ||||
-rw-r--r-- | src/passes/Table64Lowering.cpp | 4 | ||||
-rw-r--r-- | src/passes/TypeGeneralizing.cpp | 2 | ||||
-rw-r--r-- | src/tools/wasm-ctor-eval.cpp | 24 | ||||
-rw-r--r-- | src/wasm-binary.h | 2 | ||||
-rw-r--r-- | src/wasm-builder.h | 14 | ||||
-rw-r--r-- | src/wasm-delegations-fields.def | 8 | ||||
-rw-r--r-- | src/wasm-delegations.def | 1 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 95 | ||||
-rw-r--r-- | src/wasm-ir-builder.h | 1 | ||||
-rw-r--r-- | src/wasm.h | 15 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 20 | ||||
-rw-r--r-- | src/wasm/wasm-ir-builder.cpp | 8 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 6 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 36 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 8 | ||||
-rw-r--r-- | src/wasm2js.h | 4 |
27 files changed, 276 insertions, 32 deletions
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index d6eaf851d..2fa658e65 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -4936,6 +4936,12 @@ switch (buf[0]) { default: goto parse_error; } } + case 'i': + if (op == "table.init"sv) { + CHECK_ERR(makeTableInit(ctx, pos, annotations)); + return Ok{}; + } + goto parse_error; case 's': { switch (buf[7]) { case 'e': diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index 8fae5d073..0f78d37b7 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -126,6 +126,7 @@ void ReFinalize::visitTableSize(TableSize* curr) { curr->finalize(); } void ReFinalize::visitTableGrow(TableGrow* curr) { curr->finalize(); } void ReFinalize::visitTableFill(TableFill* curr) { curr->finalize(); } void ReFinalize::visitTableCopy(TableCopy* curr) { curr->finalize(); } +void ReFinalize::visitTableInit(TableInit* curr) { curr->finalize(); } void ReFinalize::visitTry(Try* curr) { curr->finalize(); } void ReFinalize::visitTryTable(TryTable* curr) { curr->finalize(); } void ReFinalize::visitThrow(Throw* curr) { curr->finalize(); } diff --git a/src/ir/child-typer.h b/src/ir/child-typer.h index 2bfaefaff..725bc842e 100644 --- a/src/ir/child-typer.h +++ b/src/ir/child-typer.h @@ -742,6 +742,12 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> { note(&curr->size, Type::i32); } + void visitTableInit(TableInit* curr) { + note(&curr->dest, wasm.getTable(curr->table)->indexType); + note(&curr->offset, Type::i32); + note(&curr->size, Type::i32); + } + void visitTry(Try* curr) { note(&curr->body, curr->type); for (auto& expr : curr->catchBodies) { diff --git a/src/ir/cost.h b/src/ir/cost.h index ab8ae90ae..11370acab 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -595,6 +595,9 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> { CostType visitTableCopy(TableCopy* curr) { return 6 + visit(curr->dest) + visit(curr->source) + visit(curr->size); } + CostType visitTableInit(TableInit* curr) { + return 6 + visit(curr->dest) + visit(curr->offset) + 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 be949a8bf..fee8b3441 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -752,6 +752,10 @@ private: parent.writesTable = true; parent.implicitTrap = true; } + void visitTableInit(TableInit* 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 38fa3e5f6..e7454c7c6 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -663,6 +663,7 @@ struct InfoCollector void visitTableGrow(TableGrow* curr) { addRoot(curr); } void visitTableFill(TableFill* curr) { addRoot(curr); } void visitTableCopy(TableCopy* curr) { addRoot(curr); } + void visitTableInit(TableInit* curr) {} void visitNop(Nop* curr) {} void visitUnreachable(Unreachable* curr) {} diff --git a/src/ir/subtype-exprs.h b/src/ir/subtype-exprs.h index f563cce14..640240df9 100644 --- a/src/ir/subtype-exprs.h +++ b/src/ir/subtype-exprs.h @@ -238,6 +238,11 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> { self()->noteSubtype(self()->getModule()->getTable(curr->sourceTable)->type, self()->getModule()->getTable(curr->destTable)->type); } + void visitTableInit(TableInit* curr) { + auto* seg = self()->getModule()->getElementSegment(curr->segment); + self()->noteSubtype(seg->type, + self()->getModule()->getTable(curr->table)->type); + } void visitTry(Try* curr) { self()->noteSubtype(curr->body, curr); for (auto* body : curr->catchBodies) { diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 5acb842d6..7e70b6776 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -674,6 +674,10 @@ struct NullInstrParserCtx { makeTableCopy(Index, const std::vector<Annotation>&, TableIdxT*, TableIdxT*) { return Ok{}; } + Result<> + makeTableInit(Index, const std::vector<Annotation>&, TableIdxT*, ElemIdxT) { + return Ok{}; + } Result<> makeThrow(Index, const std::vector<Annotation>&, TagIdxT) { return Ok{}; } @@ -2325,6 +2329,15 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> { return withLoc(pos, irBuilder.makeTableCopy(*dest, *src)); } + Result<> makeTableInit(Index pos, + const std::vector<Annotation>& annotations, + Name* table, + Name elem) { + auto t = getTable(pos, table); + CHECK_ERR(t); + return withLoc(pos, irBuilder.makeTableInit(elem, *t)); + } + Result<> makeThrow(Index pos, const std::vector<Annotation>& annotations, Name tag) { return withLoc(pos, irBuilder.makeThrow(tag)); diff --git a/src/parser/parsers.h b/src/parser/parsers.h index 23286bb28..85a1febb5 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -206,6 +206,8 @@ Result<> makeTableFill(Ctx&, Index, const std::vector<Annotation>&); template<typename Ctx> Result<> makeTableCopy(Ctx&, Index, const std::vector<Annotation>&); template<typename Ctx> +Result<> makeTableInit(Ctx&, Index, const std::vector<Annotation>&); +template<typename Ctx> Result<> makeThrow(Ctx&, Index, const std::vector<Annotation>&); template<typename Ctx> Result<> makeRethrow(Ctx&, Index, const std::vector<Annotation>&); @@ -2069,6 +2071,16 @@ makeTableCopy(Ctx& ctx, Index pos, const std::vector<Annotation>& annotations) { template<typename Ctx> Result<> +makeTableInit(Ctx& ctx, Index pos, const std::vector<Annotation>& annotations) { + auto table = maybeTableidx(ctx); + CHECK_ERR(table); + auto elem = elemidx(ctx); + CHECK_ERR(elem); + return ctx.makeTableInit(pos, annotations, table.getPtr(), *elem); +} + +template<typename Ctx> +Result<> makeThrow(Ctx& ctx, Index pos, const std::vector<Annotation>& annotations) { auto tag = tagidx(ctx); CHECK_ERR(tag); diff --git a/src/passes/Directize.cpp b/src/passes/Directize.cpp index 6cb4e46d8..7faf2e23a 100644 --- a/src/passes/Directize.cpp +++ b/src/passes/Directize.cpp @@ -266,6 +266,9 @@ struct Directize : public Pass { void visitTableCopy(TableCopy* curr) { tablesWithSet.insert(curr->destTable); } + void visitTableInit(TableInit* curr) { + tablesWithSet.insert(curr->table); + } }; Finder(tablesWithSet).walkFunction(func); diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 8bf30702c..3abe1f738 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2055,6 +2055,12 @@ struct PrintExpressionContents o << ' '; curr->sourceTable.print(o); } + void visitTableInit(TableInit* curr) { + printMedium(o, "table.init "); + curr->table.print(o); + o << ' '; + curr->segment.print(o); + } void visitTry(Try* curr) { printMedium(o, "try"); if (curr->name.is()) { diff --git a/src/passes/Table64Lowering.cpp b/src/passes/Table64Lowering.cpp index 65c9a2a63..71fc6a6fe 100644 --- a/src/passes/Table64Lowering.cpp +++ b/src/passes/Table64Lowering.cpp @@ -91,6 +91,10 @@ struct Table64Lowering : public WalkerPass<PostWalker<Table64Lowering>> { wrapAddress64(curr->size, curr->destTable); } + void visitTableInit(TableInit* curr) { + wrapAddress64(curr->dest, curr->table); + } + void visitCallIndirect(CallIndirect* curr) { wrapAddress64(curr->target, curr->table); } diff --git a/src/passes/TypeGeneralizing.cpp b/src/passes/TypeGeneralizing.cpp index faf01f173..fad55e506 100644 --- a/src/passes/TypeGeneralizing.cpp +++ b/src/passes/TypeGeneralizing.cpp @@ -499,6 +499,8 @@ struct TransferFn : OverriddenVisitor<TransferFn> { // Cannot generalize table types yet. } + void visitTableInit(TableInit* curr) {} + void visitTry(Try* curr) { WASM_UNREACHABLE("TODO"); } void visitTryTable(TryTable* curr) { WASM_UNREACHABLE("TODO"); } void visitThrow(Throw* curr) { WASM_UNREACHABLE("TODO"); } diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index 22e666a52..6c72f1c73 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -86,13 +86,6 @@ public: return ModuleRunnerBase<EvallingModuleRunner>::visitGlobalGet(curr); } - - Flow visitTableSet(TableSet* curr) { - // TODO: Full dynamic table support. For now we stop evalling when we see a - // table.set. (To support this we need to track sets and add code to - // serialize them.) - throw FailToEvalException("table.set: TODO"); - } }; // Build an artificial `env` module based on a module's imports, so that the @@ -174,6 +167,9 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface { // not yet been re-added are a blind spot for it). std::unordered_set<Name> usedGlobalNames; + // Set to true after we create the instance. + bool instanceInitialized = false; + CtorEvalExternalInterface( std::map<Name, std::shared_ptr<EvallingModuleRunner>> linkedInstances_ = {}) { @@ -363,7 +359,8 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface { } Index tableSize(Name tableName) override { - throw FailToEvalException("table size"); + // See callTable above, we assume the table is not modified FIXME + return wasm->getTableOrNull(tableName)->initial; } Literal tableLoad(Name tableName, Index index) override { @@ -371,7 +368,15 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface { } // called during initialization - void tableStore(Name tableName, Index index, const Literal& value) override {} + void tableStore(Name tableName, Index index, const Literal& value) override { + // We allow stores to the table during initialization, but not after, as we + // assume the table does not change at runtime. + // TODO: Allow table changes by updating the table later like we do with the + // memory, by tracking and serializing them. + if (instanceInitialized) { + throw FailToEvalException("tableStore after init: TODO"); + } + } int8_t load8s(Address addr, Name memoryName) override { return doLoad<int8_t>(addr, memoryName); @@ -1294,6 +1299,7 @@ void evalCtors(Module& wasm, try { // create an instance for evalling EvallingModuleRunner instance(wasm, &interface, linkedInstances); + interface.instanceInitialized = true; // go one by one, in order, until we fail // TODO: if we knew priorities, we could reorder? for (auto& ctor : ctors) { diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 989ce891a..32eb7657c 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1078,6 +1078,7 @@ enum ASTNodes { TableSize = 0x10, TableFill = 0x11, TableCopy = 0x0e, + TableInit = 0x0c, RefNull = 0xd0, RefIsNull = 0xd1, RefFunc = 0xd2, @@ -1752,6 +1753,7 @@ public: bool maybeVisitTableGrow(Expression*& out, uint32_t code); bool maybeVisitTableFill(Expression*& out, uint32_t code); bool maybeVisitTableCopy(Expression*& out, uint32_t code); + bool maybeVisitTableInit(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 a4f1c5cf9..72d2a1db0 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -775,6 +775,20 @@ public: ret->finalize(); return ret; } + TableInit* makeTableInit(Name segment, + Expression* dest, + Expression* offset, + Expression* size, + Name table) { + auto* ret = wasm.allocator.alloc<TableInit>(); + ret->segment = segment; + ret->dest = dest; + ret->offset = offset; + ret->size = size; + 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 03d46020e..3be040220 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -545,6 +545,14 @@ DELEGATE_FIELD_NAME_KIND(TableCopy, sourceTable, ModuleItemKind::Table) DELEGATE_FIELD_NAME_KIND(TableCopy, destTable, ModuleItemKind::Table) DELEGATE_FIELD_CASE_END(TableCopy) +DELEGATE_FIELD_CASE_START(TableInit) +DELEGATE_FIELD_CHILD(TableInit, size) +DELEGATE_FIELD_CHILD(TableInit, offset) +DELEGATE_FIELD_CHILD(TableInit, dest) +DELEGATE_FIELD_NAME_KIND(TableInit, segment, ModuleItemKind::ElementSegment) +DELEGATE_FIELD_NAME_KIND(TableInit, table, ModuleItemKind::Table) +DELEGATE_FIELD_CASE_END(TableInit) + DELEGATE_FIELD_CASE_START(Try) DELEGATE_FIELD_SCOPE_NAME_USE(Try, delegateTarget) DELEGATE_FIELD_CHILD_VECTOR(Try, catchBodies) diff --git a/src/wasm-delegations.def b/src/wasm-delegations.def index ea801ab9b..f4552a98b 100644 --- a/src/wasm-delegations.def +++ b/src/wasm-delegations.def @@ -64,6 +64,7 @@ DELEGATE(TableSize); DELEGATE(TableGrow); DELEGATE(TableFill); DELEGATE(TableCopy); +DELEGATE(TableInit); DELEGATE(Try); DELEGATE(TryTable); DELEGATE(Throw); diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 7fd5b3cd3..644a141a2 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1417,6 +1417,7 @@ public: Flow visitTableGrow(TableGrow* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTableFill(TableFill* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTableCopy(TableCopy* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitTableInit(TableInit* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTry(Try* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTryTable(TryTable* curr) { WASM_UNREACHABLE("unimp"); } Flow visitThrow(Throw* curr) { @@ -2354,6 +2355,10 @@ public: NOTE_ENTER("TableCopy"); return Flow(NONCONSTANT_FLOW); } + Flow visitTableInit(TableInit* curr) { + NOTE_ENTER("TableInit"); + return Flow(NONCONSTANT_FLOW); + } Flow visitLoad(Load* curr) { NOTE_ENTER("Load"); return Flow(NONCONSTANT_FLOW); @@ -2821,23 +2826,24 @@ private: } } + Const zero; + zero.value = Literal(uint32_t(0)); + zero.finalize(); + ModuleUtils::iterActiveElementSegments(wasm, [&](ElementSegment* segment) { - Address offset = - (uint32_t)self()->visit(segment->offset).getSingleValue().geti32(); - - Table* table = wasm.getTable(segment->table); - ExternalInterface* extInterface = externalInterface; - Name tableName = segment->table; - if (table->imported()) { - auto inst = linkedInstances.at(table->module); - extInterface = inst->externalInterface; - tableName = inst->wasm.getExport(table->base)->value; - } + Const size; + size.value = Literal(uint32_t(segment->data.size())); + size.finalize(); - for (Index i = 0; i < segment->data.size(); ++i) { - Flow ret = self()->visit(segment->data[i]); - extInterface->tableStore(tableName, offset + i, ret.getSingleValue()); - } + TableInit init; + init.table = segment->table; + init.segment = segment->name; + init.dest = segment->offset; + init.offset = &zero; + init.size = &size; + init.finalize(); + + self()->visit(&init); droppedElementSegments.insert(segment->name); }); @@ -2865,9 +2871,10 @@ private: void initializeMemoryContents() { initializeMemorySizes(); - Const offset; - offset.value = Literal(uint32_t(0)); - offset.finalize(); + + Const zero; + zero.value = Literal(uint32_t(0)); + zero.finalize(); // apply active memory segments for (size_t i = 0, e = wasm.dataSegments.size(); i < e; ++i) { @@ -2883,7 +2890,7 @@ private: init.memory = segment->memory; init.segment = segment->name; init.dest = segment->offset; - init.offset = &offset; + init.offset = &zero; init.size = &size; init.finalize(); @@ -3251,6 +3258,54 @@ public: return {}; } + Flow visitTableInit(TableInit* curr) { + NOTE_ENTER("TableInit"); + Flow dest = self()->visit(curr->dest); + if (dest.breaking()) { + return dest; + } + Flow offset = self()->visit(curr->offset); + if (offset.breaking()) { + return offset; + } + Flow size = self()->visit(curr->size); + if (size.breaking()) { + return size; + } + NOTE_EVAL1(dest); + NOTE_EVAL1(offset); + NOTE_EVAL1(size); + + auto* segment = wasm.getElementSegment(curr->segment); + + Address destVal(dest.getSingleValue().getUnsigned()); + Address offsetVal(uint32_t(offset.getSingleValue().geti32())); + Address sizeVal(uint32_t(size.getSingleValue().geti32())); + + if (offsetVal + sizeVal > 0 && + droppedElementSegments.count(curr->segment)) { + trap("out of bounds segment access in table.init"); + } + if (offsetVal + sizeVal > segment->data.size()) { + trap("out of bounds segment access in table.init"); + } + auto info = getTableInstanceInfo(curr->table); + auto tableSize = info.interface()->tableSize(info.name); + if (destVal + sizeVal > tableSize) { + trap("out of bounds table access in table.init"); + } + for (size_t i = 0; i < sizeVal; ++i) { + // FIXME: We should not call visit() here more than once at runtime. The + // values in the segment should be computed once during startup, + // and then read here as needed. For example, if we had a + // struct.new here then we should not allocate a new struct each + // time we table.init that data. + auto value = self()->visit(segment->data[offsetVal + i]).getSingleValue(); + info.interface()->tableStore(info.name, destVal + i, value); + } + return {}; + } + Flow visitLocalGet(LocalGet* curr) { NOTE_ENTER("LocalGet"); auto index = curr->index; @@ -3741,7 +3796,7 @@ public: if (offsetVal + sizeVal > 0 && droppedDataSegments.count(curr->segment)) { trap("out of bounds segment access in memory.init"); } - if ((uint64_t)offsetVal + sizeVal > segment->data.size()) { + if (offsetVal + sizeVal > segment->data.size()) { trap("out of bounds segment access in memory.init"); } auto info = getMemoryInstanceInfo(curr->memory); diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h index eff8c85ad..d7a1dde87 100644 --- a/src/wasm-ir-builder.h +++ b/src/wasm-ir-builder.h @@ -169,6 +169,7 @@ public: [[nodiscard]] Result<> makeTableGrow(Name table); [[nodiscard]] Result<> makeTableFill(Name table); [[nodiscard]] Result<> makeTableCopy(Name destTable, Name srcTable); + [[nodiscard]] Result<> makeTableInit(Name elem, Name table); [[nodiscard]] Result<> makeTry(Name label, Type type); [[nodiscard]] Result<> makeTryTable(Name label, Type type, diff --git a/src/wasm.h b/src/wasm.h index e47b7c6ed..9d43a4f9f 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -678,6 +678,7 @@ public: TableGrowId, TableFillId, TableCopyId, + TableInitId, TryId, TryTableId, ThrowId, @@ -1421,6 +1422,20 @@ public: void finalize(); }; +class TableInit : public SpecificExpression<Expression::TableInitId> { +public: + TableInit() = default; + TableInit(MixedArena& allocator) : TableInit() {} + + Name segment; + Expression* dest; + Expression* offset; + Expression* size; + Name table; + + void finalize(); +}; + // 'try' from the old (Phase 3) EH proposal class Try : public SpecificExpression<Expression::TryId> { public: diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 4082fbf5f..2abb32837 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -4223,6 +4223,9 @@ BinaryConsts::ASTNodes WasmBinaryReader::readExpression(Expression*& curr) { if (maybeVisitTableCopy(curr, opcode)) { break; } + if (maybeVisitTableInit(curr, opcode)) { + break; + } if (maybeVisitLoad(curr, opcode, BinaryConsts::MiscPrefix)) { break; } @@ -5646,6 +5649,23 @@ bool WasmBinaryReader::maybeVisitTableCopy(Expression*& out, uint32_t code) { return true; } +bool WasmBinaryReader::maybeVisitTableInit(Expression*& out, uint32_t code) { + if (code != BinaryConsts::TableInit) { + return false; + } + auto* curr = allocator.alloc<TableInit>(); + curr->size = popNonVoidExpression(); + curr->offset = popNonVoidExpression(); + curr->dest = popNonVoidExpression(); + Index segIdx = getU32LEB(); + elemRefs[segIdx].push_back(&curr->segment); + Index memIdx = getU32LEB(); + tableRefs[memIdx].push_back(&curr->table); + curr->finalize(); + out = curr; + return true; +} + bool WasmBinaryReader::maybeVisitBinary(Expression*& out, uint8_t code) { Binary* curr; #define INT_TYPED_CODE(code) \ diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 3db6238c4..2f2f3b595 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -1523,6 +1523,14 @@ Result<> IRBuilder::makeTableCopy(Name destTable, Name srcTable) { return Ok{}; } +Result<> IRBuilder::makeTableInit(Name elem, Name table) { + TableInit curr; + curr.table = table; + CHECK_ERR(visitTableInit(&curr)); + push(builder.makeTableInit(elem, curr.dest, curr.offset, curr.size, table)); + return Ok{}; +} + Result<> IRBuilder::makeTry(Name label, Type type) { auto* tryy = wasm.allocator.alloc<Try>(); tryy->type = type; diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index a9b582f43..1c2c2c42b 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2053,6 +2053,12 @@ void BinaryInstWriter::visitTableCopy(TableCopy* curr) { o << U32LEB(parent.getTableIndex(curr->sourceTable)); } +void BinaryInstWriter::visitTableInit(TableInit* curr) { + o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::TableInit); + o << U32LEB(parent.getElementSegmentIndex(curr->segment)); + 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 b032e6bad..758ae158e 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -469,6 +469,7 @@ public: void visitTableGrow(TableGrow* curr); void visitTableFill(TableFill* curr); void visitTableCopy(TableCopy* curr); + void visitTableInit(TableInit* curr); void noteDelegate(Name name, Expression* curr); void noteRethrow(Name name, Expression* curr); void visitTry(Try* curr); @@ -2443,12 +2444,41 @@ void FunctionValidator::visitTableCopy(TableCopy* curr) { curr, "table.copy source must have right type for dest"); } + shouldBeEqualOrFirstIsUnreachable(curr->dest->type, + destTable->indexType, + curr, + "table.copy dest must be valid"); + shouldBeEqualOrFirstIsUnreachable(curr->source->type, + sourceTable->indexType, + curr, + "table.copy source must be valid"); + Type sizeType = + sourceTable->is64() && destTable->is64() ? Type::i64 : Type::i32; shouldBeEqualOrFirstIsUnreachable( - curr->dest->type, Type(Type::i32), curr, "table.copy dest must be i32"); + curr->size->type, sizeType, curr, "table.copy size must be valid"); +} + +void FunctionValidator::visitTableInit(TableInit* curr) { + shouldBeTrue(getModule()->features.hasBulkMemory(), + curr, + "table.init requires bulk-memory [--enable-bulk-memory]"); + auto* segment = getModule()->getElementSegment(curr->segment); + auto* table = getModule()->getTableOrNull(curr->table); + if (shouldBeTrue(!!segment, curr, "table.init segment must exist") && + shouldBeTrue(!!table, curr, "table.init table must exist")) { + shouldBeSubType(segment->type, + table->type, + curr, + "table.init source must have right type for dest"); + } shouldBeEqualOrFirstIsUnreachable( - curr->source->type, Type(Type::i32), curr, "table.copy source must be i32"); + curr->dest->type, table->indexType, curr, "table.init dest must be valid"); + shouldBeEqualOrFirstIsUnreachable(curr->offset->type, + Type(Type::i32), + curr, + "table.init offset must be valid"); shouldBeEqualOrFirstIsUnreachable( - curr->size->type, Type(Type::i32), curr, "table.copy size must be i32"); + curr->size->type, Type(Type::i32), curr, "table.init size must be valid"); } void FunctionValidator::noteDelegate(Name name, Expression* curr) { diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index d138e4226..ff641c7eb 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -875,6 +875,14 @@ void TableCopy::finalize() { } } +void TableInit::finalize() { + type = Type::none; + if (dest->type == Type::unreachable || offset->type == Type::unreachable || + size->type == Type::unreachable) { + type = Type::unreachable; + } +} + void Try::finalize(std::optional<Type> type_) { if (type_) { type = *type_; diff --git a/src/wasm2js.h b/src/wasm2js.h index 7fa923ceb..15ae019ea 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2241,6 +2241,10 @@ Ref Wasm2JSBuilder::processExpression(Expression* curr, visit(curr->source, EXPRESSION_RESULT), visit(curr->size, EXPRESSION_RESULT)); } + Ref visitTableInit(TableInit* curr) { + unimplemented(curr); + WASM_UNREACHABLE("unimp"); + } Ref visitTry(Try* curr) { unimplemented(curr); WASM_UNREACHABLE("unimp"); |