Version 1.0.15
Merge: Extend the check for instructions taking long operands overlapping
their result (resolved conflicts in R8RunArtTestsTest)
CL: https://r8-review.googlesource.com/c/r8/+/17600
(merge conflicts in R8RunArtTestsTest)
Change-Id: I49a4903cc1d707f0ab92ff2a3e018778084b7c18
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index f1fb55c..8442b2d 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 = "v1.0.14";
+ public static final String LABEL = "v1.0.15";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Add.java b/src/main/java/com/android/tools/r8/ir/code/Add.java
index 783d672..2abc743 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Add.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Add.java
@@ -35,12 +35,6 @@
@Override
public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
- // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
- // the first part of the result long before reading the second part of the input longs.
- // Therefore, there can be no overlap of the second part of an input long and the first
- // part of the output long.
- assert dest != left + 1;
- assert dest != right + 1;
return new AddLong(dest, left, right);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/And.java b/src/main/java/com/android/tools/r8/ir/code/And.java
index ad46715..c0bdb2a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/And.java
+++ b/src/main/java/com/android/tools/r8/ir/code/And.java
@@ -41,12 +41,6 @@
@Override
public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
- // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
- // the first part of the result long before reading the second part of the input longs.
- // Therefore, there can be no overlap of the second part of an input long and the first
- // part of the output long.
- assert dest != left + 1;
- assert dest != right + 1;
return new AndLong(dest, left, right);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Or.java b/src/main/java/com/android/tools/r8/ir/code/Or.java
index 3df3a83..2cfe9b9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Or.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Or.java
@@ -40,12 +40,6 @@
@Override
public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
- // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
- // the first part of the result long before reading the second part of the input longs.
- // Therefore, there can be no overlap of the second part of an input long and the first
- // part of the output long.
- assert dest != left + 1;
- assert dest != right + 1;
return new OrLong(dest, left, right);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Sub.java b/src/main/java/com/android/tools/r8/ir/code/Sub.java
index f9f5920..f2a173b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Sub.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Sub.java
@@ -39,12 +39,6 @@
@Override
public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
- // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
- // the first part of the result long before reading the second part of the input longs.
- // Therefore, there can be no overlap of the second part of an input long and the first
- // part of the output long.
- assert dest != left + 1;
- assert dest != right + 1;
return new SubLong(dest, left, right);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Xor.java b/src/main/java/com/android/tools/r8/ir/code/Xor.java
index d2897ed..0f37a65 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Xor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Xor.java
@@ -39,12 +39,6 @@
@Override
public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
- // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
- // the first part of the result long before reading the second part of the input longs.
- // Therefore, there can be no overlap of the second part of an input long and the first
- // part of the output long.
- assert dest != left + 1;
- assert dest != right + 1;
return new XorLong(dest, left, right);
}
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 15c186f..f67aade 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
@@ -1072,6 +1072,46 @@
return arrayReg == register;
}
+ private boolean needsSingleResultOverlappingLongOperandsWorkaround(LiveIntervals intervals) {
+ if (!options.canHaveCmpLongBug()) {
+ return false;
+ }
+ if (intervals.requiredRegisters() == 2) {
+ // Not the live range for a single value and therefore not the output of cmp-long.
+ return false;
+ }
+ if (intervals.getValue().isPhi()) {
+ // If this writes a new register pair it will be via a move and not an cmp-long operation.
+ return false;
+ }
+ if (intervals.getSplitParent() != intervals) {
+ // This is a split of a parent interval and therefore if this leads to a write of a
+ // register it will be via a move and not an cmp-long operation.
+ return false;
+ }
+ Instruction definition = intervals.getValue().definition;
+ return definition.isCmp() && definition.asCmp().inValues().get(0).outType().isWide();
+ }
+
+ private boolean singleOverlappingLong(int register1, int register2) {
+ return register1 == register2 || register1 == (register2 + 1);
+ }
+
+ // Is one of the cmp-long argument registers the same as the register we are
+ // 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);
+ }
+
// The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
// the first part of the result long before reading the second part of the input longs.
//
@@ -1081,7 +1121,10 @@
//
// Dalvik would add v0 and v2 and write that to v3. It would then read v1 and v3 and produce
// the wrong result.
- private boolean needsOverlappingLongRegisterWorkaround(LiveIntervals intervals) {
+ private boolean needsLongResultOverlappingLongOperandsWorkaround(LiveIntervals intervals) {
+ if (!options.canHaveOverlappingLongRegisterBug()) {
+ return false;
+ }
if (intervals.requiredRegisters() == 1) {
// Not the live range for a wide value.
return false;
@@ -1107,8 +1150,14 @@
return false;
}
- private boolean hasOverlappingLongRegisters(LiveIntervals unhandledInterval, int register) {
- assert needsOverlappingLongRegisterWorkaround(unhandledInterval);
+ private boolean longOverlappingLong(int register1, int register2) {
+ return register1 == register2 || register1 == (register2 + 1)
+ || (register1 + 1) == register2 || (register1 + 1) == (register2 + 1);
+ }
+
+ private boolean isLongResultOverlappingLongOperands(
+ LiveIntervals unhandledInterval, int register) {
+ assert needsLongResultOverlappingLongOperandsWorkaround(unhandledInterval);
Value left = unhandledInterval.getValue().definition.asBinop().leftValue();
Value right = unhandledInterval.getValue().definition.asBinop().rightValue();
int leftReg =
@@ -1117,11 +1166,9 @@
right.getLiveIntervals().getSplitCovering(unhandledInterval.getStart()).getRegister();
assert leftReg != NO_REGISTER && rightReg != NO_REGISTER;
// The dalvik bug is actually only for overlap with the second operand, For now we
- // make sure that there is no overlap with either operand.
- if ((leftReg + 1) == register || (rightReg + 1) == register) {
- return true;
- }
- return false;
+ // make sure that there is no overlap with either register of either operand. Some vendor
+ // optimization have bees seen to need this more conservative check.
+ return longOverlappingLong(register, leftReg) || longOverlappingLong(register, rightReg);
}
private boolean allocateSingleInterval(LiveIntervals unhandledInterval, ArgumentReuseMode mode) {
@@ -1350,8 +1397,8 @@
}
if (freePosition >= unhandledInterval.getEnd()) {
// Check for overlapping long registers issue.
- if (needsOverlappingLongRegisterWorkaround(unhandledInterval) &&
- hasOverlappingLongRegisters(unhandledInterval, register)) {
+ if (needsLongResultOverlappingLongOperandsWorkaround(unhandledInterval) &&
+ isLongResultOverlappingLongOperands(unhandledInterval, register)) {
return false;
}
// Check for aget-wide bug in recent Art VMs.
@@ -1447,9 +1494,24 @@
if (candidate == REGISTER_CANDIDATE_NOT_FOUND) {
return candidate;
}
- if (needsOverlappingLongRegisterWorkaround(unhandledInterval)) {
+ if (needsLongResultOverlappingLongOperandsWorkaround(unhandledInterval)) {
int lastCandidate = candidate;
- while (hasOverlappingLongRegisters(unhandledInterval, candidate)) {
+ while (isLongResultOverlappingLongOperands(unhandledInterval, candidate)) {
+ // Make the overlapping register unavailable for allocation and try again.
+ freePositions.set(candidate, 0);
+ candidate = getLargestCandidate(registerConstraint, freePositions, needsRegisterPair, type);
+ // If there are only invalid candidates of the give type we will end up with the same
+ // candidate returned again once we have tried them all. In that case we didn't find a
+ // valid register candidate and we need to broaden the search to other types.
+ if (lastCandidate == candidate) {
+ return REGISTER_CANDIDATE_NOT_FOUND;
+ }
+ lastCandidate = candidate;
+ }
+ }
+ if (needsSingleResultOverlappingLongOperandsWorkaround(unhandledInterval)) {
+ int lastCandidate = candidate;
+ while (isSingleResultOverlappingLongOperands(unhandledInterval, candidate)) {
// Make the overlapping register unavailable for allocation and try again.
freePositions.set(candidate, 0);
candidate = getLargestCandidate(registerConstraint, freePositions, needsRegisterPair, type);
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 610d547..45834d7 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -482,4 +482,17 @@
public boolean canHaveThisTypeVerifierBug() {
return minApiLevel < AndroidApiLevel.M.getLevel();
}
+
+ // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
+ // the first part of the result long before reading the second part of the input longs.
+ public boolean canHaveOverlappingLongRegisterBug() {
+ return minApiLevel < AndroidApiLevel.L.getLevel();
+ }
+
+ // Some dalvik versions found in the wild perform invalid JIT compilation of cmp-long
+ // instructions where the result register overlaps with the input registers.
+ // See b/74084493.
+ public boolean canHaveCmpLongBug() {
+ return minApiLevel < AndroidApiLevel.L.getLevel();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index f3fb918..0ef4ef4 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -145,6 +145,10 @@
.put("974-verify-interface-super", AndroidApiLevel.N.getLevel())
// Desugaring of interface private methods is not yet supported.
.put("975-iface-private", AndroidApiLevel.N.getLevel())
+ // The extended check for overlapping long registers cause this to run out of registers.
+ .put("421-large-frame", AndroidApiLevel.L.getLevel())
+ // The extended check for overlapping long registers cause this to run out of registers.
+ .put("551-checker-shifter-operand", AndroidApiLevel.L.getLevel())
.build();
// Tests that timeout when run with Art.