Skip to content

Commit bd7f26f

Browse files
natebiggsCommit Queue
authored andcommitted
[dart2wasm] Allow dynamic modules to run without re-compiling or running TFA on main module.
Accomplishes this by serializing more metadata in the main module metadata: 1) Most of this new metadata is to have access to the main module's dispatch table from the dynamic modules, including ProcedureMetadataAttributes and the DispatchTable itself. 2) Indices to create correct calling names from the dynamic module into the main module (or into the global updateable functions "table"). 3) Basic tree-shaking information about classes (e.g. did TFA fully delete a member or just delete its body). Some metadata from TFA is still expected throughout the compiler. For dynamic modules, we create pessimistic versions of this information and attach it to the new Component. All the same dynamic module tests that were passing (or failing) before are still in the same state. This significantly speeds up compilation of dynamic modules though as only necessary code is compiled and TFA is not run. Change-Id: I109f53cf5dcbe6579c0f78e71ce7779d593455e9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/415500 Reviewed-by: Martin Kustermann <[email protected]>
1 parent 81c3a50 commit bd7f26f

15 files changed

+1343
-866
lines changed

pkg/dart2wasm/lib/class_info.dart

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:kernel/ast.dart';
88
import 'package:wasm_builder/wasm_builder.dart' as w;
99

1010
import 'dynamic_modules.dart';
11+
import 'serialization.dart';
1112
import 'translator.dart';
1213

1314
/// Wasm struct field indices for fields that are accessed explicitly from Wasm
@@ -375,8 +376,8 @@ class ClassInfoCollector {
375376
}
376377

377378
if (translator.isDynamicModule) {
378-
final brandIndex =
379-
translator.dynamicModuleInfo!.classMetadata[cls]?.brandIndex;
379+
final brandIndex = translator
380+
.dynamicModuleInfo!.metadata.classMetadata[cls]?.brandIndex;
380381
if (brandIndex != null) {
381382
translator.typesBuilder.addBrandTypeAssignment(struct, brandIndex);
382383
}
@@ -551,16 +552,20 @@ class ClassInfoCollector {
551552

552553
final mainModuleConcreteRange =
553554
classIdNumbering.getConcreteClassIdRangeForMainModule(cls);
554-
final dynamicModuleConcreteRange =
555-
classIdNumbering.getConcreteClassIdRangeForDynamicModule(cls);
556-
557555
// Only non-extendable classes can get here so they should only have
558556
// concrete implementations in either the main module or the dynamic
559557
// module, not both.
560-
assert(mainModuleConcreteRange.isEmpty ||
561-
dynamicModuleConcreteRange.isEmpty);
562-
addRanges(mainModuleConcreteRange);
563-
addRanges(dynamicModuleConcreteRange);
558+
if (translator.isDynamicModule && mainModuleConcreteRange.isEmpty) {
559+
final dynamicModuleConcreteRange =
560+
classIdNumbering.getConcreteClassIdRangeForDynamicModule(cls);
561+
assert(dynamicModuleConcreteRange.isNotEmpty);
562+
addRanges(dynamicModuleConcreteRange);
563+
} else {
564+
assert(classIdNumbering
565+
.getConcreteClassIdRangeForDynamicModule(cls)
566+
.isEmpty);
567+
addRanges(mainModuleConcreteRange);
568+
}
564569
}
565570
final info = translator.classInfo[cls]!;
566571
info._repr = representation ?? info;
@@ -709,9 +714,9 @@ class ClassIdNumbering {
709714
final classIds = <Class, ClassId>{};
710715

711716
if (translator.isDynamicModule) {
712-
final savedMapping = translator.dynamicModuleInfo!.classMetadata;
713-
savedMapping.forEach((cls, info) {
714-
final classId = info.classId;
717+
final savedMapping = translator.dynamicModuleInfo!.metadata.classMetadata;
718+
savedMapping.forEach((cls, metadata) {
719+
final classId = metadata.classId;
715720
classIds[cls] = AbsoluteClassId(classId);
716721
savedMaxClassId = max(savedMaxClassId ?? -2, classId);
717722
if (!cls.isAbstract && !cls.isAnonymousMixin) {
@@ -805,7 +810,9 @@ class ClassIdNumbering {
805810
}
806811

807812
// Make a list of the depth-first pre-order traversal.
808-
final dfsOrder = [...?translator.dynamicModuleInfo?.dfsOrderClassIds];
813+
final dfsOrder = [
814+
...?translator.dynamicModuleInfo?.metadata.dfsOrderClassIds
815+
];
809816
final inDfsOrder = {...dfsOrder};
810817

811818
// Maps any class to a dense range of concrete class ids that are subclasses
@@ -945,6 +952,17 @@ class Range {
945952
return Range._(start, end);
946953
}
947954

955+
void serialize(DataSerializer sink) {
956+
sink.writeInt(start);
957+
sink.writeInt(end);
958+
}
959+
960+
factory Range.deserialize(DataDeserializer source) {
961+
final start = source.readInt();
962+
final end = source.readInt();
963+
return Range(start, end);
964+
}
965+
948966
int get length => 1 + (end - start);
949967
bool get isEmpty => length == 0;
950968

pkg/dart2wasm/lib/code_generator.dart

Lines changed: 52 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,8 +1828,14 @@ abstract class AstCodeGenerator
18281828
w.ValueType? intrinsicResult = intrinsifier.generateEqualsIntrinsic(node);
18291829
if (intrinsicResult != null) return intrinsicResult;
18301830

1831+
final leftType = translator.translateType(dartTypeOf(node.left));
18311832
Member? singleTarget = translator.singleTarget(node);
1832-
if (singleTarget == translator.coreTypes.objectEquals) {
1833+
if (singleTarget == translator.coreTypes.objectEquals ||
1834+
// If leftType is not a Dart type (or builtin value type) then use
1835+
// reference equality (e.g. the vtable type is not a subtype of
1836+
// topType).
1837+
(leftType is w.RefType &&
1838+
!leftType.isSubtypeOf(translator.topInfo.nullableType))) {
18331839
// Plain reference comparison
18341840
translateExpression(node.left, w.RefType.eq(nullable: true));
18351841
translateExpression(node.right, w.RefType.eq(nullable: true));
@@ -1924,64 +1930,51 @@ abstract class AstCodeGenerator
19241930
void Function(w.FunctionType signature, ParameterInfo) pushArguments,
19251931
{required bool useUncheckedEntry}) {
19261932
assert(kind != _VirtualCallKind.Get || !useUncheckedEntry);
1927-
SelectorInfo selector = translator.dispatchTable.selectorForTarget(
1928-
interfaceTarget.referenceAs(
1929-
getter: kind.isGetter, setter: kind.isSetter));
1933+
final reference = interfaceTarget.referenceAs(
1934+
getter: kind.isGetter, setter: kind.isSetter);
1935+
final dispatchTable = translator.dispatchTableForTarget(reference);
1936+
SelectorInfo selector = dispatchTable.selectorForTarget(reference);
1937+
final signature = selector.signature;
19301938
final name = selector.entryPointName(useUncheckedEntry);
19311939
assert(selector.name == interfaceTarget.name.text);
19321940

1933-
pushReceiver(selector.signature);
1934-
1935-
SelectorTargets targets;
1936-
1937-
if (!translator.dynamicModuleSupportEnabled ||
1938-
b.module == translator.mainModule) {
1939-
targets =
1940-
selector.targets(unchecked: useUncheckedEntry, dynamicModule: false);
1941-
} else {
1942-
targets =
1943-
selector.targets(unchecked: useUncheckedEntry, dynamicModule: true);
1944-
}
1945-
1946-
final isDynamicModuleOverrideable = selector.isDynamicModuleOverrideable;
1947-
if (!isDynamicModuleOverrideable) {
1948-
w.ValueType? checkRanges(List<({Range range, Reference target})> ranges) {
1949-
if (ranges.length == 1) {
1950-
final target = translator.getFunctionEntry(ranges[0].target,
1951-
uncheckedEntry: useUncheckedEntry);
1952-
final signature = translator.signatureForDirectCall(target);
1953-
final paramInfo = translator.paramInfoForDirectCall(target);
1954-
pushArguments(signature, paramInfo);
1955-
return translator.outputOrVoid(call(target));
1956-
}
1957-
if (ranges.isEmpty) {
1958-
// Unreachable call
1959-
b.comment("Virtual call of $name with no targets"
1960-
" at ${node.location}");
1961-
pushArguments(selector.signature, selector.paramInfo);
1962-
for (int i = 0; i < selector.signature.inputs.length; ++i) {
1963-
b.drop();
1964-
}
1965-
b.block(const [], selector.signature.outputs);
1966-
b.unreachable();
1967-
b.end();
1968-
return translator.outputOrVoid(selector.signature.outputs);
1941+
pushReceiver(signature);
1942+
1943+
final targets = selector.targets(unchecked: useUncheckedEntry);
1944+
List<({Range range, Reference target})> targetRanges = targets.targetRanges;
1945+
List<({Range range, Reference target})> staticDispatchRanges =
1946+
targets.staticDispatchRanges;
1947+
if (!selector.isDynamicModuleOverrideable) {
1948+
if (targetRanges.length == 1) {
1949+
final target = translator.getFunctionEntry(targetRanges[0].target,
1950+
uncheckedEntry: useUncheckedEntry);
1951+
final directCallSignature = translator.signatureForDirectCall(target);
1952+
final paramInfo = translator.paramInfoForDirectCall(target);
1953+
pushArguments(directCallSignature, paramInfo);
1954+
return translator.outputOrVoid(call(target));
1955+
}
1956+
if (targetRanges.isEmpty) {
1957+
// Unreachable call
1958+
b.comment("Virtual call of $name with no targets"
1959+
" at ${node.location}");
1960+
pushArguments(signature, selector.paramInfo);
1961+
for (int i = 0; i < signature.inputs.length; ++i) {
1962+
b.drop();
19691963
}
1970-
return null;
1964+
b.block(const [], signature.outputs);
1965+
b.unreachable();
1966+
b.end();
1967+
return translator.outputOrVoid(signature.outputs);
19711968
}
1972-
1973-
final result = checkRanges(targets.targetRanges);
1974-
if (result != null) return result;
19751969
}
19761970

19771971
// Receiver is already on stack.
1978-
w.Local receiverVar = addLocal(selector.signature.inputs.first);
1972+
w.Local receiverVar = addLocal(signature.inputs.first);
19791973
assert(!receiverVar.type.nullable);
19801974
b.local_tee(receiverVar);
1981-
pushArguments(selector.signature, selector.paramInfo);
1975+
pushArguments(signature, selector.paramInfo);
19821976

1983-
if (targets.staticDispatchRanges.isNotEmpty) {
1984-
assert(!translator.dynamicModuleSupportEnabled);
1977+
if (staticDispatchRanges.isNotEmpty) {
19851978
b.invoke(translator
19861979
.getPolymorphicDispatchersForModule(b.module)
19871980
.getPolymorphicDispatcher(selector,
@@ -1990,11 +1983,12 @@ abstract class AstCodeGenerator
19901983
b.comment("Instance $kind of '$name'");
19911984
b.local_get(receiverVar);
19921985
translator.callDispatchTable(b, selector,
1993-
interfaceTarget: interfaceTarget,
1994-
useUncheckedEntry: useUncheckedEntry);
1986+
interfaceTarget: reference,
1987+
useUncheckedEntry: useUncheckedEntry,
1988+
table: dispatchTable);
19951989
}
19961990

1997-
return translator.outputOrVoid(selector.signature.outputs);
1991+
return translator.outputOrVoid(signature.outputs);
19981992
}
19991993

20001994
@override
@@ -2102,7 +2096,7 @@ abstract class AstCodeGenerator
21022096
() => visitThis(w.RefType.struct(nullable: false)));
21032097
return w.RefType.def(closureStruct, nullable: false);
21042098
}
2105-
return _directGet(target, ThisExpression(), () => null);
2099+
return _directGet(target, ThisExpression());
21062100
}
21072101

21082102
@override
@@ -2143,10 +2137,13 @@ abstract class AstCodeGenerator
21432137
b.end(); // doneLabel
21442138
return resultType;
21452139
}
2140+
21462141
Member? singleTarget = translator.singleTarget(node);
21472142
if (singleTarget != null) {
2148-
return _directGet(singleTarget, node.receiver,
2149-
() => intrinsifier.generateInstanceGetterIntrinsic(node));
2143+
final intrinsic = intrinsifier.generateInstanceGetterIntrinsic(node);
2144+
if (intrinsic != null) return intrinsic;
2145+
2146+
return _directGet(singleTarget, node.receiver);
21502147
} else {
21512148
return _virtualCall(
21522149
node,
@@ -2229,11 +2226,7 @@ abstract class AstCodeGenerator
22292226
return translator.topInfo.nullableType;
22302227
}
22312228

2232-
w.ValueType _directGet(
2233-
Member target, Expression receiver, w.ValueType? Function() intrinsify) {
2234-
w.ValueType? intrinsicResult = intrinsify();
2235-
if (intrinsicResult != null) return intrinsicResult;
2236-
2229+
w.ValueType _directGet(Member target, Expression receiver) {
22372230
if (target is Field) {
22382231
ClassInfo info = translator.classInfo[target.enclosingClass]!;
22392232
int fieldIndex = translator.fieldIndex[target]!;

pkg/dart2wasm/lib/compile.dart

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,8 @@ Future<CompilationResult> compileToModule(
183183

184184
Component component = compilerResult!.component!;
185185
CoreTypes coreTypes = compilerResult.coreTypes!;
186-
ClassHierarchy classHierarchy = compilerResult.classHierarchy!;
186+
final classHierarchy =
187+
ClassHierarchy(component, coreTypes) as ClosedWorldClassHierarchy;
187188
LibraryIndex libraryIndex = LibraryIndex(component, [
188189
"dart:_boxed_bool",
189190
"dart:_boxed_double",
@@ -222,12 +223,11 @@ Future<CompilationResult> compileToModule(
222223
moduleStrategy = DynamicMainModuleStrategy(
223224
component,
224225
coreTypes,
225-
classHierarchy,
226226
File.fromUri(dynamicInterfaceUri).readAsStringSync(),
227227
options.dynamicInterfaceUri!);
228228
} else if (isDynamicModule) {
229-
moduleStrategy = DynamicModuleStrategy(component, options, target,
230-
coreTypes, classHierarchy, dynamicModuleMainUri);
229+
moduleStrategy = DynamicModuleStrategy(
230+
component, options, target, coreTypes, dynamicModuleMainUri);
231231
} else {
232232
moduleStrategy = DefaultModuleStrategy(component);
233233
}
@@ -268,19 +268,25 @@ Future<CompilationResult> compileToModule(
268268
final source = DataDeserializer(dynamicModuleMetadataBytes, component);
269269
mainModuleMetadata = MainModuleMetadata.deserialize(source);
270270
mainModuleMetadata.verifyDynamicModuleOptions(options);
271+
mainModuleMetadata.initializeDynamicModuleKernel(
272+
component,
273+
coreTypes,
274+
// Create a new hierarchy with generated record classes.
275+
ClassHierarchy(component, coreTypes) as ClosedWorldClassHierarchy);
271276
} else if (isDynamicMainModule) {
272277
MainModuleMetadata.verifyMainModuleOptions(options);
273278
writeComponentToBinary(component, dynamicModuleMainUri.path,
274279
includeSource: false);
275280
}
276281

277-
_patchMainTearOffs(coreTypes, component);
282+
if (!isDynamicModule) {
283+
_patchMainTearOffs(coreTypes, component);
278284

279-
// Keep the flags in-sync with
280-
// pkg/vm/test/transformations/type_flow/transformer_test.dart
281-
// TODO(natebiggs): Only run TFA on main module when dynamic modules enabled.
282-
globalTypeFlow.transformComponent(target, coreTypes, component,
283-
useRapidTypeAnalysis: false);
285+
// Keep the flags in-sync with
286+
// pkg/vm/test/transformations/type_flow/transformer_test.dart
287+
globalTypeFlow.transformComponent(target, coreTypes, component,
288+
useRapidTypeAnalysis: false);
289+
}
284290

285291
if (options.dumpKernelAfterTfa != null) {
286292
writeComponentToText(component,
@@ -295,10 +301,6 @@ Future<CompilationResult> compileToModule(
295301

296302
final moduleOutputData = moduleStrategy.buildModuleOutputData();
297303

298-
if (isDynamicMainModule) {
299-
mainModuleMetadata.initialize(component, coreTypes);
300-
}
301-
302304
var translator = Translator(component, coreTypes, libraryIndex, recordClasses,
303305
moduleOutputData, options.translatorOptions,
304306
mainModuleMetadata: mainModuleMetadata,
@@ -340,7 +342,7 @@ Future<CompilationResult> compileToModule(
340342
Uri.parse(
341343
path.setExtension(dynamicMainModuleUri!.toFilePath(), '.dyndata'));
342344
final serializer = DataSerializer(translator.component);
343-
translator.dynamicModuleInfo!.metadata.serialize(translator, serializer);
345+
translator.dynamicModuleInfo!.metadata.serialize(serializer, translator);
344346
await File.fromUri(filename).writeAsBytes(serializer.takeBytes());
345347
}
346348

pkg/dart2wasm/lib/constants.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,8 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
595595

596596
if (cls == Constants._relativeInterfaceTypeIndicator) {
597597
cls = translator.interfaceTypeClass;
598+
constant = InstanceConstant(
599+
cls.reference, constant.typeArguments, constant.fieldValues);
598600
isRelativeInterfaceType = true;
599601
}
600602

0 commit comments

Comments
 (0)