Skip to content

Commit d117760

Browse files
mralephcommit-bot@chromium.org
authored andcommitted
[vm/kernel/aot] Skip unnecessary type checks on parameters of instance methods.
This relands 75a9579. The approach works as follows: Step 1: Kernel transform. Under the closed world assumption compute the set of selectors dispatched dynamically, then mark all procedures that don't match any of those selectors as 'not-dispatched-dynamically'. Step 2: VM backend. When building IR for a function if this function was marked as not-dispatched-dynamically then omit type checks for any parameter that is not marked as generic-covariant-impl, as such arguments are guaranteed to be checked on the caller side (by front-end). +------------------------+------------+----------+-----------+--------------+ | benchmark | baseline | current | with opt | improved by | +------------------------+------------+----------+-----------+--------------+ | stock_layout_iteration | 2366.3786 | 2724.3 | 2562.75 | -5.93% | | stock_build_iteration | 3824.3 | 4914.8 | 4681 | -4.76% | +------------------------+------------+----------+-----------+--------------+ * Flutter gallery Instructions size is reduced by 11% (8748720 bytes to 7846368 bytes). Baseline is at 6196496 bytes. Alternatively to annotating individual procedures, I considered annotating Program node with a set of dynamically dispatched selectors. Decoding and passing this information around proved to be quite cumbersome in the "streaming" world, so I opted for a simpler approach where all individual procedures are annotated. Bug: #3179 Change-Id: I2f32a609e3872c74d5ae7bbd97555453aaedf15f Reviewed-on: https://dart-review.googlesource.com/38125 Commit-Queue: Samir Jindel <[email protected]> Reviewed-by: Samir Jindel <[email protected]>
1 parent 5ec6555 commit d117760

10 files changed

+539
-71
lines changed

pkg/vm/bin/dump_kernel.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import 'package:kernel/binary/ast_from_binary.dart'
1111
import 'package:vm/metadata/direct_call.dart' show DirectCallMetadataRepository;
1212
import 'package:vm/metadata/inferred_type.dart'
1313
show InferredTypeMetadataRepository;
14+
import 'package:vm/metadata/procedure_attributes.dart'
15+
show ProcedureAttributesMetadataRepository;
1416

1517
final String _usage = '''
1618
Usage: dump_kernel input.dill output.txt
@@ -31,6 +33,7 @@ main(List<String> arguments) async {
3133
// Register VM-specific metadata.
3234
program.addMetadataRepository(new DirectCallMetadataRepository());
3335
program.addMetadataRepository(new InferredTypeMetadataRepository());
36+
program.addMetadataRepository(new ProcedureAttributesMetadataRepository());
3437

3538
final List<int> bytes = new File(input).readAsBytesSync();
3639
new BinaryBuilderWithMetadata(bytes).readProgram(program);

pkg/vm/lib/kernel_front_end.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import 'package:kernel/core_types.dart' show CoreTypes;
1919

2020
import 'transformations/devirtualization.dart' as devirtualization
2121
show transformProgram;
22+
import 'transformations/no_dynamic_invocations_annotator.dart'
23+
as no_dynamic_invocations_annotator show transformProgram;
2224
import 'transformations/type_flow/transformer.dart' as globalTypeFlow
2325
show transformProgram;
2426

@@ -58,6 +60,7 @@ _runGlobalTransformations(Program program, bool strongMode) {
5860
globalTypeFlow.transformProgram(coreTypes, program);
5961
} else {
6062
devirtualization.transformProgram(coreTypes, program);
63+
no_dynamic_invocations_annotator.transformProgram(coreTypes, program);
6164
}
6265
}
6366
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
library vm.metadata.procedure_attributes;
6+
7+
import 'package:kernel/ast.dart';
8+
9+
/// Metadata for annotating procedures with various attributes.
10+
class ProcedureAttributesMetadata {
11+
final bool hasDynamicInvocations;
12+
13+
const ProcedureAttributesMetadata({this.hasDynamicInvocations});
14+
15+
const ProcedureAttributesMetadata.noDynamicInvocations()
16+
: hasDynamicInvocations = false;
17+
18+
@override
19+
String toString() => "hasDynamicInvocations:${hasDynamicInvocations}";
20+
}
21+
22+
/// Repository for [ProcedureAttributesMetadata].
23+
class ProcedureAttributesMetadataRepository
24+
extends MetadataRepository<ProcedureAttributesMetadata> {
25+
@override
26+
final String tag = 'vm.procedure-attributes.metadata';
27+
28+
@override
29+
final Map<TreeNode, ProcedureAttributesMetadata> mapping =
30+
<TreeNode, ProcedureAttributesMetadata>{};
31+
32+
@override
33+
void writeToBinary(ProcedureAttributesMetadata metadata, BinarySink sink) {
34+
assert(!metadata.hasDynamicInvocations);
35+
}
36+
37+
@override
38+
ProcedureAttributesMetadata readFromBinary(BinarySource source) {
39+
return const ProcedureAttributesMetadata.noDynamicInvocations();
40+
}
41+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
library vm.transformations.no_dynamic_invocations_annotator;
6+
7+
import 'package:kernel/ast.dart';
8+
import 'package:kernel/core_types.dart' show CoreTypes;
9+
10+
import '../metadata/procedure_attributes.dart';
11+
12+
/// Assumes strong mode and closed world. If a procedure can not be riched
13+
/// via dynamic invocation from anywhere then annotates it with appropriate
14+
/// [ProcedureAttributeMetadata] annotation.
15+
Program transformProgram(CoreTypes coreTypes, Program program) {
16+
new NoDynamicInvocationsAnnotator(program).visitProgram(program);
17+
return program;
18+
}
19+
20+
enum Action { get, set, invoke }
21+
22+
class Selector {
23+
final Action action;
24+
final Name target;
25+
26+
Selector(this.action, this.target);
27+
28+
bool operator ==(other) {
29+
return other is Selector &&
30+
other.action == this.action &&
31+
other.target == this.target;
32+
}
33+
34+
int get hashCode => (action.index * 31) ^ target.hashCode;
35+
36+
@override
37+
String toString() {
38+
switch (action) {
39+
case Action.get:
40+
return 'get:${target}';
41+
case Action.set:
42+
return 'set:${target}';
43+
case Action.invoke:
44+
return '${target}';
45+
}
46+
return '?';
47+
}
48+
}
49+
50+
class NoDynamicInvocationsAnnotator extends RecursiveVisitor<Null> {
51+
final Set<Selector> _dynamicSelectors;
52+
final ProcedureAttributesMetadataRepository _metadata;
53+
54+
NoDynamicInvocationsAnnotator(Program program)
55+
: _dynamicSelectors = DynamicSelectorsCollector.collect(program),
56+
_metadata = new ProcedureAttributesMetadataRepository() {
57+
program.addMetadataRepository(_metadata);
58+
}
59+
60+
@override
61+
visitProcedure(Procedure node) {
62+
if (node.isStatic || node.name.name == 'call') {
63+
return;
64+
}
65+
66+
Selector selector;
67+
if (node.kind == ProcedureKind.Method) {
68+
selector = new Selector(Action.invoke, node.name);
69+
} else if (node.kind == ProcedureKind.Setter) {
70+
selector = new Selector(Action.set, node.name);
71+
} else {
72+
return;
73+
}
74+
75+
if (!_dynamicSelectors.contains(selector)) {
76+
_metadata.mapping[node] =
77+
const ProcedureAttributesMetadata.noDynamicInvocations();
78+
}
79+
}
80+
}
81+
82+
class DynamicSelectorsCollector extends RecursiveVisitor<Null> {
83+
final Set<Selector> dynamicSelectors = new Set<Selector>();
84+
85+
static Set<Selector> collect(Program program) {
86+
final v = new DynamicSelectorsCollector();
87+
v.visitProgram(program);
88+
return v.dynamicSelectors;
89+
}
90+
91+
@override
92+
visitMethodInvocation(MethodInvocation node) {
93+
super.visitMethodInvocation(node);
94+
95+
if (node.dispatchCategory == DispatchCategory.dynamicDispatch) {
96+
dynamicSelectors.add(new Selector(Action.invoke, node.name));
97+
}
98+
}
99+
100+
@override
101+
visitPropertyGet(PropertyGet node) {
102+
super.visitPropertyGet(node);
103+
104+
if (node.dispatchCategory == DispatchCategory.dynamicDispatch) {
105+
dynamicSelectors.add(new Selector(Action.get, node.name));
106+
}
107+
}
108+
109+
@override
110+
visitPropertySet(PropertySet node) {
111+
super.visitPropertySet(node);
112+
113+
if (node.dispatchCategory == DispatchCategory.dynamicDispatch) {
114+
dynamicSelectors.add(new Selector(Action.set, node.name));
115+
}
116+
}
117+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// VMOptions=--reify-generic-functions
6+
7+
import "package:expect/expect.dart";
8+
9+
// This test tests that AOT compiler does not optimize away necessary
10+
// type checks.
11+
12+
class A {
13+
int _addOneToArgument(int x) => x + 1;
14+
}
15+
16+
abstract class G<T> {
17+
int _addOneToArgument(T x);
18+
}
19+
20+
class B extends A implements G<int> {}
21+
22+
class C {
23+
int _addTwoToArgument(int x) => x + 2;
24+
}
25+
26+
class D {
27+
int _addThreeToArgument(num x) {
28+
return 0;
29+
}
30+
}
31+
32+
class E extends D {
33+
int _addThreeToArgument(covariant int x) {
34+
return x + 3;
35+
}
36+
}
37+
38+
typedef dynamic F0<T>(T val);
39+
typedef U F1<T, U>(T val);
40+
41+
class F<T> {
42+
T fMethod1(F0<T> f, T val) => f(val) as T;
43+
U fMethod2<U>(F1<T, U> f, T val) => f(val);
44+
}
45+
46+
final arr = <Object>[
47+
new B(),
48+
new C(),
49+
new E(),
50+
new D(), // Just to confuse CHA
51+
new F<int>(),
52+
];
53+
54+
int _add42Int(int v) => v + 42;
55+
double _add42Double(double v) => v + 42;
56+
double _add42_0Int(int v) => v + 42.0;
57+
58+
main() {
59+
final b = arr[0] as G<num>;
60+
61+
Expect.equals(1, b._addOneToArgument(0));
62+
Expect.equals(0, b._addOneToArgument(-1));
63+
Expect.throwsTypeError(() => b._addOneToArgument(1.1));
64+
65+
final c = (arr[1] as C);
66+
final tornMethod = c._addTwoToArgument;
67+
Expect.equals(2, c._addTwoToArgument(0));
68+
Expect.equals(0, c._addTwoToArgument(-2));
69+
Expect.throwsTypeError(() => (tornMethod as dynamic)(1.1));
70+
71+
final e = (arr[2] as D);
72+
Expect.equals(3, e._addThreeToArgument(0));
73+
Expect.equals(0, e._addThreeToArgument(-3));
74+
Expect.throwsTypeError(() => e._addThreeToArgument(1.1));
75+
76+
final f = (arr[4] as F<num>);
77+
final torn1 = f.fMethod1 as dynamic;
78+
Expect.equals(43, torn1(_add42Int, 1));
79+
Expect.throwsTypeError(() => torn1(_add42Double, 1));
80+
Expect.throwsTypeError(() => torn1(_add42Int, 1.1));
81+
82+
final torn2 = f.fMethod2 as dynamic;
83+
Expect.equals(43, torn2<int>(_add42Int, 1));
84+
Expect.equals(43.0, torn2<double>(_add42_0Int, 1));
85+
Expect.throwsTypeError(() => torn2<double>(_add42Int, 1));
86+
Expect.throwsTypeError(() => torn2<int>(_add42_0Int, 1));
87+
}

runtime/tests/vm/vm.status

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ dart/spawn_shutdown_test: Skip # OOM crash can bring down the OS.
105105
cc/CorelibCompilerStats: Skip
106106
cc/Service_Profile: Skip
107107

108+
[ !$strong ]
109+
dart/callee_side_type_checks_test: SkipByDesign
110+
108111
# Following tests are failing in a weird way on macos/ia32/debug builds
109112
# need to investigate.
110113
[ $arch == ia32 && $mode == debug && $runtime == vm && $system == macos ]

runtime/vm/compiler/backend/type_propagator.cc

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -942,8 +942,18 @@ CompileType ParameterInstr::ComputeType() const {
942942
return CompileType(CompileType::kNonNullable, cid, &type);
943943
}
944944

945-
// TODO(dartbug.com/30480): Figure out how to use parameter types
946-
// without interfering with argument type checks.
945+
if (Isolate::Current()->strong() && FLAG_use_strong_mode_types) {
946+
LocalScope* scope = graph_entry->parsed_function().node_sequence()->scope();
947+
// Note: in catch-blocks we have ParameterInstr for each local variable
948+
// not only for normal parameters.
949+
if (index() < scope->num_variables()) {
950+
LocalVariable* param = scope->VariableAt(index());
951+
if (param->was_type_checked_by_caller()) {
952+
return CompileType::FromAbstractType(param->type(),
953+
CompileType::kNullable);
954+
}
955+
}
956+
}
947957

948958
return CompileType::Dynamic();
949959
}

0 commit comments

Comments
 (0)