Merge "Combine the removal of unneeded catch handlers with dead code removal"
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index f58ea72..9d8cbcf 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -453,7 +453,6 @@
       Log.debug(getClass(), "Intermediate (SSA) flow graph for %s:\n%s",
           method.toSourceString(), code);
     }
-    codeRewriter.removeUnneededCatchHandlers(code);
     // Dead code removal. Performed after simplifications to remove code that becomes dead
     // as a result of those simplifications. The following optimizations could reveal more
     // dead code which is removed right before register allocation in performRegisterAllocation.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index c280a23..f26cacd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1149,25 +1149,6 @@
     assert code.isConsistentSSA();
   }
 
-  public void removeUnneededCatchHandlers(IRCode code) {
-    DominatorTree dominator = new DominatorTree(code);
-    code.clearMarks();
-    for (BasicBlock block : code.blocks) {
-      if (block.hasCatchHandlers() && !block.canThrow()) {
-        CatchHandlers<BasicBlock> handlers = block.getCatchHandlers();
-        for (BasicBlock target : handlers.getUniqueTargets()) {
-          for (BasicBlock unlinked : block.unlink(target, dominator)) {
-            if (!unlinked.isMarked()) {
-              unlinked.mark();
-            }
-          }
-        }
-      }
-    }
-    code.removeMarkedBlocks();
-    assert code.isConsistentSSA();
-  }
-
   public void rewriteLongCompareAndRequireNonNull(IRCode code, boolean canUseObjectsNonNull) {
     InstructionIterator iterator = code.instructionIterator();
 
@@ -1204,7 +1185,6 @@
   // Note that addSuppressed() and getSuppressed() methods are final in
   // Throwable, so these changes don't have to worry about overrides.
   public void rewriteThrowableAddAndGetSuppressed(IRCode code) {
-    boolean removeUnneededCatchHandlers = false;
     DexItemFactory.ThrowableMethods throwableMethods = dexItemFactory.throwableMethods;
 
     for (BasicBlock block : code.blocks) {
@@ -1217,15 +1197,12 @@
           if (matchesMethodOfThrowable(invokedMethod, throwableMethods.addSuppressed)) {
             // Remove Throwable::addSuppressed(Throwable) call.
             iterator.remove();
-            removeUnneededCatchHandlers = true;
-
           } else if (matchesMethodOfThrowable(invokedMethod, throwableMethods.getSuppressed)) {
             Value destValue = current.outValue();
             if (destValue == null) {
               // If the result of the call was not used we don't create
               // an empty array and just remove the call.
               iterator.remove();
-              removeUnneededCatchHandlers = true;
               continue;
             }
 
@@ -1243,21 +1220,10 @@
             NewArrayEmpty newArray = new NewArrayEmpty(destValue, zero,
                 dexItemFactory.createType(dexItemFactory.throwableArrayDescriptor));
             iterator.replaceCurrentInstruction(newArray);
-
-            // NOTE: nothing needs to be changed in catch handlers since we replace
-            //       one throwable instruction with another.
           }
         }
       }
     }
-
-    // If at least one addSuppressed(...) call was removed, or we were able
-    // to remove getSuppressed() call without replacing it with a new empty array,
-    // we need to deal with possible unreachable catch handlers.
-    if (removeUnneededCatchHandlers) {
-      removeUnneededCatchHandlers(code);
-    }
-
     assert code.isConsistentSSA();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
index d6023ed..0c60642 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -4,6 +4,8 @@
 package com.android.tools.r8.ir.optimize;
 
 import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.DominatorTree;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -11,6 +13,7 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.utils.InternalOptions;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Queue;
@@ -20,14 +23,48 @@
   public static void removeDeadCode(
       IRCode code, CodeRewriter codeRewriter, InternalOptions options) {
     Queue<BasicBlock> worklist = new LinkedList<>();
+    DominatorTree dominator = new DominatorTree(code);
+    code.clearMarks();
     worklist.addAll(code.blocks);
     for (BasicBlock block = worklist.poll(); block != null; block = worklist.poll()) {
+      if (block.isMarked()) {
+        // Ignore marked blocks, as they are scheduled for removal.
+        continue;
+      }
       removeDeadInstructions(worklist, block, options);
       removeDeadPhis(worklist, block, options);
+      removeUnneededCatchHandlers(worklist, block, dominator);
     }
+    code.removeMarkedBlocks();
+    assert code.isConsistentSSA();
     codeRewriter.rewriteMoveResult(code);
   }
 
+  // Add the block from where the value originates to the worklist.
+  private static void updateWorklist(Queue<BasicBlock> worklist, Value value) {
+    BasicBlock block;
+    if (value.isPhi()) {
+      block = value.asPhi().getBlock();
+    } else {
+      block = value.definition.getBlock();
+    }
+    if (!block.isMarked()) {
+      worklist.add(block);
+    }
+  }
+
+  // Add all blocks from where the in-values to the instruction originates.
+  private static void updateWorklist(Queue<BasicBlock> worklist, Instruction instruction) {
+    for (Value inValue : instruction.inValues()) {
+      updateWorklist(worklist, inValue);
+    }
+
+    Value previousLocalValue = instruction.getPreviousLocalValue();
+    if (previousLocalValue != null) {
+      updateWorklist(worklist, previousLocalValue);
+    }
+  }
+
   private static void removeDeadPhis(
       Queue<BasicBlock> worklist, BasicBlock block, InternalOptions options) {
     List<Phi> toRemove = new ArrayList<>();
@@ -36,11 +73,7 @@
         toRemove.add(phi);
         for (Value operand : phi.getOperands()) {
           operand.removePhiUser(phi);
-          if (operand.isPhi()) {
-            worklist.add(operand.asPhi().getBlock());
-          } else {
-            worklist.add(operand.definition.getBlock());
-          }
+          updateWorklist(worklist, operand);
         }
       }
     }
@@ -83,26 +116,31 @@
       assert outValue != null;
       if (!outValue.isDead(options)) {
         continue;
+
       }
-      for (Value inValue : current.inValues()) {
-        if (inValue.isPhi()) {
-          worklist.add(inValue.asPhi().getBlock());
-        } else {
-          worklist.add(inValue.definition.getBlock());
-        }
-      }
-      Value previousLocalValue = current.getPreviousLocalValue();
-      if (previousLocalValue != null) {
-        if (previousLocalValue.isPhi()) {
-          worklist.add(previousLocalValue.asPhi().getBlock());
-        } else {
-          worklist.add(previousLocalValue.definition.getBlock());
-        }
-      }
+      updateWorklist(worklist, current);
       // All users will be removed for this instruction. Eagerly clear them so further inspection
       // of this instruction during dead code elimination will terminate here.
       outValue.clearUsers();
       iterator.remove();
     }
   }
+
+  private static void removeUnneededCatchHandlers(
+      Queue<BasicBlock> worklist, BasicBlock block, DominatorTree dominator) {
+    if (block.hasCatchHandlers() && !block.canThrow()) {
+      CatchHandlers<BasicBlock> handlers = block.getCatchHandlers();
+      for (BasicBlock target : handlers.getUniqueTargets()) {
+        for (BasicBlock unlinked : block.unlink(target, dominator)) {
+          if (!unlinked.isMarked()) {
+            Iterator<Instruction> iterator = unlinked.iterator();
+            while (iterator.hasNext()) {
+              updateWorklist(worklist, iterator.next());
+            }
+            unlinked.mark();
+          }
+        }
+      }
+    }
+  }
 }