Skip to content

Commit ec4ccb7

Browse files
rakudramacommit-bot@chromium.org
authored andcommitted
[dart2js] Update inferrer with current List constructors
Use the 'growable:' argument or its absence to pick a better type for List.of, List.from, List.generate, List.empty and List.filled. Fixes a bug where it was assumed that List.filled was always fixed length. Slightly better type analysis on some apps, but nothing that is measurable on benchmarks. Change-Id: Ibe029a93488412956273fc981f626d33bd295386 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/163132 Commit-Queue: Stephen Adams <[email protected]> Reviewed-by: Mayank Patke <[email protected]>
1 parent 3272abc commit ec4ccb7

13 files changed

+250
-22
lines changed

pkg/compiler/lib/src/common_elements.dart

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -586,11 +586,12 @@ abstract class JCommonElements implements CommonElements {
586586
/// compilation.
587587
bool isUnnamedListConstructor(ConstructorEntity element);
588588

589-
/// Returns `true` if [element] is the 'filled' constructor of `List`.
589+
/// Returns `true` if [element] is the named constructor of `List`,
590+
/// e.g. `List.of`.
590591
///
591592
/// This will not resolve the constructor if it hasn't been seen yet during
592593
/// compilation.
593-
bool isFilledListConstructor(ConstructorEntity element);
594+
bool isNamedListConstructor(String name, ConstructorEntity element);
594595

595596
bool isDefaultEqualityImplementation(MemberEntity element);
596597

@@ -879,8 +880,8 @@ class CommonElementsImpl
879880
/// This will not resolve the constructor if it hasn't been seen yet during
880881
/// compilation.
881882
@override
882-
bool isFilledListConstructor(ConstructorEntity element) =>
883-
element.name == 'filled' && element.enclosingClass == listClass;
883+
bool isNamedListConstructor(String name, ConstructorEntity element) =>
884+
element.name == name && element.enclosingClass == listClass;
884885

885886
@override
886887
DynamicType get dynamicType => _env.dynamicType;

pkg/compiler/lib/src/inferrer/abstract_value_domain.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ abstract class AbstractValueDomain {
161161
/// at runtime.
162162
AbstractValue get fixedListType;
163163

164+
/// The [AbstractValue] that represents the union of [growableListType] and
165+
/// [fixedListType], i.e. JavaScript arrays that may have their elements
166+
/// assigned.
167+
AbstractValue get mutableArrayType;
168+
164169
/// The [AbstractValue] that represents a non-null 31-bit unsigned integer at
165170
/// runtime.
166171
AbstractValue get uint31Type;

pkg/compiler/lib/src/inferrer/builder_kernel.dart

Lines changed: 80 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,7 +1188,34 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
11881188
return null;
11891189
}
11901190

1191-
/// Returns `true` if
1191+
/// Find the base type for a system List constructor from the value passed to
1192+
/// the 'growable' argument. [defaultGrowable] is the default value of the
1193+
/// 'growable' parameter.
1194+
TypeInformation _listBaseType(ir.Arguments arguments,
1195+
{bool defaultGrowable}) {
1196+
TypeInformation finish(bool /*?*/ growable) {
1197+
if (growable == true) return _types.growableListType;
1198+
if (growable == false) return _types.fixedListType;
1199+
return _types.mutableArrayType;
1200+
}
1201+
1202+
for (ir.NamedExpression named in arguments.named) {
1203+
if (named.name == 'growable') {
1204+
ir.Expression argument = named.value;
1205+
if (argument is ir.BoolLiteral) return finish(argument.value);
1206+
if (argument is ir.ConstantExpression) {
1207+
ir.Constant constant = argument.constant;
1208+
if (constant is ir.BoolConstant) return finish(constant.value);
1209+
}
1210+
// 'growable' is present, but indeterminate.
1211+
return finish(null);
1212+
}
1213+
}
1214+
// 'growable' is missing.
1215+
return finish(defaultGrowable);
1216+
}
1217+
1218+
/// Returns `true` for constructors of typed arrays.
11921219
bool _isConstructorOfTypedArraySubclass(ConstructorEntity constructor) {
11931220
ClassEntity cls = constructor.enclosingClass;
11941221
return cls.library.canonicalUri == Uris.dart__native_typed_data &&
@@ -1208,7 +1235,15 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
12081235
ArgumentsTypes argumentsTypes) {
12091236
TypeInformation returnType =
12101237
handleStaticInvoke(node, selector, constructor, argumentsTypes);
1211-
if (_elementMap.commonElements.isUnnamedListConstructor(constructor)) {
1238+
1239+
// See if we can replace the returned type with one that better describes
1240+
// the operation. For system List constructors we can treat this as the
1241+
// allocation point of a new collection. The static invoke above ensures
1242+
// that the implementation of the constructor sees the arguments.
1243+
1244+
var commonElements = _elementMap.commonElements;
1245+
1246+
if (commonElements.isUnnamedListConstructor(constructor)) {
12121247
// We have `new List(...)`.
12131248
if (arguments.positional.isEmpty && arguments.named.isEmpty) {
12141249
// We have `new List()`.
@@ -1224,18 +1259,52 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
12241259
() => _types.allocateList(_types.fixedListType, node,
12251260
_analyzedMember, _types.nullType, length));
12261261
}
1227-
} else if (_elementMap.commonElements
1228-
.isFilledListConstructor(constructor)) {
1229-
// We have `new Uint32List(len, fill)`.
1262+
}
1263+
1264+
if (commonElements.isNamedListConstructor('filled', constructor)) {
1265+
// We have something like `List.filled(len, fill)`.
12301266
int length = _findLength(arguments);
12311267
TypeInformation elementType = argumentsTypes.positional[1];
1268+
TypeInformation baseType =
1269+
_listBaseType(arguments, defaultGrowable: false);
1270+
return _inferrer.concreteTypes.putIfAbsent(
1271+
node,
1272+
() => _types.allocateList(
1273+
baseType, node, _analyzedMember, elementType, length));
1274+
}
1275+
1276+
if (commonElements.isNamedListConstructor('generate', constructor)) {
1277+
// We have something like `List.generate(len, generator)`.
1278+
int length = _findLength(arguments);
1279+
// TODO(sra): What we really want here is the result of calling the
1280+
// `generator` parameter with a non-negative integer.
1281+
TypeInformation elementType = _types.dynamicType;
1282+
TypeInformation baseType =
1283+
_listBaseType(arguments, defaultGrowable: true);
1284+
return _inferrer.concreteTypes.putIfAbsent(
1285+
node,
1286+
() => _types.allocateList(
1287+
baseType, node, _analyzedMember, elementType, length));
1288+
}
12321289

1290+
if (commonElements.isNamedListConstructor('empty', constructor)) {
1291+
// We have something like `List.empty(growable: true)`.
1292+
TypeInformation baseType =
1293+
_listBaseType(arguments, defaultGrowable: false);
12331294
return _inferrer.concreteTypes.putIfAbsent(
12341295
node,
1235-
() => _types.allocateList(_types.fixedListType, node, _analyzedMember,
1236-
elementType, length));
1237-
} else if (_isConstructorOfTypedArraySubclass(constructor)) {
1238-
// We have something like `new List.filled(len, fill)`.
1296+
() => _types.allocateList(
1297+
baseType, node, _analyzedMember, _types.nonNullEmpty(), 0));
1298+
}
1299+
if (commonElements.isNamedListConstructor('of', constructor) ||
1300+
commonElements.isNamedListConstructor('from', constructor)) {
1301+
// We have something like `List.of(elements)`.
1302+
TypeInformation baseType =
1303+
_listBaseType(arguments, defaultGrowable: true);
1304+
return baseType;
1305+
}
1306+
if (_isConstructorOfTypedArraySubclass(constructor)) {
1307+
// We have something like `Uint32List(len)`.
12391308
int length = _findLength(arguments);
12401309
MemberEntity member = _elementMap.elementEnvironment
12411310
.lookupClassMember(constructor.enclosingClass, '[]');
@@ -1248,9 +1317,9 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
12481317
_analyzedMember,
12491318
elementType,
12501319
length));
1251-
} else {
1252-
return returnType;
12531320
}
1321+
1322+
return returnType;
12541323
}
12551324

12561325
TypeInformation handleStaticInvoke(ir.Node node, Selector selector,

pkg/compiler/lib/src/inferrer/powersets/powerset_bits.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,10 @@ class PowersetBitsDomain {
589589
int get growableListType => _growableListType ??=
590590
createNonNullExact(commonElements.jsExtendableArrayClass);
591591

592+
int _mutableArrayType;
593+
int get mutableArrayType => _mutableArrayType ??=
594+
createNonNullSubtype(commonElements.jsMutableArrayClass);
595+
592596
int get nullType => nullValue;
593597

594598
int get nonNullType => powersetTop & ~nullValue;

pkg/compiler/lib/src/inferrer/powersets/powersets.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,11 @@ class PowersetDomain implements AbstractValueDomain {
744744
_abstractValueDomain.growableListType,
745745
_powersetBitsDomain.growableListType);
746746

747+
@override
748+
AbstractValue get mutableArrayType => PowersetValue(
749+
_abstractValueDomain.mutableArrayType,
750+
_powersetBitsDomain.mutableArrayType);
751+
747752
@override
748753
AbstractValue get nullType => PowersetValue(
749754
_abstractValueDomain.nullType, _powersetBitsDomain.nullType);

pkg/compiler/lib/src/inferrer/powersets/wrapped.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,10 @@ class WrappedAbstractValueDomain implements AbstractValueDomain {
541541
AbstractValue get growableListType =>
542542
WrappedAbstractValue(_abstractValueDomain.growableListType);
543543

544+
@override
545+
AbstractValue get mutableArrayType =>
546+
WrappedAbstractValue(_abstractValueDomain.mutableArrayType);
547+
544548
@override
545549
AbstractValue get nullType =>
546550
WrappedAbstractValue(_abstractValueDomain.nullType);

pkg/compiler/lib/src/inferrer/trivial.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,9 @@ class TrivialAbstractValueDomain implements AbstractValueDomain {
401401
@override
402402
AbstractValue get growableListType => const TrivialAbstractValue();
403403

404+
@override
405+
AbstractValue get mutableArrayType => const TrivialAbstractValue();
406+
404407
@override
405408
AbstractValue get nullType => const TrivialAbstractValue();
406409

pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,8 @@ class TypeGraphInferrer implements TypesInferrer {
154154
parameterResults[parameter] = type;
155155
});
156156

157-
Map<ir.TreeNode, AbstractValue> allocatedLists =
158-
<ir.TreeNode, AbstractValue>{};
159-
Set<ir.TreeNode> checkedForGrowableLists = new Set<ir.TreeNode>();
157+
Map<ir.TreeNode, AbstractValue> allocatedLists = {};
158+
Set<ir.TreeNode> checkedForGrowableLists = {};
160159
inferrer.types.allocatedLists
161160
.forEach((ir.TreeNode node, ListTypeInformation typeInformation) {
162161
ListTypeInformation info = inferrer.types.allocatedLists[node];

pkg/compiler/lib/src/inferrer/type_system.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,10 @@ class TypeSystem {
218218
getConcreteTypeFor(_abstractValueDomain.growableListType);
219219
}
220220

221+
TypeInformation _mutableArrayType;
222+
TypeInformation get mutableArrayType => _mutableArrayType ??=
223+
getConcreteTypeFor(_abstractValueDomain.mutableArrayType);
224+
221225
TypeInformation setTypeCache;
222226
TypeInformation get setType =>
223227
setTypeCache ??= getConcreteTypeFor(_abstractValueDomain.setType);

pkg/compiler/lib/src/inferrer/typemasks/masks.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ class CommonMasks implements AbstractValueDomain {
131131

132132
@override
133133
TypeMask get listType => _listType ??=
134-
new TypeMask.nonNullExact(commonElements.jsArrayClass, _closedWorld);
134+
new TypeMask.nonNullSubtype(commonElements.jsArrayClass, _closedWorld);
135135

136136
@override
137137
TypeMask get constListType => _constListType ??= new TypeMask.nonNullExact(
@@ -197,6 +197,7 @@ class CommonMasks implements AbstractValueDomain {
197197
TypeMask get readableArrayType => _readableArrayType ??=
198198
new TypeMask.nonNullSubclass(commonElements.jsArrayClass, _closedWorld);
199199

200+
@override
200201
TypeMask get mutableArrayType =>
201202
_mutableArrayType ??= new TypeMask.nonNullSubclass(
202203
commonElements.jsMutableArrayClass, _closedWorld);
@@ -999,7 +1000,11 @@ String formatType(DartTypes dartTypes, TypeMask type) {
9991000
// a null value we accidentally printed out.
10001001
if (type.isEmptyOrNull) return type.isNullable ? 'Null' : 'Empty';
10011002
String nullFlag = type.isNullable ? '?' : '';
1002-
String subFlag = type.isExact ? '' : type.isSubclass ? '+' : '*';
1003+
String subFlag = type.isExact
1004+
? ''
1005+
: type.isSubclass
1006+
? '+'
1007+
: '*';
10031008
return '${type.base.name}$nullFlag$subFlag';
10041009
}
10051010
if (type is UnionTypeMask) {

pkg/compiler/lib/src/ssa/builder_kernel.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3935,9 +3935,8 @@ class KernelSsaGraphBuilder extends ir.Visitor {
39353935
} else if (isJSArrayTypedConstructor) {
39363936
// TODO(sra): Instead of calling the identity-like factory constructor,
39373937
// simply select the single argument.
3938+
39383939
// Factory constructors take type parameters.
3939-
if (closedWorld.rtiNeed
3940-
.classNeedsTypeArguments(function.enclosingClass)) {}
39413940
List<DartType> typeArguments =
39423941
_getConstructorTypeArguments(function, invocation.arguments);
39433942
// TODO(johnniwinther): Remove this when type arguments are passed to
@@ -3962,6 +3961,9 @@ class KernelSsaGraphBuilder extends ir.Visitor {
39623961
_addImplicitInstantiation(instanceType);
39633962
_pushStaticInvocation(function, arguments, typeMask, typeArguments,
39643963
sourceInformation: sourceInformation, instanceType: instanceType);
3964+
3965+
// TODO(sra): Special handling of List.filled, List.generate, List.of and
3966+
// other list constructors where 'growable' is false.
39653967
}
39663968

39673969
HInstruction newInstance = stack.last;

pkg/compiler/lib/src/ssa/optimize.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,9 @@ class SsaInstructionSimplifier extends HBaseVisitor
13091309
HInstruction visitGetLength(HGetLength node) {
13101310
HInstruction receiver = node.receiver;
13111311
if (_graph.allocatedFixedLists.contains(receiver)) {
1312+
// TODO(sra): How do we keep this working if we lower/inline the receiver
1313+
// in an optimization?
1314+
13121315
// TODO(ngeoffray): checking if the second input is an integer
13131316
// should not be necessary but it currently makes it easier for
13141317
// other optimizations to reason about a fixed length constructor

0 commit comments

Comments
 (0)