[dart2wasm] Group all lazily initialized multi-module use constant globals into a table
This reduces ACX gallery main module by 3.3%. Instead of defining a global for every lazily initialized constant in the main module we define a table per constant type. This shrinks the main module as we pay little overhead per additional lazily initialized global as we save many exported globals. Deferred modules will then use the table slot instead of a global. Change-Id: I3d2de40a3a49d405f6ba8b2f106af6b8bdbdf45c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/458861 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Ömer Ağacan <omersa@google.com>
This commit is contained in:
committed by
Commit Queue
parent
4bc9d31957
commit
4fb4319980
@@ -25,24 +25,47 @@ const int maxArrayNewFixedLength = 10000;
|
||||
/// the main module).
|
||||
const bool forceDelayedConstantDefinition = false;
|
||||
|
||||
class ConstantDefinition {
|
||||
/// Describes where the constant slot is defined and how to access it.
|
||||
sealed class ConstantDefinition {
|
||||
bool get isLazy;
|
||||
w.BaseFunction initializer(w.ModuleBuilder usingModule);
|
||||
}
|
||||
|
||||
/// The value for the constant is stored in a global slot.
|
||||
///
|
||||
/// We use this mechanism if the constant is accessed only within a given
|
||||
/// module.
|
||||
final class GlobalBasedConstantDefinition extends ConstantDefinition {
|
||||
final w.Global global;
|
||||
final w.BaseFunction? _initFunction;
|
||||
final Map<w.ModuleBuilder, w.BaseFunction>? _initFunctionPerUsingModule;
|
||||
|
||||
ConstantDefinition(
|
||||
this.global, this._initFunction, this._initFunctionPerUsingModule) {
|
||||
assert(_initFunction == null || _initFunctionPerUsingModule == null);
|
||||
}
|
||||
GlobalBasedConstantDefinition(this.global, this._initFunction);
|
||||
|
||||
bool get isLazy =>
|
||||
_initFunction != null || _initFunctionPerUsingModule != null;
|
||||
@override
|
||||
bool get isLazy => _initFunction != null;
|
||||
|
||||
@override
|
||||
w.BaseFunction initializer(w.ModuleBuilder usingModule) => _initFunction!;
|
||||
}
|
||||
|
||||
/// The value for the (lazy) constant is stored in a table slot.
|
||||
///
|
||||
/// We use this mechanism if the constant is accessed across modules and each
|
||||
/// module will bring it's own copy of the initializer function.
|
||||
final class TableBasedConstantDefinition extends ConstantDefinition {
|
||||
final w.Table table;
|
||||
final int tableIndex;
|
||||
final Map<w.ModuleBuilder, w.BaseFunction> _initFunctionPerUsingModule;
|
||||
|
||||
TableBasedConstantDefinition(
|
||||
this.table, this.tableIndex, this._initFunctionPerUsingModule);
|
||||
|
||||
@override
|
||||
bool get isLazy => true;
|
||||
|
||||
@override
|
||||
w.BaseFunction initializer(w.ModuleBuilder usingModule) {
|
||||
if (_initFunctionPerUsingModule != null) {
|
||||
return _initFunctionPerUsingModule[usingModule]!;
|
||||
}
|
||||
return _initFunction!;
|
||||
return _initFunctionPerUsingModule[usingModule]!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,11 +118,21 @@ class ConstantInfo {
|
||||
void Function(w.Global) printLazyInitializer) {
|
||||
final definition = _definition;
|
||||
if (definition != null) {
|
||||
final initFunction = definition._initFunction;
|
||||
if (initFunction != null) {
|
||||
printFunction(initFunction);
|
||||
} else {
|
||||
printLazyInitializer(definition.global);
|
||||
switch (definition) {
|
||||
case GlobalBasedConstantDefinition():
|
||||
final initFunction = definition._initFunction;
|
||||
if (initFunction != null) {
|
||||
printFunction(initFunction);
|
||||
} else {
|
||||
printLazyInitializer(definition.global);
|
||||
}
|
||||
break;
|
||||
case TableBasedConstantDefinition():
|
||||
for (final initFunction
|
||||
in definition._initFunctionPerUsingModule.values) {
|
||||
printFunction(initFunction);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -264,7 +297,7 @@ class Constants {
|
||||
_constantAccessor._defineConstantInModuleRecursive(baseModule, info);
|
||||
}
|
||||
|
||||
if (definition != null && !definition.isLazy) {
|
||||
if (definition is GlobalBasedConstantDefinition && !definition.isLazy) {
|
||||
final definingModule = definition.global.enclosingModule;
|
||||
if (definingModule == usingModule.module) return true;
|
||||
if (definingModule == baseModule.module) return true;
|
||||
@@ -1484,6 +1517,12 @@ class _ConstantAccessor {
|
||||
/// transitively refers to.
|
||||
final Map<ConstantInfo, Set<w.ModuleBuilder>> moduleUses = {};
|
||||
|
||||
/// We maintain a table for lazily initialized constants that are used across
|
||||
/// modules. This avoids having many invidiual globals of the same type with
|
||||
/// null initializer.
|
||||
final Map<w.RefType, w.TableBuilder> lazySlotTables = {};
|
||||
late final tableImporter = WasmTableImporter(translator, 'constant-table');
|
||||
|
||||
_ConstantAccessor(this.translator);
|
||||
|
||||
/// Reads a constant.
|
||||
@@ -1571,25 +1610,36 @@ class _ConstantAccessor {
|
||||
|
||||
w.ValueType _readDefinedConstant(w.InstructionsBuilder b, ConstantInfo info,
|
||||
ConstantDefinition definition) {
|
||||
final globalDefinition = definition.global;
|
||||
final globalInitializer =
|
||||
definition.isLazy ? definition.initializer(b.moduleBuilder) : null;
|
||||
|
||||
// Eagerly initialized constant.
|
||||
if (globalInitializer == null) {
|
||||
translator.globals.readGlobal(b, globalDefinition);
|
||||
return globalDefinition.type.type;
|
||||
if (definition is GlobalBasedConstantDefinition && !definition.isLazy) {
|
||||
translator.globals.readGlobal(b, definition.global);
|
||||
return definition.global.type.type;
|
||||
}
|
||||
|
||||
// Lazily initialized constant.
|
||||
w.ValueType type = globalDefinition.type.type.withNullability(false);
|
||||
w.Label done = b.block(const [], [type]);
|
||||
translator.globals.readGlobal(b, globalDefinition);
|
||||
b.br_on_non_null(done);
|
||||
assert(definition.isLazy);
|
||||
|
||||
translator.callFunction(globalInitializer, b);
|
||||
b.end();
|
||||
return type;
|
||||
switch (definition) {
|
||||
case GlobalBasedConstantDefinition():
|
||||
// Use global & lazy initializer function.
|
||||
w.Label done = b.block(const [], [info.type]);
|
||||
translator.globals.readGlobal(b, definition.global);
|
||||
b.br_on_non_null(done);
|
||||
translator.callFunction(definition.initializer(b.moduleBuilder), b);
|
||||
b.end();
|
||||
break;
|
||||
case TableBasedConstantDefinition():
|
||||
// Use table & lazy initializer function.
|
||||
w.Label done = b.block(const [], [info.type]);
|
||||
b.i32_const(definition.tableIndex);
|
||||
b.table_get(tableImporter.get(definition.table, b.moduleBuilder));
|
||||
b.br_on_non_null(done);
|
||||
translator.callFunction(definition.initializer(b.moduleBuilder), b);
|
||||
b.end();
|
||||
break;
|
||||
}
|
||||
|
||||
return info.type;
|
||||
}
|
||||
|
||||
/// If [assignedModule] is not null assigns all undefined constants to that
|
||||
@@ -1658,6 +1708,10 @@ class _ConstantAccessor {
|
||||
break;
|
||||
}
|
||||
|
||||
// The child isn't lazy, so it cannot be a table-based constant
|
||||
// definition.
|
||||
definition as GlobalBasedConstantDefinition;
|
||||
|
||||
// If we place the constant in a module that may be loaded before the
|
||||
// constants of children, it must get initialized lazily.
|
||||
final childModule = definition.global.enclosingModule;
|
||||
@@ -1685,27 +1739,35 @@ class _ConstantAccessor {
|
||||
final ConstantDefinition definition;
|
||||
if (lazy) {
|
||||
if (targetModule == null) {
|
||||
final w.TableBuilder table = lazySlotTables.putIfAbsent(info.type, () {
|
||||
return translator.mainModule.tables
|
||||
.define(info.type.withNullability(true), 0);
|
||||
});
|
||||
final tableIndex = table.minSize++;
|
||||
final name = _constantName(info.constant);
|
||||
final global = _createLazyGlobal(translator.mainModule, name, info);
|
||||
final initFunctions = {
|
||||
for (final usingModule in deferredUses!)
|
||||
usingModule:
|
||||
_createLazyInitializer(usingModule, global, name, info),
|
||||
usingModule: _createLazyTableInitializer(
|
||||
usingModule, table, tableIndex, name, info),
|
||||
};
|
||||
definition = ConstantDefinition(global, null, initFunctions);
|
||||
definition =
|
||||
TableBasedConstantDefinition(table, tableIndex, initFunctions);
|
||||
} else {
|
||||
final (global, initFunction) = _createLazyConstant(targetModule, info);
|
||||
definition = ConstantDefinition(global, initFunction, null);
|
||||
definition = GlobalBasedConstantDefinition(global, initFunction);
|
||||
}
|
||||
} else {
|
||||
final global = _createNonLazyConstant(targetModule!, info);
|
||||
definition = ConstantDefinition(global, null, null);
|
||||
definition = GlobalBasedConstantDefinition(global, null);
|
||||
}
|
||||
info.setDefinition(definition);
|
||||
|
||||
if (info.exportByMainApp) {
|
||||
assert(translator.dynamicModuleSupportEnabled &&
|
||||
!translator.isDynamicSubmodule);
|
||||
// Current dynamic module implementation requires main module to be
|
||||
// monolitic.
|
||||
definition as GlobalBasedConstantDefinition;
|
||||
translator.exporter.exportDynamicConstant(
|
||||
targetModule!, constant, definition.global,
|
||||
initializer: definition._initFunction);
|
||||
@@ -1739,7 +1801,8 @@ class _ConstantAccessor {
|
||||
globalName, fakeInitializer);
|
||||
}
|
||||
|
||||
info._definition = ConstantDefinition(fakeGlobal, fakeInitializer, null);
|
||||
info._definition =
|
||||
GlobalBasedConstantDefinition(fakeGlobal, fakeInitializer);
|
||||
}
|
||||
|
||||
(w.GlobalBuilder, w.FunctionBuilder) _createLazyConstant(
|
||||
@@ -1748,7 +1811,7 @@ class _ConstantAccessor {
|
||||
|
||||
final definedGlobal = _createLazyGlobal(targetModule, name, info);
|
||||
final initFunction =
|
||||
_createLazyInitializer(targetModule, definedGlobal, name, info);
|
||||
_createLazyGlobalInitializer(targetModule, definedGlobal, name, info);
|
||||
|
||||
return (definedGlobal, initFunction);
|
||||
}
|
||||
@@ -1762,7 +1825,7 @@ class _ConstantAccessor {
|
||||
return definedGlobal;
|
||||
}
|
||||
|
||||
w.FunctionBuilder _createLazyInitializer(w.ModuleBuilder module,
|
||||
w.FunctionBuilder _createLazyGlobalInitializer(w.ModuleBuilder module,
|
||||
w.GlobalBuilder definedGlobal, String name, ConstantInfo info) {
|
||||
final type = info.type;
|
||||
final initFunctionType =
|
||||
@@ -1784,6 +1847,29 @@ class _ConstantAccessor {
|
||||
return initFunction;
|
||||
}
|
||||
|
||||
w.FunctionBuilder _createLazyTableInitializer(w.ModuleBuilder module,
|
||||
w.TableBuilder table, int tableIndex, String name, ConstantInfo info) {
|
||||
final type = info.type;
|
||||
final initFunctionType =
|
||||
translator.typesBuilder.defineFunction(const [], [type]);
|
||||
final initFunction =
|
||||
module.functions.define(initFunctionType, '$name (lazy initializer)}');
|
||||
final b = initFunction.body;
|
||||
b.i32_const(tableIndex);
|
||||
info._codeGen(info, b, true);
|
||||
if (info.needsRuntimeCanonicalization) {
|
||||
final valueLocal = b.addLocal(type);
|
||||
info.constant.accept(ConstantCanonicalizer(translator, b, valueLocal));
|
||||
}
|
||||
w.Local temp = b.addLocal(type);
|
||||
b.local_tee(temp);
|
||||
b.table_set(tableImporter.get(table, module));
|
||||
b.local_get(temp);
|
||||
b.end();
|
||||
|
||||
return initFunction;
|
||||
}
|
||||
|
||||
w.GlobalBuilder _createNonLazyConstant(
|
||||
w.ModuleBuilder targetModule, ConstantInfo info) {
|
||||
final constants = translator.constants;
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
(param $var0 i32)
|
||||
(result (ref $MyConstClass))))
|
||||
(global $"C370 \"bad\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C489 \"shared-const\"" (mut (ref null $JSStringImpl))
|
||||
(ref.null none))
|
||||
(global $"C490 MyConstClass" (mut (ref null $MyConstClass))
|
||||
(ref.null none))
|
||||
(table $static0-0 (export "static0-0") 2 (ref null $type1))
|
||||
(func $Error._throwWithCurrentStackTrace (param $var0 (ref $#Top)) <...>)
|
||||
(func $"mainImpl <noInline>" (param $var0 i32)
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
(field $b (ref $JSStringImpl)))))
|
||||
(global $S.h1-nonshared-const (import "S" "h1-nonshared-const") (ref extern))
|
||||
(global $S.shared-const (import "S" "shared-const") (ref extern))
|
||||
(global $"C489 \"shared-const\"" (import "module0" "global0") (ref null $JSStringImpl))
|
||||
(global $"C490 MyConstClass" (import "module0" "global1") (ref null $MyConstClass))
|
||||
(table $module0.constant-table0 (import "module0" "constant-table0") 1 (ref null $JSStringImpl) <...>)
|
||||
(table $module0.constant-table1 (import "module0" "constant-table1") 1 (ref null $MyConstClass) <...>)
|
||||
(global $"C488 MyConstClass" (ref $MyConstClass)
|
||||
(i32.const 107)
|
||||
(i32.const 0)
|
||||
@@ -32,24 +32,28 @@
|
||||
global.get $"C488 MyConstClass"
|
||||
else
|
||||
block $label0 (result (ref $MyConstClass))
|
||||
global.get $"C490 MyConstClass"
|
||||
i32.const 0
|
||||
table.get $module0.constant-table1
|
||||
br_on_non_null $label0
|
||||
i32.const 0
|
||||
i32.const 107
|
||||
i32.const 0
|
||||
block $label1 (result (ref $JSStringImpl))
|
||||
global.get $"C489 \"shared-const\""
|
||||
i32.const 0
|
||||
table.get $module0.constant-table0
|
||||
br_on_non_null $label1
|
||||
i32.const 0
|
||||
i32.const 4
|
||||
i32.const 0
|
||||
global.get $S.shared-const
|
||||
struct.new $JSStringImpl
|
||||
local.tee $var1
|
||||
global.set $"C489 \"shared-const\""
|
||||
table.set $module0.constant-table0
|
||||
local.get $var1
|
||||
end $label1
|
||||
struct.new $MyConstClass
|
||||
local.tee $var2
|
||||
global.set $"C490 MyConstClass"
|
||||
table.set $module0.constant-table1
|
||||
local.get $var2
|
||||
end $label0
|
||||
end
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
(field $field1 (mut i32))
|
||||
(field $b (ref $JSStringImpl)))))
|
||||
(global $S.shared-const (import "S" "shared-const") (ref extern))
|
||||
(global $"C489 \"shared-const\"" (import "module0" "global0") (ref null $JSStringImpl))
|
||||
(global $"C490 MyConstClass" (import "module0" "global1") (ref null $MyConstClass))
|
||||
(global $S.h0-nonshared-const (import "S" "h0-nonshared-const") (ref extern))
|
||||
(table $module0.constant-table0 (import "module0" "constant-table0") 1 (ref null $JSStringImpl) <...>)
|
||||
(table $module0.constant-table1 (import "module0" "constant-table1") 1 (ref null $MyConstClass) <...>)
|
||||
(global $"C492 MyConstClass" (ref $MyConstClass)
|
||||
(i32.const 107)
|
||||
(i32.const 0)
|
||||
@@ -32,24 +32,28 @@
|
||||
global.get $"C492 MyConstClass"
|
||||
else
|
||||
block $label0 (result (ref $MyConstClass))
|
||||
global.get $"C490 MyConstClass"
|
||||
i32.const 0
|
||||
table.get $module0.constant-table1
|
||||
br_on_non_null $label0
|
||||
i32.const 0
|
||||
i32.const 107
|
||||
i32.const 0
|
||||
block $label1 (result (ref $JSStringImpl))
|
||||
global.get $"C489 \"shared-const\""
|
||||
i32.const 0
|
||||
table.get $module0.constant-table0
|
||||
br_on_non_null $label1
|
||||
i32.const 0
|
||||
i32.const 4
|
||||
i32.const 0
|
||||
global.get $S.shared-const
|
||||
struct.new $JSStringImpl
|
||||
local.tee $var1
|
||||
global.set $"C489 \"shared-const\""
|
||||
table.set $module0.constant-table0
|
||||
local.get $var1
|
||||
end $label1
|
||||
struct.new $MyConstClass
|
||||
local.tee $var2
|
||||
global.set $"C490 MyConstClass"
|
||||
table.set $module0.constant-table1
|
||||
local.get $var2
|
||||
end $label0
|
||||
end
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
(global $"C331 _TopType" (import "module0" "global2") (ref $_TopType))
|
||||
(global $"C62 WasmArray<_Type>[1]" (import "module0" "global3") (ref $Array<_Type>))
|
||||
(global $"C306 WasmArray<_NamedParameter>[0]" (import "module0" "global4") (ref $Array<_NamedParameter>))
|
||||
(global $"C455 _FunctionType" (import "module0" "global5") (ref null $_FunctionType))
|
||||
(global $"C28 _InterfaceType" (import "module0" "global8") (ref $_InterfaceType))
|
||||
(global $"C28 _InterfaceType" (import "module0" "global7") (ref $_InterfaceType))
|
||||
(global $S.globalH1Bar< (import "S" "globalH1Bar<") (ref extern))
|
||||
(table $module0.constant-table0 (import "module0" "constant-table0") 1 (ref null $_FunctionType) <...>)
|
||||
(global $global7 (ref $#Vtable-1-1) <...>)
|
||||
(global $global4 (ref $#DummyStruct) <...>)
|
||||
(global $"C459 _FunctionType" (ref $_FunctionType) <...>)
|
||||
@@ -133,8 +133,10 @@
|
||||
ref.func $"instantiation constant trampoline"
|
||||
struct.new $#Vtable-0-1
|
||||
block $label2 (result (ref $_FunctionType))
|
||||
global.get $"C455 _FunctionType"
|
||||
i32.const 0
|
||||
table.get $module0.constant-table0
|
||||
br_on_non_null $label2
|
||||
i32.const 0
|
||||
i32.const 12
|
||||
i32.const 0
|
||||
i32.const 0
|
||||
@@ -147,7 +149,7 @@
|
||||
global.get $"C306 WasmArray<_NamedParameter>[0]"
|
||||
struct.new $_FunctionType
|
||||
local.tee $var1
|
||||
global.set $"C455 _FunctionType"
|
||||
table.set $module0.constant-table0
|
||||
local.get $var1
|
||||
end $label2
|
||||
struct.new $#Closure-0-1
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
(global $"C331 _TopType" (import "module0" "global2") (ref $_TopType))
|
||||
(global $"C62 WasmArray<_Type>[1]" (import "module0" "global3") (ref $Array<_Type>))
|
||||
(global $"C306 WasmArray<_NamedParameter>[0]" (import "module0" "global4") (ref $Array<_NamedParameter>))
|
||||
(global $"C455 _FunctionType" (import "module0" "global5") (ref null $_FunctionType))
|
||||
(global $S.globalH0Foo (import "S" "globalH0Foo") (ref extern))
|
||||
(table $module0.constant-table0 (import "module0" "constant-table0") 1 (ref null $_FunctionType) <...>)
|
||||
(global $global6 (ref $#Vtable-0-1) <...>)
|
||||
(global $global3 (ref $#DummyStruct) <...>)
|
||||
(global $"C465 globalH0Foo tear-off" (mut (ref null $#Closure-0-1))
|
||||
@@ -59,8 +59,10 @@
|
||||
global.get $global3
|
||||
global.get $global6
|
||||
block $label1 (result (ref $_FunctionType))
|
||||
global.get $"C455 _FunctionType"
|
||||
i32.const 0
|
||||
table.get $module0.constant-table0
|
||||
br_on_non_null $label1
|
||||
i32.const 0
|
||||
i32.const 12
|
||||
i32.const 0
|
||||
i32.const 0
|
||||
@@ -73,7 +75,7 @@
|
||||
global.get $"C306 WasmArray<_NamedParameter>[0]"
|
||||
struct.new $_FunctionType
|
||||
local.tee $var0
|
||||
global.set $"C455 _FunctionType"
|
||||
table.set $module0.constant-table0
|
||||
local.get $var0
|
||||
end $label1
|
||||
struct.new $#Closure-0-1
|
||||
|
||||
Reference in New Issue
Block a user