Fix a wrong split in Devirtualizer#devirtualizeInvokeInterface.

If the currently visiting block has catch handlers, we splited blocks
to be a consistent SSA. However, that split was done after adding a
necessary checkcast. Since checkcast is also a throwing instruction,
catch handlers were bound to that newly checkcast, not the devirtualized
invoke.

This CL fixes the order: split, if needed, *before* adding checkcast.

Bug: 69962188
Change-Id: I842699548824249aa44137d3648ffbebebe2ace8
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index a62d43f..362112f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -144,15 +144,30 @@
               checkCast.setPosition(invoke.getPosition());
 
               // We need to add this checkcast *before* the devirtualized invoke-virtual.
+              assert it.peekPrevious() == devirtualizedInvoke;
               it.previous();
-              it.add(checkCast);
               // If the current block has catch handlers, split the new checkcast on its own block.
-              if (block.hasCatchHandlers()) {
-                // Move the cursor backward to the newly added checkcast.
-                assert it.previous() == checkCast;
-                it.split(code, 1, blocks);
+              // Because checkcast is also a throwing instr, we should split before adding it.
+              // Otherwise, catch handlers are bound to a block with checkcast, not invoke IR.
+              BasicBlock blockWithDevirtualizedInvoke =
+                  block.hasCatchHandlers() ? it.split(code, blocks) : block;
+              if (blockWithDevirtualizedInvoke != block) {
+                // If we split, add the new checkcast at the end of the currently visiting block.
+                it = block.listIterator(block.getInstructions().size());
+                it.previous();
+                it.add(checkCast);
                 // Update the dominator tree after the split.
                 dominatorTree = new DominatorTree(code);
+                // Restore the cursor.
+                it = blockWithDevirtualizedInvoke.listIterator();
+                assert it.peekNext() == devirtualizedInvoke;
+                it.next();
+              } else {
+                // Otherwise, just add it to the current block at the position of the iterator.
+                it.add(checkCast);
+                // Restore the cursor.
+                assert it.peekNext() == devirtualizedInvoke;
+                it.next();
               }
             }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/invokeinterface/A1.java b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/invokeinterface/A1.java
index 10f80cf..258c736 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/invokeinterface/A1.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/invokeinterface/A1.java
@@ -5,7 +5,7 @@
 
 public class A1 extends A {
   @Override
-  public int get() {
-    return 1;
+  public int get() throws RuntimeException {
+    throw new RuntimeException("Expected to be discarded");
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/invokeinterface/I.java b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/invokeinterface/I.java
index ee0a0a9..48fb0b6 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/invokeinterface/I.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/invokeinterface/I.java
@@ -4,5 +4,5 @@
 package com.android.tools.r8.ir.optimize.devirtualize.invokeinterface;
 
 public interface I {
-  int get();
+  int get() throws RuntimeException;
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/invokeinterface/Main.java b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/invokeinterface/Main.java
index 570122d..d26719b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/invokeinterface/Main.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/invokeinterface/Main.java
@@ -10,15 +10,20 @@
   private static final int COUNT = 8;
 
   public static void main(String[] args) {
-    I instance = new A0();
+    I a0 = new A0();
     List<I> l = new ArrayList<>();
     for (int i = 0; i < COUNT; i++) {
-      l.add(instance);
+      l.add(a0);
     }
 
     int sum = 0;
     for (int i = 0; i < COUNT; i++) {
-      sum += l.get(i).get();
+      I instance = l.get(i);
+      try {
+        sum += instance.get();
+      } catch (RuntimeException e) {
+        // Just to introduce catch handler.
+      }
     }
     System.out.println(sum);
   }