From a7f5845a4e41704c1fea0ffe90f67fae6f09350a Mon Sep 17 00:00:00 2001 From: Nate Biggs Date: Wed, 4 Sep 2024 21:58:12 +0000 Subject: [PATCH] [dart2wasm] Add deferred loading support to dart2wasm (5/X). Add support for a StaticTable which holds references to known functions that need to be called across modules. For calls that target the DispatchTable we will still go through there if possible. But for any functions not referenced in that table (including any compiler generated functions) we add a separate static table. Also adds import/export support to both the DispatchTable and the StaticTable. The table will always be defined in the main module and imported into subsequent modules. Change-Id: Iedc683d1ecfe721393900913826010cdd9b2c3c4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/381323 Reviewed-by: Martin Kustermann --- pkg/dart2wasm/lib/code_generator.dart | 15 ++- pkg/dart2wasm/lib/dispatch_table.dart | 42 ++++++-- pkg/dart2wasm/lib/static_dispatch_table.dart | 99 +++++++++++++++++++ pkg/dart2wasm/lib/translator.dart | 33 ++++++- .../lib/src/builder/function.dart | 8 +- .../lib/src/builder/functions.dart | 2 +- pkg/wasm_builder/lib/src/builder/global.dart | 8 +- pkg/wasm_builder/lib/src/builder/globals.dart | 3 +- .../lib/src/builder/instructions.dart | 1 + pkg/wasm_builder/lib/src/builder/module.dart | 7 +- pkg/wasm_builder/lib/src/builder/table.dart | 2 +- pkg/wasm_builder/lib/src/builder/tables.dart | 11 +++ pkg/wasm_builder/lib/src/builder/types.dart | 8 +- pkg/wasm_builder/lib/src/ir/function.dart | 10 +- pkg/wasm_builder/lib/src/ir/functions.dart | 1 + pkg/wasm_builder/lib/src/ir/global.dart | 10 +- pkg/wasm_builder/lib/src/ir/globals.dart | 1 + pkg/wasm_builder/lib/src/ir/table.dart | 4 +- pkg/wasm_builder/lib/src/ir/type.dart | 12 +++ .../lib/src/serialize/sections.dart | 12 ++- 20 files changed, 250 insertions(+), 39 deletions(-) create mode 100644 pkg/dart2wasm/lib/static_dispatch_table.dart diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart index 1c5472b70ad..33314a0f582 100644 --- a/pkg/dart2wasm/lib/code_generator.dart +++ b/pkg/dart2wasm/lib/code_generator.dart @@ -707,7 +707,15 @@ abstract class AstCodeGenerator } List call(Reference target, {bool useUncheckedEntry = false}) { - return b.invoke(translator.directCallTarget(target, useUncheckedEntry)); + final targetModule = translator.moduleForReference(target); + final isLocalModuleCall = targetModule == b.module; + + if (isLocalModuleCall) { + return b.invoke(translator.directCallTarget(target, useUncheckedEntry)); + } else { + b.comment('Indirect call to $target'); + return translator.callReference(target, b); + } } @override @@ -1912,7 +1920,8 @@ abstract class AstCodeGenerator b.i32_const(offset); b.i32_add(); } - b.call_indirect(selector.signature, translator.dispatchTable.wasmTable); + b.call_indirect( + selector.signature, translator.dispatchTable.getWasmTable(b.module)); translator.functions.recordSelectorUse(selector); } @@ -4516,7 +4525,7 @@ abstract class CallTarget { /// The wasm target function to call. /// - /// This should only be accessed if caller intents to call it, as it will + /// This should only be accessed if caller intends to call it, as it will /// enqueue the function in the compilation queue. w.BaseFunction get function; } diff --git a/pkg/dart2wasm/lib/dispatch_table.dart b/pkg/dart2wasm/lib/dispatch_table.dart index 6e17a75cc00..1491dff3c7a 100644 --- a/pkg/dart2wasm/lib/dispatch_table.dart +++ b/pkg/dart2wasm/lib/dispatch_table.dart @@ -69,8 +69,6 @@ class SelectorInfo { /// class member for this selector. int? offset; - w.ModuleBuilder get m => translator.m; - /// The selector's member's name. String get name => paramInfo.member!.name.text; @@ -215,6 +213,9 @@ class SelectorInfo { /// Builds the dispatch table for member calls. class DispatchTable { + static const _tableName = 'dispatch'; + static const _functionType = w.RefType.func(nullable: true); + final Translator translator; final List _selectorMetadata; final Map _procedureAttributeMetadata; @@ -236,10 +237,14 @@ class DispatchTable { /// member for the selector. late final List _table; - /// The Wasm table for the dispatch table. - late final w.TableBuilder wasmTable; + late final w.TableBuilder _definedWasmTable; + final Map _importedWasmTables = {}; - w.ModuleBuilder get m => translator.m; + /// The Wasm table for the dispatch table. + w.Table getWasmTable(w.ModuleBuilder module) => + translator.isMainModule(module) + ? _definedWasmTable + : _importedWasmTables[module]!; DispatchTable(this.translator) : _selectorMetadata = @@ -499,7 +504,17 @@ class DispatchTable { selectors[i].offset = rows[i].offset; } - wasmTable = m.tables.define(w.RefType.func(nullable: true), _table.length); + _definedWasmTable = + translator.mainModule.tables.define(_functionType, _table.length); + if (translator.hasMultipleModules) { + final mainModuleName = translator.nameForModule(translator.mainModule); + translator.mainModule.exports.export(_tableName, _definedWasmTable); + for (final module in translator.modules) { + if (translator.isMainModule(module)) continue; + _importedWasmTables[module] = module.tables + .import(mainModuleName, _tableName, _functionType, _table.length); + } + } } void output() { @@ -507,8 +522,21 @@ class DispatchTable { Reference? target = _table[i]; if (target != null) { w.BaseFunction? fun = translator.functions.getExistingFunction(target); + // Any call to the dispatch table is guaranteed to hit a target. + // + // If a target is in a deferred module and that deferred module hasn't + // been loaded yet, then the entry is `null`. + // + // Though we can only hit a target if that target's class has been + // allocated. In order for the class to be allocated, the deferred + // module must've been loaded to call the constructor. if (fun != null) { - wasmTable.setElement(i, fun); + final targetModule = translator.moduleForReference(target); + if (translator.isMainModule(targetModule)) { + _definedWasmTable.setElement(i, fun); + } else { + _importedWasmTables[targetModule]!.setElements[fun] = i; + } } } } diff --git a/pkg/dart2wasm/lib/static_dispatch_table.dart b/pkg/dart2wasm/lib/static_dispatch_table.dart new file mode 100644 index 00000000000..bf9a77751c6 --- /dev/null +++ b/pkg/dart2wasm/lib/static_dispatch_table.dart @@ -0,0 +1,99 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:collection'; + +import 'package:wasm_builder/wasm_builder.dart' as w; + +import 'translator.dart'; + +class StaticDispatchTables { + final Translator translator; + + final Map _tables = + LinkedHashMap( + hashCode: (t) => + Object.hash(Object.hashAll(t.inputs), Object.hashAll(t.outputs)), + equals: (t1, t2) => t1.isStructurallyEqualTo(t2)); + + StaticDispatchTables(this.translator); + + StaticDispatchTableForSignature getTableForType(w.FunctionType type) { + return _tables[type] ??= + StaticDispatchTableForSignature(translator, type, _tables.length); + } + + void outputTables() { + for (final table in _tables.values) { + table.output(); + } + } +} + +/// Builds a static dispatch table for a specific function type signature. +/// +/// All calls to this table will have the same signature and so `call_indirect` +/// instructions that reference this table can omit the type check. +class StaticDispatchTableForSignature { + final String _tableName; + final w.FunctionType _functionType; + + final Translator translator; + + /// Contents of wasm table. + final Map _table = {}; + + late final w.TableBuilder _definedWasmTable; + final Map _importedWasmTables = {}; + + StaticDispatchTableForSignature( + this.translator, this._functionType, int nameCounter) + : _tableName = 'static$nameCounter' { + _definedWasmTable = translator.mainModule.tables + .define(w.RefType(_functionType, nullable: true), _table.length); + } + + /// Gets the wasm table used to reference this static dispatch table in + /// [module]. + /// + /// This can either be the table definition itself or an import of it. Imports + /// the table into [module] if it is not imported yet. + w.Table getWasmTable(w.ModuleBuilder module) { + if (translator.isMainModule(module)) { + return _definedWasmTable; + } + if (_importedWasmTables.isEmpty) { + translator.mainModule.exports.export(_tableName, _definedWasmTable); + } + return _importedWasmTables.putIfAbsent(module, () { + final mainModuleName = translator.nameForModule(translator.mainModule); + return module.tables.import(mainModuleName, _tableName, + w.RefType(_functionType, nullable: true), _table.length); + }); + } + + /// Returns the index for [function] in the table allocating one if necessary. + int indexForFunction(w.BaseFunction function) { + assert(function.type.isStructurallyEqualTo(function.type)); + return _table[function] ??= _table.length; + } + + void output() { + final importedTables = _importedWasmTables; + _table.forEach((fun, index) { + final targetModule = fun.enclosingModule; + if (translator.isMainModule(targetModule)) { + _definedWasmTable.setElement(index, fun); + } else { + (getWasmTable(targetModule) as w.ImportedTable).setElements[fun] = + index; + } + }); + + _definedWasmTable.minSize = _table.length; + for (final table in importedTables.values) { + table.minSize = _table.length; + } + } +} diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart index 79b8234874e..34676e6b3ea 100644 --- a/pkg/dart2wasm/lib/translator.dart +++ b/pkg/dart2wasm/lib/translator.dart @@ -26,6 +26,7 @@ import 'kernel_nodes.dart'; import 'param_info.dart'; import 'records.dart'; import 'reference_extensions.dart'; +import 'static_dispatch_table.dart'; import 'tags.dart'; import 'types.dart'; import 'util.dart' as util; @@ -99,6 +100,7 @@ class Translator with KernelNodes { final LibraryIndex index; late final ClosureLayouter closureLayouter; late final ClassInfoCollector classInfoCollector; + late final StaticDispatchTables staticTablesPerType; late final DispatchTable dispatchTable; late final Globals globals; late final Constants constants; @@ -305,6 +307,7 @@ class Translator with KernelNodes { subtypes = hierarchy.computeSubtypesInformation(); closureLayouter = ClosureLayouter(this); classInfoCollector = ClassInfoCollector(this); + staticTablesPerType = StaticDispatchTables(this); dispatchTable = DispatchTable(this); compilationQueue = CompilationQueue(); functions = FunctionCollector(this); @@ -337,6 +340,7 @@ class Translator with KernelNodes { constructorClosures.clear(); dispatchTable.output(); + staticTablesPerType.outputTables(); initFunction.body.end(); for (ConstantInfo info in constants.constantInfo.values) { @@ -375,14 +379,36 @@ class Translator with KernelNodes { return callFunction(functions.getFunction(reference), b); } + final Map> + _importedFunctions = {}; + /// Generates a set of instructions to call [function] adding indirection /// if the call crosses a module boundary. Calls the function directly if it /// is local. Imports the function and calls it directly if is in the main /// module. Otherwise does an indirect call through the static dispatch table. List callFunction( w.BaseFunction function, w.InstructionsBuilder b) { - // TODO(natebiggs): Add indirect call. - b.call(function); + final targetModule = function.enclosingModule; + // TODO(natebiggs): Consider inlining function body in some scenarios. + if (targetModule == b.module) { + b.call(function); + } else if (isMainModule(targetModule)) { + final importedFunctions = _importedFunctions.putIfAbsent(function, () { + final importName = 'func${_importedFunctions.length}'; + targetModule.exports.export(importName, function); + return {}; + }); + final importedFunction = importedFunctions[b.module] ??= + b.module.functions.import(nameForModule(targetModule), + function.exportedName!, function.type); + b.call(importedFunction); + } else { + final staticTable = staticTablesPerType.getTableForType(function.type); + b.i32_const(staticTable.indexForFunction(function)); + b.table_get(staticTable.getWasmTable(b.module)); + b.ref_as_non_null(); + b.call_ref(function.type); + } return function.type.outputs; } @@ -1774,7 +1800,8 @@ class PolymorphicDispatcherCodeGenerator implements CodeGenerator { b.struct_get(translator.topInfo.struct, FieldIndex.classId); b.i32_const(selector.offset!); b.i32_add(); - b.call_indirect(signature, translator.dispatchTable.wasmTable); + b.call_indirect( + signature, translator.dispatchTable.getWasmTable(b.module)); translator.functions.recordSelectorUse(selector); } diff --git a/pkg/wasm_builder/lib/src/builder/function.dart b/pkg/wasm_builder/lib/src/builder/function.dart index 77b55545059..5be1e5ab263 100644 --- a/pkg/wasm_builder/lib/src/builder/function.dart +++ b/pkg/wasm_builder/lib/src/builder/function.dart @@ -13,14 +13,14 @@ class FunctionBuilder extends ir.BaseFunction /// The body of the function. late final InstructionsBuilder body; - FunctionBuilder(ModuleBuilder module, super.index, super.type, + FunctionBuilder(super.enclosingModule, super.index, super.type, [super.functionName]) { - body = InstructionsBuilder(module, type.inputs, type.outputs); + body = InstructionsBuilder(enclosingModule, type.inputs, type.outputs); } @override - ir.DefinedFunction forceBuild() => - ir.DefinedFunction(body.build(), finalizableIndex, type, functionName); + ir.DefinedFunction forceBuild() => ir.DefinedFunction( + enclosingModule, body.build(), finalizableIndex, type, functionName); @override String toString() => exportedName ?? "#$finalizableIndex"; diff --git a/pkg/wasm_builder/lib/src/builder/functions.dart b/pkg/wasm_builder/lib/src/builder/functions.dart index 11d35fcec70..2969a5f815f 100644 --- a/pkg/wasm_builder/lib/src/builder/functions.dart +++ b/pkg/wasm_builder/lib/src/builder/functions.dart @@ -55,7 +55,7 @@ class FunctionsBuilder with Builder { ir.ImportedFunction import(String module, String name, ir.FunctionType type, [String? functionName]) { final function = ir.ImportedFunction( - module, name, ir.FinalizableIndex(), type, functionName); + _module, module, name, ir.FinalizableIndex(), type, functionName); _importedFunctions.add(function); _addName(functionName, function); return function; diff --git a/pkg/wasm_builder/lib/src/builder/global.dart b/pkg/wasm_builder/lib/src/builder/global.dart index d5d7f9026e9..920f952f7c5 100644 --- a/pkg/wasm_builder/lib/src/builder/global.dart +++ b/pkg/wasm_builder/lib/src/builder/global.dart @@ -8,11 +8,11 @@ part of 'globals.dart'; class GlobalBuilder extends ir.Global with IndexableBuilder { final InstructionsBuilder initializer; - GlobalBuilder(ModuleBuilder module, super.index, super.type, + GlobalBuilder(super.enclosingModule, super.index, super.type, [super.globalName]) - : initializer = InstructionsBuilder(module, [], [type.type]); + : initializer = InstructionsBuilder(enclosingModule, [], [type.type]); @override - ir.DefinedGlobal forceBuild() => - ir.DefinedGlobal(initializer.build(), finalizableIndex, type, globalName); + ir.DefinedGlobal forceBuild() => ir.DefinedGlobal( + enclosingModule, initializer.build(), finalizableIndex, type, globalName); } diff --git a/pkg/wasm_builder/lib/src/builder/globals.dart b/pkg/wasm_builder/lib/src/builder/globals.dart index 259f5d9e318..25eb5284f23 100644 --- a/pkg/wasm_builder/lib/src/builder/globals.dart +++ b/pkg/wasm_builder/lib/src/builder/globals.dart @@ -42,7 +42,8 @@ class GlobalsBuilder with Builder { /// Imports a global variable into this module. ir.ImportedGlobal import(String module, String name, ir.GlobalType type) { - final global = ir.ImportedGlobal(module, name, ir.FinalizableIndex(), type); + final global = + ir.ImportedGlobal(_module, module, name, ir.FinalizableIndex(), type); _importedGlobals.add(global); return global; } diff --git a/pkg/wasm_builder/lib/src/builder/instructions.dart b/pkg/wasm_builder/lib/src/builder/instructions.dart index 548f979b75f..01155f78aa2 100644 --- a/pkg/wasm_builder/lib/src/builder/instructions.dart +++ b/pkg/wasm_builder/lib/src/builder/instructions.dart @@ -635,6 +635,7 @@ class InstructionsBuilder with Builder { void call(ir.BaseFunction function) { assert(_verifyTypes(function.type.inputs, function.type.outputs, trace: ['call', function])); + assert(function.enclosingModule == module); _add(ir.Call(function)); } diff --git a/pkg/wasm_builder/lib/src/builder/module.dart b/pkg/wasm_builder/lib/src/builder/module.dart index 9a58e3fe149..f2ff5e68b97 100644 --- a/pkg/wasm_builder/lib/src/builder/module.dart +++ b/pkg/wasm_builder/lib/src/builder/module.dart @@ -10,7 +10,7 @@ import 'builder.dart'; class ModuleBuilder with Builder { final Uri? sourceMapUrl; final List watchPoints; - late final types = TypesBuilder(this); + late final TypesBuilder types; late final functions = FunctionsBuilder(this); final tables = TablesBuilder(); final memories = MemoriesBuilder(); @@ -25,7 +25,10 @@ class ModuleBuilder with Builder { /// bytes to watch. When the module is serialized, the stack traces leading /// to the production of all watched bytes are printed. This can be used to /// debug runtime errors happening at specific offsets within the module. - ModuleBuilder(this.sourceMapUrl, {this.watchPoints = const []}); + ModuleBuilder(this.sourceMapUrl, + {ModuleBuilder? parent, this.watchPoints = const []}) { + types = TypesBuilder(this, parent: parent?.types); + } @override ir.Module forceBuild() { diff --git a/pkg/wasm_builder/lib/src/builder/table.dart b/pkg/wasm_builder/lib/src/builder/table.dart index 7eae81fcfb9..2b6e04d2403 100644 --- a/pkg/wasm_builder/lib/src/builder/table.dart +++ b/pkg/wasm_builder/lib/src/builder/table.dart @@ -12,7 +12,7 @@ class TableBuilder extends ir.Table with IndexableBuilder { : elements = List.filled(minSize, null, growable: true); void setElement(int index, ir.BaseFunction function) { - assert(type == ir.RefType.func(nullable: true), + assert(type.isSubtypeOf(ir.RefType.func(nullable: true)), "Elements are only supported for funcref tables"); assert(maxSize == null || index < maxSize!, 'Index $index greater than max table size $maxSize'); diff --git a/pkg/wasm_builder/lib/src/builder/tables.dart b/pkg/wasm_builder/lib/src/builder/tables.dart index fe58dd5958a..22f231f2b76 100644 --- a/pkg/wasm_builder/lib/src/builder/tables.dart +++ b/pkg/wasm_builder/lib/src/builder/tables.dart @@ -36,4 +36,15 @@ class TablesBuilder with Builder { _importedTables, _tableBuilders); return ir.Tables(_importedTables, built); } + + void collectUsedTypes(Set types) { + for (final table in _tableBuilders) { + final defType = table.type.containedDefType; + if (defType != null) types.add(defType); + } + for (final table in _importedTables) { + final defType = table.type.containedDefType; + if (defType != null) types.add(defType); + } + } } diff --git a/pkg/wasm_builder/lib/src/builder/types.dart b/pkg/wasm_builder/lib/src/builder/types.dart index 2aa26fa57c3..94ce6d5cacd 100644 --- a/pkg/wasm_builder/lib/src/builder/types.dart +++ b/pkg/wasm_builder/lib/src/builder/types.dart @@ -235,10 +235,11 @@ class _RecGroupBuilder { class TypesBuilder with Builder { final ModuleBuilder _module; - late final Map<_FunctionTypeKey, ir.FunctionType> _functionTypeMap = {}; - late final _RecGroupBuilder _recGroupBuilder = _RecGroupBuilder(); + final Map<_FunctionTypeKey, ir.FunctionType> _functionTypeMap = {}; + final _RecGroupBuilder _recGroupBuilder; - TypesBuilder(this._module); + TypesBuilder(this._module, {TypesBuilder? parent}) + : _recGroupBuilder = parent?._recGroupBuilder ?? _RecGroupBuilder(); /// Add a new function type to the module. /// @@ -287,6 +288,7 @@ class TypesBuilder with Builder { Set _collectUsedTypes() { final usedTypes = {}; + _module.tables.collectUsedTypes(usedTypes); _module.functions.collectUsedTypes(usedTypes); _module.globals.collectUsedTypes(usedTypes); _module.tags.collectUsedTypes(usedTypes); diff --git a/pkg/wasm_builder/lib/src/ir/function.dart b/pkg/wasm_builder/lib/src/ir/function.dart index e39f23af8c8..5ccfd9691e9 100644 --- a/pkg/wasm_builder/lib/src/ir/function.dart +++ b/pkg/wasm_builder/lib/src/ir/function.dart @@ -21,9 +21,11 @@ abstract class BaseFunction with Indexable implements Exportable { final FinalizableIndex finalizableIndex; final FunctionType type; final String? functionName; + final ModuleBuilder enclosingModule; String? exportedName; - BaseFunction(this.finalizableIndex, this.type, this.functionName); + BaseFunction(this.enclosingModule, this.finalizableIndex, this.type, + this.functionName); @override String get name => functionName ?? super.name; @@ -44,7 +46,8 @@ class DefinedFunction extends BaseFunction implements Serializable { /// All local variables defined in the function, including its inputs. List get locals => body.locals; - DefinedFunction(this.body, super.finalizableIndex, super.type, + DefinedFunction( + super.enclosingModule, this.body, super.finalizableIndex, super.type, [super.functionName]); @override @@ -85,7 +88,8 @@ class ImportedFunction extends BaseFunction implements Import { @override final String name; - ImportedFunction(this.module, this.name, super.finalizableIndex, super.type, + ImportedFunction(super.enclosingModule, this.module, this.name, + super.finalizableIndex, super.type, [super.functionName]); @override diff --git a/pkg/wasm_builder/lib/src/ir/functions.dart b/pkg/wasm_builder/lib/src/ir/functions.dart index 759405ef512..7ccc03550cc 100644 --- a/pkg/wasm_builder/lib/src/ir/functions.dart +++ b/pkg/wasm_builder/lib/src/ir/functions.dart @@ -2,6 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import '../builder/module.dart'; import '../serialize/serialize.dart'; import 'ir.dart'; diff --git a/pkg/wasm_builder/lib/src/ir/global.dart b/pkg/wasm_builder/lib/src/ir/global.dart index c31b03b3a37..f56b9d31ca0 100644 --- a/pkg/wasm_builder/lib/src/ir/global.dart +++ b/pkg/wasm_builder/lib/src/ir/global.dart @@ -9,11 +9,13 @@ abstract class Global with Indexable implements Exportable { @override final FinalizableIndex finalizableIndex; final GlobalType type; + final ModuleBuilder enclosingModule; /// Name of the global in the names section. final String? globalName; - Global(this.finalizableIndex, this.type, this.globalName); + Global( + this.enclosingModule, this.finalizableIndex, this.type, this.globalName); @override String toString() => globalName ?? "$finalizableIndex"; @@ -26,7 +28,8 @@ abstract class Global with Indexable implements Exportable { class DefinedGlobal extends Global implements Serializable { final Instructions initializer; - DefinedGlobal(this.initializer, super.finalizableIndex, super.type, + DefinedGlobal(super.enclosingModule, this.initializer, super.finalizableIndex, + super.type, [super.globalName]); @override @@ -44,7 +47,8 @@ class ImportedGlobal extends Global implements Import { @override final String name; - ImportedGlobal(this.module, this.name, super.finalizableIndex, super.type, + ImportedGlobal(super.enclosingModule, this.module, this.name, + super.finalizableIndex, super.type, [super.globalName]); @override diff --git a/pkg/wasm_builder/lib/src/ir/globals.dart b/pkg/wasm_builder/lib/src/ir/globals.dart index 4a1fda01926..1f04d32d014 100644 --- a/pkg/wasm_builder/lib/src/ir/globals.dart +++ b/pkg/wasm_builder/lib/src/ir/globals.dart @@ -2,6 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import '../builder/module.dart'; import '../serialize/serialize.dart'; import 'ir.dart'; diff --git a/pkg/wasm_builder/lib/src/ir/table.dart b/pkg/wasm_builder/lib/src/ir/table.dart index cc2b2ceb9e6..b6dad3289b1 100644 --- a/pkg/wasm_builder/lib/src/ir/table.dart +++ b/pkg/wasm_builder/lib/src/ir/table.dart @@ -9,7 +9,9 @@ class Table with Indexable implements Exportable, Serializable { @override final FinalizableIndex finalizableIndex; final RefType type; - final int minSize; + // Mutable so that a table's size does not need to be known prior to the table + // being instantiated. + int minSize; final int? maxSize; Table(this.finalizableIndex, this.type, this.minSize, this.maxSize); diff --git a/pkg/wasm_builder/lib/src/ir/type.dart b/pkg/wasm_builder/lib/src/ir/type.dart index 4829599d2b9..75ea1faeaa9 100644 --- a/pkg/wasm_builder/lib/src/ir/type.dart +++ b/pkg/wasm_builder/lib/src/ir/type.dart @@ -697,6 +697,18 @@ class FunctionType extends DefType { return true; } + bool isStructurallyEqualTo(FunctionType other) { + if (inputs.length != other.inputs.length) return false; + if (outputs.length != other.outputs.length) return false; + for (int i = 0; i < inputs.length; i++) { + if (inputs[i] != other.inputs[i]) return false; + } + for (int i = 0; i < outputs.length; i++) { + if (outputs[i] != other.outputs[i]) return false; + } + return true; + } + @override void serializeDefinitionInner(Serializer s) { s.writeByte(0x60); // -0x20 diff --git a/pkg/wasm_builder/lib/src/serialize/sections.dart b/pkg/wasm_builder/lib/src/serialize/sections.dart index bbfe3243f98..0bf13a956a9 100644 --- a/pkg/wasm_builder/lib/src/serialize/sections.dart +++ b/pkg/wasm_builder/lib/src/serialize/sections.dart @@ -226,7 +226,7 @@ class _Element implements Serializable { @override void serialize(Serializer s) { if (table.index != 0) { - s.writeByte(0x02); + s.writeByte(0x06); s.writeUnsigned(table.index); } else { s.writeByte(0x00); @@ -235,11 +235,17 @@ class _Element implements Serializable { s.writeSigned(startIndex); s.writeByte(0x0B); // end if (table.index != 0) { - s.writeByte(0x00); // elemkind + s.write(table.type); } s.writeUnsigned(entries.length); for (var entry in entries) { - s.writeUnsigned(entry.index); + if (table.index == 0) { + s.writeUnsigned(entry.index); + } else { + s.writeByte(0xD2); // ref.func + s.writeSigned(entry.index); + s.writeByte(0x0B); // end + } } } }