Skip to content

Commit 3c10662

Browse files
mkustermannCommit Queue
authored andcommitted
[dart2wasm] Remove special casing of noSuchMethod in dispatch table builder
So far we force-included the `noSuchMethod` selector in the dispatch table, making us have an entry for each class id (i.e. a dense row). This is wasteful for most cases, because there may be few (if any) overrides of `noSuchMethod`. So instead we use the same policy on how to call `noSuchMethod` as with all the other selectors, namely if there's only a single target we call it directly, if there's a few we may use a static polymorphic dispatcher function and otherwise use dispatch table. Change-Id: I5067fb54908d905396c3b93b824bf251dc73ff50 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/428521 Reviewed-by: Ömer Ağacan <[email protected]> Commit-Queue: Martin Kustermann <[email protected]>
1 parent f725026 commit 3c10662

File tree

3 files changed

+67
-54
lines changed

3 files changed

+67
-54
lines changed

pkg/dart2wasm/lib/code_generator.dart

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1953,31 +1953,37 @@ abstract class AstCodeGenerator
19531953
List<({Range range, Reference target})> targetRanges = targets.targetRanges;
19541954
List<({Range range, Reference target})> staticDispatchRanges =
19551955
targets.staticDispatchRanges;
1956-
if (!selector.isDynamicSubmoduleOverridable) {
1957-
if (targetRanges.length == 1) {
1958-
final target = translator.getFunctionEntry(targetRanges[0].target,
1959-
uncheckedEntry: useUncheckedEntry);
1960-
final directCallSignature = translator.signatureForDirectCall(target);
1961-
final paramInfo = translator.paramInfoForDirectCall(target);
1962-
pushArguments(directCallSignature, paramInfo);
1963-
return translator.outputOrVoid(call(target));
1964-
}
1965-
if (targetRanges.isEmpty) {
1966-
// Unreachable call
1967-
b.comment("Virtual call of $name with no targets"
1968-
" at ${node.location}");
1969-
pushArguments(signature, selector.paramInfo);
1970-
for (int i = 0; i < signature.inputs.length; ++i) {
1971-
b.drop();
1972-
}
1973-
b.block(const [], signature.outputs);
1974-
b.unreachable();
1975-
b.end();
1976-
return translator.outputOrVoid(signature.outputs);
1956+
1957+
// NOTE: Keep this in sync with
1958+
// `dynamic_forwarders.dart:generateNoSuchMethodCall`.
1959+
final bool noTarget =
1960+
targetRanges.isEmpty && !selector.isDynamicSubmoduleOverridable;
1961+
final bool directCall =
1962+
targetRanges.length == 1 && staticDispatchRanges.length == 1;
1963+
final callPolymorphicDispatcher =
1964+
!directCall && staticDispatchRanges.isNotEmpty;
1965+
1966+
if (noTarget) {
1967+
// Unreachable call
1968+
b.comment("Virtual call of $name with no targets"
1969+
" at ${node.location}");
1970+
pushArguments(signature, selector.paramInfo);
1971+
for (int i = 0; i < signature.inputs.length; ++i) {
1972+
b.drop();
19771973
}
1974+
b.block(const [], signature.outputs);
1975+
b.unreachable();
1976+
b.end();
1977+
return translator.outputOrVoid(signature.outputs);
1978+
}
1979+
if (directCall) {
1980+
final target = translator.getFunctionEntry(targetRanges[0].target,
1981+
uncheckedEntry: useUncheckedEntry);
1982+
final directCallSignature = translator.signatureForDirectCall(target);
1983+
final paramInfo = translator.paramInfoForDirectCall(target);
1984+
pushArguments(directCallSignature, paramInfo);
1985+
return translator.outputOrVoid(call(target));
19781986
}
1979-
1980-
final callPolymorphicDispatcher = staticDispatchRanges.isNotEmpty;
19811987

19821988
// Receiver is already on stack.
19831989
w.Local receiverVar = addLocal(signature.inputs.first);

pkg/dart2wasm/lib/dispatch_table.dart

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,6 @@ class SelectorInfo {
6161
/// The selector's member's name.
6262
final String name;
6363

64-
/// Whether the selector is for `Object.noSuchMethod`. We introduce instance
65-
/// invocations of `noSuchMethod` in dynamic calls, so we always add
66-
/// `noSuchMethod` overrides to the dispatch table.
67-
final bool isNoSuchMethod;
68-
6964
SelectorTargets? _checked;
7065
SelectorTargets? _unchecked;
7166
SelectorTargets? _normal;
@@ -88,7 +83,6 @@ class SelectorInfo {
8883
this.callCount,
8984
this.paramInfo, {
9085
required this.isSetter,
91-
required this.isNoSuchMethod,
9286
});
9387

9488
void serialize(DataSerializer sink) {
@@ -100,7 +94,6 @@ class SelectorInfo {
10094
useMultipleEntryPoints,
10195
isDynamicSubmoduleOverridable,
10296
isDynamicSubmoduleCallable,
103-
isNoSuchMethod
10497
]);
10598
sink.writeNullable(_checked, (targets) => targets.serialize(sink));
10699
sink.writeNullable(_unchecked, (targets) => targets.serialize(sink));
@@ -118,7 +111,6 @@ class SelectorInfo {
118111
useMultipleEntryPoints,
119112
isDynamicSubmoduleOverridable,
120113
isDynamicSubmoduleCallable,
121-
isNoSuchMethod
122114
] = source.readBoolList();
123115
final checked =
124116
source.readNullable(() => SelectorTargets.deserialize(source));
@@ -132,7 +124,7 @@ class SelectorInfo {
132124
ParameterInfo.fromMember(references.first),
133125
(p, e) => p..merge(ParameterInfo.fromMember(e)));
134126
return SelectorInfo._(dispatchTable, id, name, callCount, paramInfo,
135-
isSetter: isSetter, isNoSuchMethod: isNoSuchMethod)
127+
isSetter: isSetter)
136128
..useMultipleEntryPoints = useMultipleEntryPoints
137129
..isDynamicSubmoduleCallable = isDynamicSubmoduleCallable
138130
..isDynamicSubmoduleOverridable = isDynamicSubmoduleOverridable
@@ -484,16 +476,16 @@ class DispatchTable {
484476
final isDynamicSubmoduleOverridable =
485477
member.isDynamicSubmoduleOverridable(translator.coreTypes);
486478

479+
// The compiler will generate calls to `noSuchMethod` in the dynamic
480+
// invocation forwarders. So we ensure that the call count is positive.
481+
final isNoSuchMethod = member == translator.objectNoSuchMethod;
482+
final callCount =
483+
_selectorMetadata[selectorId].callCount + (isNoSuchMethod ? 1 : 0);
487484
final selector = _selectorInfo.putIfAbsent(
488485
selectorId,
489-
() => SelectorInfo._(
490-
this,
491-
selectorId,
492-
member.name.text,
493-
_selectorMetadata[selectorId].callCount,
486+
() => SelectorInfo._(this, selectorId, member.name.text, callCount,
494487
ParameterInfo.fromMember(target),
495-
isSetter: isSetter,
496-
isNoSuchMethod: member == translator.objectNoSuchMethod));
488+
isSetter: isSetter));
497489
assert(selector.isSetter == isSetter);
498490
final useMultipleEntryPoints = !member.isAbstract &&
499491
!member.isExternal &&
@@ -872,17 +864,8 @@ class DispatchTable {
872864
}
873865

874866
bool _isUsedViaDispatchTableCall(SelectorInfo selector) {
875-
// Special case for `objectNoSuchMethod`: we introduce instance
876-
// invocations of `objectNoSuchMethod` in dynamic calls, so keep it alive
877-
// even if there was no references to it from the Dart code.
878-
if (selector.isNoSuchMethod) {
879-
return true;
880-
}
881867
if (selector.isDynamicSubmoduleCallable) return true;
882-
883-
if (selector.callCount == 0) {
884-
return false;
885-
}
868+
if (selector.callCount == 0) return false;
886869
if (selector.isDynamicSubmoduleOverridable) return true;
887870

888871
final targets = selector.targets(unchecked: false);

pkg/dart2wasm/lib/dynamic_forwarders.dart

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -782,10 +782,26 @@ void generateNoSuchMethodCall(
782782
translator.functions.recordSelectorUse(noSuchMethodSelector, false);
783783
final signature = noSuchMethodSelector.signature;
784784

785+
final targetRanges =
786+
noSuchMethodSelector.targets(unchecked: false).targetRanges;
787+
final staticDispatchRanges =
788+
noSuchMethodSelector.targets(unchecked: false).staticDispatchRanges;
789+
790+
// NOTE: Keep this in sync with
791+
// `code_generator.dart:AstCodeGenerator._virtualCall`.
792+
final bool directCall =
793+
targetRanges.length == 1 && staticDispatchRanges.length == 1;
794+
final callPolymorphicDispatcher =
795+
!directCall && staticDispatchRanges.isNotEmpty;
796+
785797
final noSuchMethodParamInfo = noSuchMethodSelector.paramInfo;
786798
final noSuchMethodWasmFunctionType = signature;
787799

788800
pushReceiver();
801+
if (callPolymorphicDispatcher) {
802+
b.struct_get(translator.topInfo.struct, FieldIndex.classId);
803+
pushReceiver();
804+
}
789805
pushInvocationObject();
790806

791807
final invocationFactory = translator.functions
@@ -817,11 +833,19 @@ void generateNoSuchMethodCall(
817833

818834
assert(wasmArgIdx == noSuchMethodWasmFunctionType.inputs.length);
819835

820-
// Get class id for virtual call
821-
pushReceiver();
822-
translator.callDispatchTable(b, noSuchMethodSelector,
823-
interfaceTarget: translator.objectNoSuchMethod.reference,
824-
useUncheckedEntry: false);
836+
if (directCall) {
837+
translator.callReference(targetRanges[0].target, b);
838+
} else if (callPolymorphicDispatcher) {
839+
b.invoke(translator
840+
.getPolymorphicDispatchersForModule(b.module)
841+
.getPolymorphicDispatcher(noSuchMethodSelector,
842+
useUncheckedEntry: false));
843+
} else {
844+
pushReceiver();
845+
translator.callDispatchTable(b, noSuchMethodSelector,
846+
interfaceTarget: translator.objectNoSuchMethod.reference,
847+
useUncheckedEntry: false);
848+
}
825849
}
826850

827851
class ClassIdRange {

0 commit comments

Comments
 (0)