[dart2js] `late final` initialized instance field getters are idempotent
I wanted to detect that the synthesized getter is a late final field
getter and set the 'allowCSE' property in the optimizer, but as there
is not obvious predicate, used the annotation instead.
Change-Id: Idd8797e3e7e8ececabe57e57e10c3fb9ade71477
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/430921
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Nate Biggs <natebiggs@google.com>
diff --git a/pkg/compiler/lib/src/kernel/transformations/modular/late_lowering.dart b/pkg/compiler/lib/src/kernel/transformations/modular/late_lowering.dart
index 4dd98cb..1d3ee75 100644
--- a/pkg/compiler/lib/src/kernel/transformations/modular/late_lowering.dart
+++ b/pkg/compiler/lib/src/kernel/transformations/modular/late_lowering.dart
@@ -76,6 +76,10 @@
final ExtensionTable _extensionTable = ExtensionTable();
+ late final InstanceConstant pragmaAllowCSE = _pragmaConstant(
+ 'dart2js:allow-cse',
+ );
+
LateLowering(this._coreTypes, CompilerOptions? _options)
: _omitLateNames = _options?.omitLateNames ?? false,
_readLocal = _Reader(_coreTypes.cellReadLocal),
@@ -674,6 +678,12 @@
// transformer flags to reflect whether the getter contains super calls.
getter.transformerFlags = field.transformerFlags;
_copyAnnotations(getter, field);
+ if (initializer != null && field.isFinal) {
+ getter.addAnnotation(
+ ConstantExpression(pragmaAllowCSE, _coreTypes.pragmaNonNullableRawType)
+ ..fileOffset = field.fileOffset,
+ );
+ }
enclosingClass.addProcedure(getter);
VariableDeclaration setterValue = VariableDeclaration(
@@ -750,6 +760,13 @@
}
}
+ InstanceConstant _pragmaConstant(String pragmaName) {
+ return InstanceConstant(_coreTypes.pragmaClass.reference, [], {
+ _coreTypes.pragmaName.fieldReference: StringConstant(pragmaName),
+ _coreTypes.pragmaOptions.fieldReference: NullConstant(),
+ });
+ }
+
TreeNode transformField(Field field, Member contextMember) {
_contextMember = contextMember;
diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart
index 56ece5e..fe211a7 100644
--- a/pkg/compiler/lib/src/ssa/builder.dart
+++ b/pkg/compiler/lib/src/ssa/builder.dart
@@ -7873,11 +7873,19 @@
return false;
}
- // Don't inline functions marked with 'allow-cse' and 'allow-dce' since we
- // need the call instruction to do these optimizations. We might be able to
+ // Don't inline functions marked with 'allow-dce' since we need the call
+ // instruction to recognize the whole call as unused. We might be able to
// inline simple methods afterwards.
- if (closedWorld.annotationsData.allowCSE(function) ||
- closedWorld.annotationsData.allowDCE(function)) {
+ if (closedWorld.annotationsData.allowDCE(function)) {
+ return false;
+ }
+
+ // Don't inline functions marked with 'allow-cse' since we need the call
+ // instructions to recognize repeated calls. We might be able to inline
+ // simple methods afterwards. If this is the only call site, we will never
+ // find the repeated call, so we should consider inlining here.
+ if (closedWorld.annotationsData.allowCSE(function) &&
+ !_isCalledOnce(function)) {
return false;
}
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 636be70..ee060bd 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -1344,6 +1344,9 @@
bool hasSameType = typeEquals(other);
assert(hasSameType == (_gvnType == other._gvnType));
if (!hasSameType) return false;
+ // Check the data first to ensure we are considering the same element or
+ // selector.
+ if (!dataEquals(other)) return false;
assert((useGvn() && other.useGvn()) || (allowCSE && other.allowCSE));
if (sideEffects != other.sideEffects) return false;
// Check that the inputs match.
@@ -1355,8 +1358,7 @@
return false;
}
}
- // Check that the data in the instruction matches.
- return dataEquals(other);
+ return true;
}
int gvnHashCode() {
@@ -2092,13 +2094,8 @@
@override
bool dataEquals(HInvokeDynamic other) {
- // Use the name and the kind instead of [Selector.operator==]
- // because we don't need to check the arity (already checked in
- // [gvnEquals]), and the receiver types may not be in sync.
- // TODO(sra): If we GVN calls with named (optional) arguments then the
- // selector needs a deeper check for the same subset of named arguments.
- return selector.name == other.selector.name &&
- selector.kind == other.selector.kind;
+ return selector == other.selector &&
+ (useGvn() == other.useGvn() || allowCSE == other.allowCSE);
}
}
diff --git a/pkg/front_end/testcases/dart2js/late_fields.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/late_fields.dart.strong.transformed.expect
index cabb345..590ece1 100644
--- a/pkg/front_end/testcases/dart2js/late_fields.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/late_fields.dart.strong.transformed.expect
@@ -32,6 +32,7 @@
}
set c(synthesized core::int value) → void
this.{self::C::_#C#c#AI} = value;
+ @#C3
get d() → core::int {
synthesized core::int value = this.{self::C::_#C#d#FI}{core::int};
if(_in::isSentinel(value)) {
@@ -68,6 +69,11 @@
core::print(self::c.{self::C::d}{core::int});
}
+constants {
+ #C1 = "dart2js:allow-cse"
+ #C2 = null
+ #C3 = core::pragma {name:#C1, options:#C2}
+}
Extra constant evaluation status:
Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_fields.dart:15:16 -> DoubleConstant(-1.0)
diff --git a/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.strong.transformed.expect
index 1228214..cb5c17d 100644
--- a/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.strong.transformed.expect
@@ -42,6 +42,7 @@
this.{self::C::_#C#c#AI} = value;
@#C5
@#C9
+ @#C11
get d() → core::int {
synthesized core::int value = this.{self::C::_#C#d#FI}{core::int};
if(_in::isSentinel(value)) {
@@ -88,6 +89,8 @@
#C7 = core::pragma {name:#C6, options:#C2}
#C8 = "dart2js:noInline"
#C9 = core::pragma {name:#C8, options:#C2}
+ #C10 = "dart2js:allow-cse"
+ #C11 = core::pragma {name:#C10, options:#C2}
}
Extra constant evaluation status:
diff --git a/pkg/front_end/testcases/dart2js/late_from_dill/main.dart.strong.expect b/pkg/front_end/testcases/dart2js/late_from_dill/main.dart.strong.expect
index 2d935e1..053ab4b 100644
--- a/pkg/front_end/testcases/dart2js/late_from_dill/main.dart.strong.expect
+++ b/pkg/front_end/testcases/dart2js/late_from_dill/main.dart.strong.expect
@@ -119,6 +119,7 @@
}
set c(synthesized core::int value) → void
this.{mai::C::_#C#c#AI} = value;
+ @#C3
get d() → core::int {
synthesized core::int value = this.{mai::C::_#C#d#FI}{core::int};
if(_in::isSentinel(value)) {
@@ -212,3 +213,9 @@
return mai2::_#b.{_la::_Cell::readField}<core::int>(){() → core::int};
static set b(synthesized core::int value) → void
return mai2::_#b.{_la::_Cell::finalFieldValue} = value;
+
+constants {
+ #C1 = "dart2js:allow-cse"
+ #C2 = null
+ #C3 = core::pragma {name:#C1, options:#C2}
+}
diff --git a/pkg/front_end/testcases/dart2js/late_from_dill/main.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/late_from_dill/main.dart.strong.transformed.expect
index 4128801..0d24073 100644
--- a/pkg/front_end/testcases/dart2js/late_from_dill/main.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/late_from_dill/main.dart.strong.transformed.expect
@@ -119,6 +119,7 @@
}
set c(synthesized core::int value) → void
this.{mai::C::_#C#c#AI} = value;
+ @#C3
get d() → core::int {
synthesized core::int value = this.{mai::C::_#C#d#FI}{core::int};
if(_in::isSentinel(value)) {
@@ -213,6 +214,11 @@
static set b(synthesized core::int value) → void
return mai2::_#b.{_la::_Cell::finalFieldValue} = value;
+constants {
+ #C1 = "dart2js:allow-cse"
+ #C2 = null
+ #C3 = core::pragma {name:#C1, options:#C2}
+}
Extra constant evaluation status:
Evaluated: InstanceInvocation @ org-dartlang-testcase:///main_lib1.dart:8:16 -> DoubleConstant(-1.0)