Fix shifts in AbstractCalculator
Bug: b/348499741
Change-Id: Ief9d63fae9a981d2e9800ccab8d8766abb984184
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/arithmetic/AbstractCalculator.java b/src/main/java/com/android/tools/r8/ir/analysis/value/arithmetic/AbstractCalculator.java
index ed6c059..9b37213 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/arithmetic/AbstractCalculator.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/arithmetic/AbstractCalculator.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.analysis.value.arithmetic;
+import static com.android.tools.r8.utils.BitUtils.INTEGER_SHIFT_MASK;
+
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.utils.BitUtils;
@@ -96,7 +98,8 @@
return shlIntegers(appView, left, rightConst);
}
- public static AbstractValue shlIntegers(AppView<?> appView, AbstractValue left, int rightConst) {
+ public static AbstractValue shlIntegers(AppView<?> appView, AbstractValue left, int right) {
+ int rightConst = right & INTEGER_SHIFT_MASK;
if (rightConst == 0) {
return left;
}
@@ -104,7 +107,7 @@
int result = left.asSingleNumberValue().getIntValue() << rightConst;
return appView.abstractValueFactory().createUncheckedSingleNumberValue(result);
}
- if (left.hasDefinitelySetAndUnsetBitsInformation() && rightConst > 0) {
+ if (left.hasDefinitelySetAndUnsetBitsInformation()) {
// Shift the known bits and add that we now know that the lowermost n bits are definitely
// unset. Note that when rightConst is 31, 1 << rightConst is Integer.MIN_VALUE. When
// subtracting 1 we overflow and get 0111...111, as desired.
@@ -126,7 +129,8 @@
return shrIntegers(appView, left, rightConst);
}
- public static AbstractValue shrIntegers(AppView<?> appView, AbstractValue left, int rightConst) {
+ public static AbstractValue shrIntegers(AppView<?> appView, AbstractValue left, int right) {
+ int rightConst = right & INTEGER_SHIFT_MASK;
if (rightConst == 0) {
return left;
}
@@ -149,7 +153,7 @@
if (!right.isSingleNumberValue()) {
return AbstractValue.unknown();
}
- int rightConst = right.asSingleNumberValue().getIntValue();
+ int rightConst = right.asSingleNumberValue().getIntValue() & INTEGER_SHIFT_MASK;
if (rightConst == 0) {
return left;
}
@@ -157,7 +161,7 @@
int result = left.asSingleNumberValue().getIntValue() >>> rightConst;
return appView.abstractValueFactory().createUncheckedSingleNumberValue(result);
}
- if (left.hasDefinitelySetAndUnsetBitsInformation() && rightConst > 0) {
+ if (left.hasDefinitelySetAndUnsetBitsInformation()) {
// Shift the known bits information and add that we now know that the uppermost n bits are
// definitely unset.
return appView
diff --git a/src/main/java/com/android/tools/r8/utils/BitUtils.java b/src/main/java/com/android/tools/r8/utils/BitUtils.java
index 3a391e4..b6945ae 100644
--- a/src/main/java/com/android/tools/r8/utils/BitUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/BitUtils.java
@@ -8,6 +8,7 @@
public static final int ALL_BITS_SET_MASK = -1;
public static final int ONLY_SIGN_BIT_SET_MASK = Integer.MIN_VALUE;
+ public static final int INTEGER_SHIFT_MASK = 0x1f;
public static boolean isBitSet(int value, int which) {
return isBitInMaskSet(value, 1 << (which - 1));
diff --git a/src/test/java/com/android/tools/r8/ir/ShiftIssueTest.java b/src/test/java/com/android/tools/r8/ir/ShiftIssueTest.java
new file mode 100644
index 0000000..e535d3e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/ShiftIssueTest.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2024, 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;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ShiftIssueTest extends TestBase {
+
+ private static final String EXPECTED_RESULT = StringUtils.lines("7", "7", "7", "3", "3", "14");
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withCfRuntimes().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public ShiftIssueTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters)
+ .allowStdoutMessages()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ static class Main {
+ public static void main(String[] args) {
+ ushr32();
+ shr32();
+ shl32();
+ ushr33();
+ shr33();
+ shl33();
+ }
+
+ @NeverInline
+ private static void ushr32() {
+ int d = 1;
+ for (int g = 0; g < 10; g++) {
+ d = 7;
+ }
+ d >>>= 32;
+ System.out.println(d);
+ }
+
+ @NeverInline
+ private static void shr32() {
+ int d = 1;
+ for (int g = 0; g < 10; g++) {
+ d = 7;
+ }
+ d >>= 32;
+ System.out.println(d);
+ }
+
+ @NeverInline
+ private static void shl32() {
+ int d = 1;
+ for (int g = 0; g < 10; g++) {
+ d = 7;
+ }
+ d <<= 32;
+ System.out.println(d);
+ }
+
+ @NeverInline
+ private static void ushr33() {
+ int d = 1;
+ for (int g = 0; g < 10; g++) {
+ d = 7;
+ }
+ d >>>= 33;
+ System.out.println(d);
+ }
+
+ @NeverInline
+ private static void shr33() {
+ int d = 1;
+ for (int g = 0; g < 10; g++) {
+ d = 7;
+ }
+ d >>= 33;
+ System.out.println(d);
+ }
+
+ @NeverInline
+ private static void shl33() {
+ int d = 1;
+ for (int g = 0; g < 10; g++) {
+ d = 7;
+ }
+ d <<= 33;
+ System.out.println(d);
+ }
+ }
+}