Disable MemberValuePropagation when -dontoptimize.
Bug: 36800551, 68292416
Change-Id: I28f99c8f8eb037ac28894c9befbc3e68ea6aff58
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index d0c99d5..62ccc67 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -222,12 +222,17 @@
internal.skipMinification = true;
assert internal.useTreeShaking;
internal.useTreeShaking = false;
+
+ // Disable some of R8 optimizations.
assert internal.inlineAccessors;
internal.inlineAccessors = false;
assert internal.removeSwitchMaps;
internal.removeSwitchMaps = false;
assert internal.outline.enabled;
internal.outline.enabled = false;
+ assert internal.propagateMemberValue;
+ internal.propagateMemberValue = false;
+
internal.outputMode = getOutputMode();
internal.diagnosticsHandler = getDiagnosticsHandler();
internal.enableDesugaring = getEnableDesugaring();
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index e93f147..c067e59 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -476,6 +476,7 @@
internal.inlineAccessors = false;
internal.removeSwitchMaps = false;
internal.outline.enabled = false;
+ internal.propagateMemberValue = false;
}
assert !internal.skipMinification;
internal.skipMinification = !useMinification() || !proguardConfiguration.isObfuscating();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index de6d86b..e31c592 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -102,7 +102,8 @@
assert appInfo.hasSubtyping();
this.inliner = new Inliner(appInfo.withSubtyping(), graphLense, options);
this.outliner = new Outliner(appInfo, options);
- this.memberValuePropagation = new MemberValuePropagation(appInfo);
+ this.memberValuePropagation =
+ options.propagateMemberValue ? new MemberValuePropagation(appInfo) : null;
this.lensCodeRewriter = new LensCodeRewriter(graphLense, appInfo.withSubtyping());
if (appInfo.hasLiveness()) {
this.protoLiteRewriter = new ProtoLitePruner(appInfo.withLiveness());
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 282b37a..d7006af 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -59,6 +59,7 @@
public boolean inlineAccessors = true;
public boolean removeSwitchMaps = true;
public final OutlineOptions outline = new OutlineOptions();
+ public boolean propagateMemberValue = true;
// Number of threads to use while processing the dex files.
public int numberOfThreads = NOT_SPECIFIED;
diff --git a/src/test/examples/write_only_field/WriteOnlyCls.java b/src/test/examples/write_only_field/WriteOnlyCls.java
new file mode 100644
index 0000000..b68e4aa
--- /dev/null
+++ b/src/test/examples/write_only_field/WriteOnlyCls.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2017, 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 write_only_field;
+
+public class WriteOnlyCls {
+
+ static class DataObj {
+ final int f1;
+ DataObj(int f1) {
+ this.f1 = f1;
+ }
+ }
+
+ static DataObj static_field = new DataObj(1);
+
+ DataObj instance_field;
+
+ public WriteOnlyCls(int n) {
+ instance_field = new DataObj(n);
+ }
+
+ public static void main(String[] args) {
+ WriteOnlyCls instance = new WriteOnlyCls(2);
+ }
+
+}
diff --git a/src/test/examples/write_only_field/keep-rules-dontoptimize.txt b/src/test/examples/write_only_field/keep-rules-dontoptimize.txt
new file mode 100644
index 0000000..34957b1
--- /dev/null
+++ b/src/test/examples/write_only_field/keep-rules-dontoptimize.txt
@@ -0,0 +1,11 @@
+# Copyright (c) 2017, 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class write_only_field.WriteOnlyCls {
+ public static void main(...);
+}
+
+-dontoptimize
diff --git a/src/test/examples/write_only_field/keep-rules.txt b/src/test/examples/write_only_field/keep-rules.txt
new file mode 100644
index 0000000..3cc9314
--- /dev/null
+++ b/src/test/examples/write_only_field/keep-rules.txt
@@ -0,0 +1,9 @@
+# Copyright (c) 2017, 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class write_only_field.WriteOnlyCls {
+ public static void main(...);
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
index a1692c8..261fd71 100644
--- a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
@@ -1,3 +1,6 @@
+// Copyright (c) 2017, 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.classmerging;
import static org.junit.Assert.assertFalse;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
new file mode 100644
index 0000000..5b1c92a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
@@ -0,0 +1,111 @@
+// Copyright (c) 2017, 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.InvokeDirect;
+import com.android.tools.r8.code.IputObject;
+import com.android.tools.r8.code.NewInstance;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.code.SputObject;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.FileUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class MemberValuePropagationTest {
+ private static final String WRITE_ONLY_FIELD = "write_only_field";
+ private static final Path EXAMPLE_JAR = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR)
+ .resolve(WRITE_ONLY_FIELD + FileUtils.JAR_EXTENSION);
+ private static final Path EXAMPLE_KEEP = Paths.get(ToolHelper.EXAMPLES_DIR)
+ .resolve(WRITE_ONLY_FIELD).resolve("keep-rules.txt");
+ private static final Path DONT_OPTIMIZE = Paths.get(ToolHelper.EXAMPLES_DIR)
+ .resolve(WRITE_ONLY_FIELD).resolve("keep-rules-dontoptimize.txt");
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Test
+ public void testWriteOnlyField_putObject_gone() throws Exception {
+ Path processedApp = runR8(EXAMPLE_KEEP);
+ DexInspector inspector = new DexInspector(processedApp);
+ ClassSubject clazz = inspector.clazz(WRITE_ONLY_FIELD + ".WriteOnlyCls");
+ clazz.forAllMethods(methodSubject -> {
+ if (methodSubject.isClassInitializer()) {
+ DexEncodedMethod encodedMethod = methodSubject.getMethod();
+ DexCode code = encodedMethod.getCode().asDexCode();
+ assertEquals(4, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof NewInstance);
+ assertTrue(code.instructions[1] instanceof Const4);
+ assertTrue(code.instructions[2] instanceof InvokeDirect);
+ assertTrue(code.instructions[3] instanceof ReturnVoid);
+ }
+ if (methodSubject.isInstanceInitializer()) {
+ DexEncodedMethod encodedMethod = methodSubject.getMethod();
+ DexCode code = encodedMethod.getCode().asDexCode();
+ assertEquals(4, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof InvokeDirect);
+ assertTrue(code.instructions[1] instanceof NewInstance);
+ assertTrue(code.instructions[2] instanceof InvokeDirect);
+ assertTrue(code.instructions[3] instanceof ReturnVoid);
+ }
+ });
+ }
+
+ @Test
+ public void testWriteOnlyField_dontoptimize() throws Exception {
+ Path processedApp = runR8(DONT_OPTIMIZE);
+ DexInspector inspector = new DexInspector(processedApp);
+ ClassSubject clazz = inspector.clazz(WRITE_ONLY_FIELD + ".WriteOnlyCls");
+ clazz.forAllMethods(methodSubject -> {
+ if (methodSubject.isClassInitializer()) {
+ DexEncodedMethod encodedMethod = methodSubject.getMethod();
+ DexCode code = encodedMethod.getCode().asDexCode();
+ assertEquals(5, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof NewInstance);
+ assertTrue(code.instructions[1] instanceof Const4);
+ assertTrue(code.instructions[2] instanceof InvokeDirect);
+ assertTrue(code.instructions[3] instanceof SputObject);
+ assertTrue(code.instructions[4] instanceof ReturnVoid);
+ }
+ if (methodSubject.isInstanceInitializer()) {
+ DexEncodedMethod encodedMethod = methodSubject.getMethod();
+ DexCode code = encodedMethod.getCode().asDexCode();
+ assertEquals(5, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof InvokeDirect);
+ assertTrue(code.instructions[1] instanceof NewInstance);
+ assertTrue(code.instructions[2] instanceof InvokeDirect);
+ assertTrue(code.instructions[3] instanceof IputObject);
+ assertTrue(code.instructions[4] instanceof ReturnVoid);
+ }
+ });
+ }
+
+ private Path runR8(Path proguardConfig) throws IOException, CompilationException {
+ Path dexOutputDir = temp.newFolder().toPath();
+ ToolHelper.runR8(
+ R8Command.builder()
+ .setOutputPath(dexOutputDir)
+ .addProgramFiles(EXAMPLE_JAR)
+ .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
+ .addProguardConfigurationFiles(proguardConfig)
+ .setMinification(false)
+ .build(),
+ null);
+ return dexOutputDir.resolve("classes.dex");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspector.java b/src/test/java/com/android/tools/r8/utils/DexInspector.java
index 4b2b6ae..5d7a831 100644
--- a/src/test/java/com/android/tools/r8/utils/DexInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/DexInspector.java
@@ -520,6 +520,8 @@
public abstract boolean isBridge();
+ public abstract boolean isInstanceInitializer();
+
public abstract boolean isClassInitializer();
public abstract DexEncodedMethod getMethod();
@@ -569,6 +571,11 @@
}
@Override
+ public boolean isInstanceInitializer() {
+ return false;
+ }
+
+ @Override
public boolean isClassInitializer() {
return false;
}
@@ -630,6 +637,11 @@
}
@Override
+ public boolean isInstanceInitializer() {
+ return dexMethod.isInstanceInitializer();
+ }
+
+ @Override
public boolean isClassInitializer() {
return dexMethod.isClassInitializer();
}