Fix performance of unneeded catch handler removal.

Reduces the compilation time for the reproduction
case from 12 minutes to 6 seconds on my machine. :-\

R=mikaelpeltier@google.com, sgjesse@google.com

Bug: 77182828
Change-Id: Ia84724f6c4cd2ab1ec13bc848996862c212d2f76
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index ee2c6ab..163c20b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -569,13 +569,15 @@
   }
 
   /**
-   * Unlinks this block from a single predecessor and successor.
+   * Unlinks the current block based on the assumption that it is a catch handler.
    *
-   * @return Returns the unlinked successor
+   * Catch handlers always have only one predecessor and at most one successor.
+   * That is because we have edge-split form for all exceptional flow.
    */
-  public BasicBlock unlinkSingle() {
-    unlinkSinglePredecessor();
-    return unlinkSingleSuccessor();
+  public void unlinkCatchHandler() {
+    assert predecessors.size() == 1;
+    predecessors.get(0).removeSuccessor(this);
+    predecessors.clear();
   }
 
   public void detachAllSuccessors() {
@@ -590,34 +592,41 @@
     assert successor.predecessors.contains(this);
     List<BasicBlock> removedBlocks = new ArrayList<>();
     for (BasicBlock dominated : dominator.dominatedBlocks(successor)) {
+      dominated.cleanForRemoval();
       removedBlocks.add(dominated);
-      for (BasicBlock block : dominated.successors) {
-        block.removePredecessor(dominated);
-      }
-      dominated.successors.clear();
-      for (BasicBlock block : dominated.predecessors) {
-        block.removeSuccessor(dominated);
-      }
-      dominated.predecessors.clear();
-      for (Phi phi : dominated.getPhis()) {
-        for (Value operand : phi.getOperands()) {
-          operand.removePhiUser(phi);
-        }
-      }
-      dominated.getPhis().clear();
-      for (Instruction instruction : dominated.getInstructions()) {
-        for (Value value : instruction.inValues) {
-          value.removeUser(instruction);
-        }
-        for (Value value : instruction.getDebugValues()) {
-          value.removeDebugUser(instruction);
-        }
-      }
     }
     assert blocksClean(removedBlocks);
     return removedBlocks;
   }
 
+  public void cleanForRemoval() {
+    for (BasicBlock block : successors) {
+      block.removePredecessor(this);
+    }
+    successors.clear();
+    for (BasicBlock block : predecessors) {
+      block.removeSuccessor(this);
+    }
+    predecessors.clear();
+    for (Phi phi : getPhis()) {
+      for (Value operand : phi.getOperands()) {
+        operand.removePhiUser(phi);
+      }
+    }
+    getPhis().clear();
+    for (Instruction instruction : getInstructions()) {
+      if (instruction.outValue != null) {
+        instruction.outValue.clearUsers();
+      }
+      for (Value value : instruction.inValues) {
+        value.removeUser(instruction);
+      }
+      for (Value value : instruction.getDebugValues()) {
+        value.removeDebugUser(instruction);
+      }
+    }
+  }
+
   public void linkCatchSuccessors(List<DexType> guards, List<BasicBlock> targets) {
     List<Integer> successorIndexes = new ArrayList<>(targets.size());
     for (BasicBlock target : targets) {