Version 1.3.50
Merge: Implement workaround for arm64 Art codegen for long-to-int.
CL: https://r8-review.googlesource.com/c/r8/+/32420
R=ricow@google.com
Bug: 80262475
Change-Id: I85b591879dfeb10317ccb0aae790fdc63b158150
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index e3cb075..ecf2c9a 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.3.49";
+ public static final String LABEL = "1.3.50";
private Version() {
}
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 d676216..e8dc599 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
@@ -37,6 +37,10 @@
this.to = to;
}
+ public boolean isLongToIntConversion() {
+ return from == NumericType.LONG && to == NumericType.INT;
+ }
+
@Override
public void buildDex(DexBuilder builder) {
com.android.tools.r8.code.Instruction instruction;
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 9fd568b..5133dbe 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -1422,7 +1422,7 @@
}
private boolean needsSingleResultOverlappingLongOperandsWorkaround(LiveIntervals intervals) {
- if (!options.canHaveCmpLongBug()) {
+ if (!options.canHaveCmpLongBug() && !options.canHaveLongToIntBug()) {
return false;
}
if (intervals.requiredRegisters() == 2) {
@@ -1439,7 +1439,11 @@
return false;
}
Instruction definition = intervals.getValue().definition;
- return definition.isCmp() && definition.asCmp().inValues().get(0).outType().isWide();
+ if (definition.isCmp()) {
+ return definition.inValues().get(0).outType().isWide();
+ }
+ return definition.isNumberConversion()
+ && definition.asNumberConversion().isLongToIntConversion();
}
private boolean singleOverlappingLong(int register1, int register2) {
@@ -1450,15 +1454,23 @@
// allocating for the result?
private boolean isSingleResultOverlappingLongOperands(LiveIntervals intervals, int register) {
assert needsSingleResultOverlappingLongOperandsWorkaround(intervals);
- Value left = intervals.getValue().definition.asCmp().leftValue();
- Value right = intervals.getValue().definition.asCmp().rightValue();
- int leftReg =
- left.getLiveIntervals().getSplitCovering(intervals.getStart()).getRegister();
- int rightReg =
- right.getLiveIntervals().getSplitCovering(intervals.getStart()).getRegister();
- assert leftReg != NO_REGISTER;
- assert rightReg != NO_REGISTER;
- return singleOverlappingLong(register, leftReg) || singleOverlappingLong(register, rightReg);
+ if (intervals.getValue().definition.isCmp()) {
+ Value left = intervals.getValue().definition.asCmp().leftValue();
+ Value right = intervals.getValue().definition.asCmp().rightValue();
+ int leftReg =
+ left.getLiveIntervals().getSplitCovering(intervals.getStart()).getRegister();
+ int rightReg =
+ right.getLiveIntervals().getSplitCovering(intervals.getStart()).getRegister();
+ assert leftReg != NO_REGISTER;
+ assert rightReg != NO_REGISTER;
+ return singleOverlappingLong(register, leftReg) || singleOverlappingLong(register, rightReg);
+ } else {
+ assert intervals.getValue().definition.isNumberConversion();
+ Value inputValue = intervals.getValue().definition.asNumberConversion().inValues().get(0);
+ int inputReg
+ = inputValue.getLiveIntervals().getSplitCovering(intervals.getStart()).getRegister();
+ return register == inputReg;
+ }
}
// The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
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 5e23686..9b7fbf4 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -751,7 +751,7 @@
//
// We used to insert a empty loop at the end, however, mediatek has an optimizer
// on lollipop devices that cannot deal with an unreachable infinite loop, so we
- // couldn't do that. See b/119895393.
+ // couldn't do that. See b/119895393.2
public boolean canHaveTracingPastInstructionsStreamBug() {
return minApiLevel < AndroidApiLevel.L.getLevel();
}
@@ -783,4 +783,27 @@
// TODO(ager): Update this with an actual bound when the issue has been fixed.
return true;
}
+
+ // Some Art Lollipop version do not deal correctly with long-to-int conversions.
+ //
+ // In particular, the following code performs an out of bounds array access when the
+ // long loaded from the long array is very large (has non-zero values in the upper 32 bits).
+ //
+ // aget-wide v9, v3, v1
+ // long-to-int v9, v9
+ // aget-wide v10, v3, v9
+ //
+ // The issue seems to be that the higher bits of the 64-bit register holding the long
+ // are not cleared and the integer is therefore a 64-bit integer that is not truncated
+ // to 32 bits.
+ //
+ // As a workaround, we do not allow long-to-int to have the same source and target register
+ // for min-apis where lollipop devices could be targeted.
+ //
+ // See b/80262475.
+ public boolean canHaveLongToIntBug() {
+ // We have only seen this happening on Lollipop arm64 backends. We have tested on
+ // Marshmallow and Nougat arm64 devices and they do not have the bug.
+ return minApiLevel < AndroidApiLevel.M.getLevel();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/regress/b80262475/B80262475.java b/src/test/java/com/android/tools/r8/regress/b80262475/B80262475.java
new file mode 100644
index 0000000..b3fc08f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b80262475/B80262475.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2018 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.regress.b80262475;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.LongToInt;
+import com.android.tools.r8.code.Return;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.code.ReturnWide;
+import com.android.tools.r8.code.Throw;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+class TestClass {
+ public static void f(long[] a) {
+ int i = (int) a[0];
+ a[i] = a[0];
+ }
+}
+
+public class B80262475 extends TestBase {
+
+ private boolean overlappingLongToIntInputAndOutput(Instruction instruction) {
+ if (instruction instanceof LongToInt) {
+ LongToInt longToInt = (LongToInt) instruction;
+ return longToInt.A == longToInt.B;
+ }
+ return false;
+ }
+
+ @Test
+ public void testLongToIntOverlap()
+ throws IOException, CompilationFailedException, ExecutionException {
+ MethodSubject method = getMethodSubject(AndroidApiLevel.L);
+ Instruction[] instructions = method.getMethod().getCode().asDexCode().instructions;
+ for (Instruction instruction : instructions) {
+ assertFalse(overlappingLongToIntInputAndOutput(instruction));
+ }
+ }
+
+ private MethodSubject getMethodSubject(AndroidApiLevel level)
+ throws CompilationFailedException, IOException, ExecutionException {
+ CodeInspector inspector = testForD8()
+ .addProgramClasses(TestClass.class)
+ .setMinApi(level)
+ .compile()
+ .inspector();
+ ClassSubject clazz = inspector.clazz(TestClass.class);
+ assertThat(clazz, isPresent());
+ MethodSubject method = clazz.method("void", "f", ImmutableList.of("long[]"));
+ assertThat(method, isPresent());
+ return method;
+ }
+}