Redo "Extend allocator-initialization to some primitive types"
Change-Id: Ib9495232da4d6abf1fee4485784ef45dbc50e5ed
Reviewed-on: https://dart-review.googlesource.com/c/88234
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
diff --git a/pkg/compiler/lib/src/js_backend/allocator_analysis.dart b/pkg/compiler/lib/src/js_backend/allocator_analysis.dart
index 6e15c77..9312d15 100644
--- a/pkg/compiler/lib/src/js_backend/allocator_analysis.dart
+++ b/pkg/compiler/lib/src/js_backend/allocator_analysis.dart
@@ -31,12 +31,12 @@
// this.x = this.z = null;
//
class KAllocatorAnalysis implements AllocatorAnalysis {
+ final CompilerOptions _options;
final KernelToElementMap _elementMap;
- final Map<KField, ConstantValue> _fixedInitializers =
- <KField, ConstantValue>{};
+ final Map<KField, ConstantValue> _fixedInitializers = {};
- KAllocatorAnalysis(KernelFrontEndStrategy kernelStrategy)
+ KAllocatorAnalysis(this._options, KernelFrontEndStrategy kernelStrategy)
: _elementMap = kernelStrategy.elementMap;
// Register class during resolution. Use simple syntactic analysis to find
@@ -44,12 +44,29 @@
void registerInstantiatedClass(KClass class_) {
ir.Class classNode = _elementMap.getClassNode(class_);
- Set<ir.Field> nulls = new Set<ir.Field>();
+ Map<ir.Field, ConstantValue> inits = {};
for (ir.Field field in classNode.fields) {
if (!field.isInstanceMember) continue;
ir.Expression initializer = field.initializer;
+ // TODO(sra): Should really be using constant evaluator to determine
+ // value.
if (initializer == null || initializer is ir.NullLiteral) {
- nulls.add(field);
+ inits[field] = const NullConstantValue();
+ } else if (initializer is ir.IntLiteral) {
+ if (_options.useStartupEmitter) {
+ BigInt intValue = BigInt.from(initializer.value).toUnsigned(64);
+ inits[field] = IntConstantValue(intValue);
+ }
+ } else if (initializer is ir.BoolLiteral) {
+ if (_options.useStartupEmitter) {
+ inits[field] = BoolConstantValue(initializer.value);
+ }
+ } else if (initializer is ir.StringLiteral) {
+ if (_options.useStartupEmitter) {
+ if (initializer.value.length <= 20) {
+ inits[field] = StringConstantValue(initializer.value);
+ }
+ }
}
}
@@ -58,15 +75,14 @@
if (initializer is ir.FieldInitializer) {
// TODO(sra): Check explicit initializer value to see if consistent
// over all constructors.
- nulls.remove(initializer.field);
+ inits.remove(initializer.field);
}
}
}
- for (var fieldNode in nulls) {
- _fixedInitializers[_elementMap.getField(fieldNode)] =
- const NullConstantValue();
- }
+ inits.forEach((ir.Field fieldNode, ConstantValue value) {
+ _fixedInitializers[_elementMap.getField(fieldNode)] = value;
+ });
}
}
@@ -77,8 +93,7 @@
// --csp and --fast-startup have different constraints to the generated code.
final CompilerOptions _options;
- final Map<JField, ConstantValue> _fixedInitializers =
- <JField, ConstantValue>{};
+ final Map<JField, ConstantValue> _fixedInitializers = {};
JAllocatorAnalysis._(this._options);
@@ -90,8 +105,7 @@
int fieldCount = source.readInt();
for (int i = 0; i < fieldCount; i++) {
JField field = source.readMember();
- // TODO(sra): Deserialize constant, when non-null is supported.
- ConstantValue value = const NullConstantValue();
+ ConstantValue value = source.readConstant();
analysis._fixedInitializers[field] = value;
}
source.end(tag);
@@ -104,8 +118,7 @@
sink.writeInt(_fixedInitializers.length);
_fixedInitializers.forEach((JField field, ConstantValue value) {
sink.writeMember(field);
- // TODO(sra): Serialize constant, when non-null is supported.
- assert(value.isNull);
+ sink.writeConstant(value);
});
sink.end(tag);
}
@@ -115,8 +128,9 @@
var result = new JAllocatorAnalysis._(options);
kAnalysis._fixedInitializers.forEach((KField kField, ConstantValue value) {
- // TODO(sra): Translate constant, but Null does not need translating.
- if (value.isNull) {
+ // TODO(sra): Translate constant, but Null and these primitives do not
+ // need translating.
+ if (value.isNull || value.isInt || value.isBool || value.isString) {
JField jField = map.toBackendMember(kField);
if (jField != null) {
result._fixedInitializers[jField] = value;
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index 1bdea15..09bfcda 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -556,7 +556,7 @@
nativeBasicData,
_backendUsageBuilder);
_allocatorResolutionAnalysis =
- new KAllocatorAnalysis(compiler.frontendStrategy);
+ new KAllocatorAnalysis(compiler.options, compiler.frontendStrategy);
ClassQueries classQueries = compiler.frontendStrategy.createClassQueries();
ClassHierarchyBuilder classHierarchyBuilder =
new ClassHierarchyBuilder(commonElements, classQueries);
diff --git a/pkg/compiler/lib/src/js_emitter/full_emitter/class_emitter.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/class_emitter.dart
index 61df0af..be7634c 100644
--- a/pkg/compiler/lib/src/js_emitter/full_emitter/class_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/full_emitter/class_emitter.dart
@@ -133,7 +133,8 @@
bool needsFieldsForConstructor = !emitStatics && !classIsNative;
if (needsFieldsForConstructor || needsAccessor) {
List<jsAst.Literal> fieldNameParts = <jsAst.Literal>[];
- if (field.nullInitializerInAllocator) {
+ if (field.initializerInAllocator != null) {
+ assert(field.initializerInAllocator.isNull);
fieldNameParts.add(js.stringPart('0'));
}
if (!needsAccessor) {
diff --git a/pkg/compiler/lib/src/js_emitter/model.dart b/pkg/compiler/lib/src/js_emitter/model.dart
index ab6fbae..933f41c 100644
--- a/pkg/compiler/lib/src/js_emitter/model.dart
+++ b/pkg/compiler/lib/src/js_emitter/model.dart
@@ -390,17 +390,11 @@
final bool needsCheckedSetter;
- final bool nullInitializerInAllocator; // TODO(sra): Generalize.
+ final ConstantValue initializerInAllocator;
// TODO(floitsch): support renamed fields.
- Field(
- this.element,
- this.name,
- this.accessorName,
- this.getterFlags,
- this.setterFlags,
- this.needsCheckedSetter,
- this.nullInitializerInAllocator);
+ Field(this.element, this.name, this.accessorName, this.getterFlags,
+ this.setterFlags, this.needsCheckedSetter, this.initializerInAllocator);
bool get needsGetter => getterFlags != 0;
bool get needsUncheckedSetter => setterFlags != 0;
diff --git a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
index ea3c298..023faf2 100644
--- a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
+++ b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
@@ -1067,15 +1067,13 @@
}
}
- // TODO(sra): Generalize for constants other than null.
- bool nullInitializerInAllocator = false;
+ ConstantValue initializerInAllocator = null;
if (_allocatorAnalysis.isInitializedInAllocator(field)) {
- assert(_allocatorAnalysis.initializerValue(field).isNull);
- nullInitializerInAllocator = true;
+ initializerInAllocator = _allocatorAnalysis.initializerValue(field);
}
fields.add(new Field(field, name, accessorName, getterFlags, setterFlags,
- needsCheckedSetter, nullInitializerInAllocator));
+ needsCheckedSetter, initializerInAllocator));
}
FieldVisitor visitor = new FieldVisitor(_options, _elementEnvironment,
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index b51116a..5ef3622 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -935,26 +935,31 @@
const int maxChainLength = 30;
js.Expression assignment = null;
int chainLength = 0;
- bool previousIsNull = false;
+ ConstantValue previousConstant = null;
void flushAssignment() {
if (assignment != null) {
statements.add(js.js.statement('#;', assignment));
assignment = null;
chainLength = 0;
- previousIsNull = false;
+ previousConstant = null;
}
}
for (Field field in cls.fields) {
- if (field.nullInitializerInAllocator) {
- if (previousIsNull && chainLength < maxChainLength) {
+ ConstantValue constant = field.initializerInAllocator;
+ if (constant != null) {
+ if (constant == previousConstant && chainLength < maxChainLength) {
assignment = js.js('#.# = #', [thisRef, field.name, assignment]);
} else {
flushAssignment();
- assignment = js.js('#.# = null', [thisRef, field.name]);
+ assignment = js.js('#.# = #', [
+ thisRef,
+ field.name,
+ constantEmitter.generate(constant),
+ ]);
}
++chainLength;
- previousIsNull = true;
+ previousConstant = constant;
} else {
flushAssignment();
js.Parameter parameter = new js.Parameter('t${parameters.length}');
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 8bcaf1d..f2bdf20 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -498,9 +498,9 @@
functionNode: node.function, checks: TargetChecks.none);
}
- // [fieldValues] accumulates the field initializer values, which may be
- // overwritten by initializer-list initializers.
- ConstructorData constructorData = new ConstructorData();
+ // [constructorData.fieldValues] accumulates the field initializer values,
+ // which may be overwritten by initializer-list initializers.
+ ConstructorData constructorData = ConstructorData();
_buildInitializers(node, constructorData);
List<HInstruction> constructorArguments = <HInstruction>[];
@@ -731,22 +731,28 @@
failedAt(field, "Unexpected member definition $definition.");
}
+ bool ignoreAllocatorAnalysis = false;
+ if (nativeData.isNativeOrExtendsNative(cls)) {
+ // @Native classes have 'fields' which are really getters/setter. Do
+ // not try to initialize e.g. 'tagName'.
+ if (nativeData.isNativeClass(cls)) return;
+ // Fields that survive this test are fields of custom elements.
+ ignoreAllocatorAnalysis = true;
+ }
+
if (node.initializer == null) {
- // Unassigned fields of native classes are not initialized to
- // prevent overwriting pre-initialized native properties.
- if (!nativeData.isNativeOrExtendsNative(cls)) {
- if (!_allocatorAnalysis.isInitializedInAllocator(field)) {
- constructorData.fieldValues[field] =
- graph.addConstantNull(closedWorld);
- }
+ if (ignoreAllocatorAnalysis ||
+ !_allocatorAnalysis.isInitializedInAllocator(field)) {
+ constructorData.fieldValues[field] =
+ graph.addConstantNull(closedWorld);
}
- } else if (node.initializer is! ir.NullLiteral ||
- !nativeData.isNativeClass(cls)) {
+ } else {
// Compile the initializer in the context of the field so we know that
// class type parameters are accessed as values.
// TODO(sra): It would be sufficient to know the context was a field
// initializer.
- if (!_allocatorAnalysis.isInitializedInAllocator(field)) {
+ if (ignoreAllocatorAnalysis ||
+ !_allocatorAnalysis.isInitializedInAllocator(field)) {
inlinedFrom(field,
_sourceInformationBuilder.buildAssignment(node.initializer), () {
node.initializer.accept(this);