Member value propagation for float and double fields
Bug: 142571411
Change-Id: I725ae1932e8ce45c3e77f3527aa3b5cb089a16bb
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index f48a6a6..aa6b651 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -580,6 +580,12 @@
}
@Override
+ public ConstInstruction asConstInstruction(
+ AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
+ return code.createFloatConstant(value, local);
+ }
+
+ @Override
public int hashCode() {
return (int) (value * 19);
}
@@ -638,6 +644,12 @@
}
@Override
+ public ConstInstruction asConstInstruction(
+ AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
+ return code.createDoubleConstant(value, local);
+ }
+
+ @Override
public int hashCode() {
return (int) (value * 29);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index f0c062b..99a4956 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -1008,9 +1008,14 @@
return createValue(typeLattice, null);
}
- public ConstNumber createDoubleConstant(long value, DebugLocalInfo local) {
+ public ConstNumber createDoubleConstant(double value, DebugLocalInfo local) {
Value out = createValue(TypeLatticeElement.DOUBLE, local);
- return new ConstNumber(out, value);
+ return new ConstNumber(out, Double.doubleToLongBits(value));
+ }
+
+ public ConstNumber createFloatConstant(float value, DebugLocalInfo local) {
+ Value out = createValue(TypeLatticeElement.FLOAT, local);
+ return new ConstNumber(out, Float.floatToIntBits(value));
}
public ConstNumber createIntConstant(int value) {
diff --git a/src/test/examples/shaking2/Shaking.java b/src/test/examples/shaking2/Shaking.java
index f99fd8c..d521c29 100644
--- a/src/test/examples/shaking2/Shaking.java
+++ b/src/test/examples/shaking2/Shaking.java
@@ -26,6 +26,7 @@
" " + StaticFields.readBoolean+
" " + StaticFields.readByte +
" " + StaticFields.readChar +
+ " " + StaticFields.readFloat +
" " + StaticFields.readObject +
" " + StaticFields.readShort +
" " + StaticFields.readDouble);
@@ -33,9 +34,10 @@
StaticFields.writeBoolean = true;
StaticFields.writeByte = 2;
StaticFields.writeChar = 3;
+ StaticFields.writeFloat = 3.3f;
StaticFields.writeObject = new Object();
StaticFields.writeShort = 3;
- StaticFields.writeDouble = 3.3;
+ StaticFields.writeDouble = 3.3d;
}
public static void main(String[] args) {
diff --git a/src/test/examples/shaking2/StaticFields.java b/src/test/examples/shaking2/StaticFields.java
index 80bf6ba..f685893 100644
--- a/src/test/examples/shaking2/StaticFields.java
+++ b/src/test/examples/shaking2/StaticFields.java
@@ -18,6 +18,8 @@
public static byte writeByte;
public static char readChar;
public static char writeChar;
+ public static float readFloat;
+ public static float writeFloat;
public static Object readObject;
public static Object writeObject;
public static short readShort;
diff --git a/src/test/examples/shaking2/keep-rules-printusage.txt b/src/test/examples/shaking2/keep-rules-printusage.txt
index 5db2d89..bbe7eb6 100644
--- a/src/test/examples/shaking2/keep-rules-printusage.txt
+++ b/src/test/examples/shaking2/keep-rules-printusage.txt
@@ -6,3 +6,10 @@
-dontobfuscate
-printusage
+
+# TODO(b/142571411): If the class becomes entirely dead in the second round of
+# tree shaking, we respond incorrectly to printusage.
+-neverpropagatevalue class shaking2.StaticFields {
+ float readDouble;
+ float readFloat;
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/ClassInitializerDefaultsPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/ClassInitializerDefaultsPropagationTest.java
new file mode 100644
index 0000000..28f75a7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/ClassInitializerDefaultsPropagationTest.java
@@ -0,0 +1,81 @@
+// 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.assertNotEquals;
+
+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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ClassInitializerDefaultsPropagationTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ClassInitializerDefaultsPropagationTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("42.42", "42.42");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(classSubject, isPresent());
+
+ // Both fields end up being dead due to member value propagation.
+ assertEquals(0, classSubject.allFields().size());
+
+ // Verify that there are two distinct numbers in the code.
+ long floatValue = Float.floatToIntBits(42.42f);
+ long doubleValue = Double.doubleToLongBits(42.42d);
+ assertNotEquals(floatValue, doubleValue);
+ assertEquals(
+ 2,
+ classSubject
+ .mainMethod()
+ .streamInstructions()
+ .filter(x -> x.isConstNumber(floatValue) || x.isConstNumber(doubleValue))
+ .count());
+ }
+
+ static class TestClass {
+
+ static float f = 42.42f;
+ static double d = 42.42d;
+
+ public static void main(String[] args) {
+ // Test that we correctly map from float bits to int bits during member value propagation.
+ System.out.println(f);
+
+ // Test that we correctly map from double bits to long bits during member value propagation.
+ System.out.println(d);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
index e4bd7c8..327e1b5 100644
--- a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
@@ -62,11 +62,10 @@
testForR8(backend)
.addProgramFiles(Paths.get(programFile))
.addKeepRuleFiles(ListUtils.map(keepRulesFiles, Paths::get))
- .addKeepRules(
- "-printusage " + out.resolve(test + PRINT_USAGE_FILE_SUFFIX)
- )
+ .addKeepRules("-printusage " + out.resolve(test + PRINT_USAGE_FILE_SUFFIX))
// Disable inlining to make this test not depend on inlining decisions.
.addOptionsModification(o -> o.enableInlining = false)
+ .enableProguardTestOptions()
.compile();
}