Skip visited blocks already seen when munching

Bug: 172781146
Change-Id: I21bb56971ddf61c4fee1891eb863a3db647418f4
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 0dd2895..889089f 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
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.Sets;
 import java.util.ListIterator;
 import java.util.Set;
 
@@ -21,6 +22,7 @@
 
   private BasicBlock currentBlock;
   private InstructionListIterator currentBlockIterator;
+  private Set<BasicBlock> seenBlocks = Sets.newIdentityHashSet();
 
   public LinearFlowInstructionListIterator(IRCode code, BasicBlock block) {
     this(code, block, 0);
@@ -32,12 +34,17 @@
     this.currentBlockIterator = block.listIterator(code, index);
     // If index is pointing after the last instruction, and it is a goto with a linear edge,
     // we have to move the pointer. This is achieved by calling previous and next.
+    seenBlocks.add(block);
     if (index > 0) {
       this.previous();
       this.next();
     }
   }
 
+  public boolean hasVisitedBlock(BasicBlock basicBlock) {
+    return seenBlocks.contains(basicBlock);
+  }
+
   @Override
   public void replaceCurrentInstruction(Instruction newInstruction, Set<Value> affectedValues) {
     currentBlockIterator.replaceCurrentInstruction(newInstruction, affectedValues);
@@ -146,6 +153,7 @@
       target = candidate;
     }
     currentBlock = target;
+    seenBlocks.add(target);
     currentBlockIterator = currentBlock.listIterator(code);
     return currentBlockIterator.next();
   }
@@ -183,6 +191,7 @@
       return currentBlockIterator.previous();
     }
     currentBlock = target;
+    seenBlocks.add(target);
     currentBlockIterator = currentBlock.listIterator(code, currentBlock.getInstructions().size());
     // Iterate over the jump.
     currentBlockIterator.previous();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/BasicBlockMuncher.java b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/BasicBlockMuncher.java
index fc48072..b196de9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/BasicBlockMuncher.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/BasicBlockMuncher.java
@@ -5,13 +5,14 @@
 package com.android.tools.r8.ir.optimize.peepholes;
 
 import static com.android.tools.r8.utils.InternalOptions.TestingOptions.NO_LIMIT;
+import static com.android.tools.r8.utils.PredicateUtils.not;
 
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.ir.code.LinearFlowInstructionListIterator;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.IteratorUtils;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
 import java.util.ListIterator;
@@ -45,7 +46,7 @@
     int iterations = 0;
     while (blocksIterator.hasPrevious()) {
       BasicBlock currentBlock = blocksIterator.previous();
-      InstructionListIterator it =
+      LinearFlowInstructionListIterator it =
           new LinearFlowInstructionListIterator(
               code, currentBlock, currentBlock.getInstructions().size());
       boolean matched = false;
@@ -74,6 +75,11 @@
               new LinearFlowInstructionListIterator(
                   code, currentBlock, currentBlock.getInstructions().size());
         } else {
+          // Move the iterator to the first block we have not seen.
+          if (IteratorUtils.previousUntilUnsafe(blocksIterator, not(it::hasVisitedBlock)) != null) {
+            // Ensure that we visit the first not visited block.
+            blocksIterator.next();
+          }
           break;
         }
       }
diff --git a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
index 2ebce28..35f950a 100644
--- a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
@@ -87,6 +87,16 @@
     throw new Unreachable();
   }
 
+  public static <T> T previousUntilUnsafe(ListIterator<T> iterator, Predicate<T> predicate) {
+    while (iterator.hasPrevious()) {
+      T previous = iterator.previous();
+      if (predicate.test(previous)) {
+        return previous;
+      }
+    }
+    return null;
+  }
+
   public static <T> T removeFirst(Iterator<T> iterator, Predicate<T> predicate) {
     while (iterator.hasNext()) {
       T item = iterator.next();