Fix handling of debug information for common return block

Change-Id: Idf3f930c8dbd16347edbf1ce6c5bd845129c4d27
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 52fff08..1cc21a3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -124,6 +124,12 @@
     debugValues.clear();
   }
 
+  public void moveDebugValue(Value value, Instruction target) {
+    assert debugValues.contains(value);
+    value.replaceDebugUser(this, target);
+    debugValues.remove(value);
+  }
+
   /**
    * Returns the basic block containing this instruction.
    */
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 5eeda96..32159c1 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
@@ -79,6 +79,7 @@
 import com.android.tools.r8.ir.code.ValueNumberGenerator;
 import com.android.tools.r8.ir.code.Xor;
 import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
@@ -1789,6 +1790,26 @@
         assert returnType == MoveType.fromDexType(method.method.proto.returnType);
       }
       closeCurrentBlock();
+
+      // Collect the debug values which are live on all returns.
+      Set<Value> debugValuesForReturn = Sets.newIdentityHashSet();
+      for (Value value : exitBlocks.get(0).exit().getDebugValues()) {
+        boolean include = true;
+        for (int i = 1; i < exitBlocks.size() && include; i++) {
+          include = exitBlocks.get(i).exit().getDebugValues().contains(value);
+        }
+        if (include) {
+          debugValuesForReturn.add(value);
+        }
+      }
+
+      // Move all these debug values to the new return.
+      for (Value value : debugValuesForReturn) {
+        for (BasicBlock block : exitBlocks) {
+          block.exit().moveDebugValue(value, normalExitBlock.exit());
+        }
+      }
+
       // Replace each return instruction with a goto to the new exit block.
       List<Value> operands = new ArrayList<>();
       for (BasicBlock block : exitBlocks) {
diff --git a/src/test/debugTestResources/Locals.java b/src/test/debugTestResources/Locals.java
index d42dd61..890cb85 100644
--- a/src/test/debugTestResources/Locals.java
+++ b/src/test/debugTestResources/Locals.java
@@ -244,6 +244,24 @@
     return sum + x + y;
   }
 
+  public static int argumentLiveAtReturn(int x) {
+    switch (x) {
+      case 0:
+        return 0;
+      case 1:
+        return 0;
+      case 2:
+        return 0;
+      case 100:
+        return 1;
+      case 101:
+        return 1;
+      case 102:
+        return 1;
+    }
+    return -1;
+  }
+
   public static void main(String[] args) {
     noLocals();
     unusedLocals();
@@ -259,5 +277,6 @@
     stepNonEmptyForLoopBody(3);
     tempInCase(42);
     localSwap(1, 2);
+    argumentLiveAtReturn(-1);
   }
 }
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 6340728..b2b77bc 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -973,7 +973,7 @@
             .filter(m -> methodSignature.equals(m.methodSignature)).collect(
                 Collectors.toList());
       }
-      Assert.assertFalse("No method found", methodInfos.isEmpty());
+      Assert.assertFalse("No method named " + methodName + " found", methodInfos.isEmpty());
       // There must be only one matching method
       Assert.assertEquals("More than 1 method found: please specify a signature", 1,
           methodInfos.size());
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 02fb694..6f73f02c 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalsTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
@@ -470,4 +470,18 @@
         checkNoLocal("t"),
         run());
   }
+
+  @Test
+  public void argumentLiveAtReturn() throws Throwable {
+    runDebugTest(
+        "Locals",
+        breakpoint("Locals", "argumentLiveAtReturn"),
+        run(),
+        checkLine(SOURCE_FILE, 248),
+        stepOver(),
+        checkLine(SOURCE_FILE, 262),
+        checkLocal("x", Value.createInt(-1)),
+        checkNoLocal("t"),
+        run());
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
index ad6f925..31872ed 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.StringUtils;
 import java.io.IOException;
 import java.util.HashSet;
 import java.util.List;
@@ -102,7 +103,8 @@
     DexDebugEntry previousEntry = null;
     for (DexDebugEntry entry : entries) {
       if (previousEntry != null) {
-        assertTrue("More than one entry defined for PC " + entry.address,
+        assertTrue(
+            "More than one entry defined for PC " + StringUtils.hexString(entry.address, 2),
             entry.address > previousEntry.address);
       }
       previousEntry = entry;
diff --git a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
index 2782c91..5ffd815 100644
--- a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
+++ b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
@@ -336,4 +336,105 @@
     info.checkLineHasExactLocals(8, "param", "int", "x", "int");
     info.checkLineHasExactLocals(9, "param", "int");
   }
+
+  @Test
+  public void argumentLiveAtReturn() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    /*
+     This is the original Java source code.
+
+     public static int argumentLiveAtReturn(int x) {  // Line 1
+       switch (x) {
+         case 0:
+           return 0;
+         case 1:
+           return 0;
+         case 2:
+           return 0;
+         case 100:
+           return 1;
+         case 101:
+           return 1;
+         case 102:
+           return 1;
+       }
+       return -1;
+     }
+   */
+    MethodSignature foo = clazz.addStaticMethod("argumentLiveAtReturn", ImmutableList.of("I"), "I",
+        ".limit stack 2",
+        ".limit locals 1",
+        ".var 0 is x I from L0 to L8",
+        "L0:",
+        ".line 2",
+        "  iload 0",
+        "lookupswitch",
+        "  0: L1",
+        "  1: L2",
+        "  2: L3",
+        "  100: L4",
+        "  101: L5",
+        "  102: L6",
+        "  default: L7",
+        "L1:",
+        ".line 4",
+        "  iconst_0",
+        "  ireturn",
+        "L2:",
+        ".line 6",
+        "  iconst_0",
+        "  ireturn",
+        "L3:",
+        ".line 8",
+        "  iconst_0",
+        "  ireturn",
+        "L4:",
+        ".line 10",
+        "  iconst_1",
+        "  ireturn",
+        "L5:",
+        ".line 12",
+        "  iconst_1",
+        "  ireturn",
+        "L6:",
+        ".line 14",
+        "  iconst_1",
+        "  ireturn",
+        "L7:",
+        ".line 16",
+        "  iconst_m1",
+        "  ireturn",
+        "L8:"
+    );
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc -1",
+        "  invokestatic Test/argumentLiveAtReturn(I)I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    String expected = "-1";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+
+    AndroidApp jasminApp = builder.build();
+    AndroidApp d8App = ToolHelper.runD8(jasminApp);
+    String artResult = runOnArt(d8App, clazz.name);
+    assertEquals(expected, artResult);
+    DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
+    info.checkStartLine(2);
+    info.checkLineHasExactLocals(2, "x", "int");
+    info.checkLineHasExactLocals(4, "x", "int");
+    info.checkLineHasExactLocals(6, "x", "int");
+    info.checkLineHasExactLocals(8, "x", "int");
+    info.checkLineHasExactLocals(10, "x", "int");
+    info.checkLineHasExactLocals(12, "x", "int");
+    info.checkLineHasExactLocals(14, "x", "int");
+    info.checkLineHasExactLocals(16, "x", "int");
+  }
 }