Skip to content

Commit c833c12

Browse files
mkustermannCommit Queue
authored andcommitted
[dart2wasm] Add support for dispatch table calls to unchecked entries
Now that we have support for generating checked & unchecked entrypoints, we can make dispatch tables also target unchecked entry points. This is beneficial especially in cases where there's dispatches on `this` that require covariant checks but we don't know the target method (i.e. we cannot devirtualize it because the method we dispatch to may beoverriden). We keep the existing selectors that we have, but a selector will now have * one row if none of the implementations of the selector need to perform type checks => `SelectorInfo` has a `SelectorTargets _normal` * two rows if any of the implementations of the selector need t perform a type check => `SelectorInfo` has a `SelectorTargets _checked` => `SelectorInfo` has a `SelectorTargets _unchecked` Once an unchecked entrypoint is also used in the dispatch table (only if there's any unchecked calls to that selector) then binaryen can no longer optimize the signature of the function. It means we may have perform e.g. downcasts / boxing in the unchecked entry where we wouldn't do before (because we only had static calls to unchecked entry before this PR). So we're going to force-inline calls to unchecked entrypoints. This avoids sometimes down casts and boxing. It also seems to actually shrink the binary size. Change-Id: I3ba4980c42886cc883fb610533f5fac9cce39b65 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/407740 Reviewed-by: Ömer Ağacan <[email protected]> Commit-Queue: Martin Kustermann <[email protected]>
1 parent 7c22f94 commit c833c12

File tree

6 files changed

+242
-125
lines changed

6 files changed

+242
-125
lines changed

pkg/dart2wasm/lib/code_generator.dart

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -670,11 +670,13 @@ abstract class AstCodeGenerator
670670
List<w.ValueType> call(Reference target) {
671671
final targetModule = translator.moduleForReference(target);
672672
final isLocalModuleCall = targetModule == b.module;
673+
final name = translator.functions.getFunctionName(target);
673674

674675
if (isLocalModuleCall) {
676+
b.comment('Direct call to $name');
675677
return b.invoke(translator.directCallTarget(target));
676678
} else {
677-
b.comment('Indirect call to $target');
679+
b.comment('Direct call to $name (across modules)');
678680
return translator.callReference(target, b);
679681
}
680682
}
@@ -1669,7 +1671,7 @@ abstract class AstCodeGenerator
16691671
translateExpression(node.receiver, signature.inputs.first),
16701672
(w.FunctionType signature, ParameterInfo paramInfo) {
16711673
_visitArguments(node.arguments, signature, paramInfo, 1);
1672-
});
1674+
}, useUncheckedEntry: useUncheckedEntry);
16731675
}
16741676

16751677
@override
@@ -1808,18 +1810,21 @@ abstract class AstCodeGenerator
18081810
}
18091811
}
18101812

1813+
final useUncheckedEntry =
1814+
translator.canUseUncheckedEntry(node.left, node);
18111815
if (singleTarget != null) {
18121816
left();
18131817
right();
18141818
call(translator.getFunctionEntry(singleTarget.reference,
1815-
uncheckedEntry: translator.canUseUncheckedEntry(node, node.left)));
1819+
uncheckedEntry: useUncheckedEntry));
18161820
} else {
18171821
_virtualCall(
18181822
node,
18191823
node.interfaceTarget,
18201824
_VirtualCallKind.Call,
18211825
left,
18221826
right,
1827+
useUncheckedEntry: useUncheckedEntry,
18231828
);
18241829
}
18251830
if (leftNullable || rightNullable) {
@@ -1853,33 +1858,34 @@ abstract class AstCodeGenerator
18531858
_VirtualCallKind kind,
18541859
void Function(w.FunctionType signature) pushReceiver,
18551860
void Function(w.FunctionType signature, ParameterInfo) pushArguments,
1856-
{bool useUncheckedEntry = false}) {
1861+
{required bool useUncheckedEntry}) {
18571862
assert(kind != _VirtualCallKind.Get || !useUncheckedEntry);
18581863
SelectorInfo selector = translator.dispatchTable.selectorForTarget(
18591864
interfaceTarget.referenceAs(
18601865
getter: kind.isGetter, setter: kind.isSetter));
1866+
final name = selector.entryPointName(useUncheckedEntry);
18611867
assert(selector.name == interfaceTarget.name.text);
18621868

18631869
pushReceiver(selector.signature);
18641870

1865-
if (selector.targetRanges.length == 1) {
1871+
final targets = selector.targets(unchecked: useUncheckedEntry);
1872+
1873+
if (targets.targetRanges.length == 1) {
18661874
// TODO(natebiggs): Ensure dynamic modules exclude this.
18671875

1868-
assert(selector.staticDispatchRanges.length == 1);
1869-
final target = translator.getFunctionEntry(
1870-
selector.targetRanges[0].target,
1871-
uncheckedEntry: useUncheckedEntry);
1876+
assert(targets.staticDispatchRanges.length == 1);
1877+
final target = targets.targetRanges.single.target;
18721878
final signature = translator.signatureForDirectCall(target);
18731879
final paramInfo = translator.paramInfoForDirectCall(target);
18741880
pushArguments(signature, paramInfo);
18751881
return translator.outputOrVoid(call(target));
18761882
}
18771883

1878-
if (selector.targetRanges.isEmpty) {
1884+
if (targets.targetRanges.isEmpty) {
18791885
// TODO(natebiggs): Ensure dynamic modules exclude this.
18801886

18811887
// Unreachable call
1882-
b.comment("Virtual call of ${selector.name} with no targets"
1888+
b.comment("Virtual call of $name with no targets"
18831889
" at ${node.location}");
18841890
pushArguments(selector.signature, selector.paramInfo);
18851891
for (int i = 0; i < selector.signature.inputs.length; ++i) {
@@ -1897,16 +1903,18 @@ abstract class AstCodeGenerator
18971903
b.local_tee(receiverVar);
18981904
pushArguments(selector.signature, selector.paramInfo);
18991905

1900-
if (selector.staticDispatchRanges.isNotEmpty) {
1906+
if (targets.staticDispatchRanges.isNotEmpty) {
19011907
// TODO(natebiggs): Ensure dynamic modules exclude this.
19021908

19031909
b.invoke(translator
19041910
.getPolymorphicDispatchersForModule(b.module)
1905-
.getPolymorphicDispatcher(selector));
1911+
.getPolymorphicDispatcher(selector,
1912+
useUncheckedEntry: useUncheckedEntry));
19061913
} else {
1907-
b.comment("Instance $kind of '${selector.name}'");
1914+
b.comment("Instance $kind of '$name'");
19081915
b.local_get(receiverVar);
1909-
translator.callDispatchTable(b, selector);
1916+
translator.callDispatchTable(b, selector,
1917+
useUncheckedEntry: useUncheckedEntry);
19101918
}
19111919

19121920
return translator.outputOrVoid(selector.signature.outputs);
@@ -2039,7 +2047,7 @@ abstract class AstCodeGenerator
20392047
w.Label nullLabel = b.block();
20402048
translateExpression(node.receiver, translator.topInfo.nullableType);
20412049
b.br_on_null(nullLabel);
2042-
}, (_, __) {});
2050+
}, (_, __) {}, useUncheckedEntry: false);
20432051
b.br(doneLabel);
20442052
b.end(); // nullLabel
20452053
switch (target.name.text) {
@@ -2069,7 +2077,8 @@ abstract class AstCodeGenerator
20692077
_VirtualCallKind.Get,
20702078
(signature) =>
20712079
translateExpression(node.receiver, signature.inputs.first),
2072-
(_, __) {});
2080+
(_, __) {},
2081+
useUncheckedEntry: false);
20732082
}
20742083
}
20752084

@@ -2181,7 +2190,7 @@ abstract class AstCodeGenerator
21812190
b.br_on_null(nullLabel);
21822191
translator.convertType(
21832192
b, translator.topInfo.nullableType, signature.inputs[0]);
2184-
}, (_, __) {});
2193+
}, (_, __) {}, useUncheckedEntry: false);
21852194
b.br(doneLabel);
21862195
b.end(); // nullLabel
21872196
switch (target.name.text) {
@@ -2212,7 +2221,8 @@ abstract class AstCodeGenerator
22122221
_VirtualCallKind.Get,
22132222
(signature) =>
22142223
translateExpression(node.receiver, signature.inputs.first),
2215-
(_, __) {});
2224+
(_, __) {},
2225+
useUncheckedEntry: false);
22162226
}
22172227

22182228
@override
@@ -3187,13 +3197,13 @@ CodeGenerator? getInlinableMemberCodeGenerator(Translator translator,
31873197

31883198
class SynchronousProcedureCodeGenerator extends AstCodeGenerator {
31893199
final Procedure member;
3190-
final SynchronousProcedureKind kind;
3200+
final EntryPoint kind;
31913201

31923202
SynchronousProcedureCodeGenerator(Translator translator,
31933203
w.FunctionType functionType, this.member, this.kind)
31943204
: super(translator, functionType, member) {
3195-
assert(!translator.needToCheckTypesFor(member) ||
3196-
kind != SynchronousProcedureKind.normal);
3205+
assert(
3206+
!translator.needToCheckTypesFor(member) || kind != EntryPoint.normal);
31973207
}
31983208

31993209
@override
@@ -3216,16 +3226,16 @@ class SynchronousProcedureCodeGenerator extends AstCodeGenerator {
32163226
closures = translator.getClosures(member);
32173227

32183228
switch (kind) {
3219-
case SynchronousProcedureKind.normal:
3229+
case EntryPoint.normal:
32203230
b.comment('Normal Entry');
32213231
_makeNonMultiEntryPointFunction();
3222-
case SynchronousProcedureKind.checked:
3232+
case EntryPoint.checked:
32233233
b.comment('Checked Entry');
32243234
_makeMultipleEntryPoint(true);
3225-
case SynchronousProcedureKind.unchecked:
3235+
case EntryPoint.unchecked:
32263236
b.comment('Unchecked Entry');
32273237
_makeMultipleEntryPoint(false);
3228-
case SynchronousProcedureKind.body:
3238+
case EntryPoint.body:
32293239
b.comment('Body for Checked & Unchecked Entry');
32303240
_makeMultipleEntryPointSharedBody();
32313241
break;

0 commit comments

Comments
 (0)