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