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);
}