Include FieldInitializer in allocator analysis

Change-Id: Id3ce8113816193408f704045c130e65fe65e6244
Reviewed-on: https://dart-review.googlesource.com/c/93424
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
This commit is contained in:
Johnni Winther 2019-02-20 10:53:01 +00:00 committed by commit-bot@chromium.org
parent 8db489cb10
commit 596e0aa645
10 changed files with 324 additions and 55 deletions

View file

@ -1758,7 +1758,7 @@ class PositionalArgumentReference extends ConstantExpression {
@override
ConstantValue evaluate(
EvaluationEnvironment environment, ConstantSystem constantSystem) {
throw new UnsupportedError('PositionalArgumentReference.evaluate');
return new NonConstantValue();
}
@override
@ -1797,7 +1797,7 @@ class NamedArgumentReference extends ConstantExpression {
@override
ConstantValue evaluate(
EvaluationEnvironment environment, ConstantSystem constantSystem) {
throw new UnsupportedError('NamedArgumentReference.evaluate');
return new NonConstantValue();
}
@override

View file

@ -9,7 +9,7 @@ import '../js_model/elements.dart' show JField;
import '../js_model/js_world_builder.dart';
import '../kernel/element_map.dart';
import '../kernel/kernel_strategy.dart';
import '../kernel/kelements.dart' show KClass, KField;
import '../kernel/kelements.dart' show KClass, KField, KConstructor;
import '../options.dart';
import '../serialization/serialization.dart';
@ -34,7 +34,7 @@ abstract class AllocatorAnalysis {}
class KAllocatorAnalysis implements AllocatorAnalysis {
final KernelToElementMap _elementMap;
final Map<KField, ConstantValue> _fixedInitializers = {};
final Map<KField, AllocatorData> _fixedInitializers = {};
KAllocatorAnalysis(KernelFrontEndStrategy kernelStrategy)
: _elementMap = kernelStrategy.elementMap;
@ -44,36 +44,133 @@ class KAllocatorAnalysis implements AllocatorAnalysis {
void registerInstantiatedClass(KClass class_) {
ir.Class classNode = _elementMap.getClassNode(class_);
Map<ir.Field, ConstantValue> inits = {};
Map<ir.Field, AllocatorData> fieldData = {};
for (ir.Field field in classNode.fields) {
if (!field.isInstanceMember) continue;
ir.Expression expression = field.initializer;
ConstantValue value = _elementMap.getConstantValue(expression,
requireConstant: false, implicitNull: true);
if (value != null && value.isConstant) {
inits[field] = value;
fieldData[field] = new AllocatorData(value);
}
}
for (ir.Constructor constructor in classNode.constructors) {
KConstructor constructorElement = _elementMap.getConstructor(constructor);
for (ir.Initializer initializer in constructor.initializers) {
if (initializer is ir.FieldInitializer) {
// TODO(sra): Check explicit initializer value to see if consistent
// over all constructors.
inits.remove(initializer.field);
AllocatorData data = fieldData[initializer.field];
if (data == null) {
// TODO(johnniwinther): Support initializers with side-effects?
// The field has a non-constant initializer.
continue;
}
Initializer initializerValue = const Initializer.complex();
ir.Expression value = initializer.value;
ConstantValue constantValue = _elementMap.getConstantValue(value,
requireConstant: false, implicitNull: true);
if (constantValue != null && constantValue.isConstant) {
initializerValue = new Initializer.direct(constantValue);
} else if (value is ir.VariableGet) {
ir.VariableDeclaration parameter = value.variable;
int position =
constructor.function.positionalParameters.indexOf(parameter);
if (position != -1) {
if (position >= constructor.function.requiredParameterCount) {
constantValue = _elementMap.getConstantValue(
parameter.initializer,
requireConstant: false,
implicitNull: true);
if (constantValue != null && constantValue.isConstant) {
initializerValue =
new Initializer.positional(position, constantValue);
}
}
} else {
position =
constructor.function.namedParameters.indexOf(parameter);
if (position != -1) {
constantValue = _elementMap.getConstantValue(
parameter.initializer,
requireConstant: false,
implicitNull: true);
if (constantValue != null && constantValue.isConstant) {
initializerValue =
new Initializer.named(parameter.name, constantValue);
}
}
}
}
data.initializers[constructorElement] = initializerValue;
}
}
}
inits.forEach((ir.Field fieldNode, ConstantValue value) {
_fixedInitializers[_elementMap.getField(fieldNode)] = value;
fieldData.forEach((ir.Field fieldNode, AllocatorData data) {
_fixedInitializers[_elementMap.getField(fieldNode)] = data;
});
}
ConstantValue getFixedInitializerForTesting(KField field) =>
AllocatorData getFixedInitializerForTesting(KField field) =>
_fixedInitializers[field];
}
class AllocatorData {
final ConstantValue initialValue;
final Map<KConstructor, Initializer> initializers = {};
AllocatorData(this.initialValue);
}
enum InitializerKind {
direct,
positional,
named,
complex,
}
class Initializer {
final InitializerKind kind;
final int index;
final String name;
final ConstantValue value;
Initializer.direct(this.value)
: kind = InitializerKind.direct,
index = null,
name = null;
Initializer.positional(this.index, this.value)
: kind = InitializerKind.positional,
name = null;
Initializer.named(this.name, this.value)
: kind = InitializerKind.named,
index = null;
const Initializer.complex()
: kind = InitializerKind.complex,
index = null,
name = null,
value = null;
String shortText() {
switch (kind) {
case InitializerKind.direct:
return value.toStructuredText();
case InitializerKind.positional:
return '$index:${value.toStructuredText()}';
case InitializerKind.named:
return '$name:${value.toStructuredText()}';
case InitializerKind.complex:
return '?';
}
throw new UnsupportedError('Unexpected kind $kind');
}
}
class JAllocatorAnalysis implements AllocatorAnalysis {
/// Tag used for identifying serialized [JAllocatorAnalysis] objects in a
/// debugging data stream.
@ -114,12 +211,20 @@ class JAllocatorAnalysis implements AllocatorAnalysis {
JsToFrontendMap map, CompilerOptions options) {
var result = JAllocatorAnalysis._();
kAnalysis._fixedInitializers.forEach((KField kField, ConstantValue value) {
if (value.isNull || value.isInt || value.isBool || value.isString) {
// TODO(johnniwinther): Support non-primitive constants.
JField jField = map.toBackendMember(kField);
if (jField != null) {
result._fixedInitializers[jField] = map.toBackendConstant(value);
kAnalysis._fixedInitializers.forEach((KField kField, AllocatorData data) {
// TODO(johnniwinther): Use liveness of constructors and elided optional
// parameters to recognize more constant initializers.
if (data.initialValue != null && data.initializers.isEmpty) {
ConstantValue value = data.initialValue;
if (value.isNull || value.isInt || value.isBool || value.isString) {
// TODO(johnniwinther,sra): Support non-primitive constants in
// allocators when it does cause allocators to deoptimized because
// of deferred loading.
JField jField = map.toBackendMember(kField);
if (jField != null) {
result._fixedInitializers[jField] = map.toBackendConstant(value);
}
}
}
});

View file

@ -169,25 +169,6 @@ class KFactoryConstructor extends KConstructor {
bool get isGenerativeConstructor => false;
}
class KConstructorBody extends KFunction implements ConstructorBodyEntity {
final ConstructorEntity constructor;
KConstructorBody(this.constructor)
: super(
constructor.library,
constructor.enclosingClass,
constructor.memberName,
constructor.parameterStructure,
AsyncMarker.SYNC,
isStatic: false,
isExternal: false);
@override
bool get isFunction => true;
String get _kind => 'constructor_body';
}
class KMethod extends KFunction {
final bool isAbstract;

View file

@ -0,0 +1,30 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
main() {
new Class1.a();
new Class1.b();
}
class Class1 {
var field1 = 0;
var field2;
var field3;
/*element: Class1.field4:initial=IntConstant(4)*/
var field4 = 4;
var field5 = 5;
Class1.a()
: field1 = 1,
field2 = 1,
field3 = 3,
field5 = 5;
Class1.b()
: field2 = 2,
field3 = 3,
field5 = 5;
}

View file

@ -0,0 +1,26 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
main() {
new Class1(0);
new Class1(0, 1);
new Class2(0);
new Class2(0, field2: 1);
}
class Class1 {
var field1;
var field2;
var field3;
Class1(this.field1, [this.field2 = 2, this.field3 = 3]);
}
class Class2 {
var field1;
var field2;
var field3;
Class2(this.field1, {this.field2 = 2, this.field3 = 3});
}

View file

@ -5,7 +5,6 @@
import 'dart:io';
import 'package:async_helper/async_helper.dart';
import 'package:compiler/src/compiler.dart';
import 'package:compiler/src/constants/values.dart';
import 'package:compiler/src/elements/entities.dart';
import 'package:compiler/src/ir/util.dart';
import 'package:compiler/src/js_backend/allocator_analysis.dart';
@ -39,11 +38,17 @@ class KAllocatorAnalysisDataComputer extends DataComputer<Features> {
KAllocatorAnalysis allocatorAnalysis =
compiler.backend.allocatorResolutionAnalysisForTesting;
ir.Member node = frontendStrategy.elementMap.getMemberNode(member);
ConstantValue initialValue =
AllocatorData data =
allocatorAnalysis.getFixedInitializerForTesting(member);
Features features = new Features();
if (initialValue != null) {
features[Tags.initialValue] = initialValue.toStructuredText();
if (data != null) {
if (data.initialValue != null) {
features[Tags.initialValue] = data.initialValue.toStructuredText();
}
data.initializers.forEach((constructor, value) {
features['${constructor.enclosingClass.name}.${constructor.name}'] =
value?.shortText();
});
}
Id id = computeEntityId(node);
actualMap[id] = new ActualData<Features>(

View file

@ -0,0 +1,51 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
main() {
new Class1.a();
new Class1.b();
}
class Class1 {
/*element: Class1.field1:
Class1.a=IntConstant(1),
initial=IntConstant(0)
*/
var field1 = 0;
/*element: Class1.field2:
Class1.a=IntConstant(1),
Class1.b=IntConstant(2),
initial=NullConstant
*/
var field2;
/*element: Class1.field3:
Class1.a=IntConstant(3),
Class1.b=IntConstant(3),
initial=NullConstant
*/
var field3;
/*element: Class1.field4:initial=IntConstant(4)*/
var field4 = 4;
/*element: Class1.field5:
Class1.a=IntConstant(5),
Class1.b=IntConstant(5),
initial=IntConstant(5)
*/
var field5 = 5;
Class1.a()
: field1 = 1,
field2 = 1,
field3 = 3,
field5 = 5;
Class1.b()
: field2 = 2,
field3 = 3,
field5 = 5;
}

View file

@ -0,0 +1,54 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
main() {
new Class1(0);
new Class1(0, 1);
new Class2(0);
new Class2(0, field2: 1);
}
class Class1 {
/*element: Class1.field1:
Class1.=?,
initial=NullConstant
*/
var field1;
/*element: Class1.field2:
Class1.=1:IntConstant(2),
initial=NullConstant
*/
var field2;
/*element: Class1.field3:
Class1.=2:IntConstant(3),
initial=NullConstant
*/
var field3;
Class1(this.field1, [this.field2 = 2, this.field3 = 3]);
}
class Class2 {
/*element: Class2.field1:
Class2.=?,
initial=NullConstant
*/
var field1;
/*element: Class2.field2:
Class2.=field2:IntConstant(2),
initial=NullConstant
*/
var field2;
/*element: Class2.field3:
Class2.=field3:IntConstant(3),
initial=NullConstant
*/
var field3;
Class2(this.field1, {this.field2 = 2, this.field3 = 3});
}

View file

@ -0,0 +1,17 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
main() {
new Class1();
}
method1() => 1;
class Class1 {
var field1 = method1();
var field2 = throw 'foo';
var field3 = method1();
Class1() : field3 = null;
}

View file

@ -57,46 +57,46 @@ class Class1 {
}
class Class2 {
/*element: Class2.field1:*/
/*element: Class2.field1:Class2.=NullConstant,initial=NullConstant*/
var field1;
/*element: Class2.field2:*/
/*element: Class2.field2:Class2.=BoolConstant(true),initial=NullConstant*/
var field2;
/*element: Class2.field3:*/
/*element: Class2.field3:Class2.=BoolConstant(false),initial=NullConstant*/
var field3;
/*element: Class2.field4:*/
/*element: Class2.field4:Class2.=IntConstant(0),initial=NullConstant*/
var field4;
/*element: Class2.field5:*/
/*element: Class2.field5:Class2.=IntConstant(1),initial=NullConstant*/
var field5;
/*element: Class2.field6:*/
/*element: Class2.field6:Class2.=StringConstant(""),initial=NullConstant*/
var field6;
/*element: Class2.field7:*/
/*element: Class2.field7:Class2.=StringConstant("foo"),initial=NullConstant*/
var field7;
/*element: Class2.field8:*/
/*element: Class2.field8:Class2.=DoubleConstant(0.5),initial=NullConstant*/
var field8;
/*element: Class2.field9:*/
/*element: Class2.field9:Class2.=ListConstant([]),initial=NullConstant*/
var field9;
/*element: Class2.field10:*/
/*element: Class2.field10:Class2.=MapConstant({}),initial=NullConstant*/
var field10;
/*element: Class2.field11:*/
/*element: Class2.field11:Class2.=ConstructedConstant(Symbol(_name=StringConstant("foo"))),initial=NullConstant*/
var field11;
/*element: Class2.field12:*/
/*element: Class2.field12:Class2.=IntConstant(5),initial=NullConstant*/
var field12;
/*element: Class2.field13:*/
/*element: Class2.field13:Class2.=BoolConstant(true),initial=NullConstant*/
var field13;
/*element: Class2.field14:*/
/*element: Class2.field14:Class2.=?,initial=NullConstant*/
var field14;
Class2()