Skip to content

Commit 5976718

Browse files
natebiggsCommit Queue
authored andcommitted
[dart2wasm] Dynamic module shared constants.
Updates the main module to export constants accessible from dynamic modules. This includes the backing global store as well as the initialization function if the constant is 'lazy'. The dynamic module then imports these directly from the main module and does not need to duplicate any of the code. Change-Id: I2154572688b1e913b720e400286e7fc305694c22 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/422343 Reviewed-by: Martin Kustermann <[email protected]>
1 parent b3e7e2f commit 5976718

File tree

3 files changed

+160
-32
lines changed

3 files changed

+160
-32
lines changed

pkg/dart2wasm/lib/constants.dart

Lines changed: 125 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import 'package:wasm_builder/wasm_builder.dart' as w;
1414
import 'class_info.dart';
1515
import 'closures.dart';
1616
import 'code_generator.dart';
17+
import 'dynamic_module_kernel_metadata.dart'
18+
show DynamicModuleConstantRepository;
1719
import 'dynamic_modules.dart';
1820
import 'param_info.dart';
1921
import 'translator.dart';
@@ -71,7 +73,10 @@ typedef ConstantCodeGenerator = void Function(w.InstructionsBuilder);
7173
class Constants {
7274
final Translator translator;
7375
final Map<Constant, ConstantInfo> constantInfo = {};
74-
final Map<Constant, ConstantInfo> dynamicModuleConstantInfo = {};
76+
late final Map<Constant, int>? dynamicMainModuleConstantId = (translator
77+
.component.metadata[DynamicModuleConstantRepository.repositoryTag]
78+
as DynamicModuleConstantRepository?)
79+
?.mapping[translator.component] ??= {};
7580
w.DataSegmentBuilder? int32Segment;
7681
late final ClassInfo typeInfo = translator.classInfo[translator.typeClass]!;
7782

@@ -494,55 +499,106 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
494499
constant = constants._lowerTypeConstant(type);
495500
}
496501

497-
final cache = translator.dynamicModuleSupportEnabled &&
498-
!translator.isMainModule(targetModule)
499-
? constants.dynamicModuleConstantInfo
500-
: constants.constantInfo;
501-
ConstantInfo? info = cache[constant];
502+
ConstantInfo? info = constants.constantInfo[constant];
502503
if (info == null) {
503504
info = constant.accept(this);
504505
if (info != null) {
505-
cache[constant] = info;
506+
constants.constantInfo[constant] = info;
506507
}
507508
}
508509
return info;
509510
}
510511

512+
static String _dynamicModuleConstantExportName(int id) => '#c$id';
513+
static String _dynamicModuleInitFunctionExportName(int id) => '#cf$id';
514+
511515
ConstantInfo createConstant(
512516
Constant constant, w.RefType type, ConstantCodeGenerator generator,
513517
{bool lazy = false}) {
514518
assert(!type.nullable);
515-
if (lazy || translator.dynamicModuleSupportEnabled) {
519+
520+
// This function is only called once per [Constant]. If we compile a
521+
// dynamic module then the [dynamicModuleConstantIdMap] is pre-populated and
522+
// we may find an export name. If we compile the main module, then the id
523+
// will be `null`.
524+
final dynamicModuleConstantIdMap = constants.dynamicMainModuleConstantId;
525+
final mainModuleExportId = dynamicModuleConstantIdMap?[constant];
526+
final isShareableAcrossModules = dynamicModuleConstantIdMap != null &&
527+
constant.accept(_ConstantDynamicModuleSharedChecker(translator));
528+
final needsRuntimeCanonicalization = isShareableAcrossModules &&
529+
translator.isDynamicModule &&
530+
mainModuleExportId == null;
531+
532+
if (lazy || needsRuntimeCanonicalization) {
516533
// Create uninitialized global and function to initialize it.
517-
final global =
518-
targetModule.globals.define(w.GlobalType(type.withNullability(true)));
519-
global.initializer.ref_null(w.HeapType.none);
520-
global.initializer.end();
534+
final globalType = w.GlobalType(type.withNullability(true));
535+
w.Global global;
536+
w.BaseFunction initFunction;
537+
521538
w.FunctionType ftype =
522539
translator.typesBuilder.defineFunction(const [], [type]);
523-
final function = targetModule.functions.define(ftype, "$constant");
524-
final b2 = function.body;
525-
generator(b2);
526-
if (translator.dynamicModuleSupportEnabled) {
527-
final valueLocal = b2.addLocal(type);
528-
constant.accept(ConstantCanonicalizer(translator, b2, valueLocal));
540+
541+
if (mainModuleExportId != null) {
542+
global = targetModule.globals.import(translator.mainModule.moduleName,
543+
_dynamicModuleConstantExportName(mainModuleExportId), globalType);
544+
initFunction = targetModule.functions.import(
545+
translator.mainModule.moduleName,
546+
_dynamicModuleInitFunctionExportName(mainModuleExportId),
547+
ftype);
548+
} else {
549+
final definedGlobal = global = targetModule.globals.define(globalType);
550+
definedGlobal.initializer.ref_null(w.HeapType.none);
551+
definedGlobal.initializer.end();
552+
553+
final function =
554+
initFunction = targetModule.functions.define(ftype, "$constant");
555+
556+
if (isShareableAcrossModules) {
557+
final exportId = dynamicModuleConstantIdMap[constant] =
558+
dynamicModuleConstantIdMap.length;
559+
560+
targetModule.exports.export(
561+
_dynamicModuleConstantExportName(exportId), definedGlobal);
562+
targetModule.exports
563+
.export(_dynamicModuleInitFunctionExportName(exportId), function);
564+
}
565+
final b2 = function.body;
566+
generator(b2);
567+
if (needsRuntimeCanonicalization) {
568+
final valueLocal = b2.addLocal(type);
569+
constant.accept(ConstantCanonicalizer(translator, b2, valueLocal));
570+
}
571+
w.Local temp = b2.addLocal(type);
572+
b2.local_tee(temp);
573+
b2.global_set(global);
574+
b2.local_get(temp);
575+
b2.end();
529576
}
530-
w.Local temp = b2.addLocal(type);
531-
b2.local_tee(temp);
532-
b2.global_set(global);
533-
b2.local_get(temp);
534-
b2.end();
535577

536-
return ConstantInfo(constant, global, function);
578+
return ConstantInfo(constant, global, initFunction);
537579
} else {
538580
// Create global with the constant in its initializer.
539581
assert(!constants.currentlyCreating);
540-
constants.currentlyCreating = true;
541-
final global =
542-
targetModule.globals.define(w.GlobalType(type, mutable: false));
543-
generator(global.initializer);
544-
global.initializer.end();
545-
constants.currentlyCreating = false;
582+
final globalType = w.GlobalType(type, mutable: false);
583+
w.Global global;
584+
if (mainModuleExportId != null) {
585+
global = targetModule.globals.import(translator.mainModule.moduleName,
586+
_dynamicModuleConstantExportName(mainModuleExportId), globalType);
587+
} else {
588+
constants.currentlyCreating = true;
589+
final definedGlobal = global = targetModule.globals.define(globalType);
590+
generator(definedGlobal.initializer);
591+
definedGlobal.initializer.end();
592+
constants.currentlyCreating = false;
593+
594+
if (isShareableAcrossModules) {
595+
final exportId = dynamicModuleConstantIdMap[constant] =
596+
dynamicModuleConstantIdMap.length;
597+
598+
targetModule.exports.export(
599+
_dynamicModuleConstantExportName(exportId), definedGlobal);
600+
}
601+
}
546602

547603
return ConstantInfo(constant, global, null);
548604
}
@@ -651,6 +707,10 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
651707
args = supertype.typeArguments;
652708
}
653709

710+
// If the class ID is relative then it needs to be globalized when
711+
// initializing the object which is a non-const operation.
712+
lazy |= info.classId is RelativeClassId;
713+
654714
return createConstant(constant, type, lazy: lazy, (b) {
655715
b.pushObjectHeaderFields(translator, info);
656716
for (int i = FieldIndex.objectFieldBase; i < fieldCount; i++) {
@@ -910,8 +970,8 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
910970

911971
// The vtable for the target will be stored on a global in the target's
912972
// module.
913-
final isLazy =
914-
translator.moduleForReference(constant.targetReference) != targetModule;
973+
final isLazy = translator.moduleForReference(constant.targetReference) !=
974+
translator.mainModule;
915975
// The dummy struct must be declared before the constant global so that the
916976
// constant's initializer can reference it.
917977
final dummyStructGlobal = translator
@@ -1158,3 +1218,36 @@ List<int> _intToLittleEndianBytes(int i) {
11581218
}
11591219

11601220
String _intToBase64(int i) => base64.encode(_intToLittleEndianBytes(i));
1221+
1222+
/// Resolves to true if the visited Constant is accessible from dynamic modules.
1223+
///
1224+
/// Constants that are accessible from dynamic modules should be:
1225+
/// (1) Exported from the main module if they exist there and then imported
1226+
/// into dynamic modules.
1227+
/// (2) Runtime canonicalized by dynamic modules if they are not in the main
1228+
/// module.
1229+
class _ConstantDynamicModuleSharedChecker extends ConstantVisitor<bool>
1230+
with ConstantVisitorDefaultMixin<bool> {
1231+
final Translator translator;
1232+
1233+
_ConstantDynamicModuleSharedChecker(this.translator);
1234+
1235+
// TODO(natebiggs): Make this more precise by handling more specific
1236+
// constants.
1237+
@override
1238+
bool defaultConstant(Constant constant) => true;
1239+
1240+
@override
1241+
bool visitInstanceConstant(InstanceConstant constant) {
1242+
final cls = constant.classNode;
1243+
if (!cls.enclosingLibrary.isFromMainModule(translator.coreTypes)) {
1244+
return false;
1245+
}
1246+
if (cls == translator.wasmArrayClass ||
1247+
cls == translator.immutableWasmArrayClass) {
1248+
return true;
1249+
}
1250+
return constant.classNode.constructors.any(
1251+
(c) => c.isConst && c.isDynamicModuleCallable(translator.coreTypes));
1252+
}
1253+
}

pkg/dart2wasm/lib/dynamic_module_kernel_metadata.dart

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,39 @@ class DynamicModuleGlobalIdRepository extends MetadataRepository<int> {
5959
}
6060
}
6161

62+
/// Repository for kernel constants.
63+
class DynamicModuleConstantRepository
64+
extends MetadataRepository<Map<Constant, int>> {
65+
static const repositoryTag = 'wasm.dynamic-modules.constants';
66+
67+
@override
68+
final String tag = repositoryTag;
69+
70+
@override
71+
final Map<TreeNode, Map<Constant, int>> mapping = {};
72+
73+
@override
74+
Map<Constant, int> readFromBinary(Node node, BinarySource source) {
75+
final length = source.readUInt30();
76+
final Map<Constant, int> constants = {};
77+
for (int i = 0; i < length; i++) {
78+
final constant = source.readConstantReference();
79+
final id = source.readUInt30();
80+
constants[constant] = id;
81+
}
82+
return constants;
83+
}
84+
85+
@override
86+
void writeToBinary(Map<Constant, int> constants, Node node, BinarySink sink) {
87+
sink.writeUInt30(constants.length);
88+
constants.forEach((constant, id) {
89+
sink.writeConstantReference(constant);
90+
sink.writeUInt30(id);
91+
});
92+
}
93+
}
94+
6295
class ClassMetadata {
6396
/// The class numbering ID assigned to this class.
6497
final int classId;
@@ -454,6 +487,7 @@ Future<(Component, JSMethods)> generateDynamicModuleComponent(
454487
optimizedMainComponentBytes.length, dynamicModuleComponentBytes);
455488
final newComponent = Component()
456489
..addMetadataRepository(DynamicModuleGlobalIdRepository())
490+
..addMetadataRepository(DynamicModuleConstantRepository())
457491
..addMetadataRepository(ProcedureAttributesMetadataRepository())
458492
..addMetadataRepository(TableSelectorMetadataRepository())
459493
..addMetadataRepository(DirectCallMetadataRepository())

pkg/dart2wasm/lib/dynamic_modules.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ class DynamicMainModuleStrategy extends ModuleStrategy with KernelNodes {
116116
addPragma(lib, _mainModLibPragma, coreTypes);
117117
}
118118

119+
component.addMetadataRepository(DynamicModuleConstantRepository());
119120
component.addMetadataRepository(DynamicModuleGlobalIdRepository());
120121
}
121122

0 commit comments

Comments
 (0)