Skip to content

Commit bfd1582

Browse files
alexmarkovcommit-bot@chromium.org
authored andcommitted
[vm] Specialize List.generate factory constructor invocations
List.generate could return both growable and fixed-size lists. This change specializes invocations of List.generate when value of 'growable' argument is known (constant or omitted), so it becomes possible to infer actual type returned by the factory. This becomes more important with null safety as List.generate is used more often to initialize lists of non-nullable elements. Migrated NNBD benchmarks in AOT mode on x64: Sudoku +11% DartMicroBenchMM.{Min,Max}Lib +11-13% DartMicroBenchMM.{Min,Max}Code +19-27% ForInGeneratedLoop +19% ForEachLoop +85% ForInLoop +64% ForLoop +680% This change also includes test for inferred types of various List constructors. Change-Id: I801231b0a70e3aa8fb14ec9fe749f1dd420b1b9c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/153388 Reviewed-by: Aske Simon Christensen <[email protected]> Commit-Queue: Alexander Markov <[email protected]>
1 parent 255c6fa commit bfd1582

File tree

10 files changed

+299
-24
lines changed

10 files changed

+299
-24
lines changed

pkg/front_end/lib/src/fasta/source/source_loader.dart

+4
Original file line numberDiff line numberDiff line change
@@ -1296,18 +1296,22 @@ class List<E> extends Iterable {
12961296
factory List() => null;
12971297
factory List.unmodifiable(elements) => null;
12981298
factory List.filled(int length, E fill, {bool growable = false}) => null;
1299+
factory List.generate(int length, E generator(int index),
1300+
{bool growable = true}) => null;
12991301
void add(E) {}
13001302
E operator [](int index) => null;
13011303
}
13021304
13031305
class _GrowableList<E> {
13041306
factory _GrowableList() => null;
13051307
factory _GrowableList.filled() => null;
1308+
factory _GrowableList.generate(int length, E generator(int index)) => null;
13061309
}
13071310
13081311
class _List<E> {
13091312
factory _List() => null;
13101313
factory _List.filled() => null;
1314+
factory _List.generate(int length, E generator(int index)) => null;
13111315
}
13121316
13131317
class MapEntry<K, V> {

pkg/vm/lib/transformations/list_factory_specializer.dart

+60-20
Original file line numberDiff line numberDiff line change
@@ -16,33 +16,47 @@ import 'package:kernel/core_types.dart' show CoreTypes;
1616
/// new List.filled(n, x, growable: true) => new _GrowableList.filled(n, x)
1717
/// new List.filled(n, null) => new _List(n)
1818
/// new List.filled(n, x) => new _List.filled(n, x)
19+
/// new List.generate(n, y) => new _GrowableList.generate(n, y)
20+
/// new List.generate(n, y, growable: false) => new _List.generate(n, y)
1921
///
2022
class ListFactorySpecializer {
2123
final Procedure _defaultListFactory;
2224
final Procedure _listFilledFactory;
25+
final Procedure _listGenerateFactory;
2326
final Procedure _growableListFactory;
2427
final Procedure _growableListFilledFactory;
28+
final Procedure _growableListGenerateFactory;
2529
final Procedure _fixedListFactory;
2630
final Procedure _fixedListFilledFactory;
31+
final Procedure _fixedListGenerateFactory;
2732

2833
ListFactorySpecializer(CoreTypes coreTypes)
2934
: _defaultListFactory =
3035
coreTypes.index.getMember('dart:core', 'List', ''),
3136
_listFilledFactory =
3237
coreTypes.index.getMember('dart:core', 'List', 'filled'),
38+
_listGenerateFactory =
39+
coreTypes.index.getMember('dart:core', 'List', 'generate'),
3340
_growableListFactory =
3441
coreTypes.index.getMember('dart:core', '_GrowableList', ''),
3542
_growableListFilledFactory =
3643
coreTypes.index.getMember('dart:core', '_GrowableList', 'filled'),
44+
_growableListGenerateFactory =
45+
coreTypes.index.getMember('dart:core', '_GrowableList', 'generate'),
3746
_fixedListFactory = coreTypes.index.getMember('dart:core', '_List', ''),
3847
_fixedListFilledFactory =
39-
coreTypes.index.getMember('dart:core', '_List', 'filled') {
48+
coreTypes.index.getMember('dart:core', '_List', 'filled'),
49+
_fixedListGenerateFactory =
50+
coreTypes.index.getMember('dart:core', '_List', 'generate') {
4051
assert(_defaultListFactory.isFactory);
4152
assert(_listFilledFactory.isFactory);
53+
assert(_listGenerateFactory.isFactory);
4254
assert(_growableListFactory.isFactory);
4355
assert(_growableListFilledFactory.isFactory);
56+
assert(_growableListGenerateFactory.isFactory);
4457
assert(_fixedListFactory.isFactory);
4558
assert(_fixedListFilledFactory.isFactory);
59+
assert(_fixedListGenerateFactory.isFactory);
4660
}
4761

4862
TreeNode transformStaticInvocation(StaticInvocation node) {
@@ -64,25 +78,10 @@ class ListFactorySpecializer {
6478
final fill = args.positional[1];
6579
final fillingWithNull = fill is NullLiteral ||
6680
(fill is ConstantExpression && fill.constant is NullConstant);
67-
bool growable;
68-
if (args.named.isEmpty) {
69-
growable = false;
70-
} else {
71-
final namedArg = args.named.single;
72-
assert(namedArg.name == 'growable');
73-
final value = namedArg.value;
74-
if (value is BoolLiteral) {
75-
growable = value.value;
76-
} else if (value is ConstantExpression) {
77-
final constant = value.constant;
78-
if (constant is BoolConstant) {
79-
growable = constant.value;
80-
} else {
81-
return node;
82-
}
83-
} else {
84-
return node;
85-
}
81+
final bool growable =
82+
_getConstantOptionalArgument(args, 'growable', false);
83+
if (growable == null) {
84+
return node;
8685
}
8786
if (growable) {
8887
if (fillingWithNull) {
@@ -105,8 +104,49 @@ class ListFactorySpecializer {
105104
..fileOffset = node.fileOffset;
106105
}
107106
}
107+
} else if (target == _listGenerateFactory) {
108+
final args = node.arguments;
109+
assert(args.positional.length == 2);
110+
final length = args.positional[0];
111+
final generator = args.positional[1];
112+
final bool growable =
113+
_getConstantOptionalArgument(args, 'growable', true);
114+
if (growable == null) {
115+
return node;
116+
}
117+
if (growable) {
118+
return StaticInvocation(_growableListGenerateFactory,
119+
Arguments([length, generator], types: args.types))
120+
..fileOffset = node.fileOffset;
121+
} else {
122+
return StaticInvocation(_fixedListGenerateFactory,
123+
Arguments([length, generator], types: args.types))
124+
..fileOffset = node.fileOffset;
125+
}
108126
}
109127

110128
return node;
111129
}
130+
131+
/// Returns constant value of the only optional argument in [args],
132+
/// or null if it is not a constant. Returns [defaultValue] if optional
133+
/// argument is not passed. Argument is asserted to have the given [name].
134+
bool /*?*/ _getConstantOptionalArgument(
135+
Arguments args, String name, bool defaultValue) {
136+
if (args.named.isEmpty) {
137+
return defaultValue;
138+
}
139+
final namedArg = args.named.single;
140+
assert(namedArg.name == name);
141+
final value = namedArg.value;
142+
if (value is BoolLiteral) {
143+
return value.value;
144+
} else if (value is ConstantExpression) {
145+
final constant = value.constant;
146+
if (constant is BoolConstant) {
147+
return constant.value;
148+
}
149+
}
150+
return null;
151+
}
112152
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) 2020, 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+
nonConstant() => int.parse('1') == 1;
6+
7+
class A {
8+
final literal1 = <int>[];
9+
final literal2 = [1, 2, 3];
10+
final constLiteral1 = const <int>[];
11+
final constLiteral2 = const [1, 2];
12+
final defaultConstructor1 = List<int>();
13+
final defaultConstructor2 = List<int>(3);
14+
final filledFactory1 = List<int>.filled(2, 0);
15+
final filledFactory2 = List<int>.filled(2, 0, growable: true);
16+
final filledFactory3 = List<int>.filled(2, 0, growable: false);
17+
final filledFactory4 = List<int>.filled(2, 0, growable: nonConstant());
18+
final filledFactory5 = List<int>.filled(2, null);
19+
final filledFactory6 = List<int>.filled(2, null, growable: true);
20+
final filledFactory7 = List<int>.filled(2, null, growable: false);
21+
final filledFactory8 = List<int>.filled(2, null, growable: nonConstant());
22+
final generateFactory1 = List<int>.generate(2, (i) => i);
23+
final generateFactory2 = List<int>.generate(2, (i) => i, growable: true);
24+
final generateFactory3 = List<int>.generate(2, (i) => i, growable: false);
25+
final generateFactory4 =
26+
List<int>.generate(2, (i) => i, growable: nonConstant());
27+
final generateFactory5 = List<List<int>>.generate(2, (_) => <int>[]);
28+
}
29+
30+
main() {
31+
A x = A();
32+
// Make sure fields are not tree-shaken.
33+
print(x.literal1);
34+
print(x.literal2);
35+
print(x.constLiteral1);
36+
print(x.constLiteral2);
37+
print(x.defaultConstructor1);
38+
print(x.defaultConstructor2);
39+
print(x.filledFactory1);
40+
print(x.filledFactory2);
41+
print(x.filledFactory3);
42+
print(x.filledFactory4);
43+
print(x.filledFactory5);
44+
print(x.filledFactory6);
45+
print(x.filledFactory7);
46+
print(x.filledFactory8);
47+
print(x.generateFactory1);
48+
print(x.generateFactory2);
49+
print(x.generateFactory3);
50+
print(x.generateFactory4);
51+
print(x.generateFactory5);
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
library #lib;
2+
import self as self;
3+
import "dart:core" as core;
4+
import "dart:_internal" as _in;
5+
6+
class A extends core::Object {
7+
[@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] final field core::List<core::int*>* literal1 = <core::int*>[];
8+
[@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4] final field core::List<core::int*>* literal2 = <core::int*>[1, 2, 3];
9+
[@vm.inferred-type.metadata=dart.core::_ImmutableList (value: const <dart.core::int*>[])] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6] final field core::List<core::int*>* constLiteral1 = #C1;
10+
[@vm.inferred-type.metadata=dart.core::_ImmutableList (value: const <dart.core::int*>[1, 2])] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7,getterSelectorId:8] final field core::List<core::int*>* constLiteral2 = #C4;
11+
[@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:9,getterSelectorId:10] final field core::List<core::int*>* defaultConstructor1 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] core::_GrowableList::•<core::int*>(0);
12+
[@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:11,getterSelectorId:12] final field core::List<core::int*>* defaultConstructor2 = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] core::_List::•<core::int*>(3);
13+
[@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:13,getterSelectorId:14] final field core::List<core::int*>* filledFactory1 = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] core::_List::filled<core::int*>(2, 0);
14+
[@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:15,getterSelectorId:16] final field core::List<core::int*>* filledFactory2 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] core::_GrowableList::filled<core::int*>(2, 0);
15+
[@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:17,getterSelectorId:18] final field core::List<core::int*>* filledFactory3 = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] core::_List::filled<core::int*>(2, 0);
16+
[@vm.inferred-type.metadata=!] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:19,getterSelectorId:20] final field core::List<core::int*>* filledFactory4 = let core::int* #t1 = 2 in let core::int* #t2 = 0 in let core::bool #t3 = _in::unsafeCast<core::bool>([@vm.inferred-type.metadata=dart.core::bool] self::nonConstant()) in [@vm.inferred-type.metadata=!] core::List::filled<core::int*>(#t1, #t2, #t3);
17+
[@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:21,getterSelectorId:22] final field core::List<core::int*>* filledFactory5 = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] core::_List::•<core::int*>(2);
18+
[@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:23,getterSelectorId:24] final field core::List<core::int*>* filledFactory6 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] core::_GrowableList::•<core::int*>(2);
19+
[@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:25,getterSelectorId:26] final field core::List<core::int*>* filledFactory7 = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] core::_List::•<core::int*>(2);
20+
[@vm.inferred-type.metadata=!] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:27,getterSelectorId:28] final field core::List<core::int*>* filledFactory8 = let core::int* #t4 = 2 in let core::Null? #t5 = null in let core::bool #t6 = _in::unsafeCast<core::bool>([@vm.inferred-type.metadata=dart.core::bool] self::nonConstant()) in [@vm.inferred-type.metadata=!] core::List::filled<core::int*>(#t4, #t5, #t6);
21+
[@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:29,getterSelectorId:30] final field core::List<core::int*>* generateFactory1 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] core::_GrowableList::generate<core::int*>(2, (core::int* i) → core::int* => i);
22+
[@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:31,getterSelectorId:32] final field core::List<core::int*>* generateFactory2 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] core::_GrowableList::generate<core::int*>(2, (core::int* i) → core::int* => i);
23+
[@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:33,getterSelectorId:34] final field core::List<core::int*>* generateFactory3 = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] core::_List::generate<core::int*>(2, (core::int* i) → core::int* => i);
24+
[@vm.inferred-type.metadata=!] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:35,getterSelectorId:36] final field core::List<core::int*>* generateFactory4 = let core::int* #t7 = 2 in let (core::int*) →* core::int* #t8 = (core::int* i) → core::int* => i in let core::bool #t9 = _in::unsafeCast<core::bool>([@vm.inferred-type.metadata=dart.core::bool] self::nonConstant()) in [@vm.inferred-type.metadata=!] core::List::generate<core::int*>(#t7, #t8, #t9);
25+
[@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::List<dart.core::int*>*>] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:37,getterSelectorId:38] final field core::List<core::List<core::int*>*>* generateFactory5 = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::List<dart.core::int*>*>] core::_GrowableList::generate<core::List<core::int*>*>(2, (core::int* _) → core::List<core::int*>* => <core::int*>[]);
26+
synthetic constructor •() → self::A*
27+
: super core::Object::•()
28+
;
29+
}
30+
static method nonConstant() → dynamic
31+
return [@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] [@vm.inferred-type.metadata=int] core::int::parse("1").{core::num::==}(1);
32+
static method main() → dynamic {
33+
self::A* x = new self::A::•();
34+
core::print([@vm.direct-call.metadata=#lib::A.literal1] [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] x.{self::A::literal1});
35+
core::print([@vm.direct-call.metadata=#lib::A.literal2] [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] x.{self::A::literal2});
36+
core::print([@vm.direct-call.metadata=#lib::A.constLiteral1] [@vm.inferred-type.metadata=dart.core::_ImmutableList (value: const <dart.core::int*>[])] x.{self::A::constLiteral1});
37+
core::print([@vm.direct-call.metadata=#lib::A.constLiteral2] [@vm.inferred-type.metadata=dart.core::_ImmutableList (value: const <dart.core::int*>[1, 2])] x.{self::A::constLiteral2});
38+
core::print([@vm.direct-call.metadata=#lib::A.defaultConstructor1] [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] x.{self::A::defaultConstructor1});
39+
core::print([@vm.direct-call.metadata=#lib::A.defaultConstructor2] [@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] x.{self::A::defaultConstructor2});
40+
core::print([@vm.direct-call.metadata=#lib::A.filledFactory1] [@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] x.{self::A::filledFactory1});
41+
core::print([@vm.direct-call.metadata=#lib::A.filledFactory2] [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] x.{self::A::filledFactory2});
42+
core::print([@vm.direct-call.metadata=#lib::A.filledFactory3] [@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] x.{self::A::filledFactory3});
43+
core::print([@vm.direct-call.metadata=#lib::A.filledFactory4] [@vm.inferred-type.metadata=!] x.{self::A::filledFactory4});
44+
core::print([@vm.direct-call.metadata=#lib::A.filledFactory5] [@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] x.{self::A::filledFactory5});
45+
core::print([@vm.direct-call.metadata=#lib::A.filledFactory6] [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] x.{self::A::filledFactory6});
46+
core::print([@vm.direct-call.metadata=#lib::A.filledFactory7] [@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] x.{self::A::filledFactory7});
47+
core::print([@vm.direct-call.metadata=#lib::A.filledFactory8] [@vm.inferred-type.metadata=!] x.{self::A::filledFactory8});
48+
core::print([@vm.direct-call.metadata=#lib::A.generateFactory1] [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] x.{self::A::generateFactory1});
49+
core::print([@vm.direct-call.metadata=#lib::A.generateFactory2] [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int*>] x.{self::A::generateFactory2});
50+
core::print([@vm.direct-call.metadata=#lib::A.generateFactory3] [@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] x.{self::A::generateFactory3});
51+
core::print([@vm.direct-call.metadata=#lib::A.generateFactory4] [@vm.inferred-type.metadata=!] x.{self::A::generateFactory4});
52+
core::print([@vm.direct-call.metadata=#lib::A.generateFactory5] [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::List<dart.core::int*>*>] x.{self::A::generateFactory5});
53+
}

0 commit comments

Comments
 (0)