[dart2wasm] Allow using table slots for storing values of dart globals
This seems to result in -0.6% compressed main module and a bit less in uncompressed mode. Sometimes we have many fields with lazy initializers of the same type. That led us to emit 1 nullable wasm global for each such field. For example all proto classes have a `static BuilderInfo i_` field. This has led to thousands of `(mut (ref null $BuilderInfo))` wasm globals. Now we use a wasm table for this, which is a O(1) in the binary as they all get `null` by default, saving us all these globals. The downside is that accesses have an extra instruction now, but overall this is a win. The CL also cleans up `globals.dart` by separating the concept of a wasm global and reading/writing to it from the concept of a Dart global - as Dart globals can now be backed by wasm globals or table slots. This CL uses the new capability made possible by the refactoring in [0] - namely to emit element sections which initialize wasm table slots with non-function expressions. [0] https://dart-review.googlesource.com/c/sdk/+/459440 Change-Id: Ie57206dff8c0a57a1df5e48c0808167f822bc4a2 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/475800 Reviewed-by: Nate Biggs <natebiggs@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
committed by
Commit Queue
parent
27d4f2c79d
commit
ea6fb0a16f
@@ -13,6 +13,7 @@ import 'class_info.dart';
|
||||
import 'closures.dart';
|
||||
import 'dispatch_table.dart';
|
||||
import 'dynamic_forwarders.dart';
|
||||
import 'globals.dart';
|
||||
import 'intrinsics.dart';
|
||||
import 'param_info.dart';
|
||||
import 'records.dart';
|
||||
@@ -4050,16 +4051,21 @@ class StaticFieldInitializerCodeGenerator extends AstCodeGenerator {
|
||||
// Static field initializer function
|
||||
closures = translator.getClosures(field);
|
||||
|
||||
w.Global global = translator.globals.getGlobalForStaticField(field);
|
||||
w.Global? flag = translator.globals.getGlobalInitializedFlag(field);
|
||||
translateExpression(field.initializer!, global.type.type);
|
||||
translator.globals.writeGlobal(b, global);
|
||||
final globalDefinition =
|
||||
translator.dartGlobals.getDefinitionForStaticField(field);
|
||||
final flag = globalDefinition.initializedFlag;
|
||||
|
||||
final local = b.addLocal(globalDefinition.type);
|
||||
globalDefinition.write(translator, b, (b) {
|
||||
translateExpression(field.initializer!, local.type);
|
||||
b.local_tee(local);
|
||||
});
|
||||
b.local_get(local);
|
||||
translator.convertType(b, local.type, outputs.single);
|
||||
if (flag != null) {
|
||||
b.i32_const(1);
|
||||
translator.globals.writeGlobal(b, flag);
|
||||
}
|
||||
translator.globals.readGlobal(b, global);
|
||||
translator.convertType(b, global.type.type, outputs.single);
|
||||
b.end();
|
||||
}
|
||||
}
|
||||
@@ -4094,36 +4100,39 @@ class StaticFieldImplicitAccessorCodeGenerator extends AstCodeGenerator {
|
||||
|
||||
@override
|
||||
void generateInternal() {
|
||||
final global = translator.globals.getGlobalForStaticField(field);
|
||||
final flag = translator.globals.getGlobalInitializedFlag(field);
|
||||
final globalDefinition =
|
||||
translator.dartGlobals.getDefinitionForStaticField(field);
|
||||
if (isImplicitGetter) {
|
||||
final initFunction =
|
||||
translator.functions.getExistingFunction(field.fieldReference);
|
||||
_generateGetter(global, flag, initFunction);
|
||||
_generateGetter(globalDefinition, initFunction);
|
||||
} else {
|
||||
_generateSetter(global, flag);
|
||||
_generateSetter(globalDefinition);
|
||||
}
|
||||
b.end();
|
||||
}
|
||||
|
||||
void _generateGetter(
|
||||
w.Global global, w.Global? flag, w.BaseFunction? initFunction) {
|
||||
DartGlobalDefinition definition, w.BaseFunction? initFunction) {
|
||||
final flag = definition.initializedFlag;
|
||||
|
||||
if (initFunction == null) {
|
||||
// Statically initialized
|
||||
translator.globals.readGlobal(b, global);
|
||||
definition.read(translator, b);
|
||||
// b.ref_cast(functionType.outputs.single as w.RefType);
|
||||
} else {
|
||||
if (flag != null) {
|
||||
// Explicit initialization flag
|
||||
translator.globals.readGlobal(b, flag);
|
||||
b.if_(const [], [global.type.type]);
|
||||
translator.globals.readGlobal(b, global);
|
||||
b.if_(const [], [definition.type]);
|
||||
definition.read(translator, b);
|
||||
b.else_();
|
||||
translator.callFunction(initFunction, b);
|
||||
b.end();
|
||||
} else {
|
||||
// Null signals uninitialized
|
||||
w.Label block = b.block(const [], [initFunction.type.outputs.single]);
|
||||
translator.globals.readGlobal(b, global);
|
||||
definition.read(translator, b);
|
||||
b.br_on_non_null(block);
|
||||
translator.callFunction(initFunction, b);
|
||||
b.end();
|
||||
@@ -4131,9 +4140,11 @@ class StaticFieldImplicitAccessorCodeGenerator extends AstCodeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
void _generateSetter(w.Global global, w.Global? flag) {
|
||||
b.local_get(paramLocals.single);
|
||||
translator.globals.writeGlobal(b, global);
|
||||
void _generateSetter(DartGlobalDefinition definition) {
|
||||
definition.write(translator, b, (b) {
|
||||
b.local_get(paramLocals.single);
|
||||
});
|
||||
final flag = definition.initializedFlag;
|
||||
if (flag != null) {
|
||||
b.i32_const(1); // true
|
||||
translator.globals.writeGlobal(b, flag);
|
||||
|
||||
+193
-67
@@ -7,39 +7,29 @@ import 'package:kernel/ast.dart';
|
||||
import 'package:wasm_builder/wasm_builder.dart' as w;
|
||||
|
||||
import 'code_generator.dart' show EagerStaticFieldInitializerCodeGenerator;
|
||||
import 'table_based_globals.dart';
|
||||
import 'translator.dart';
|
||||
import 'util.dart' as util;
|
||||
|
||||
/// If we have more than this number of fields of the same type, we prefer using
|
||||
/// a wasm table to hold field values over globals.
|
||||
const dartFieldTableUseCutoff = 10;
|
||||
|
||||
/// Handles lazy initialization of static fields.
|
||||
class Globals {
|
||||
final Translator translator;
|
||||
|
||||
/// Maps a static field to its global holding the field value.
|
||||
final Map<Field, w.GlobalBuilder> _globals = {};
|
||||
|
||||
/// When a global is read from a module other than the module defining it,
|
||||
/// this maps the global to the getter function defined and exported in
|
||||
/// the defining module.
|
||||
final Map<w.Global, w.BaseFunction> _globalGetters = {};
|
||||
final Map<w.Global, w.BaseFunction> _globalSetters = {};
|
||||
|
||||
final Map<Field, w.Global> _globalInitializedFlag = {};
|
||||
final WasmGlobalImporter _globalsModuleMap;
|
||||
|
||||
Globals(this.translator)
|
||||
: _globalsModuleMap = WasmGlobalImporter(translator, 'global');
|
||||
|
||||
Constant? getConstantInitializer(Field variable) {
|
||||
Expression? init = variable.initializer;
|
||||
if (init == null || init is NullLiteral) return NullConstant();
|
||||
if (init is IntLiteral) return IntConstant(init.value);
|
||||
if (init is DoubleLiteral) return DoubleConstant(init.value);
|
||||
if (init is BoolLiteral) return BoolConstant(init.value);
|
||||
if (init is StringLiteral) return StringConstant(init.value);
|
||||
if (init is ConstantExpression) return init.constant;
|
||||
return null;
|
||||
}
|
||||
|
||||
void declareMainAppGlobalExportWithName(String name, w.Global exportable) {
|
||||
_globalsModuleMap.exportDefinitionWithName(name, exportable);
|
||||
}
|
||||
@@ -96,29 +86,69 @@ class Globals {
|
||||
translator.callFunction(setter, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DartGlobals {
|
||||
final Translator translator;
|
||||
final Map<w.ValueType, int> _fieldTypeCount = {};
|
||||
|
||||
final Map<Field, DartGlobalDefinition> _definitions = {};
|
||||
|
||||
DartGlobals(this.translator) {
|
||||
for (final library in translator.component.libraries) {
|
||||
for (final field in library.fields) {
|
||||
final wasmType = translator.translateTypeOfField(field);
|
||||
_fieldTypeCount[wasmType] = (_fieldTypeCount[wasmType] ?? 0) + 1;
|
||||
}
|
||||
for (final klass in library.classes) {
|
||||
for (final field in klass.fields) {
|
||||
if (field.isInstanceMember) continue;
|
||||
final wasmType = translator.translateTypeOfField(field);
|
||||
_fieldTypeCount[wasmType] = (_fieldTypeCount[wasmType] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Constant? getConstantInitializer(Field variable) {
|
||||
Expression? init = variable.initializer;
|
||||
if (init == null || init is NullLiteral) return NullConstant();
|
||||
if (init is IntLiteral) return IntConstant(init.value);
|
||||
if (init is DoubleLiteral) return DoubleConstant(init.value);
|
||||
if (init is BoolLiteral) return BoolConstant(init.value);
|
||||
if (init is StringLiteral) return StringConstant(init.value);
|
||||
if (init is ConstantExpression) return init.constant;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Return (and if needed create) the Wasm global corresponding to a static
|
||||
/// field.
|
||||
w.Global getGlobalForStaticField(Field field) {
|
||||
DartGlobalDefinition getDefinitionForStaticField(Field field) {
|
||||
assert(!field.isLate);
|
||||
return _globals.putIfAbsent(field, () {
|
||||
w.ValueType fieldType = translator.translateTypeOfField(field);
|
||||
final module = translator.moduleForReference(field.fieldReference);
|
||||
final memberName = field.toString();
|
||||
return _definitions.putIfAbsent(field, () {
|
||||
final fieldType = translator.translateTypeOfField(field);
|
||||
final numberOfFieldsWithSameType = _fieldTypeCount[fieldType]!;
|
||||
final useTableSlot =
|
||||
numberOfFieldsWithSameType >= dartFieldTableUseCutoff &&
|
||||
fieldType is w.RefType;
|
||||
|
||||
// Maybe we can emit the initialization in the globals section. If so,
|
||||
// then that's preferred as we can make the global as non-mutable.
|
||||
final module = translator.moduleForReference(field.fieldReference);
|
||||
|
||||
// If the initializer expression is a constant expression then the field
|
||||
// doesn't have to become lazy.
|
||||
//
|
||||
// If the type is non-nullable we prefer to use a global as using a table
|
||||
// can cause null checks on usages. Oterhwise we use [useTableSlot]
|
||||
// heuristic to determine whether to use a table or not.
|
||||
final Constant? init = getConstantInitializer(field);
|
||||
if (init != null &&
|
||||
translator.constants
|
||||
.tryInstantiateEagerlyFrom(module, init, fieldType)) {
|
||||
// Initialized to a constant
|
||||
final global = module.globals.define(
|
||||
w.GlobalType(fieldType, mutable: !field.isFinal), memberName);
|
||||
translator.constants
|
||||
.instantiateConstant(global.initializer, init, fieldType);
|
||||
global.initializer.end();
|
||||
return global;
|
||||
if (useTableSlot && fieldType.nullable) {
|
||||
return _defineTableBasedField(field, fieldType, module, init, null);
|
||||
}
|
||||
return _defineGlobalBasedField(
|
||||
field, fieldType, module, !field.isFinal, init, null);
|
||||
}
|
||||
|
||||
// Maybe we can emit the initialization in the start function. If so,
|
||||
@@ -126,60 +156,100 @@ class Globals {
|
||||
// access.
|
||||
final initializer = field.initializer;
|
||||
if (initializer != null && _initializeAtStartup(field)) {
|
||||
final dummyCollector =
|
||||
translator.getDummyValuesCollectorForModule(module);
|
||||
final global =
|
||||
module.globals.define(w.GlobalType(fieldType), memberName);
|
||||
dummyCollector.instantiateDummyValue(global.initializer, fieldType);
|
||||
global.initializer.end();
|
||||
final definition =
|
||||
_defineGlobalBasedField(field, fieldType, module, true, init, null);
|
||||
|
||||
if (module.module == translator.initFunction.enclosingModule) {
|
||||
// We have to initialize the global field in the same module as where
|
||||
// the field value is defined in.
|
||||
// TODO: Once dynamic modules only compile code for the submodule and
|
||||
// not the main module, we should turn this into an assert.
|
||||
EagerStaticFieldInitializerCodeGenerator(translator, field, global)
|
||||
EagerStaticFieldInitializerCodeGenerator(
|
||||
translator, field, definition.global)
|
||||
.generate(translator.initFunction.body, [], null);
|
||||
}
|
||||
|
||||
return global;
|
||||
return definition;
|
||||
}
|
||||
|
||||
// We will have to initialize the global lazily, meaning each access will
|
||||
// check if it's initialized and if not, cause initialization.
|
||||
final w.ValueType globalType;
|
||||
if (fieldType is w.RefType && !fieldType.nullable) {
|
||||
// Null signals uninitialized
|
||||
globalType = fieldType.withNullability(true);
|
||||
} else {
|
||||
// Explicit initialization flag
|
||||
globalType = fieldType;
|
||||
final flag = module.globals
|
||||
.define(w.GlobalType(w.NumType.i32), "$memberName initialized");
|
||||
flag.initializer.i32_const(0);
|
||||
flag.initializer.end();
|
||||
_globalInitializedFlag[field] = flag;
|
||||
}
|
||||
|
||||
final global =
|
||||
module.globals.define(w.GlobalType(globalType), memberName);
|
||||
translator
|
||||
.getDummyValuesCollectorForModule(module)
|
||||
.instantiateDummyValue(global.initializer, globalType);
|
||||
global.initializer.end();
|
||||
|
||||
// Add initializer function to the compilation queue.
|
||||
translator.functions.getFunction(field.fieldReference);
|
||||
return global;
|
||||
|
||||
// We will have to initialize the global lazily, meaning each access will
|
||||
// check if it's initialized and if not, cause initialization.
|
||||
final w.ValueType newFieldType;
|
||||
final w.GlobalBuilder? initializerFlagGlobal;
|
||||
if (fieldType is w.RefType && !fieldType.nullable) {
|
||||
// Null signals uninitialized
|
||||
newFieldType = fieldType.withNullability(true);
|
||||
initializerFlagGlobal = null;
|
||||
} else {
|
||||
// Explicit initialization flag
|
||||
newFieldType = fieldType;
|
||||
initializerFlagGlobal = _defineInitializerFlag(field, module);
|
||||
}
|
||||
|
||||
if (useTableSlot && newFieldType.nullable) {
|
||||
return _defineTableBasedField(field, newFieldType as w.RefType, module,
|
||||
null, initializerFlagGlobal);
|
||||
}
|
||||
return _defineGlobalBasedField(
|
||||
field, newFieldType, module, true, null, initializerFlagGlobal);
|
||||
});
|
||||
}
|
||||
|
||||
/// Return the Wasm global containing the flag indicating whether this static
|
||||
/// field has been initialized, if such a flag global is needed.
|
||||
///
|
||||
/// Note that [getGlobalForStaticField] must have been called for the field beforehand.
|
||||
w.Global? getGlobalInitializedFlag(Field variable) =>
|
||||
_globalInitializedFlag[variable];
|
||||
w.GlobalBuilder _defineInitializerFlag(Field field, w.ModuleBuilder module) {
|
||||
final memberName = _memberName(field);
|
||||
final global = module.globals
|
||||
.define(w.GlobalType(w.NumType.i32), "$memberName initialized");
|
||||
global.initializer.i32_const(0);
|
||||
global.initializer.end();
|
||||
return global;
|
||||
}
|
||||
|
||||
TableBasedDartGlobal _defineTableBasedField(
|
||||
Field field,
|
||||
w.RefType fieldType,
|
||||
w.ModuleBuilder module,
|
||||
Constant? init,
|
||||
w.GlobalBuilder? initializerFlag) {
|
||||
final table =
|
||||
translator.tableBasedGlobals.getTableForType(fieldType.heapType);
|
||||
if (init != null && init is! NullConstant) {
|
||||
return TableBasedDartGlobal(
|
||||
table,
|
||||
table.indexForObject(field, module, (ib) {
|
||||
translator.constants.instantiateConstant(ib, init, fieldType);
|
||||
ib.end();
|
||||
}));
|
||||
}
|
||||
return TableBasedDartGlobal(table, table.indexForObject(field),
|
||||
initializedFlag: initializerFlag);
|
||||
}
|
||||
|
||||
WasmGlobalDartGlobal _defineGlobalBasedField(
|
||||
Field field,
|
||||
w.ValueType fieldType,
|
||||
w.ModuleBuilder module,
|
||||
bool mutable,
|
||||
Constant? init,
|
||||
w.GlobalBuilder? initializerFlag) {
|
||||
final memberName = _memberName(field);
|
||||
final global = module.globals
|
||||
.define(w.GlobalType(fieldType, mutable: mutable), memberName);
|
||||
if (init != null) {
|
||||
translator.constants
|
||||
.instantiateConstant(global.initializer, init, fieldType);
|
||||
} else {
|
||||
final dummyCollector =
|
||||
translator.getDummyValuesCollectorForModule(module);
|
||||
dummyCollector.instantiateDummyValue(global.initializer, fieldType);
|
||||
}
|
||||
global.initializer.end();
|
||||
return WasmGlobalDartGlobal(global, initializedFlag: initializerFlag);
|
||||
}
|
||||
|
||||
String _memberName(Field field) => field.toString();
|
||||
|
||||
bool _initializeAtStartup(Annotatable node) =>
|
||||
util.getPragma<bool>(
|
||||
@@ -187,3 +257,59 @@ class Globals {
|
||||
defaultValue: true) ??
|
||||
false;
|
||||
}
|
||||
|
||||
sealed class DartGlobalDefinition {
|
||||
final w.Global? initializedFlag;
|
||||
DartGlobalDefinition({this.initializedFlag});
|
||||
|
||||
w.ValueType get type;
|
||||
w.ValueType read(Translator translator, w.InstructionsBuilder b);
|
||||
void write(Translator translator, w.InstructionsBuilder b,
|
||||
void Function(w.InstructionsBuilder) pushValue);
|
||||
}
|
||||
|
||||
final class WasmGlobalDartGlobal extends DartGlobalDefinition {
|
||||
final w.Global global;
|
||||
WasmGlobalDartGlobal(this.global, {super.initializedFlag});
|
||||
|
||||
@override
|
||||
w.ValueType get type => global.type.type;
|
||||
|
||||
@override
|
||||
w.ValueType read(Translator translator, w.InstructionsBuilder b) {
|
||||
return translator.globals.readGlobal(b, global);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(Translator translator, w.InstructionsBuilder b,
|
||||
void Function(w.InstructionsBuilder) pushValue) {
|
||||
pushValue(b);
|
||||
translator.globals.writeGlobal(b, global);
|
||||
}
|
||||
}
|
||||
|
||||
final class TableBasedDartGlobal extends DartGlobalDefinition {
|
||||
final TypeSpecificGlobalTable table;
|
||||
final int index;
|
||||
|
||||
TableBasedDartGlobal(this.table, this.index, {super.initializedFlag});
|
||||
|
||||
@override
|
||||
w.ValueType get type => table.type;
|
||||
|
||||
@override
|
||||
w.RefType read(Translator translator, w.InstructionsBuilder b) {
|
||||
final wasmTable = table.getWasmTable(b.moduleBuilder);
|
||||
b.i32_const(index);
|
||||
b.table_get(wasmTable);
|
||||
return wasmTable.type;
|
||||
}
|
||||
|
||||
@override
|
||||
void write(Translator translator, w.InstructionsBuilder b,
|
||||
void Function(w.InstructionsBuilder) pushValue) {
|
||||
b.i32_const(index);
|
||||
pushValue(b);
|
||||
b.table_set(table.getWasmTable(b.moduleBuilder));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2026, 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 'package:wasm_builder/wasm_builder.dart' as w;
|
||||
|
||||
import 'translator.dart';
|
||||
|
||||
class TableBasedGlobals {
|
||||
final Translator translator;
|
||||
|
||||
final Map<w.HeapType, TypeSpecificGlobalTable> _tables = {};
|
||||
|
||||
TableBasedGlobals(this.translator);
|
||||
|
||||
TypeSpecificGlobalTable getTableForType(w.HeapType type) {
|
||||
return _tables[type] ??= TypeSpecificGlobalTable(
|
||||
translator, type, 'global-table-${_tables.length}');
|
||||
}
|
||||
|
||||
void outputTables() {
|
||||
for (final table in _tables.values) {
|
||||
table.output();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TypeSpecificGlobalTable {
|
||||
final Translator translator;
|
||||
final w.HeapType _tableHeapType;
|
||||
|
||||
/// Contents of wasm table.
|
||||
final Map<Object, (int, w.InstructionsBuilder?)> _table = {};
|
||||
|
||||
late final w.TableBuilder _definedWasmTable = translator.mainModule.tables
|
||||
.define(w.RefType(_tableHeapType, nullable: true), _table.length);
|
||||
final WasmTableImporter _importedWasmTables;
|
||||
|
||||
TypeSpecificGlobalTable(
|
||||
this.translator, this._tableHeapType, String tableName)
|
||||
: _importedWasmTables = WasmTableImporter(translator, tableName) {
|
||||
assert(_tableHeapType.isStructuralSubtypeOf(w.HeapType.any));
|
||||
}
|
||||
|
||||
w.RefType get type => w.RefType(_tableHeapType, nullable: true);
|
||||
|
||||
/// Gets the wasm table used to reference this 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) {
|
||||
return _importedWasmTables.get(_definedWasmTable, module);
|
||||
}
|
||||
|
||||
/// Returns the index for [function] in the table allocating one if necessary.
|
||||
int indexForObject(Object object,
|
||||
[w.ModuleBuilder? initModule,
|
||||
void Function(w.InstructionsBuilder)? init]) {
|
||||
assert((initModule != null) == (init != null));
|
||||
final existing = _table[object];
|
||||
if (existing != null) return existing.$1;
|
||||
|
||||
w.InstructionsBuilder? expression;
|
||||
if (initModule != null) {
|
||||
expression = w.InstructionsBuilder(
|
||||
initModule, [], [w.RefType(_tableHeapType, nullable: false)],
|
||||
constantExpression: true);
|
||||
init!(expression);
|
||||
}
|
||||
|
||||
return (_table[object] = (_table.length, expression)).$1;
|
||||
}
|
||||
|
||||
void output() {
|
||||
final importedTables = _importedWasmTables;
|
||||
_table.forEach((fun, tuple) {
|
||||
final (index, expression) = tuple;
|
||||
if (expression != null) {
|
||||
final moduleBuilder = expression.moduleBuilder;
|
||||
if (translator.isMainModule(moduleBuilder)) {
|
||||
_definedWasmTable.moduleBuilder.elements
|
||||
.activeExpressionSegmentBuilderFor(_definedWasmTable)
|
||||
.setExpressionAt(index, expression);
|
||||
} else {
|
||||
// This will generate the imported table if it doesn't already exist.
|
||||
final importedTable = getWasmTable(moduleBuilder) as w.ImportedTable;
|
||||
moduleBuilder.elements
|
||||
.activeExpressionSegmentBuilderFor(importedTable)
|
||||
.setExpressionAt(index, expression);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_definedWasmTable.minSize = _table.length;
|
||||
for (final table in importedTables.imports) {
|
||||
table.minSize = _table.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ import 'reference_extensions.dart';
|
||||
import 'serialization.dart';
|
||||
import 'static_dispatch_table.dart';
|
||||
import 'symbols.dart';
|
||||
import 'table_based_globals.dart';
|
||||
import 'tags.dart';
|
||||
import 'types.dart';
|
||||
import 'util.dart' as util;
|
||||
@@ -194,9 +195,11 @@ class Translator with KernelNodes {
|
||||
late final ClosureLayouter closureLayouter;
|
||||
late final ClassInfoCollector classInfoCollector;
|
||||
late final CrossModuleFunctionTable crossModuleFunctionTable;
|
||||
late final TableBasedGlobals tableBasedGlobals;
|
||||
late final DispatchTable dispatchTable;
|
||||
DispatchTable? dynamicMainModuleDispatchTable;
|
||||
late final Globals globals;
|
||||
late final DartGlobals dartGlobals;
|
||||
late final Constants constants;
|
||||
late final Types types;
|
||||
late final ExceptionTags _exceptionTags;
|
||||
@@ -521,6 +524,7 @@ class Translator with KernelNodes {
|
||||
closureLayouter = ClosureLayouter(this);
|
||||
classInfoCollector = ClassInfoCollector(this);
|
||||
crossModuleFunctionTable = CrossModuleFunctionTable(this);
|
||||
tableBasedGlobals = TableBasedGlobals(this);
|
||||
dispatchTable = DispatchTable(isDynamicSubmoduleTable: isDynamicSubmodule)
|
||||
..translator = this;
|
||||
if (isDynamicSubmodule) {
|
||||
@@ -595,6 +599,7 @@ class Translator with KernelNodes {
|
||||
classInfoCollector.collect();
|
||||
|
||||
globals = Globals(this);
|
||||
dartGlobals = DartGlobals(this);
|
||||
constants = Constants(this);
|
||||
|
||||
dispatchTable.build();
|
||||
@@ -616,6 +621,7 @@ class Translator with KernelNodes {
|
||||
constructorClosures.clear();
|
||||
dispatchTable.output();
|
||||
crossModuleFunctionTable.output();
|
||||
tableBasedGlobals.outputTables();
|
||||
|
||||
for (ConstantInfo info in constants.constantInfo.values) {
|
||||
info.printInitializer((function) {
|
||||
@@ -1970,7 +1976,7 @@ class Translator with KernelNodes {
|
||||
if (target == member.setterReference) return true;
|
||||
|
||||
// Implicit getter for static fields may invoke lazy static initializer.
|
||||
if (globals.getConstantInitializer(member) != null) {
|
||||
if (dartGlobals.getConstantInitializer(member) != null) {
|
||||
// This global will get it's initializer eagerly set, so no lazy init
|
||||
// function to be called.
|
||||
return true;
|
||||
|
||||
@@ -94,9 +94,9 @@
|
||||
global.get $"C515 FooConst5"
|
||||
array.new_fixed $Array<Object?> 6
|
||||
call $GrowableList._withData
|
||||
local.tee $var1
|
||||
global.set $allFooConstants
|
||||
global.get $allFooConstants
|
||||
ref.as_non_null
|
||||
local.get $var1
|
||||
end $label0
|
||||
local.tee $var1
|
||||
struct.get $WasmListBase $_length
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright (c) 2026, 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.
|
||||
|
||||
// functionFilter=DartGlobals
|
||||
// tableFilter=cross-module-funcs|global-table
|
||||
// globalFilter=NoMatch
|
||||
// typeFilter=NoMatch
|
||||
// compilerOption=--no-minify
|
||||
// compilerOption=-O0
|
||||
|
||||
void main() {
|
||||
// Ensure we read and write the globals.
|
||||
print(DartGlobals.foo0_constInit);
|
||||
print(DartGlobals.foo1_newInit);
|
||||
print(DartGlobals.foo2_newInit_final);
|
||||
print(DartGlobals.foo3_newInit);
|
||||
print(DartGlobals.foo4_newInit);
|
||||
print(DartGlobals.foo5_newInit);
|
||||
print(DartGlobals.foo6_newInit);
|
||||
print(DartGlobals.foo7_newInit);
|
||||
print(DartGlobals.foo8_newInit);
|
||||
print(DartGlobals.foo9_newInit);
|
||||
|
||||
print(DartGlobals.bar0_constInit);
|
||||
print(DartGlobals.bar1_newInit);
|
||||
print(DartGlobals.bar2_newInit_final);
|
||||
print(DartGlobals.bar3_noInit);
|
||||
print(DartGlobals.bar4_noInit);
|
||||
print(DartGlobals.bar5_noInit);
|
||||
print(DartGlobals.bar6_noInit);
|
||||
print(DartGlobals.bar7_noInit);
|
||||
print(DartGlobals.bar8_noInit);
|
||||
print(DartGlobals.bar9_noInit);
|
||||
|
||||
print(DartGlobals.baz0_constInit);
|
||||
print(DartGlobals.baz1_newInit);
|
||||
print(DartGlobals.baz2_newInit_final);
|
||||
print(DartGlobals.baz3_noInit);
|
||||
|
||||
DartGlobals.foo0_constInit = Foo('');
|
||||
DartGlobals.foo1_newInit = Foo('');
|
||||
DartGlobals.foo3_newInit = Foo('');
|
||||
DartGlobals.foo4_newInit = Foo('');
|
||||
DartGlobals.foo5_newInit = Foo('');
|
||||
DartGlobals.foo6_newInit = Foo('');
|
||||
DartGlobals.foo7_newInit = Foo('');
|
||||
DartGlobals.foo8_newInit = Foo('');
|
||||
DartGlobals.foo9_newInit = Foo('');
|
||||
|
||||
DartGlobals.bar0_constInit = null;
|
||||
DartGlobals.bar1_newInit = null;
|
||||
DartGlobals.bar3_noInit = Bar('');
|
||||
DartGlobals.bar4_noInit = Bar('');
|
||||
DartGlobals.bar5_noInit = Bar('');
|
||||
DartGlobals.bar6_noInit = Bar('');
|
||||
DartGlobals.bar7_noInit = Bar('');
|
||||
DartGlobals.bar8_noInit = Bar('');
|
||||
DartGlobals.bar9_noInit = Bar('');
|
||||
|
||||
DartGlobals.baz0_constInit = null;
|
||||
DartGlobals.baz1_newInit = null;
|
||||
DartGlobals.baz3_noInit = Baz('');
|
||||
}
|
||||
|
||||
class DartGlobals {
|
||||
// Field type has more than 10 such fields, so it qualifies for table based
|
||||
// slot. Though depending on which case we may still prefer global.
|
||||
static Foo foo0_constInit = const Foo('foo0');
|
||||
static Foo foo1_newInit = Foo('foo1');
|
||||
static final Foo foo2_newInit_final = Foo('foo2');
|
||||
static Foo foo3_newInit = Foo('foo3');
|
||||
static Foo foo4_newInit = Foo('foo4');
|
||||
static Foo foo5_newInit = Foo('foo5');
|
||||
static Foo foo6_newInit = Foo('foo6');
|
||||
static Foo foo7_newInit = Foo('foo7');
|
||||
static Foo foo8_newInit = Foo('foo8');
|
||||
static Foo foo9_newInit = Foo('foo9');
|
||||
|
||||
// Field type has more than 10 such fields, so it qualifies for table based
|
||||
// slot. Though depending on which case we may still prefer global.
|
||||
static Bar? bar0_constInit = const Bar('bar0');
|
||||
static Bar? bar1_newInit = Bar('bar1');
|
||||
static final Bar? bar2_newInit_final = Bar('bar2');
|
||||
static Bar? bar3_noInit;
|
||||
static Bar? bar4_noInit;
|
||||
static Bar? bar5_noInit;
|
||||
static Bar? bar6_noInit;
|
||||
static Bar? bar7_noInit;
|
||||
static Bar? bar8_noInit;
|
||||
static Bar? bar9_noInit;
|
||||
|
||||
// Field type has less than 10 such fields, so it doesn't qualify for a table
|
||||
// based slot, we use global fields.
|
||||
static Baz? baz0_constInit = const Baz('baz0');
|
||||
static Baz? baz1_newInit = Baz('baz1');
|
||||
static final Baz? baz2_newInit_final = Baz('baz2');
|
||||
static Baz? baz3_noInit;
|
||||
}
|
||||
|
||||
class Foo {
|
||||
final String value;
|
||||
const Foo(this.value);
|
||||
|
||||
String toString() => 'Foo($value)';
|
||||
}
|
||||
|
||||
class Bar {
|
||||
final String value;
|
||||
const Bar(this.value);
|
||||
|
||||
String toString() => 'Bar($value)';
|
||||
}
|
||||
|
||||
class Baz {
|
||||
final String value;
|
||||
const Baz(this.value);
|
||||
|
||||
String toString() => 'Baz($value)';
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
(module $module0
|
||||
(type $Bar <...>)
|
||||
(type $Baz <...>)
|
||||
(type $Foo <...>)
|
||||
(type $JSStringImpl <...>)
|
||||
(table $dtable0 10 (ref null $Bar))
|
||||
(table $dtable2 9 (ref null $Foo))
|
||||
(global $"C348 \"baz1\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C352 \"bar1\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C355 \"foo9\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C356 \"foo8\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C357 \"foo7\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C358 \"foo6\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C359 \"foo5\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C360 \"foo4\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C361 \"foo3\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C362 \"foo1\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C366 \"baz2\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C367 \"bar2\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C368 \"foo2\"" (ref $JSStringImpl) <...>)
|
||||
(global $"DartGlobals.bar1_newInit initialized" (mut i32) <...>)
|
||||
(global $"DartGlobals.bar2_newInit_final initialized" (mut i32) <...>)
|
||||
(global $"DartGlobals.baz1_newInit initialized" (mut i32) <...>)
|
||||
(global $"DartGlobals.baz2_newInit_final initialized" (mut i32) <...>)
|
||||
(global $DartGlobals.baz0_constInit (mut (ref null $Baz)) <...>)
|
||||
(global $DartGlobals.baz1_newInit (mut (ref null $Baz)) <...>)
|
||||
(global $DartGlobals.baz2_newInit_final (mut (ref null $Baz)) <...>)
|
||||
(global $DartGlobals.baz3_noInit (mut (ref null $Baz)) <...>)
|
||||
(global $DartGlobals.foo0_constInit (mut (ref $Foo)) <...>)
|
||||
(elem $dtable0 <...>)
|
||||
(func $"DartGlobals.bar0_constInit implicit getter" (result (ref null $Bar))
|
||||
i32.const 8
|
||||
table.get $dtable0
|
||||
)
|
||||
(func $"DartGlobals.bar0_constInit= implicit setter" (param $var0 (ref null $Bar))
|
||||
i32.const 8
|
||||
local.get $var0
|
||||
table.set $dtable0
|
||||
)
|
||||
(func $"DartGlobals.bar1_newInit field initializer" (result (ref null $Bar))
|
||||
(local $var0 (ref null $Bar))
|
||||
i32.const 7
|
||||
global.get $"C352 \"bar1\""
|
||||
call $Bar
|
||||
local.tee $var0
|
||||
table.set $dtable0
|
||||
local.get $var0
|
||||
i32.const 1
|
||||
global.set $"DartGlobals.bar1_newInit initialized"
|
||||
)
|
||||
(func $"DartGlobals.bar1_newInit implicit getter" (result (ref null $Bar))
|
||||
global.get $"DartGlobals.bar1_newInit initialized"
|
||||
if (result (ref null $Bar))
|
||||
i32.const 7
|
||||
table.get $dtable0
|
||||
else
|
||||
call $"DartGlobals.bar1_newInit field initializer"
|
||||
end
|
||||
)
|
||||
(func $"DartGlobals.bar1_newInit= implicit setter" (param $var0 (ref null $Bar))
|
||||
i32.const 7
|
||||
local.get $var0
|
||||
table.set $dtable0
|
||||
i32.const 1
|
||||
global.set $"DartGlobals.bar1_newInit initialized"
|
||||
)
|
||||
(func $"DartGlobals.bar2_newInit_final field initializer" (result (ref null $Bar))
|
||||
(local $var0 (ref null $Bar))
|
||||
i32.const 9
|
||||
global.get $"C367 \"bar2\""
|
||||
call $Bar
|
||||
local.tee $var0
|
||||
table.set $dtable0
|
||||
local.get $var0
|
||||
i32.const 1
|
||||
global.set $"DartGlobals.bar2_newInit_final initialized"
|
||||
)
|
||||
(func $"DartGlobals.bar2_newInit_final implicit getter" (result (ref null $Bar))
|
||||
global.get $"DartGlobals.bar2_newInit_final initialized"
|
||||
if (result (ref null $Bar))
|
||||
i32.const 9
|
||||
table.get $dtable0
|
||||
else
|
||||
call $"DartGlobals.bar2_newInit_final field initializer"
|
||||
end
|
||||
)
|
||||
(func $"DartGlobals.bar3_noInit implicit getter" (result (ref null $Bar))
|
||||
i32.const 6
|
||||
table.get $dtable0
|
||||
)
|
||||
(func $"DartGlobals.bar3_noInit= implicit setter" (param $var0 (ref null $Bar))
|
||||
i32.const 6
|
||||
local.get $var0
|
||||
table.set $dtable0
|
||||
)
|
||||
(func $"DartGlobals.bar4_noInit implicit getter" (result (ref null $Bar))
|
||||
i32.const 5
|
||||
table.get $dtable0
|
||||
)
|
||||
(func $"DartGlobals.bar4_noInit= implicit setter" (param $var0 (ref null $Bar))
|
||||
i32.const 5
|
||||
local.get $var0
|
||||
table.set $dtable0
|
||||
)
|
||||
(func $"DartGlobals.bar5_noInit implicit getter" (result (ref null $Bar))
|
||||
i32.const 4
|
||||
table.get $dtable0
|
||||
)
|
||||
(func $"DartGlobals.bar5_noInit= implicit setter" (param $var0 (ref null $Bar))
|
||||
i32.const 4
|
||||
local.get $var0
|
||||
table.set $dtable0
|
||||
)
|
||||
(func $"DartGlobals.bar6_noInit implicit getter" (result (ref null $Bar))
|
||||
i32.const 3
|
||||
table.get $dtable0
|
||||
)
|
||||
(func $"DartGlobals.bar6_noInit= implicit setter" (param $var0 (ref null $Bar))
|
||||
i32.const 3
|
||||
local.get $var0
|
||||
table.set $dtable0
|
||||
)
|
||||
(func $"DartGlobals.bar7_noInit implicit getter" (result (ref null $Bar))
|
||||
i32.const 2
|
||||
table.get $dtable0
|
||||
)
|
||||
(func $"DartGlobals.bar7_noInit= implicit setter" (param $var0 (ref null $Bar))
|
||||
i32.const 2
|
||||
local.get $var0
|
||||
table.set $dtable0
|
||||
)
|
||||
(func $"DartGlobals.bar8_noInit implicit getter" (result (ref null $Bar))
|
||||
i32.const 1
|
||||
table.get $dtable0
|
||||
)
|
||||
(func $"DartGlobals.bar8_noInit= implicit setter" (param $var0 (ref null $Bar))
|
||||
i32.const 1
|
||||
local.get $var0
|
||||
table.set $dtable0
|
||||
)
|
||||
(func $"DartGlobals.bar9_noInit implicit getter" (result (ref null $Bar))
|
||||
i32.const 0
|
||||
table.get $dtable0
|
||||
)
|
||||
(func $"DartGlobals.bar9_noInit= implicit setter" (param $var0 (ref null $Bar))
|
||||
i32.const 0
|
||||
local.get $var0
|
||||
table.set $dtable0
|
||||
)
|
||||
(func $"DartGlobals.baz0_constInit implicit getter" (result (ref null $Baz))
|
||||
global.get $DartGlobals.baz0_constInit
|
||||
)
|
||||
(func $"DartGlobals.baz0_constInit= implicit setter" (param $var0 (ref null $Baz))
|
||||
local.get $var0
|
||||
global.set $DartGlobals.baz0_constInit
|
||||
)
|
||||
(func $"DartGlobals.baz1_newInit field initializer" (result (ref null $Baz))
|
||||
(local $var0 (ref null $Baz))
|
||||
global.get $"C348 \"baz1\""
|
||||
call $Baz
|
||||
local.tee $var0
|
||||
global.set $DartGlobals.baz1_newInit
|
||||
local.get $var0
|
||||
i32.const 1
|
||||
global.set $"DartGlobals.baz1_newInit initialized"
|
||||
)
|
||||
(func $"DartGlobals.baz1_newInit implicit getter" (result (ref null $Baz))
|
||||
global.get $"DartGlobals.baz1_newInit initialized"
|
||||
if (result (ref null $Baz))
|
||||
global.get $DartGlobals.baz1_newInit
|
||||
else
|
||||
call $"DartGlobals.baz1_newInit field initializer"
|
||||
end
|
||||
)
|
||||
(func $"DartGlobals.baz1_newInit= implicit setter" (param $var0 (ref null $Baz))
|
||||
local.get $var0
|
||||
global.set $DartGlobals.baz1_newInit
|
||||
i32.const 1
|
||||
global.set $"DartGlobals.baz1_newInit initialized"
|
||||
)
|
||||
(func $"DartGlobals.baz2_newInit_final field initializer" (result (ref null $Baz))
|
||||
(local $var0 (ref null $Baz))
|
||||
global.get $"C366 \"baz2\""
|
||||
call $Baz
|
||||
local.tee $var0
|
||||
global.set $DartGlobals.baz2_newInit_final
|
||||
local.get $var0
|
||||
i32.const 1
|
||||
global.set $"DartGlobals.baz2_newInit_final initialized"
|
||||
)
|
||||
(func $"DartGlobals.baz2_newInit_final implicit getter" (result (ref null $Baz))
|
||||
global.get $"DartGlobals.baz2_newInit_final initialized"
|
||||
if (result (ref null $Baz))
|
||||
global.get $DartGlobals.baz2_newInit_final
|
||||
else
|
||||
call $"DartGlobals.baz2_newInit_final field initializer"
|
||||
end
|
||||
)
|
||||
(func $"DartGlobals.baz3_noInit implicit getter" (result (ref null $Baz))
|
||||
global.get $DartGlobals.baz3_noInit
|
||||
)
|
||||
(func $"DartGlobals.baz3_noInit= implicit setter" (param $var0 (ref null $Baz))
|
||||
local.get $var0
|
||||
global.set $DartGlobals.baz3_noInit
|
||||
)
|
||||
(func $"DartGlobals.foo0_constInit implicit getter" (result (ref $Foo))
|
||||
global.get $DartGlobals.foo0_constInit
|
||||
)
|
||||
(func $"DartGlobals.foo0_constInit= implicit setter" (param $var0 (ref $Foo))
|
||||
local.get $var0
|
||||
global.set $DartGlobals.foo0_constInit
|
||||
)
|
||||
(func $"DartGlobals.foo1_newInit field initializer" (result (ref $Foo))
|
||||
(local $var0 (ref null $Foo))
|
||||
i32.const 7
|
||||
global.get $"C362 \"foo1\""
|
||||
call $Foo
|
||||
local.tee $var0
|
||||
table.set $dtable2
|
||||
local.get $var0
|
||||
ref.as_non_null
|
||||
)
|
||||
(func $"DartGlobals.foo1_newInit implicit getter" (result (ref $Foo))
|
||||
block $label0 (result (ref $Foo))
|
||||
i32.const 7
|
||||
table.get $dtable2
|
||||
br_on_non_null $label0
|
||||
call $"DartGlobals.foo1_newInit field initializer"
|
||||
end $label0
|
||||
)
|
||||
(func $"DartGlobals.foo1_newInit= implicit setter" (param $var0 (ref $Foo))
|
||||
i32.const 7
|
||||
local.get $var0
|
||||
table.set $dtable2
|
||||
)
|
||||
(func $"DartGlobals.foo2_newInit_final field initializer" (result (ref $Foo))
|
||||
(local $var0 (ref null $Foo))
|
||||
i32.const 8
|
||||
global.get $"C368 \"foo2\""
|
||||
call $Foo
|
||||
local.tee $var0
|
||||
table.set $dtable2
|
||||
local.get $var0
|
||||
ref.as_non_null
|
||||
)
|
||||
(func $"DartGlobals.foo2_newInit_final implicit getter" (result (ref $Foo))
|
||||
block $label0 (result (ref $Foo))
|
||||
i32.const 8
|
||||
table.get $dtable2
|
||||
br_on_non_null $label0
|
||||
call $"DartGlobals.foo2_newInit_final field initializer"
|
||||
end $label0
|
||||
)
|
||||
(func $"DartGlobals.foo3_newInit field initializer" (result (ref $Foo))
|
||||
(local $var0 (ref null $Foo))
|
||||
i32.const 6
|
||||
global.get $"C361 \"foo3\""
|
||||
call $Foo
|
||||
local.tee $var0
|
||||
table.set $dtable2
|
||||
local.get $var0
|
||||
ref.as_non_null
|
||||
)
|
||||
(func $"DartGlobals.foo3_newInit implicit getter" (result (ref $Foo))
|
||||
block $label0 (result (ref $Foo))
|
||||
i32.const 6
|
||||
table.get $dtable2
|
||||
br_on_non_null $label0
|
||||
call $"DartGlobals.foo3_newInit field initializer"
|
||||
end $label0
|
||||
)
|
||||
(func $"DartGlobals.foo3_newInit= implicit setter" (param $var0 (ref $Foo))
|
||||
i32.const 6
|
||||
local.get $var0
|
||||
table.set $dtable2
|
||||
)
|
||||
(func $"DartGlobals.foo4_newInit field initializer" (result (ref $Foo))
|
||||
(local $var0 (ref null $Foo))
|
||||
i32.const 5
|
||||
global.get $"C360 \"foo4\""
|
||||
call $Foo
|
||||
local.tee $var0
|
||||
table.set $dtable2
|
||||
local.get $var0
|
||||
ref.as_non_null
|
||||
)
|
||||
(func $"DartGlobals.foo4_newInit implicit getter" (result (ref $Foo))
|
||||
block $label0 (result (ref $Foo))
|
||||
i32.const 5
|
||||
table.get $dtable2
|
||||
br_on_non_null $label0
|
||||
call $"DartGlobals.foo4_newInit field initializer"
|
||||
end $label0
|
||||
)
|
||||
(func $"DartGlobals.foo4_newInit= implicit setter" (param $var0 (ref $Foo))
|
||||
i32.const 5
|
||||
local.get $var0
|
||||
table.set $dtable2
|
||||
)
|
||||
(func $"DartGlobals.foo5_newInit field initializer" (result (ref $Foo))
|
||||
(local $var0 (ref null $Foo))
|
||||
i32.const 4
|
||||
global.get $"C359 \"foo5\""
|
||||
call $Foo
|
||||
local.tee $var0
|
||||
table.set $dtable2
|
||||
local.get $var0
|
||||
ref.as_non_null
|
||||
)
|
||||
(func $"DartGlobals.foo5_newInit implicit getter" (result (ref $Foo))
|
||||
block $label0 (result (ref $Foo))
|
||||
i32.const 4
|
||||
table.get $dtable2
|
||||
br_on_non_null $label0
|
||||
call $"DartGlobals.foo5_newInit field initializer"
|
||||
end $label0
|
||||
)
|
||||
(func $"DartGlobals.foo5_newInit= implicit setter" (param $var0 (ref $Foo))
|
||||
i32.const 4
|
||||
local.get $var0
|
||||
table.set $dtable2
|
||||
)
|
||||
(func $"DartGlobals.foo6_newInit field initializer" (result (ref $Foo))
|
||||
(local $var0 (ref null $Foo))
|
||||
i32.const 3
|
||||
global.get $"C358 \"foo6\""
|
||||
call $Foo
|
||||
local.tee $var0
|
||||
table.set $dtable2
|
||||
local.get $var0
|
||||
ref.as_non_null
|
||||
)
|
||||
(func $"DartGlobals.foo6_newInit implicit getter" (result (ref $Foo))
|
||||
block $label0 (result (ref $Foo))
|
||||
i32.const 3
|
||||
table.get $dtable2
|
||||
br_on_non_null $label0
|
||||
call $"DartGlobals.foo6_newInit field initializer"
|
||||
end $label0
|
||||
)
|
||||
(func $"DartGlobals.foo6_newInit= implicit setter" (param $var0 (ref $Foo))
|
||||
i32.const 3
|
||||
local.get $var0
|
||||
table.set $dtable2
|
||||
)
|
||||
(func $"DartGlobals.foo7_newInit field initializer" (result (ref $Foo))
|
||||
(local $var0 (ref null $Foo))
|
||||
i32.const 2
|
||||
global.get $"C357 \"foo7\""
|
||||
call $Foo
|
||||
local.tee $var0
|
||||
table.set $dtable2
|
||||
local.get $var0
|
||||
ref.as_non_null
|
||||
)
|
||||
(func $"DartGlobals.foo7_newInit implicit getter" (result (ref $Foo))
|
||||
block $label0 (result (ref $Foo))
|
||||
i32.const 2
|
||||
table.get $dtable2
|
||||
br_on_non_null $label0
|
||||
call $"DartGlobals.foo7_newInit field initializer"
|
||||
end $label0
|
||||
)
|
||||
(func $"DartGlobals.foo7_newInit= implicit setter" (param $var0 (ref $Foo))
|
||||
i32.const 2
|
||||
local.get $var0
|
||||
table.set $dtable2
|
||||
)
|
||||
(func $"DartGlobals.foo8_newInit field initializer" (result (ref $Foo))
|
||||
(local $var0 (ref null $Foo))
|
||||
i32.const 1
|
||||
global.get $"C356 \"foo8\""
|
||||
call $Foo
|
||||
local.tee $var0
|
||||
table.set $dtable2
|
||||
local.get $var0
|
||||
ref.as_non_null
|
||||
)
|
||||
(func $"DartGlobals.foo8_newInit implicit getter" (result (ref $Foo))
|
||||
block $label0 (result (ref $Foo))
|
||||
i32.const 1
|
||||
table.get $dtable2
|
||||
br_on_non_null $label0
|
||||
call $"DartGlobals.foo8_newInit field initializer"
|
||||
end $label0
|
||||
)
|
||||
(func $"DartGlobals.foo8_newInit= implicit setter" (param $var0 (ref $Foo))
|
||||
i32.const 1
|
||||
local.get $var0
|
||||
table.set $dtable2
|
||||
)
|
||||
(func $"DartGlobals.foo9_newInit field initializer" (result (ref $Foo))
|
||||
(local $var0 (ref null $Foo))
|
||||
i32.const 0
|
||||
global.get $"C355 \"foo9\""
|
||||
call $Foo
|
||||
local.tee $var0
|
||||
table.set $dtable2
|
||||
local.get $var0
|
||||
ref.as_non_null
|
||||
)
|
||||
(func $"DartGlobals.foo9_newInit implicit getter" (result (ref $Foo))
|
||||
block $label0 (result (ref $Foo))
|
||||
i32.const 0
|
||||
table.get $dtable2
|
||||
br_on_non_null $label0
|
||||
call $"DartGlobals.foo9_newInit field initializer"
|
||||
end $label0
|
||||
)
|
||||
(func $"DartGlobals.foo9_newInit= implicit setter" (param $var0 (ref $Foo))
|
||||
i32.const 0
|
||||
local.get $var0
|
||||
table.set $dtable2
|
||||
)
|
||||
(func $Bar (param $value (ref $JSStringImpl)) (result (ref $Bar)) <...>)
|
||||
(func $Baz (param $value (ref $JSStringImpl)) (result (ref $Baz)) <...>)
|
||||
(func $Foo (param $value (ref $JSStringImpl)) (result (ref $Foo)) <...>)
|
||||
)
|
||||
@@ -35,8 +35,9 @@
|
||||
(func $"testBoolValueNullable <noInline>"
|
||||
(local $var0 (ref null $#Top))
|
||||
global.get $"boolValueNullable initialized"
|
||||
i32.eqz
|
||||
if
|
||||
if (result (ref null $#Top))
|
||||
global.get $boolValueNullable
|
||||
else
|
||||
call $"ktrue implicit getter"
|
||||
if (result (ref null $#Top))
|
||||
global.get $"C40 true"
|
||||
@@ -46,11 +47,12 @@
|
||||
else
|
||||
ref.null none
|
||||
end
|
||||
local.tee $var0
|
||||
global.set $boolValueNullable
|
||||
i32.const 1
|
||||
global.set $"boolValueNullable initialized"
|
||||
local.get $var0
|
||||
end
|
||||
global.get $boolValueNullable
|
||||
local.tee $var0
|
||||
ref.is_null
|
||||
if (result externref)
|
||||
|
||||
@@ -33,8 +33,9 @@
|
||||
(func $"testDoubleValueNullable <noInline>"
|
||||
(local $var0 (ref null $BoxedDouble))
|
||||
global.get $"doubleValueNullable initialized"
|
||||
i32.eqz
|
||||
if
|
||||
if (result (ref null $BoxedDouble))
|
||||
global.get $doubleValueNullable
|
||||
else
|
||||
call $"ktrue implicit getter"
|
||||
if (result (ref null $BoxedDouble))
|
||||
i32.const 90
|
||||
@@ -43,11 +44,12 @@
|
||||
else
|
||||
ref.null none
|
||||
end
|
||||
local.tee $var0
|
||||
global.set $doubleValueNullable
|
||||
i32.const 1
|
||||
global.set $"doubleValueNullable initialized"
|
||||
local.get $var0
|
||||
end
|
||||
global.get $doubleValueNullable
|
||||
local.tee $var0
|
||||
ref.is_null
|
||||
if (result externref)
|
||||
|
||||
@@ -35,8 +35,9 @@
|
||||
(func $"testIntValueNullable <noInline>"
|
||||
(local $var0 (ref null $BoxedInt))
|
||||
global.get $"intValueNullable initialized"
|
||||
i32.eqz
|
||||
if
|
||||
if (result (ref null $BoxedInt))
|
||||
global.get $intValueNullable
|
||||
else
|
||||
call $"ktrue implicit getter"
|
||||
if (result (ref null $BoxedInt))
|
||||
i32.const 69
|
||||
@@ -45,11 +46,12 @@
|
||||
else
|
||||
ref.null none
|
||||
end
|
||||
local.tee $var0
|
||||
global.set $intValueNullable
|
||||
i32.const 1
|
||||
global.set $"intValueNullable initialized"
|
||||
local.get $var0
|
||||
end
|
||||
global.get $intValueNullable
|
||||
local.tee $var0
|
||||
ref.is_null
|
||||
if (result externref)
|
||||
|
||||
@@ -43,19 +43,21 @@
|
||||
(func $"testNumValueNullable <noInline>"
|
||||
(local $var0 (ref null $#Top))
|
||||
global.get $"numValueNullable initialized"
|
||||
i32.eqz
|
||||
if
|
||||
if (result (ref null $#Top))
|
||||
global.get $numValueNullable
|
||||
else
|
||||
call $"ktrue implicit getter"
|
||||
if (result (ref null $#Top))
|
||||
call $"numValue implicit getter"
|
||||
else
|
||||
ref.null none
|
||||
end
|
||||
local.tee $var0
|
||||
global.set $numValueNullable
|
||||
i32.const 1
|
||||
global.set $"numValueNullable initialized"
|
||||
local.get $var0
|
||||
end
|
||||
global.get $numValueNullable
|
||||
local.tee $var0
|
||||
ref.is_null
|
||||
if (result externref)
|
||||
|
||||
@@ -35,26 +35,29 @@
|
||||
)
|
||||
(func $"testStringValueNullable <noInline>"
|
||||
(local $var0 (ref null $JSStringImpl))
|
||||
(local $var1 (ref null $JSStringImpl))
|
||||
global.get $"stringValueNullable initialized"
|
||||
i32.eqz
|
||||
if
|
||||
if (result (ref null $JSStringImpl))
|
||||
global.get $stringValueNullable
|
||||
else
|
||||
call $"ktrue implicit getter"
|
||||
if (result (ref null $JSStringImpl))
|
||||
call $"stringValue implicit getter"
|
||||
else
|
||||
ref.null none
|
||||
end
|
||||
local.tee $var0
|
||||
global.set $stringValueNullable
|
||||
i32.const 1
|
||||
global.set $"stringValueNullable initialized"
|
||||
local.get $var0
|
||||
end
|
||||
global.get $stringValueNullable
|
||||
local.tee $var0
|
||||
local.tee $var1
|
||||
ref.is_null
|
||||
if (result externref)
|
||||
ref.null noextern
|
||||
else
|
||||
local.get $var0
|
||||
local.get $var1
|
||||
call $jsifyRaw
|
||||
end
|
||||
call $"dart2wasm._299 (import)"
|
||||
|
||||
@@ -93,7 +93,7 @@ while [ $# -gt 0 ]; do
|
||||
shift
|
||||
;;
|
||||
|
||||
-O0 | --optimization-level=0)
|
||||
-O0 | --optimization-level=0 | --extra-compiler-option=-O0 )
|
||||
DART2WASM_ARGS+=("-O0")
|
||||
RUN_BINARYEN=0
|
||||
shift
|
||||
|
||||
@@ -11,13 +11,15 @@ import 'builder.dart';
|
||||
class ElementsBuilder with Builder<ir.Elements> {
|
||||
final ModuleBuilder _moduleBuilder;
|
||||
final _functionTableBuilders = <ir.Table, ActiveFunctionSegmentBuilder>{};
|
||||
final _expressionTableBuilders = <ir.Table, ActiveExpressionSegmentBuilder>{};
|
||||
|
||||
late final declarativeSegmentBuilder =
|
||||
DeclarativeSegmentBuilder(_moduleBuilder);
|
||||
|
||||
ElementsBuilder(this._moduleBuilder);
|
||||
|
||||
bool get hasActiveElementSegments => _functionTableBuilders.isNotEmpty;
|
||||
bool get hasActiveElementSegments =>
|
||||
_functionTableBuilders.isNotEmpty || _expressionTableBuilders.isNotEmpty;
|
||||
|
||||
ActiveFunctionSegmentBuilder activeFunctionSegmentBuilderFor(ir.Table table) {
|
||||
assert(table.type.isSubtypeOf(ir.RefType.func(nullable: true)));
|
||||
@@ -26,12 +28,22 @@ class ElementsBuilder with Builder<ir.Elements> {
|
||||
table, () => ActiveFunctionSegmentBuilder(table));
|
||||
}
|
||||
|
||||
ActiveExpressionSegmentBuilder activeExpressionSegmentBuilderFor(
|
||||
ir.Table table) {
|
||||
assert(table.enclosingModule == _moduleBuilder.module);
|
||||
return _expressionTableBuilders.putIfAbsent(
|
||||
table, () => ActiveExpressionSegmentBuilder(table));
|
||||
}
|
||||
|
||||
@override
|
||||
ir.Elements forceBuild() {
|
||||
final segments = <ir.ElementSegment>[];
|
||||
for (final b in _functionTableBuilders.values) {
|
||||
segments.addAll(b.build());
|
||||
}
|
||||
for (final b in _expressionTableBuilders.values) {
|
||||
segments.addAll(b.build());
|
||||
}
|
||||
if (declarativeSegmentBuilder._declaredFunctions.isNotEmpty) {
|
||||
segments.add(declarativeSegmentBuilder.build());
|
||||
}
|
||||
@@ -107,3 +119,42 @@ class ActiveFunctionSegmentBuilder with Builder<List<ir.ActiveElementSegment>> {
|
||||
return segments;
|
||||
}
|
||||
}
|
||||
|
||||
class ActiveExpressionSegmentBuilder
|
||||
with Builder<List<ir.ActiveElementSegment>> {
|
||||
final ir.Table table;
|
||||
final Map<int, InstructionsBuilder> _expressions = {};
|
||||
|
||||
ActiveExpressionSegmentBuilder(this.table);
|
||||
|
||||
void setExpressionAt(int index, InstructionsBuilder init) {
|
||||
assert(init.moduleBuilder.module == table.enclosingModule);
|
||||
assert(table.maxSize == null || index < table.maxSize!,
|
||||
'Index $index greater than max table size ${table.maxSize}');
|
||||
_expressions[index] = init;
|
||||
table.minSize = math.max(table.minSize, index + 1);
|
||||
}
|
||||
|
||||
@override
|
||||
List<ir.ActiveElementSegment> forceBuild() {
|
||||
final entries = _expressions.entries.toList()
|
||||
..sort((a, b) => a.key.compareTo(b.key));
|
||||
|
||||
final segments = <ir.ActiveElementSegment>[];
|
||||
|
||||
ir.ActiveExpressionElementSegment? current;
|
||||
int lastIndex = -2;
|
||||
for (final entry in entries) {
|
||||
final index = entry.key;
|
||||
final expression = entry.value;
|
||||
if (index != lastIndex + 1) {
|
||||
current = ir.ActiveExpressionElementSegment(table, table.type, index);
|
||||
segments.add(current);
|
||||
}
|
||||
current!.expressions.add(expression.build().instructions);
|
||||
lastIndex = index;
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user