Fix incorrect instance-get canonicalization
Fixes: b/315877832
Change-Id: I09db1a0e0cb1b873417a1fe55a3ca5d782e77455
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
index 1b14342..1ef539a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
@@ -445,13 +445,14 @@
if (instanceGet.instructionMayHaveSideEffects(appView, context)) {
return false;
}
+ NewInstance newInstance = null;
if (instanceGet.object().isDefinedByInstructionSatisfying(Instruction::isNewInstance)) {
- NewInstance newInstance = instanceGet.object().getDefinition().asNewInstance();
+ newInstance = instanceGet.object().getDefinition().asNewInstance();
if (newInstance.getUniqueConstructorInvoke(appView.dexItemFactory()) == null) {
return false;
}
}
- if (!isReadOfEffectivelyFinalFieldOutsideInitializer(instanceGet)) {
+ if (!isReadOfEffectivelyFinalFieldOutsideInitializer(instanceGet, newInstance)) {
return false;
}
if (getOrComputeIneligibleInstanceGetInstructions().contains(instanceGet)) {
@@ -484,7 +485,12 @@
return true;
}
- private boolean isReadOfEffectivelyFinalFieldOutsideInitializer(FieldGet fieldGet) {
+ private boolean isReadOfEffectivelyFinalFieldOutsideInitializer(StaticGet staticGet) {
+ return isReadOfEffectivelyFinalFieldOutsideInitializer(staticGet, null);
+ }
+
+ private boolean isReadOfEffectivelyFinalFieldOutsideInitializer(
+ FieldGet fieldGet, NewInstance newInstance) {
if (getOrComputeIsAccessingVolatileField()) {
// A final field may be initialized concurrently. A requirement for this is that the field is
// volatile. However, the reading or writing of another volatile field also allows for
@@ -512,6 +518,14 @@
if (!resolvedField.isFinalOrEffectivelyFinal(appViewWithClassHierarchy)) {
return false;
}
+ if (!resolvedField.getAccessFlags().isFinal() && newInstance != null) {
+ // The effectively final property captured in the enqueuer may be invalidated by constructor
+ // inlining (in particular, fields that used only to be written in instance initializers from
+ // the enclosing class may now be written outside such constructors). If we see an
+ // instance-get on a newly created instance, we therefore bail-out since the field may in
+ // principle not be effectively final in this method.
+ return false;
+ }
if (appView.getKeepInfo(resolvedField).isPinned(appView.options())) {
// The final flag could be unset using reflection.
return false;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/EffectivelyFinalInstanceFieldCanonicalizationAfterConstructorInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/EffectivelyFinalInstanceFieldCanonicalizationAfterConstructorInliningTest.java
index 5d80951..d5865d7 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/EffectivelyFinalInstanceFieldCanonicalizationAfterConstructorInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/EffectivelyFinalInstanceFieldCanonicalizationAfterConstructorInliningTest.java
@@ -14,6 +14,7 @@
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
+/** Regression test for b/315877832. */
@RunWith(Parameterized.class)
public class EffectivelyFinalInstanceFieldCanonicalizationAfterConstructorInliningTest
extends TestBase {
@@ -35,11 +36,7 @@
.enableNoRedundantFieldLoadEliminationAnnotations()
.setMinApi(parameters)
.run(parameters.getRuntime(), Main.class)
- // TODO(b/315877832): Fix canonicalization.
- .assertSuccessWithOutputLines(
- parameters.canInitNewInstanceUsingSuperclassConstructor()
- ? "null, world!"
- : "Hello, world!");
+ .assertSuccessWithOutputLines("Hello, world!");
}
static class Main {