Add debug tests for reordering of blocks.

These tests witness current errors in the debug info and are ignored.

R=ager, shertz

Bug: 65618023
Change-Id: Id189ec7d96b27150ce382e993bec85a3927ad2d1
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 77f1a16..f2dfce4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -397,6 +397,10 @@
     // Package up the IR code.
     IRCode ir = new IRCode(method, blocks, normalExitBlock, valueNumberGenerator);
 
+    if (options.testing.invertConditionals) {
+      invertConditionalsForTesting(ir);
+    }
+
     // Create block order and make sure that all blocks are immediately followed by their
     // fallthrough block if any.
     ir.traceBlocks();
@@ -2057,41 +2061,6 @@
     blocks.addAll(newBlocks);
   }
 
-  /**
-   * Trace blocks and attempt to put fallthrough blocks immediately after the block that
-   * falls through. When we fail to do that we create a new fallthrough block with an explicit
-   * goto to the actual fallthrough block.
-   */
-  private void traceBlocks(IRCode code) {
-    BasicBlock[] sorted = code.topologicallySortedBlocks();
-    code.clearMarks();
-    int nextBlockNumber = blocks.size();
-    LinkedList<BasicBlock> tracedBlocks = new LinkedList<>();
-    for (BasicBlock block : sorted) {
-      if (!block.isMarked()) {
-        block.mark();
-        tracedBlocks.add(block);
-        BasicBlock current = block;
-        BasicBlock fallthrough = block.exit().fallthroughBlock();
-        while (fallthrough != null && !fallthrough.isMarked()) {
-          fallthrough.mark();
-          tracedBlocks.add(fallthrough);
-          current = fallthrough;
-          fallthrough = fallthrough.exit().fallthroughBlock();
-        }
-        if (fallthrough != null) {
-          BasicBlock newFallthrough = BasicBlock.createGotoBlock(fallthrough, nextBlockNumber++);
-          current.exit().setFallthroughBlock(newFallthrough);
-          newFallthrough.getPredecessors().add(current);
-          fallthrough.replacePredecessor(current, newFallthrough);
-          newFallthrough.mark();
-          tracedBlocks.add(newFallthrough);
-        }
-      }
-    }
-    code.blocks = tracedBlocks;
-  }
-
   // Other stuff.
 
   boolean isIntegerType(NumericType type) {
@@ -2102,6 +2071,14 @@
     return type != NumericType.FLOAT && type != NumericType.DOUBLE && type != NumericType.LONG;
   }
 
+  private static void invertConditionalsForTesting(IRCode code) {
+    for (BasicBlock block : code.blocks) {
+      if (block.exit().isIf()) {
+        block.exit().asIf().invert();
+      }
+    }
+  }
+
   @Override
   public String toString() {
     StringBuilder builder = new StringBuilder();
@@ -2113,3 +2090,4 @@
     return builder.toString();
   }
 }
+
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 95762e4..875c2ef 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -204,6 +204,8 @@
 
     public Function<Set<DexEncodedMethod>, Set<DexEncodedMethod>> irOrdering =
         Function.identity();
+
+    public boolean invertConditionals = false;
   }
 
   public static class AttributeRemovalOptions {
diff --git a/src/test/debugTestResources/BlockReordering.java b/src/test/debugTestResources/BlockReordering.java
new file mode 100644
index 0000000..ebc6610
--- /dev/null
+++ b/src/test/debugTestResources/BlockReordering.java
@@ -0,0 +1,47 @@
+// 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.
+
+public class BlockReordering {
+
+  public static int conditionalReturn(boolean test) {
+    if (test) return 1;
+    else return 2;
+  }
+
+  public static int callConditionalReturn(boolean test) {
+    return conditionalReturn(test);
+  }
+
+  public static int invertConditionalReturn(boolean test) {
+    if (!test) return 1;
+    else return 2;
+  }
+
+  public static int callInvertConditionalReturn(boolean test) {
+    return invertConditionalReturn(test);
+  }
+
+  public static int fallthroughReturn(int x) {
+    if (x <= 5) if (x <= 4) if (x <= 3) if (x <= 2) if (x <= 1) return x + 1;
+    else return x + 2;
+    else return x + 3;
+    else return x + 4;
+    else return x + 5;
+    return x;
+  }
+
+  public static int callFallthroughReturn(int x) {
+    return fallthroughReturn(x);
+  }
+
+  public static void main(String[] args) {
+    System.out.println(callConditionalReturn(true));
+    System.out.println(callConditionalReturn(false));
+    System.out.println(callInvertConditionalReturn(true));
+    System.out.println(callInvertConditionalReturn(false));
+    System.out.println(callFallthroughReturn(1));
+    System.out.println(callFallthroughReturn(5));
+    System.out.println(callFallthroughReturn(6));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java b/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
new file mode 100644
index 0000000..3c40b74
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
@@ -0,0 +1,76 @@
+// 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.debug;
+
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Test single stepping behaviour across reordered blocks.
+ */
+public class BlockReorderingTest extends DebugTestBase {
+
+  public static final String CLASS = "BlockReordering";
+  public static final String FILE = "BlockReordering.java";
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    // Force inversion of all conditionals to reliably construct a regression test for incorrect
+    // line information when reording blocks.
+    setUp(options -> options.testing.invertConditionals = true);
+  }
+
+  @Test
+  @Ignore("b/65618023")
+  public void testConditionalReturn() throws Throwable {
+    final String method = "conditionalReturn";
+    runDebugTest(CLASS,
+        breakpoint(CLASS, method),
+        run(),
+        checkLine(FILE, 8), stepOver(),
+        checkLine(FILE, 13),
+        run(),
+        checkLine(FILE, 8), stepOver(),
+        checkLine(FILE, 9), stepOver(),
+        checkLine(FILE, 13),
+        run());
+  }
+
+  @Test
+  @Ignore("b/65618023")
+  public void testInvertConditionalReturn() throws Throwable {
+    final String method = "invertConditionalReturn";
+    runDebugTest(CLASS,
+        breakpoint(CLASS, method),
+        run(),
+        checkLine(FILE, 17), stepOver(),
+        checkLine(FILE, 18), stepOver(),
+        checkLine(FILE, 22),
+        run(),
+        checkLine(FILE, 17), stepOver(),
+        checkLine(FILE, 22),
+        run());
+  }
+
+  @Test
+  @Ignore("b/65618023")
+  public void testFallthroughReturn() throws Throwable {
+    final String method = "fallthroughReturn";
+    runDebugTest(CLASS,
+        breakpoint(CLASS, method),
+        run(),
+        checkLine(FILE, 26), stepOver(),
+        checkLine(FILE, 35),
+        run(),
+        checkLine(FILE, 26), stepOver(),
+        checkLine(FILE, 30), stepOver(),
+        checkLine(FILE, 35),
+        run(),
+        checkLine(FILE, 26), stepOver(),
+        checkLine(FILE, 31), stepOver(),
+        checkLine(FILE, 35),
+        run());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index f655dd7..65fe4aa 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -112,14 +112,21 @@
 
   @BeforeClass
   public static void setUp() throws Exception {
+    setUp(null);
+  }
+
+  protected static void setUp(Consumer<InternalOptions> optionsConsumer) throws Exception {
     // Convert jar to dex with d8 with debug info
     jdwpDexD8 = compileToDex(null, JDWP_JAR);
-    debuggeeDexD8 = compileToDex(null, DEBUGGEE_JAR);
+    debuggeeDexD8 = compileToDex(optionsConsumer, DEBUGGEE_JAR);
     debuggeeJava8DexD8 = compileToDex(options -> {
           // Enable desugaring for preN runtimes
           options.interfaceMethodDesugaring = OffOrAuto.Auto;
+          if (optionsConsumer != null) {
+            optionsConsumer.accept(options);
+          }
         }, DEBUGGEE_JAVA8_JAR);
-    debuggeeKotlinDexD8 = compileToDex(null, DEBUGGEE_KOTLIN_JAR);
+    debuggeeKotlinDexD8 = compileToDex(optionsConsumer, DEBUGGEE_KOTLIN_JAR);
   }
 
   protected static Path compileToDex(Consumer<InternalOptions> optionsConsumer,