Skip to content

Commit a8155f2

Browse files
alexmarkovcommit-bot@chromium.org
authored andcommitted
[vm/aot/tfa] Infer int type, devirtualize dynamic calls
This change improves type flow analysis to infer int types, in addition to concrete classes (int type is abstract and has 2 concrete subclasses, _Smi and _Mint). Also, this CL enables devirtualization of dynamic calls (previously, only calls with known interface target were devirtualized). DartMicroBench.PrimeNumber: x64: +221% armv8: +461% armv7h: +47.98% No measurable impact on Flutter gallery build time in release mode. Issue: flutter/flutter#19677 Change-Id: I34db195f52f2a434218ee11eee7f308d4661738b Reviewed-on: https://dart-review.googlesource.com/69520 Commit-Queue: Alexander Markov <[email protected]> Reviewed-by: Martin Kustermann <[email protected]> Reviewed-by: Vyacheslav Egorov <[email protected]>
1 parent d97920e commit a8155f2

21 files changed

+250
-102
lines changed

pkg/vm/lib/metadata/inferred_type.dart

+15-8
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,25 @@ import 'package:kernel/ast.dart';
99
/// Metadata for annotating nodes with an inferred type information.
1010
class InferredType {
1111
final Reference _concreteClassReference;
12-
final bool nullable;
12+
final int _flags;
1313

14-
InferredType(Class concreteClass, bool nullable)
15-
: this._byReference(getClassReference(concreteClass), nullable);
14+
static const int flagNullable = 1 << 0;
15+
static const int flagInt = 1 << 1;
1616

17-
InferredType._byReference(this._concreteClassReference, this.nullable);
17+
InferredType(Class concreteClass, bool nullable, bool isInt)
18+
: this._byReference(getClassReference(concreteClass),
19+
(nullable ? flagNullable : 0) | (isInt ? flagInt : 0));
20+
21+
InferredType._byReference(this._concreteClassReference, this._flags);
1822

1923
Class get concreteClass => _concreteClassReference?.asClass;
2024

25+
bool get nullable => (_flags & flagNullable) != 0;
26+
bool get isInt => (_flags & flagInt) != 0;
27+
2128
@override
2229
String toString() =>
23-
"${concreteClass != null ? concreteClass : '!'}${nullable ? '?' : ''}";
30+
"${concreteClass != null ? concreteClass : (isInt ? 'int' : '!')}${nullable ? '?' : ''}";
2431
}
2532

2633
/// Repository for [InferredType].
@@ -35,14 +42,14 @@ class InferredTypeMetadataRepository extends MetadataRepository<InferredType> {
3542
void writeToBinary(InferredType metadata, Node node, BinarySink sink) {
3643
sink.writeCanonicalNameReference(
3744
getCanonicalNameOfClass(metadata.concreteClass));
38-
sink.writeByte(metadata.nullable ? 1 : 0);
45+
sink.writeByte(metadata._flags);
3946
}
4047

4148
@override
4249
InferredType readFromBinary(Node node, BinarySource source) {
4350
final concreteClassReference =
4451
source.readCanonicalNameReference()?.getReference();
45-
final nullable = (source.readByte() != 0);
46-
return new InferredType._byReference(concreteClassReference, nullable);
52+
final flags = source.readByte();
53+
return new InferredType._byReference(concreteClassReference, flags);
4754
}
4855
}

pkg/vm/lib/transformations/devirtualization.dart

+36-28
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ abstract class Devirtualization extends RecursiveVisitor<Null> {
7272
directCall.checkReceiverForNull &&
7373
_objectMemberNames.contains(directCall.target.name);
7474

75-
DirectCallMetadata getDirectCall(TreeNode node, Member target,
75+
DirectCallMetadata getDirectCall(TreeNode node, Member interfaceTarget,
7676
{bool setter = false});
7777

7878
makeDirectCall(TreeNode node, Member target, DirectCallMetadata directCall) {
@@ -96,45 +96,50 @@ abstract class Devirtualization extends RecursiveVisitor<Null> {
9696
visitMethodInvocation(MethodInvocation node) {
9797
super.visitMethodInvocation(node);
9898

99-
Member target = node.interfaceTarget;
100-
if ((target != null) && isMethod(target)) {
101-
DirectCallMetadata directCall = getDirectCall(node, target);
102-
// TODO(dartbug.com/30480): Convert _isLegalTargetForMethodInvocation()
103-
// check into an assertion once front-end implements override checks.
104-
if ((directCall != null) &&
105-
isMethod(directCall.target) &&
106-
isLegalTargetForMethodInvocation(directCall.target, node.arguments) &&
107-
!hasExtraTargetForNull(directCall)) {
108-
makeDirectCall(node, target, directCall);
109-
}
99+
final Member target = node.interfaceTarget;
100+
if (target != null && !isMethod(target)) {
101+
return;
102+
}
103+
104+
final DirectCallMetadata directCall = getDirectCall(node, target);
105+
106+
// TODO(alexmarkov): Convert _isLegalTargetForMethodInvocation()
107+
// check into an assertion once front-end implements all override checks.
108+
if ((directCall != null) &&
109+
isMethod(directCall.target) &&
110+
isLegalTargetForMethodInvocation(directCall.target, node.arguments) &&
111+
!hasExtraTargetForNull(directCall)) {
112+
makeDirectCall(node, target, directCall);
110113
}
111114
}
112115

113116
@override
114117
visitPropertyGet(PropertyGet node) {
115118
super.visitPropertyGet(node);
116119

117-
Member target = node.interfaceTarget;
118-
if ((target != null) && isFieldOrGetter(target)) {
119-
DirectCallMetadata directCall = getDirectCall(node, target);
120-
if ((directCall != null) &&
121-
isFieldOrGetter(directCall.target) &&
122-
!hasExtraTargetForNull(directCall)) {
123-
makeDirectCall(node, target, directCall);
124-
}
120+
final Member target = node.interfaceTarget;
121+
if (target != null && !isFieldOrGetter(target)) {
122+
return;
123+
}
124+
125+
final DirectCallMetadata directCall = getDirectCall(node, target);
126+
127+
if ((directCall != null) &&
128+
isFieldOrGetter(directCall.target) &&
129+
!hasExtraTargetForNull(directCall)) {
130+
makeDirectCall(node, target, directCall);
125131
}
126132
}
127133

128134
@override
129135
visitPropertySet(PropertySet node) {
130136
super.visitPropertySet(node);
131137

132-
Member target = node.interfaceTarget;
133-
if (target != null) {
134-
DirectCallMetadata directCall = getDirectCall(node, target, setter: true);
135-
if (directCall != null) {
136-
makeDirectCall(node, target, directCall);
137-
}
138+
final Member target = node.interfaceTarget;
139+
final DirectCallMetadata directCall =
140+
getDirectCall(node, target, setter: true);
141+
if (directCall != null) {
142+
makeDirectCall(node, target, directCall);
138143
}
139144
}
140145
}
@@ -148,10 +153,13 @@ class CHADevirtualization extends Devirtualization {
148153
: super(coreTypes, component, hierarchy);
149154

150155
@override
151-
DirectCallMetadata getDirectCall(TreeNode node, Member target,
156+
DirectCallMetadata getDirectCall(TreeNode node, Member interfaceTarget,
152157
{bool setter = false}) {
158+
if (interfaceTarget == null) {
159+
return null;
160+
}
153161
Member singleTarget = _hierarchySubtype
154-
.getSingleTargetForInterfaceInvocation(target, setter: setter);
162+
.getSingleTargetForInterfaceInvocation(interfaceTarget, setter: setter);
155163
if (singleTarget == null) {
156164
return null;
157165
}

pkg/vm/lib/transformations/type_flow/analysis.dart

+7
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,13 @@ class _DispatchableInvocation extends _Invocation {
450450
}
451451
_getReceiverTypeBuilder(targets, kNoSuchMethodMarker)
452452
.addConcreteType(receiver);
453+
} else if (selector is DynamicSelector) {
454+
if (kPrintTrace) {
455+
tracePrint(
456+
"Dynamic selector - adding noSuchMethod for receiver $receiver");
457+
}
458+
_getReceiverTypeBuilder(targets, kNoSuchMethodMarker)
459+
.addConcreteType(receiver);
453460
} else {
454461
if (kPrintTrace) {
455462
tracePrint("Target is not found for receiver $receiver");

pkg/vm/lib/transformations/type_flow/summary_collector.dart

+25-12
Original file line numberDiff line numberDiff line change
@@ -1067,16 +1067,17 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
10671067
}
10681068

10691069
@override
1070-
visitSwitchStatement(SwitchStatement node) {
1070+
TypeExpr visitSwitchStatement(SwitchStatement node) {
10711071
_visit(node.expression);
10721072
for (var switchCase in node.cases) {
10731073
switchCase.expressions.forEach(_visit);
10741074
_visit(switchCase.body);
10751075
}
1076+
return null;
10761077
}
10771078

10781079
@override
1079-
visitTryCatch(TryCatch node) {
1080+
TypeExpr visitTryCatch(TryCatch node) {
10801081
_visit(node.body);
10811082
for (var catchClause in node.catches) {
10821083
if (catchClause.exception != null) {
@@ -1087,49 +1088,56 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
10871088
}
10881089
_visit(catchClause.body);
10891090
}
1091+
return null;
10901092
}
10911093

10921094
@override
1093-
visitTryFinally(TryFinally node) {
1095+
TypeExpr visitTryFinally(TryFinally node) {
10941096
_visit(node.body);
10951097
_visit(node.finalizer);
1098+
return null;
10961099
}
10971100

10981101
@override
1099-
visitVariableDeclaration(VariableDeclaration node) {
1102+
TypeExpr visitVariableDeclaration(VariableDeclaration node) {
11001103
final v = _declareVariable(node, addInitType: true);
11011104
if (node.initializer == null) {
11021105
v.values.add(_nullType);
11031106
}
1107+
return null;
11041108
}
11051109

11061110
@override
1107-
visitWhileStatement(WhileStatement node) {
1111+
TypeExpr visitWhileStatement(WhileStatement node) {
11081112
_addUse(_visit(node.condition));
11091113
_visit(node.body);
1114+
return null;
11101115
}
11111116

11121117
@override
1113-
visitYieldStatement(YieldStatement node) {
1118+
TypeExpr visitYieldStatement(YieldStatement node) {
11141119
_visit(node.expression);
1120+
return null;
11151121
}
11161122

11171123
@override
1118-
visitFieldInitializer(FieldInitializer node) {
1124+
TypeExpr visitFieldInitializer(FieldInitializer node) {
11191125
final value = _visit(node.value);
11201126
final args = new Args<TypeExpr>([_receiver, value]);
11211127
_makeCall(node,
11221128
new DirectSelector(node.field, callKind: CallKind.PropertySet), args);
1129+
return null;
11231130
}
11241131

11251132
@override
1126-
visitRedirectingInitializer(RedirectingInitializer node) {
1133+
TypeExpr visitRedirectingInitializer(RedirectingInitializer node) {
11271134
final args = _visitArguments(_receiver, node.arguments);
11281135
_makeCall(node, new DirectSelector(node.target), args);
1136+
return null;
11291137
}
11301138

11311139
@override
1132-
visitSuperInitializer(SuperInitializer node) {
1140+
TypeExpr visitSuperInitializer(SuperInitializer node) {
11331141
final args = _visitArguments(_receiver, node.arguments);
11341142

11351143
Constructor target = null;
@@ -1146,22 +1154,27 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
11461154
}
11471155
assertx(target != null);
11481156
_makeCall(node, new DirectSelector(target), args);
1157+
return null;
11491158
}
11501159

11511160
@override
1152-
visitLocalInitializer(LocalInitializer node) {
1161+
TypeExpr visitLocalInitializer(LocalInitializer node) {
11531162
visitVariableDeclaration(node.variable);
1163+
return null;
11541164
}
11551165

11561166
@override
1157-
visitAssertInitializer(AssertInitializer node) {
1167+
TypeExpr visitAssertInitializer(AssertInitializer node) {
11581168
if (!kRemoveAsserts) {
11591169
_visit(node.statement);
11601170
}
1171+
return null;
11611172
}
11621173

11631174
@override
1164-
visitInvalidInitializer(InvalidInitializer node) {}
1175+
TypeExpr visitInvalidInitializer(InvalidInitializer node) {
1176+
return null;
1177+
}
11651178

11661179
@override
11671180
TypeExpr visitConstantExpression(ConstantExpression node) {

pkg/vm/lib/transformations/type_flow/transformer.dart

+15-11
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ class TFADevirtualization extends Devirtualization {
9696
_typeFlowAnalysis.environment.hierarchy);
9797

9898
@override
99-
DirectCallMetadata getDirectCall(TreeNode node, Member target,
99+
DirectCallMetadata getDirectCall(TreeNode node, Member interfaceTarget,
100100
{bool setter = false}) {
101101
final callSite = _typeFlowAnalysis.callSite(node);
102102
if (callSite != null) {
@@ -115,10 +115,12 @@ class AnnotateKernel extends RecursiveVisitor<Null> {
115115
final TypeFlowAnalysis _typeFlowAnalysis;
116116
final InferredTypeMetadataRepository _inferredTypeMetadata;
117117
final UnreachableNodeMetadataRepository _unreachableNodeMetadata;
118+
final DartType _intType;
118119

119120
AnnotateKernel(Component component, this._typeFlowAnalysis)
120121
: _inferredTypeMetadata = new InferredTypeMetadataRepository(),
121-
_unreachableNodeMetadata = new UnreachableNodeMetadataRepository() {
122+
_unreachableNodeMetadata = new UnreachableNodeMetadataRepository(),
123+
_intType = _typeFlowAnalysis.environment.intType {
122124
component.addMetadataRepository(_inferredTypeMetadata);
123125
component.addMetadataRepository(_unreachableNodeMetadata);
124126
}
@@ -127,23 +129,25 @@ class AnnotateKernel extends RecursiveVisitor<Null> {
127129
assertx(type != null);
128130

129131
Class concreteClass;
132+
bool isInt = false;
130133

131134
final nullable = type is NullableType;
132135
if (nullable) {
133-
final baseType = (type as NullableType).baseType;
136+
type = (type as NullableType).baseType;
137+
}
134138

135-
if (baseType == const EmptyType()) {
136-
concreteClass = _typeFlowAnalysis.environment.coreTypes.nullClass;
137-
} else {
138-
concreteClass =
139-
baseType.getConcreteClass(_typeFlowAnalysis.hierarchyCache);
140-
}
139+
if (nullable && type == const EmptyType()) {
140+
concreteClass = _typeFlowAnalysis.environment.coreTypes.nullClass;
141141
} else {
142142
concreteClass = type.getConcreteClass(_typeFlowAnalysis.hierarchyCache);
143+
144+
if (concreteClass == null) {
145+
isInt = type.isSubtypeOf(_typeFlowAnalysis.hierarchyCache, _intType);
146+
}
143147
}
144148

145-
if ((concreteClass != null) || !nullable) {
146-
return new InferredType(concreteClass, nullable);
149+
if ((concreteClass != null) || !nullable || isInt) {
150+
return new InferredType(concreteClass, nullable, isInt);
147151
}
148152

149153
return null;

pkg/vm/lib/transformations/type_flow/types.dart

+18
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ abstract class Type extends TypeExpr {
9595

9696
Class getConcreteClass(TypeHierarchy typeHierarchy) => null;
9797

98+
bool isSubtypeOf(TypeHierarchy typeHierarchy, DartType dartType) => false;
99+
98100
@override
99101
Type getComputedType(List<Type> types) => this;
100102

@@ -167,6 +169,10 @@ class NullableType extends Type {
167169
@override
168170
String toString() => "${baseType}?";
169171

172+
@override
173+
bool isSubtypeOf(TypeHierarchy typeHierarchy, DartType dartType) =>
174+
baseType.isSubtypeOf(typeHierarchy, dartType);
175+
170176
@override
171177
int get order => TypeOrder.Nullable.index;
172178

@@ -278,6 +284,10 @@ class SetType extends Type {
278284
@override
279285
String toString() => "_T ${types}";
280286

287+
@override
288+
bool isSubtypeOf(TypeHierarchy typeHierarchy, DartType dartType) =>
289+
types.every((ConcreteType t) => t.isSubtypeOf(typeHierarchy, dartType));
290+
281291
@override
282292
int get order => TypeOrder.Set.index;
283293

@@ -396,6 +406,10 @@ class ConeType extends Type {
396406
.specializeTypeCone(dartType)
397407
.getConcreteClass(typeHierarchy);
398408

409+
@override
410+
bool isSubtypeOf(TypeHierarchy typeHierarchy, DartType dartType) =>
411+
typeHierarchy.isSubtype(this.dartType, dartType);
412+
399413
@override
400414
int get hashCode => (dartType.hashCode + 37) & kHashMask;
401415

@@ -504,6 +518,10 @@ class ConcreteType extends Type implements Comparable<ConcreteType> {
504518
Class getConcreteClass(TypeHierarchy typeHierarchy) =>
505519
(dartType as InterfaceType).classNode;
506520

521+
@override
522+
bool isSubtypeOf(TypeHierarchy typeHierarchy, DartType dartType) =>
523+
typeHierarchy.isSubtype(this.dartType, dartType);
524+
507525
@override
508526
int get hashCode => (classId.hashCode ^ 0x1234) & kHashMask;
509527

pkg/vm/testcases/transformations/type_flow/transformer/annotation.dart.expect

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class B extends core::Object {
3737
}
3838
static method foo([@vm.inferred-type.metadata=dart.core::Null?] (core::List<core::int>) → void a) → core::int {
3939
@self::VarAnnotation::•() core::int x = 2;
40-
return [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] x.{core::num::+}(2);
40+
return [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] [@vm.inferred-type.metadata=int?] x.{core::num::+}(2);
4141
}
4242
@self::ParametrizedAnnotation::•<core::Null>(null)
4343
static method main(core::List<core::String> args) → dynamic {

0 commit comments

Comments
 (0)