Add double_to_float conversion to value propagation.
Bug: b/465094702
Change-Id: I923239e146b4ba08d6f2d9a38952e0f49d6f6471
diff --git a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
index d16ecb9..5e24100 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
@@ -207,6 +207,9 @@
case DOUBLE_TO_LONG:
long rawDoubleToLongValue = (long) num.getDoubleValue();
return valueFactory.createSingleNumberValue(rawDoubleToLongValue, typeElement);
+ case DOUBLE_TO_FLOAT:
+ long rawDoubleToFloatValue = LongUtils.encodeFloat((float) num.getDoubleValue());
+ return valueFactory.createSingleNumberValue(rawDoubleToFloatValue, typeElement);
default:
return super.getAbstractValue(appView, context, abstractValueSupplier);
}
diff --git a/src/test/java/com/android/tools/r8/optimize/numberconversion/DoubleToFloatTest.java b/src/test/java/com/android/tools/r8/optimize/numberconversion/DoubleToFloatTest.java
new file mode 100644
index 0000000..458428d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/numberconversion/DoubleToFloatTest.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2025, 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.optimize.numberconversion;
+
+import static org.junit.Assume.assumeFalse;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.utils.LongUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class DoubleToFloatTest extends NumberConversionTestBase {
+
+ @Parameterized.Parameter(0)
+ public double input;
+
+ @Parameterized.Parameter(1)
+ public TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}, {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ new Double[] {
+ 128.12D,
+ 65408.61623D,
+ -65408.61623D,
+ 32768.574D,
+ -32768.574D,
+ 42D,
+ -32D,
+ 0D,
+ -0D,
+ (double) Integer.MAX_VALUE,
+ (double) Integer.MIN_VALUE,
+ Double.MAX_VALUE,
+ Double.MIN_VALUE,
+ -Double.MAX_VALUE,
+ -Double.MIN_VALUE,
+ Double.NaN,
+ Double.longBitsToDouble(0xFFFFFFFFFFFFFFFFL), // Non-canonical NaN
+ Double.longBitsToDouble(0xFFFFF0000000000FL), // Non-canonical NaN
+ Double.longBitsToDouble(0x7F90000000000000L), // Non-canonical NaN
+ Double.longBitsToDouble(0x7FBFFFFFFFFFFFFFL), // Non-canonical NaN
+ Double.POSITIVE_INFINITY,
+ Double.NEGATIVE_INFINITY
+ },
+ TestParameters.builder().withNoneRuntime().build());
+ }
+
+ @Test
+ public void testBitwise() throws Exception {
+ // Check that input is bitwise preserved.
+ testConversion(
+ mv -> {
+ mv.visitLdcInsn(input);
+ mv.visitInsn(Opcodes.D2F);
+ },
+ NumericType.FLOAT,
+ LongUtils.encodeFloat((float) input));
+ }
+
+ @Test
+ public void testCompare() throws Exception {
+ // FCMPG returns 1 if any of the inputs are NaN.
+ assumeFalse(Double.isNaN(input));
+ // Comparison is used to verify the internal state of constants rather than just the output
+ // instructions. This avoids the situation where the input is not truncated during
+ // optimization, but is still truncated in the final instructions because of typed extraction.
+ testConversion(
+ mv -> {
+ mv.visitLdcInsn(input);
+ mv.visitInsn(Opcodes.D2F);
+ mv.visitLdcInsn((float) input);
+ mv.visitInsn(Opcodes.FCMPG);
+ },
+ NumericType.INT,
+ 0);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/numberconversion/FloatToDoubleTest.java b/src/test/java/com/android/tools/r8/optimize/numberconversion/FloatToDoubleTest.java
index bb7383a..df7ccaf 100644
--- a/src/test/java/com/android/tools/r8/optimize/numberconversion/FloatToDoubleTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/numberconversion/FloatToDoubleTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.utils.LongUtils;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -62,13 +63,13 @@
mv.visitInsn(Opcodes.F2D);
},
NumericType.DOUBLE,
- Double.doubleToRawLongBits((double) input));
+ LongUtils.encodeDouble((double) input));
}
@Test
public void testCompare() throws Exception {
// DCMPG returns 1 if any of the inputs are NaN.
- assumeFalse(Double.isNaN(input));
+ assumeFalse(Float.isNaN(input));
// Comparison is used to verify the internal state of constants rather than just the output
// instructions. This avoids the situation where the input is not truncated during
// optimization, but is still truncated in the final instructions because of typed extraction.