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(); }