Devirtualizer: Fix verification by copying catch handlers to split block
Conflicts resulting form https://r8-review.googlesource.com/c/r8/+/55236
where resolved for the cherry-pick.
Bug: 174167294
Change-Id: I7e2c1a3c154be219904fb97e74b5342a3ff3bee4
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index e3fa34e..a7e2552 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -436,6 +436,16 @@
return newBlock;
}
+ @Override
+ public BasicBlock splitCopyCatchHandlers(
+ IRCode code, ListIterator<BasicBlock> blockIterator, InternalOptions options) {
+ BasicBlock splitBlock = split(code, blockIterator, false);
+ assert !block.hasCatchHandlers();
+ assert splitBlock.hasCatchHandlers();
+ block.copyCatchHandlers(code, blockIterator, splitBlock, options);
+ return splitBlock;
+ }
+
private boolean canThrow(IRCode code) {
InstructionIterator iterator = code.instructionIterator();
while (iterator.hasNext()) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
index bec911e..fc2f3f4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
@@ -79,6 +79,12 @@
}
@Override
+ public BasicBlock splitCopyCatchHandlers(
+ IRCode code, ListIterator<BasicBlock> blockIterator, InternalOptions options) {
+ throw new Unimplemented();
+ }
+
+ @Override
public BasicBlock inlineInvoke(
AppView<?> appView,
IRCode code,
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index 2790e66..c9c99e7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -136,6 +136,9 @@
return split(code, null);
}
+ BasicBlock splitCopyCatchHandlers(
+ IRCode code, ListIterator<BasicBlock> blockIterator, InternalOptions options);
+
/**
* Split the block into three blocks. The first split is at the point of the {@link ListIterator}
* cursor and the second split is <code>instructions</code> after the cursor. The existing
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
index d851eb9..7665d39 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -88,6 +88,12 @@
}
@Override
+ public BasicBlock splitCopyCatchHandlers(
+ IRCode code, ListIterator<BasicBlock> blockIterator, InternalOptions options) {
+ return currentBlockIterator.splitCopyCatchHandlers(code, blockIterator, options);
+ }
+
+ @Override
public BasicBlock inlineInvoke(
AppView<?> appView,
IRCode code,
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 0ed998a..1094700 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
@@ -25,6 +25,7 @@
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
@@ -46,9 +47,11 @@
public class Devirtualizer {
private final AppView<AppInfoWithLiveness> appView;
+ private final InternalOptions options;
public Devirtualizer(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
+ this.options = appView.options();
}
public void devirtualizeInvokeInterface(IRCode code) {
@@ -114,6 +117,7 @@
}
}
}
+ continue;
}
if (appView.options().testing.enableInvokeSuperToInvokeVirtualRewriting) {
@@ -209,7 +213,18 @@
if (castedReceiverCache.containsKey(receiver)
&& castedReceiverCache.get(receiver).containsKey(holderType)) {
Value cachedReceiver = castedReceiverCache.get(receiver).get(holderType);
- if (dominatorTree.dominatedBy(block, cachedReceiver.definition.getBlock())) {
+ BasicBlock cachedReceiverBlock = cachedReceiver.definition.getBlock();
+ BasicBlock dominatorBlock = null;
+ if (cachedReceiverBlock.hasCatchHandlers()) {
+ if (cachedReceiverBlock.hasUniqueNormalSuccessor()) {
+ dominatorBlock = cachedReceiverBlock.getUniqueNormalSuccessor();
+ } else {
+ assert false;
+ }
+ } else {
+ dominatorBlock = cachedReceiverBlock;
+ }
+ if (dominatorBlock != null && dominatorTree.dominatedBy(block, dominatorBlock)) {
newReceiver = cachedReceiver;
}
}
@@ -229,16 +244,18 @@
// We need to add this checkcast *before* the devirtualized invoke-virtual.
assert it.peekPrevious() == devirtualizedInvoke;
it.previous();
- // If the current block has catch handlers, split the new checkcast on its own block.
- // 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.
+
+ // If the current block has catch handlers, then split the block before adding the new
+ // check-cast instruction. The catch handlers are copied to the split block to ensure
+ // that all throwing instructions are covered by a catch-all catch handler in case of
+ // monitor instructions (see also b/174167294).
BasicBlock blockWithDevirtualizedInvoke =
- block.hasCatchHandlers() ? it.split(code, blocks) : block;
+ block.hasCatchHandlers()
+ ? it.splitCopyCatchHandlers(code, blocks, options)
+ : block;
if (blockWithDevirtualizedInvoke != block) {
// If we split, add the new checkcast at the end of the currently visiting block.
- it = block.listIterator(code, block.getInstructions().size());
- it.previous();
- it.add(checkCast);
+ block.listIterator(code, block.getInstructions().size() - 1).add(checkCast);
// Update the dominator tree after the split.
dominatorTree = new DominatorTree(code);
// Restore the cursor.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 0dd3aa4..abe6c1b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -305,11 +305,10 @@
// Insert the definition of the replacement.
replacement.setPosition(position);
if (block.hasCatchHandlers()) {
- BasicBlock splitBlock = iterator.split(code, blocks, false);
- splitBlock.listIterator(code).add(replacement);
- assert !block.hasCatchHandlers();
- assert splitBlock.hasCatchHandlers();
- block.copyCatchHandlers(code, blocks, splitBlock, appView.options());
+ iterator
+ .splitCopyCatchHandlers(code, blocks, appView.options())
+ .listIterator(code)
+ .add(replacement);
} else {
iterator.add(replacement);
}
@@ -407,11 +406,10 @@
// Insert the definition of the replacement.
replacement.setPosition(position);
if (block.hasCatchHandlers()) {
- BasicBlock splitBlock = iterator.split(code, blocks, false);
- splitBlock.listIterator(code).add(replacement);
- assert !block.hasCatchHandlers();
- assert splitBlock.hasCatchHandlers();
- block.copyCatchHandlers(code, blocks, splitBlock, appView.options());
+ iterator
+ .splitCopyCatchHandlers(code, blocks, appView.options())
+ .listIterator(code)
+ .add(replacement);
} else {
iterator.add(replacement);
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/DevirtualizeWithCatchHandlersTest.java b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/DevirtualizeWithCatchHandlersTest.java
index e45f34c..a126f0f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/DevirtualizeWithCatchHandlersTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/DevirtualizeWithCatchHandlersTest.java
@@ -41,12 +41,7 @@
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLinesIf(
- parameters.isCfRuntime() || parameters.getDexRuntimeVersion().isDalvik(), "A")
- // TODO(b/174167294): Should succeed.
- .assertFailureWithErrorThatThrowsIf(
- parameters.isDexRuntime() && !parameters.getDexRuntimeVersion().isDalvik(),
- VerifyError.class);
+ .assertSuccessWithOutputLines("A");
}
static class Main {
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index 3f7bb60..55048aa 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -144,6 +144,12 @@
}
@Override
+ public BasicBlock splitCopyCatchHandlers(
+ IRCode code, ListIterator<BasicBlock> blockIterator, InternalOptions options) {
+ throw new Unimplemented();
+ }
+
+ @Override
public BasicBlock inlineInvoke(
AppView<?> appView,
IRCode code,