Reland "Initial support for member value propagation for instance fields"
This reverts commit f99a4cf73a37acfed319ec1e2fe9816775c8a4c8.
This guards member value propagation for instance fields behind a flag.
Bug: 125282093
Change-Id: I585e844962ab54b2a3f017cf23740596ac386ebb
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
index 2c86c7b..17b91f3 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -25,12 +25,12 @@
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.DominatorTree;
import com.android.tools.r8.ir.code.DominatorTree.Assumption;
+import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -56,9 +56,11 @@
AppView<AppInfoWithLiveness> appView,
IRCode code,
OptimizationFeedback feedback,
+ DexProgramClass clazz,
DexEncodedMethod method) {
+ assert clazz.type == method.method.holder;
this.appView = appView;
- this.clazz = appView.definitionFor(method.method.holder).asProgramClass();
+ this.clazz = clazz;
this.code = code;
this.feedback = feedback;
this.method = method;
@@ -67,11 +69,29 @@
public static void run(
AppView<?> appView, IRCode code, OptimizationFeedback feedback, DexEncodedMethod method) {
- if (appView.enableWholeProgramOptimizations() && method.isClassInitializer()) {
- assert appView.appInfo().hasLiveness();
- new FieldValueAnalysis(appView.withLiveness(), code, feedback, method)
- .computeFieldOptimizationInfo();
+ if (!appView.enableWholeProgramOptimizations()) {
+ return;
}
+ assert appView.appInfo().hasLiveness();
+ if (!method.isInitializer()) {
+ return;
+ }
+ DexProgramClass clazz = appView.definitionFor(method.method.holder).asProgramClass();
+ if (method.isInstanceInitializer()) {
+ if (!appView.options().enableValuePropagationForInstanceFields) {
+ return;
+ }
+ DexEncodedMethod otherInstanceInitializer =
+ clazz.lookupDirectMethod(other -> other.isInstanceInitializer() && other != method);
+ if (otherInstanceInitializer != null) {
+ // Conservatively bail out.
+ // TODO(b/125282093): Handle multiple instance initializers on the same class.
+ return;
+ }
+ }
+
+ new FieldValueAnalysis(appView.withLiveness(), code, feedback, clazz, method)
+ .computeFieldOptimizationInfo();
}
private Map<BasicBlock, AbstractFieldSet> getOrCreateFieldsMaybeReadBeforeBlockInclusive() {
@@ -86,55 +106,51 @@
AppInfoWithLiveness appInfo = appView.appInfo();
DominatorTree dominatorTree = null;
- if (method.isClassInitializer()) {
- DexType context = method.method.holder;
+ DexType context = method.method.holder;
- // Find all the static-put instructions that assign a field in the enclosing class which is
- // guaranteed to be assigned only in the current initializer.
- boolean isStraightLineCode = true;
- Map<DexEncodedField, LinkedList<StaticPut>> staticPutsPerField = new IdentityHashMap<>();
- for (Instruction instruction : code.instructions()) {
- if (instruction.isStaticPut()) {
- StaticPut staticPut = instruction.asStaticPut();
- DexField field = staticPut.getField();
- DexEncodedField encodedField = appInfo.resolveField(field);
- if (encodedField != null
- && encodedField.field.holder == context
- && appInfo.isStaticFieldWrittenOnlyInEnclosingStaticInitializer(encodedField)) {
- staticPutsPerField
- .computeIfAbsent(encodedField, ignore -> new LinkedList<>())
- .add(staticPut);
- }
- }
- if (instruction.isJumpInstruction()) {
- if (!instruction.isGoto() && !instruction.isReturn()) {
- isStraightLineCode = false;
- }
+ // Find all the static-put instructions that assign a field in the enclosing class which is
+ // guaranteed to be assigned only in the current initializer.
+ boolean isStraightLineCode = true;
+ Map<DexEncodedField, LinkedList<FieldInstruction>> putsPerField = new IdentityHashMap<>();
+ for (Instruction instruction : code.instructions()) {
+ if (instruction.isFieldPut()) {
+ FieldInstruction fieldPut = instruction.asFieldInstruction();
+ DexField field = fieldPut.getField();
+ DexEncodedField encodedField = appInfo.resolveField(field);
+ if (encodedField != null
+ && encodedField.field.holder == context
+ && appInfo.isFieldOnlyWrittenInMethod(encodedField, method)) {
+ putsPerField.computeIfAbsent(encodedField, ignore -> new LinkedList<>()).add(fieldPut);
}
}
-
- List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks();
- for (Entry<DexEncodedField, LinkedList<StaticPut>> entry : staticPutsPerField.entrySet()) {
- DexEncodedField encodedField = entry.getKey();
- LinkedList<StaticPut> staticPuts = entry.getValue();
- if (staticPuts.size() > 1) {
- continue;
+ if (instruction.isJumpInstruction()) {
+ if (!instruction.isGoto() && !instruction.isReturn()) {
+ isStraightLineCode = false;
}
- StaticPut staticPut = staticPuts.getFirst();
- if (!isStraightLineCode) {
- if (dominatorTree == null) {
- dominatorTree = new DominatorTree(code, Assumption.NO_UNREACHABLE_BLOCKS);
- }
- if (!dominatorTree.dominatesAllOf(staticPut.getBlock(), normalExitBlocks)) {
- continue;
- }
- }
- if (fieldMaybeReadBeforeInstruction(encodedField, staticPut)) {
- continue;
- }
- updateFieldOptimizationInfo(encodedField, staticPut.value());
}
}
+
+ List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks();
+ for (Entry<DexEncodedField, LinkedList<FieldInstruction>> entry : putsPerField.entrySet()) {
+ DexEncodedField encodedField = entry.getKey();
+ LinkedList<FieldInstruction> fieldPuts = entry.getValue();
+ if (fieldPuts.size() > 1) {
+ continue;
+ }
+ FieldInstruction fieldPut = fieldPuts.getFirst();
+ if (!isStraightLineCode) {
+ if (dominatorTree == null) {
+ dominatorTree = new DominatorTree(code, Assumption.NO_UNREACHABLE_BLOCKS);
+ }
+ if (!dominatorTree.dominatesAllOf(fieldPut.getBlock(), normalExitBlocks)) {
+ continue;
+ }
+ }
+ if (fieldMaybeReadBeforeInstruction(encodedField, fieldPut)) {
+ continue;
+ }
+ updateFieldOptimizationInfo(encodedField, fieldPut.value());
+ }
}
private boolean fieldMaybeReadBeforeInstruction(
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index d1028f2..83d6bd1 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -761,22 +761,26 @@
return false;
}
- public boolean isStaticFieldWrittenOnlyInEnclosingStaticInitializer(DexEncodedField field) {
+ public boolean isFieldOnlyWrittenInMethod(DexEncodedField field, DexEncodedMethod method) {
assert checkIfObsolete();
assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
if (!isPinned(field.field)) {
- DexEncodedMethod staticInitializer =
- definitionFor(field.field.holder).asProgramClass().getClassInitializer();
- if (staticInitializer != null) {
- FieldAccessInfo fieldAccessInfo = fieldAccessInfoCollection.get(field.field);
- return fieldAccessInfo != null
- && fieldAccessInfo.isWritten()
- && !fieldAccessInfo.isWrittenOutside(staticInitializer);
- }
+ FieldAccessInfo fieldAccessInfo = fieldAccessInfoCollection.get(field.field);
+ return fieldAccessInfo != null
+ && fieldAccessInfo.isWritten()
+ && !fieldAccessInfo.isWrittenOutside(method);
}
return false;
}
+ public boolean isStaticFieldWrittenOnlyInEnclosingStaticInitializer(DexEncodedField field) {
+ assert checkIfObsolete();
+ assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
+ DexEncodedMethod staticInitializer =
+ definitionFor(field.field.holder).asProgramClass().getClassInitializer();
+ return staticInitializer != null && isFieldOnlyWrittenInMethod(field, staticInitializer);
+ }
+
public boolean mayPropagateValueFor(DexReference reference) {
assert checkIfObsolete();
return !isPinned(reference) && !neverPropagateValue.contains(reference);
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 4bc3518..77cc41e 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -246,6 +246,8 @@
public boolean enableInitializedClassesInInstanceMethodsAnalysis = true;
public boolean enableRedundantFieldLoadElimination = true;
public boolean enableValuePropagation = true;
+ // TODO(b/125282093): Enable member value propagation for instance fields.
+ public boolean enableValuePropagationForInstanceFields = false;
public boolean enableUninstantiatedTypeOptimization = true;
// TODO(b/138917494): Disable until we have numbers on potential performance penalties.
public boolean enableRedundantConstNumberOptimization = false;
diff --git a/src/test/examples/shaking18/Options.java b/src/test/examples/shaking18/Options.java
index edb9d77..7012340 100644
--- a/src/test/examples/shaking18/Options.java
+++ b/src/test/examples/shaking18/Options.java
@@ -4,9 +4,7 @@
package shaking18;
public class Options {
- // TODO(b/138913138): member value propagation can behave same with and without initialization.
- // public boolean alwaysFalse = false;
- public boolean alwaysFalse;
+ public boolean alwaysFalse = false;
public boolean dummy = false;
public Options() {}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java
new file mode 100644
index 0000000..58ad103
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java
@@ -0,0 +1,141 @@
+// Copyright (c) 2019, the R8 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.
+
+package com.android.tools.r8.ir.optimize.membervaluepropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InstanceFieldValuePropagationTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public InstanceFieldValuePropagationTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(InstanceFieldValuePropagationTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(options -> {
+ // TODO(b/125282093): Remove options modification once landed.
+ assert !options.enableValuePropagationForInstanceFields;
+ options.enableValuePropagationForInstanceFields = true;
+ })
+ .enableClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(
+ StringUtils.times(StringUtils.lines("A", "42", "Hello world!"), 2));
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+ assertThat(testClassSubject, isPresent());
+
+ // Verify that all instance-get instructions in testDefinitelyNotNull() has been removed by
+ // member value propagation.
+ MethodSubject testDefinitelyNotNullMethodSubject =
+ testClassSubject.uniqueMethodWithName("testDefinitelyNotNull");
+ assertThat(testDefinitelyNotNullMethodSubject, isPresent());
+ assertTrue(
+ testDefinitelyNotNullMethodSubject
+ .streamInstructions()
+ .noneMatch(InstructionSubject::isInstanceGet));
+ // TODO(b/125282093): Should be able to remove the new-instance instruction since the instance
+ // ends up being unused.
+ assertTrue(
+ testDefinitelyNotNullMethodSubject
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isNewInstance));
+
+ // Verify that all instance-get instructions in testMaybeNull() has been removed by member value
+ // propagation.
+ MethodSubject testMaybeNullMethodSubject =
+ testClassSubject.uniqueMethodWithName("testMaybeNull");
+ assertThat(testMaybeNullMethodSubject, isPresent());
+ // TODO(b/125282093): Should synthesize a null-check and still propagate the field values even
+ // when the receiver is nullable.
+ assertTrue(
+ testMaybeNullMethodSubject
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isInstanceGet));
+ // TODO(b/125282093): Should be able to remove the new-instance instruction since the instance
+ // ends up being unused.
+ assertTrue(
+ testMaybeNullMethodSubject
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isNewInstance));
+
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ // TODO(b/125282093): Need to remove the instance-put instructions in A.<init>(). This can not
+ // be done safely by the time we process A.<init>(), so some kind of post-processing is needed.
+ assertEquals(3, aClassSubject.allInstanceFields().size());
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ testDefinitelyNotNull();
+ testMaybeNull();
+ }
+
+ @NeverInline
+ static void testDefinitelyNotNull() {
+ A a = new A();
+ System.out.println(a.e);
+ System.out.println(a.i);
+ System.out.println(a.s);
+ }
+
+ @NeverInline
+ static void testMaybeNull() {
+ A a = System.currentTimeMillis() >= 0 ? new A() : null;
+ System.out.println(a.e);
+ System.out.println(a.i);
+ System.out.println(a.s);
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ MyEnum e = MyEnum.A;
+ int i = 42;
+ String s = "Hello world!";
+ }
+
+ enum MyEnum {
+ A,
+ B
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.java
new file mode 100644
index 0000000..d2b739e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2019, the R8 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.
+
+package com.android.tools.r8.ir.optimize.membervaluepropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InstanceFieldValuePropagationWithMultipleInstanceInitializersTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public InstanceFieldValuePropagationWithMultipleInstanceInitializersTest(
+ TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+ assertThat(testClassSubject, isPresent());
+
+ // Verify that the instance-get instruction in main() is still present in main(), since the
+ // value of `a.greeting` depends on the constructor being used.
+ MethodSubject mainMethodSubject = testClassSubject.mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+ assertTrue(mainMethodSubject.streamInstructions().anyMatch(InstructionSubject::isInstanceGet));
+
+ // Verify that the `greeting` field is still present.
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertThat(aClassSubject.uniqueFieldWithName("greeting"), isPresent());
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ A a = System.currentTimeMillis() >= 0 ? new A() : new A(new Object());
+ System.out.println(a.greeting);
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ String greeting;
+
+ A() {
+ this.greeting = "Hello world!";
+ }
+
+ A(Object unused) {
+ this.greeting = ":-(";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
index 5d110e4..9da1f4a 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
@@ -82,6 +82,7 @@
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("inlined"), 1);
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("inSwitch"), 11);
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("differentTypeStaticField"), 1);
+ assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("nonStaticGet"), 1);
} else {
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("simple"));
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("local"));
@@ -89,12 +90,12 @@
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("inlined"));
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("inSwitch"));
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("differentTypeStaticField"));
+ assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
}
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("libraryType"));
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("nonValueStaticField"));
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("phi"));
- assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
}
@Test
@@ -125,12 +126,14 @@
assertNameReplacedWithConst(clazz.uniqueMethodWithName("multipleUsages"), expectedConst);
assertNameReplacedWithConst(clazz.uniqueMethodWithName("inlined"), "TWO");
assertNameReplacedWithConst(clazz.uniqueMethodWithName("differentTypeStaticField"), "DOWN");
+ assertNameReplacedWithConst(clazz.uniqueMethodWithName("nonStaticGet"), "TWO");
} else {
assertNameWasNotReplaced(clazz.uniqueMethodWithName("simple"));
assertNameWasNotReplaced(clazz.uniqueMethodWithName("local"));
assertNameWasNotReplaced(clazz.uniqueMethodWithName("multipleUsages"));
assertNameWasNotReplaced(clazz.uniqueMethodWithName("inlined"));
assertNameWasNotReplaced(clazz.uniqueMethodWithName("differentTypeStaticField"));
+ assertNameWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
}
// TODO(jakew) this should be allowed!
@@ -138,7 +141,6 @@
assertNameWasNotReplaced(clazz.uniqueMethodWithName("nonValueStaticField"));
assertNameWasNotReplaced(clazz.uniqueMethodWithName("phi"));
- assertNameWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
}
@Test
@@ -178,6 +180,7 @@
assertToStringReplacedWithConst(clazz.uniqueMethodWithName("nonValueStaticField"), "TWO");
assertToStringReplacedWithConst(
clazz.uniqueMethodWithName("differentTypeStaticField"), "DOWN");
+ assertToStringReplacedWithConst(clazz.uniqueMethodWithName("nonStaticGet"), "TWO");
} else {
assertToStringWasNotReplaced(clazz.uniqueMethodWithName("noToString"));
assertToStringWasNotReplaced(clazz.uniqueMethodWithName("local"));
@@ -185,11 +188,11 @@
assertToStringWasNotReplaced(clazz.uniqueMethodWithName("inlined"));
assertToStringWasNotReplaced(clazz.uniqueMethodWithName("nonValueStaticField"));
assertToStringWasNotReplaced(clazz.uniqueMethodWithName("differentTypeStaticField"));
+ assertToStringWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
}
assertToStringWasNotReplaced(clazz.uniqueMethodWithName("libraryType"));
assertToStringWasNotReplaced(clazz.uniqueMethodWithName("phi"));
- assertToStringWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
}
private static void assertOrdinalReplacedWithConst(MethodSubject method, int expectedConst) {
diff --git a/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java b/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java
index ea96ead..92d16ae 100644
--- a/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java
@@ -56,43 +56,50 @@
.enableMergeAnnotations()
.setMinApi(parameters.getRuntime())
.compile()
- .inspect(codeInspector -> {
- ClassSubject main = codeInspector.clazz(MAIN);
- assertThat(main, isPresent());
+ .inspect(
+ codeInspector -> {
+ ClassSubject main = codeInspector.clazz(MAIN);
+ assertThat(main, isPresent());
- MethodSubject mainMethod = main.mainMethod();
- assertThat(mainMethod, isPresent());
+ MethodSubject mainMethod = main.mainMethod();
+ assertThat(mainMethod, isPresent());
- assertTrue(
- mainMethod.streamInstructions().noneMatch(
- i -> i.isConstString("Dead code: 1", JumboStringMode.ALLOW)));
- // TODO(b/138913138): effectively final, and default value is set.
- assertFalse(
- mainMethod.streamInstructions().noneMatch(
- i -> i.isConstString("Dead code: 2", JumboStringMode.ALLOW)));
- // TODO(b/138913138): not trivial; assigned only once in <init>
- assertFalse(
- mainMethod.streamInstructions().noneMatch(
- i -> i.isConstString("Dead code: 3", JumboStringMode.ALLOW)));
- assertTrue(
- mainMethod.streamInstructions().noneMatch(
- i -> i.isConstString("Dead code: 4", JumboStringMode.ALLOW)));
- // TODO(b/138913138): effectively final, and default value is set.
- assertFalse(
- mainMethod.streamInstructions().noneMatch(
- i -> i.isConstString("Dead code: 5", JumboStringMode.ALLOW)));
- // TODO(b/138913138): not trivial; assigned multiple times, but within a certain range.
- assertFalse(
- mainMethod.streamInstructions().noneMatch(
- i -> i.isConstString("Dead code: 6", JumboStringMode.ALLOW)));
- assertTrue(
- mainMethod.streamInstructions().noneMatch(
- i -> i.isConstString("Dead code: 7", JumboStringMode.ALLOW)));
- // TODO(b/138913138): effectively final, and default value is set.
- assertFalse(
- mainMethod.streamInstructions().noneMatch(
- i -> i.isConstString("Dead code: 8", JumboStringMode.ALLOW)));
- })
+ assertTrue(
+ mainMethod
+ .streamInstructions()
+ .noneMatch(i -> i.isConstString("Dead code: 1", JumboStringMode.ALLOW)));
+ assertTrue(
+ mainMethod
+ .streamInstructions()
+ .noneMatch(i -> i.isConstString("Dead code: 2", JumboStringMode.ALLOW)));
+ // TODO(b/138913138): not trivial; assigned only once in <init>
+ assertFalse(
+ mainMethod
+ .streamInstructions()
+ .noneMatch(i -> i.isConstString("Dead code: 3", JumboStringMode.ALLOW)));
+ assertTrue(
+ mainMethod
+ .streamInstructions()
+ .noneMatch(i -> i.isConstString("Dead code: 4", JumboStringMode.ALLOW)));
+ assertTrue(
+ mainMethod
+ .streamInstructions()
+ .noneMatch(i -> i.isConstString("Dead code: 5", JumboStringMode.ALLOW)));
+ // TODO(b/138913138): not trivial; assigned multiple times, but within a certain
+ // range.
+ assertFalse(
+ mainMethod
+ .streamInstructions()
+ .noneMatch(i -> i.isConstString("Dead code: 6", JumboStringMode.ALLOW)));
+ assertTrue(
+ mainMethod
+ .streamInstructions()
+ .noneMatch(i -> i.isConstString("Dead code: 7", JumboStringMode.ALLOW)));
+ assertTrue(
+ mainMethod
+ .streamInstructions()
+ .noneMatch(i -> i.isConstString("Dead code: 8", JumboStringMode.ALLOW)));
+ })
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("The end");
}