Merge "Test debug stepping in <clinit>"
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
index ffaf42b..3d68ace 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -91,8 +91,8 @@
// If this is the end of the block clear out the pending state.
pendingLocals = null;
pendingLocalChanges = false;
- } else {
- // For non-exit / pc-advancing instructions emit any pending changes.
+ } else if (pc != emittedPc) {
+ // For non-exit / pc-advancing instructions emit any pending changes once possible.
emitLocalChanges(pc);
}
}
@@ -224,6 +224,7 @@
DexString nextFile,
List<DexDebugEvent> events,
DexItemFactory factory) {
+ assert previousPc != nextPc;
int pcDelta = previousPc == NO_PC_INFO ? nextPc : nextPc - previousPc;
int lineDelta = nextLine == NO_LINE_INFO ? 0 : nextLine - previousLine;
assert pcDelta >= 0;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 31201a2..dafaa26 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -2307,8 +2307,19 @@
}
}
+ private boolean isExitingThrow(InsnNode insn) {
+ List<TryCatchBlock> handlers = getTryHandlers(insn);
+ if (handlers.isEmpty()) {
+ return true;
+ }
+ if (!isSynchronized() || handlers.size() > 1) {
+ return false;
+ }
+ return handlers.get(0) == EXCEPTIONAL_SYNC_EXIT;
+ }
+
private void addThrow(InsnNode insn, int register, IRBuilder builder) {
- if (getTryHandlers(insn).isEmpty()) {
+ if (isExitingThrow(insn)) {
processLocalVariablesAtExit(insn, builder);
} else {
processLocalVariablesAtControlEdge(insn, builder);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
index a7d6e8d..35cadf2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
@@ -84,7 +84,9 @@
commonSuffixSize = Math.min(
commonSuffixSize, sharedSuffixSizeExcludingExit(firstPred, pred, allocator));
}
- assert commonSuffixSize >= 1;
+ if (commonSuffixSize == 0) {
+ continue;
+ }
int blockNumber = startNumberOfNewBlock + newBlocks.size();
BasicBlock newBlock = createAndInsertBlockForSuffix(
blockNumber, commonSuffixSize, predsWithSameLastInstruction, block);
@@ -160,7 +162,9 @@
Instruction i0 = it0.previous();
Instruction i1 = it1.previous();
if (!i0.identicalAfterRegisterAllocation(i1, allocator)) {
- return suffixSize;
+ // If the shared suffix follows a debug position at least one instruction must remain
+ // unshared to ensure the debug position is at a different pc than the shared suffix.
+ return i0.isDebugPosition() || i1.isDebugPosition() ? suffixSize - 1 : suffixSize;
}
suffixSize++;
}
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 9b3eb83..d6a64a4 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
@@ -24,7 +24,6 @@
import com.android.tools.r8.ir.code.Xor;
import com.android.tools.r8.ir.regalloc.RegisterPositions.Type;
import com.android.tools.r8.logging.Log;
-import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringUtils;
@@ -39,7 +38,6 @@
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.ArrayList;
-import java.util.Comparator;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
@@ -139,8 +137,7 @@
// List of intervals where the current instruction falls into one of their live range holes.
private List<LiveIntervals> inactive = new LinkedList<>();
// List of intervals that no register has been allocated to sorted by first live range.
- private PriorityQueue<LiveIntervals> unhandled =
- new PriorityQueue<>(Comparator.comparingInt(LiveIntervals::getStart));
+ private PriorityQueue<LiveIntervals> unhandled = new PriorityQueue<>();
// The first register used for parallel moves. After register allocation the parallel move
// temporary registers are [firstParallelMoveTemporary, maxRegisterNumber].
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
index 65628c3..0d8c6b8 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
@@ -15,7 +15,7 @@
import java.util.List;
import java.util.TreeSet;
-public class LiveIntervals {
+public class LiveIntervals implements Comparable<LiveIntervals> {
private final Value value;
private LiveIntervals nextConsecutive;
@@ -454,6 +454,12 @@
}
@Override
+ public int compareTo(LiveIntervals other) {
+ int startDiff = getStart() - other.getStart();
+ return startDiff != 0 ? startDiff : (value.getNumber() - other.value.getNumber());
+ }
+
+ @Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("(cons ");
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMove.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMove.java
index 9cb873e..4a21e8a 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMove.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMove.java
@@ -10,7 +10,7 @@
// Register moves used by the spilling register allocator. These are used both for spill and
// for phi moves and they are moves between actual registers represented by their register number.
-public class RegisterMove {
+public class RegisterMove implements Comparable<RegisterMove> {
MoveType type;
int dst;
int src;
@@ -70,4 +70,30 @@
RegisterMove o = (RegisterMove) other;
return o.src == src && o.dst == dst && o.type == type && o.definition == definition;
}
+
+ @Override
+ public int compareTo(RegisterMove o) {
+ int srcDiff = src - o.src;
+ if (srcDiff != 0) {
+ return srcDiff;
+ }
+ int dstDiff = dst - o.dst;
+ if (dstDiff != 0) {
+ return dstDiff;
+ }
+ int typeDiff = o.type.ordinal() - type.ordinal();
+ if (typeDiff != 0) {
+ return typeDiff;
+ }
+ if (definition == null) {
+ if (o.definition != null) {
+ return -1;
+ }
+ return 0;
+ }
+ if (o.definition == null) {
+ return 1;
+ }
+ return definition.getNumber() - o.definition.getNumber();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
index cc26729..007aa6f 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
@@ -14,15 +14,15 @@
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeSet;
public class RegisterMoveScheduler {
// The set of moves to schedule.
- private Set<RegisterMove> moveSet = new LinkedHashSet<>();
+ private Set<RegisterMove> moveSet = new TreeSet<>();
// Mapping to keep track of which values currently corresponds to each other.
// This is initially an identity map but changes as we insert moves.
private Map<Integer, Integer> valueMap = new HashMap<>();
diff --git a/src/test/debugTestResources/Locals.java b/src/test/debugTestResources/Locals.java
index 56613b3..5afcabb 100644
--- a/src/test/debugTestResources/Locals.java
+++ b/src/test/debugTestResources/Locals.java
@@ -308,6 +308,16 @@
return "OK";
}
+ public static void regression65066975(boolean bit) {
+ nop();
+ if (bit) {
+ nop();
+ } else {
+ nop();
+ }
+ nop();
+ }
+
public static void main(String[] args) {
noLocals();
unusedLocals();
@@ -327,5 +337,6 @@
switchRewriteToIfs(1);
switchRewriteToSwitches(1);
regression65039701(true);
+ regression65066975(false);
}
}
diff --git a/src/test/java/com/android/tools/r8/debug/LocalsTest.java b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
index 926e84a..12fd23d 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalsTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
@@ -546,4 +546,23 @@
checkLine(SOURCE_FILE, 308),
run());
}
+
+ @Test
+ public void regression65066975() throws Throwable {
+ runDebugTest(
+ "Locals",
+ breakpoint("Locals", "regression65066975"),
+ run(),
+ checkLine(SOURCE_FILE, 312),
+ checkLocal("bit", Value.createBoolean(false)),
+ stepOver(),
+ checkLine(SOURCE_FILE, 313),
+ stepOver(),
+ checkLine(SOURCE_FILE, 316),
+ stepOver(),
+ checkLine(SOURCE_FILE, 318),
+ stepOver(),
+ checkLine(SOURCE_FILE, 319),
+ run());
+ }
}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTest.java b/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTest.java
index be0a122..74992e9 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTest.java
@@ -19,8 +19,43 @@
return -Math.abs(x);
}
+ private static synchronized int throwing(int cond) {
+ int x = 42;
+ if (cond < 0) {
+ throw new IllegalStateException();
+ }
+ return 2;
+ }
+
+ private static synchronized int monitorExitRegression(int cond) {
+ int x = 42;
+ switch (cond) {
+ case 1:
+ return 1;
+ case 2:
+ throw new IllegalStateException();
+ case 3:
+ throw new RuntimeException();
+ case 4:
+ return 2;
+ case 5:
+ x = 7;
+ case 6:
+ return 3;
+ default:
+ }
+ if (cond > 0) {
+ x = cond + cond;
+ } else {
+ throw new ArithmeticException();
+ }
+ return 2;
+ }
+
public static void main(String[] args) {
System.out.println(syncStatic(1234));
System.out.println(new SynchronizedMethodTest().syncInstance(1234));
+ System.out.println(throwing(1234));
+ System.out.println(monitorExitRegression(1234));
}
}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTestRunner.java
index 7cee4a3..ba0849b 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTestRunner.java
@@ -5,8 +5,9 @@
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
import org.junit.Test;
public class SynchronizedMethodTestRunner extends DebugInfoTestBase {
@@ -18,7 +19,7 @@
AndroidApp d8App = compileWithD8(clazz);
AndroidApp dxApp = getDxCompiledSources();
- String expected = "42" + ToolHelper.LINE_SEPARATOR + "42" + ToolHelper.LINE_SEPARATOR;
+ String expected = StringUtils.lines("42", "42", "2", "2");
assertEquals(expected, runOnJava(clazz));
assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
@@ -28,6 +29,14 @@
checkSyncInstance(inspectMethod(d8App, clazz, "int", "syncInstance", "int"));
checkSyncInstance(inspectMethod(dxApp, clazz, "int", "syncInstance", "int"));
+
+ checkThrowing(inspectMethod(d8App, clazz, "int", "throwing", "int"), false);
+ checkThrowing(inspectMethod(dxApp, clazz, "int", "throwing", "int"), true);
+
+ checkMonitorExitRegression(
+ inspectMethod(d8App, clazz, "int", "monitorExitRegression", "int"), false);
+ checkMonitorExitRegression(
+ inspectMethod(dxApp, clazz, "int", "monitorExitRegression", "int"), true);
}
private void checkSyncStatic(DebugInfoInspector info) {
@@ -48,4 +57,25 @@
info.checkLineHasExactLocals(19, locals);
info.checkNoLine(20);
}
+
+ private void checkThrowing(DebugInfoInspector info, boolean dx) {
+ info.checkStartLine(23);
+ if (!dx) {
+ info.checkLineHasExactLocals(23, "cond", "int");
+ }
+ info.checkLineHasExactLocals(24, "cond", "int", "x", "int");
+ info.checkLineHasExactLocals(25, "cond", "int", "x", "int");
+ info.checkNoLine(26);
+ info.checkLineHasExactLocals(27, "cond", "int", "x", "int");
+ }
+
+ private void checkMonitorExitRegression(DebugInfoInspector info, boolean dx) {
+ info.checkStartLine(31);
+ for (int line : Arrays.asList(32, 34, 36, 38, 40, 42, 44, 48, 50, 52)) {
+ if (dx && line == 40) {
+ continue;
+ }
+ info.checkLineHasExactLocals(line, "cond", "int", "x", "int");
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/internal/D8FrameworkDeterministicTest.java b/src/test/java/com/android/tools/r8/internal/D8FrameworkDeterministicTest.java
new file mode 100644
index 0000000..2cbd60a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/D8FrameworkDeterministicTest.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2017, 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.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class D8FrameworkDeterministicTest extends CompilationTestBase {
+ private static final int MIN_SDK = 24;
+ private static final String JAR = "third_party/framework/framework_160115954.jar";
+
+ private AndroidApp doRun(D8Command command) throws IOException, CompilationException {
+ return ToolHelper.runD8(command);
+ }
+
+ @Test
+ public void verifyDebugBuild()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ D8Command command = D8Command.builder()
+ .addProgramFiles(Paths.get(JAR))
+ .setMode(CompilationMode.DEBUG)
+ .setMinApiLevel(MIN_SDK)
+ .build();
+ AndroidApp app1 = doRun(command);
+ AndroidApp app2 = doRun(command);
+ assertIdenticalApplications(app1, app2);
+ }
+
+ @Test
+ public void verifyReleaseBuild()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ D8Command command = D8Command.builder()
+ .addProgramFiles(Paths.get(JAR))
+ .setMode(CompilationMode.RELEASE)
+ .setMinApiLevel(MIN_SDK)
+ .build();
+ AndroidApp app1 = doRun(command);
+ AndroidApp app2 = doRun(command);
+ assertIdenticalApplications(app1, app2);
+ }
+}