Extend redundant block removal to blocks with non-jump instructions

Change-Id: I3c78fb78dd85f2da009cb4e80af9f2bf2db30bff
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index 840655f..d687be2 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -253,11 +253,11 @@
     assert !callerPosition.isOutline();
     // Copy the callee frame to ensure transfer of the outline key if present.
     PositionBuilder<?, ?> newCallerBuilder =
-        outermostCallee.builderWithCopy().setMethod(callerPosition.getMethod());
-    // Transfer the callers outer frames if any.
-    if (callerPosition.hasCallerPosition()) {
-      newCallerBuilder.setCallerPosition(callerPosition.getCallerPosition());
-    }
+        outermostCallee
+            .builderWithCopy()
+            .setIsD8R8Synthesized(callerPosition.isD8R8Synthesized())
+            .setMethod(callerPosition.getMethod())
+            .setCallerPosition(callerPosition.getCallerPosition());
     // If the callee is an outline, the line must be that of the outline to maintain the positions.
     if (outermostCallee.isOutline()) {
       // This does not implement inlining an outline. The cases this hits should always be a full
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 7250c3a..5924200 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
@@ -120,7 +120,7 @@
     return localsAtEntry;
   }
 
-  public void replaceLastInstruction(Instruction instruction, IRCode code) {
+  public void replaceLastInstruction(Instruction instruction) {
     InstructionListIterator iterator = listIterator(getInstructions().size());
     iterator.previous();
     iterator.replaceCurrentInstruction(instruction);
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index 50e4f53..3f61db3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -886,7 +886,7 @@
 
   private boolean noRedundantBlocks() {
     for (BasicBlock block : blocks) {
-      assert !isRedundantBlock(block);
+      assert !isBlockWithRedundantSuccessorBlock(block);
     }
     return true;
   }
@@ -1315,7 +1315,30 @@
     return true;
   }
 
-  private boolean isRedundantBlock(BasicBlock block) {
+  private boolean isBlockWithRedundantSuccessorBlock(BasicBlock block) {
+    if (options.debug) {
+      return isBlockWithRedundantSuccessorBlockInDebug(block);
+    } else {
+      return isBlockWithRedundantSuccessorBlockInRelease(block);
+    }
+  }
+
+  private boolean isBlockWithRedundantSuccessorBlockInRelease(BasicBlock block) {
+    if (!block.hasUniqueSuccessorWithUniquePredecessor()
+        || !block.exit().isGoto()
+        || !block.exit().getDebugValues().isEmpty()) {
+      return false;
+    }
+    BasicBlock successor = block.getUniqueSuccessor();
+    if (successor.hasCatchHandlers()) {
+      if (block.isEntry() || block.canThrow()) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  private boolean isBlockWithRedundantSuccessorBlockInDebug(BasicBlock block) {
     return block.hasUniqueSuccessorWithUniquePredecessor()
         && block.getInstructions().size() == 1
         && block.exit().isGoto()
@@ -1324,31 +1347,59 @@
   }
 
   public void removeRedundantBlocks() {
-    List<BasicBlock> blocksToRemove = new ArrayList<>();
-
+    // See b/237567012.
+    assert verifyNoStackInstructions();
+    Set<BasicBlock> blocksToRemove = Sets.newIdentityHashSet();
+    // Run over all blocks while merging successor blocks into the current block.
     for (BasicBlock block : blocks) {
-      // Check that there are no redundant blocks.
-      assert !blocksToRemove.contains(block);
-      if (isRedundantBlock(block)) {
-        assert block.getUniqueSuccessor().getMutablePredecessors().size() == 1;
-        assert block.getUniqueSuccessor().getMutablePredecessors().get(0) == block;
-        assert block.getUniqueSuccessor().getPhis().size() == 0;
-        // Let the successor consume this block.
+      if (blocksToRemove.contains(block)) {
+        continue;
+      }
+      while (isBlockWithRedundantSuccessorBlock(block)) {
+        // Let the current block consume the successor.;
         BasicBlock successor = block.getUniqueSuccessor();
-        successor.getMutablePredecessors().clear();
-        successor.getMutablePredecessors().addAll(block.getPredecessors());
-        successor.getPhis().addAll(block.getPhis());
-        successor.getPhis().forEach(phi -> phi.setBlock(block.getUniqueSuccessor()));
-        block
-            .getPredecessors()
-            .forEach(predecessors -> predecessors.replaceSuccessor(block, successor));
-        block.getMutablePredecessors().clear();
+        assert !successor.hasCatchHandlers() || !block.canThrow();
+        assert !successor.hasPhis();
+        Instruction instruction = successor.entry();
+        while (instruction != null) {
+          Instruction next = instruction.getNext();
+          if (instruction.isJumpInstruction()) {
+            block.replaceLastInstruction(instruction);
+          } else {
+            block.getInstructions().addBefore(instruction, block.exit());
+          }
+          instruction = next;
+        }
+
+        // Unlink successor block.
         block.getMutableSuccessors().clear();
-        block.getPhis().clear();
-        blocksToRemove.add(block);
+        if (successor.hasCatchHandlers()) {
+          block.moveCatchHandlers(successor);
+        }
+        block.getMutableSuccessors().addAll(successor.getSuccessors());
+        block.forEachNormalSuccessor(
+            successorOfSuccessor -> successorOfSuccessor.replacePredecessor(successor, block));
+
+        // Clean successor block and record for removal.
+        successor.getInstructions().clear();
+        successor.getMutablePredecessors().clear();
+        successor.getMutableSuccessors().clear();
+        blocksToRemove.add(successor);
       }
     }
     blocks.removeAll(blocksToRemove);
+    assert noRedundantBlocks();
+  }
+
+  private boolean verifyNoStackInstructions() {
+    for (BasicBlock block : blocks) {
+      for (Instruction instruction : block.getInstructions()) {
+        assert !instruction.isStore();
+        assert !instruction.isPop();
+        assert !instruction.hasOutValue() || !instruction.outValue().isValueOnStack();
+      }
+    }
+    return true;
   }
 
   public boolean removeAllDeadAndTrivialPhis() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionList.java b/src/main/java/com/android/tools/r8/ir/code/InstructionList.java
index f95b67a..feb88a9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionList.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionList.java
@@ -94,7 +94,6 @@
    * end.
    */
   public void addBefore(Instruction newInstruction, Instruction existingInstruction) {
-    assert newInstruction.block == null;
     if (existingInstruction != null) {
       assert linearScanFinds(existingInstruction);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index b862664..2ef3b67 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -171,7 +171,6 @@
       }
       timing.end();
     }
-    code.removeRedundantBlocks();
     assert code.isConsistentGraph(appView, false);
     previousPrintString =
         IRConverter.printMethodIR(
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 9686713..8b5d9ee 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
@@ -809,9 +809,7 @@
     if (assumeInserter != null) {
       timing.begin("Remove assume instructions");
       CodeRewriter.removeAssumeInstructions(appView, code);
-      code.removeRedundantBlocks();
       timing.end();
-      assert code.isConsistentSSA(appView);
       previous = printMethod(code, "IR after removing assume instructions (SSA)", previous);
 
       // TODO(b/214496607): Remove when dynamic types are safe w.r.t. interface assignment rules.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRToDexFinalizer.java b/src/main/java/com/android/tools/r8/ir/conversion/IRToDexFinalizer.java
index 84dbfb0..20ba9f1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRToDexFinalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRToDexFinalizer.java
@@ -50,7 +50,7 @@
   }
 
   private void workaroundBugs(IRCode code, Timing timing) {
-    RuntimeWorkaroundCodeRewriter.workaroundNumberConversionRegisterAllocationBug(code, options);
+    RuntimeWorkaroundCodeRewriter.workaroundNumberConversionRegisterAllocationBug(appView, code);
     RuntimeWorkaroundCodeRewriter.workaroundDex2OatInliningIssue(appView, code);
     if (RuntimeWorkaroundCodeRewriter.workaroundInstanceOfTypeWeakeningInVerifier(appView, code)) {
       deadCodeRemover.run(code, timing);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/BranchSimplifier.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/BranchSimplifier.java
index a72f171..1c19267 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/BranchSimplifier.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/BranchSimplifier.java
@@ -111,11 +111,11 @@
         continue;
       }
       if (block.exit().isIf()) {
-        flipIfBranchesIfNeeded(code, block);
-        if (rewriteIfWithConstZero(code, block)) {
+        flipIfBranchesIfNeeded(block);
+        if (rewriteIfWithConstZero(block)) {
           simplified = true;
         }
-        if (rewriteIfWithObjectsIsNullOrNonNull(code, block)) {
+        if (rewriteIfWithObjectsIsNullOrNonNull(block)) {
           simplified = true;
         }
 
@@ -145,7 +145,7 @@
         if (behavioralSubsumption.isSubsumedBy(
             theIf.inValues().get(0), theIf.getPosition(),
             theIf.getTrueTarget(), theIf.fallthroughBlock())) {
-          simplifyIfWithKnownCondition(code, block, theIf, theIf.fallthroughBlock());
+          simplifyIfWithKnownCondition(block, theIf, theIf.fallthroughBlock());
           simplified = true;
         }
       }
@@ -188,10 +188,6 @@
       return this;
     }
 
-    public boolean anyAffectedValues() {
-      return anyAffectedValues;
-    }
-
     public boolean anySimplifications() {
       return anySimplifications;
     }
@@ -215,7 +211,7 @@
     if (lhsRoot.isConstNumber()) {
       ConstNumber cond = lhsRoot.getConstInstruction().asConstNumber();
       BasicBlock target = theIf.targetFromCondition(cond);
-      simplifyIfWithKnownCondition(code, block, theIf, target);
+      simplifyIfWithKnownCondition(block, theIf, target);
       return true;
     }
 
@@ -223,12 +219,12 @@
       assert theIf.getType() == IfType.EQ || theIf.getType() == IfType.NE;
 
       if (lhs.isAlwaysNull(appView)) {
-        simplifyIfWithKnownCondition(code, block, theIf, theIf.targetFromNullObject());
+        simplifyIfWithKnownCondition(block, theIf, theIf.targetFromNullObject());
         return true;
       }
 
       if (lhs.isNeverNull()) {
-        simplifyIfWithKnownCondition(code, block, theIf, theIf.targetFromNonNullObject());
+        simplifyIfWithKnownCondition(block, theIf, theIf.targetFromNonNullObject());
         return true;
       }
     }
@@ -238,7 +234,7 @@
       if (lhsAbstractValue.isConstantOrNonConstantNumberValue()
           && !lhsAbstractValue.asConstantOrNonConstantNumberValue().maybeContainsInt(0)) {
         // Value doesn't contain zero at all.
-        simplifyIfWithKnownCondition(code, block, theIf, theIf.targetFromCondition(1));
+        simplifyIfWithKnownCondition(block, theIf, theIf.targetFromCondition(1));
         return true;
       }
       if (!lhsRoot.isPhi() && lhsRoot.getDefinition().isXor()) {
@@ -247,7 +243,7 @@
         if (input != null) {
           // ifeqz !a => ifnez a
           // ifnez !a => ifeqz a
-          block.replaceLastInstruction(new If(theIf.getType().inverted(), input), code);
+          block.replaceLastInstruction(new If(theIf.getType().inverted(), input));
           return true;
         }
       }
@@ -258,7 +254,7 @@
       if (!interval.containsValue(0)) {
         // Interval doesn't contain zero at all.
         int sign = Long.signum(interval.getMin());
-        simplifyIfWithKnownCondition(code, block, theIf, sign);
+        simplifyIfWithKnownCondition(block, theIf, sign);
         return true;
       }
 
@@ -270,7 +266,7 @@
           // [a, b] < 0 is always false if a >= 0.
           // In both cases a zero condition takes the right branch.
           if (interval.getMin() == 0) {
-            simplifyIfWithKnownCondition(code, block, theIf, 0);
+            simplifyIfWithKnownCondition(block, theIf, 0);
             return true;
           }
           break;
@@ -281,7 +277,7 @@
           // [a, b] > 0 is always false if b <= 0.
           // In both cases a zero condition takes the right branch.
           if (interval.getMax() == 0) {
-            simplifyIfWithKnownCondition(code, block, theIf, 0);
+            simplifyIfWithKnownCondition(block, theIf, 0);
             return true;
           }
           break;
@@ -317,7 +313,7 @@
     Value rhsRoot = rhs.getAliasedValue();
     if (lhsRoot == rhsRoot) {
       // Comparing the same value.
-      simplifyIfWithKnownCondition(code, block, theIf, theIf.targetFromCondition(0));
+      simplifyIfWithKnownCondition(block, theIf, theIf.targetFromCondition(0));
       return true;
     }
 
@@ -325,7 +321,7 @@
         && rhsRoot.isDefinedByInstructionSatisfying(Instruction::isCreatingInstanceOrArray)) {
       // Comparing two newly created objects.
       assert theIf.getType() == IfType.EQ || theIf.getType() == IfType.NE;
-      simplifyIfWithKnownCondition(code, block, theIf, theIf.targetFromCondition(1));
+      simplifyIfWithKnownCondition(block, theIf, theIf.targetFromCondition(1));
       return true;
     }
 
@@ -334,7 +330,7 @@
       ConstNumber left = lhsRoot.getConstInstruction().asConstNumber();
       ConstNumber right = rhsRoot.getConstInstruction().asConstNumber();
       BasicBlock target = theIf.targetFromCondition(left, right);
-      simplifyIfWithKnownCondition(code, block, theIf, target);
+      simplifyIfWithKnownCondition(block, theIf, target);
       return true;
     }
 
@@ -349,7 +345,7 @@
             rhsAbstractValue.asConstantOrNonConstantNumberValue();
         if (!lhsNumberValue.mayOverlapWith(rhsNumberValue)) {
           // No overlap.
-          simplifyIfWithKnownCondition(code, block, theIf, 1);
+          simplifyIfWithKnownCondition(block, theIf, 1);
           return true;
         }
       }
@@ -364,7 +360,7 @@
       if (!leftRange.overlapsWith(rightRange)) {
         // No overlap.
         int cond = Long.signum(leftRange.getMin() - rightRange.getMin());
-        simplifyIfWithKnownCondition(code, block, theIf, cond);
+        simplifyIfWithKnownCondition(block, theIf, cond);
         return true;
       }
 
@@ -376,7 +372,7 @@
           // [a, b] >= [c, d] is always true when a == d.
           // In both cases 0 condition will choose the right branch.
           if (leftRange.getMin() == rightRange.getMax()) {
-            simplifyIfWithKnownCondition(code, block, theIf, 0);
+            simplifyIfWithKnownCondition(block, theIf, 0);
             return true;
           }
           break;
@@ -386,7 +382,7 @@
           // [a, b] <= [c, d] is always true when b == c.
           // In both cases 0 condition will choose the right branch.
           if (leftRange.getMax() == rightRange.getMin()) {
-            simplifyIfWithKnownCondition(code, block, theIf, 0);
+            simplifyIfWithKnownCondition(block, theIf, 0);
             return true;
           }
           break;
@@ -407,7 +403,6 @@
           SingleConstClassValue otherSingleConstClassValue =
               otherAbstractValue.asSingleConstClassValue();
           simplifyIfWithKnownCondition(
-              code,
               block,
               theIf,
               BooleanUtils.intValue(
@@ -423,7 +418,7 @@
           SingleFieldValue singleFieldValue = abstractValue.asSingleFieldValue();
           SingleFieldValue otherSingleFieldValue = otherAbstractValue.asSingleFieldValue();
           if (singleFieldValue.getField() == otherSingleFieldValue.getField()) {
-            simplifyIfWithKnownCondition(code, block, theIf, 0);
+            simplifyIfWithKnownCondition(block, theIf, 0);
             return true;
           }
 
@@ -434,7 +429,7 @@
             DexEncodedField otherField =
                 otherSingleFieldValue.getField().lookupOnClass(otherHolder);
             if (otherField != null && otherField.isEnum()) {
-              simplifyIfWithKnownCondition(code, block, theIf, 1);
+              simplifyIfWithKnownCondition(block, theIf, 1);
               return true;
             }
           }
@@ -445,15 +440,14 @@
     return false;
   }
 
-  private void simplifyIfWithKnownCondition(
-      IRCode code, BasicBlock block, If theIf, BasicBlock target) {
+  private void simplifyIfWithKnownCondition(BasicBlock block, If theIf, BasicBlock target) {
     BasicBlock deadTarget =
         target == theIf.getTrueTarget() ? theIf.fallthroughBlock() : theIf.getTrueTarget();
-    rewriteIfToGoto(code, block, theIf, target, deadTarget);
+    rewriteIfToGoto(block, theIf, target, deadTarget);
   }
 
-  private void simplifyIfWithKnownCondition(IRCode code, BasicBlock block, If theIf, int cond) {
-    simplifyIfWithKnownCondition(code, block, theIf, theIf.targetFromCondition(cond));
+  private void simplifyIfWithKnownCondition(BasicBlock block, If theIf, int cond) {
+    simplifyIfWithKnownCondition(block, theIf, theIf.targetFromCondition(cond));
   }
 
   /* Identify simple diamond shapes converting boolean true/false to 1/0. We consider the forms:
@@ -547,7 +541,7 @@
           // If all phis were removed, there is no need for the diamond shape anymore
           // and it can be rewritten to a goto to one of the branches.
           if (deadPhis == targetBlock.getPhis().size()) {
-            rewriteIfToGoto(code, block, theIf, trueBlock, falseBlock);
+            rewriteIfToGoto(block, theIf, trueBlock, falseBlock);
             return true;
           }
           return deadPhis > 0;
@@ -594,15 +588,15 @@
   }
 
   private void rewriteIfToGoto(
-      IRCode code, BasicBlock block, If theIf, BasicBlock target, BasicBlock deadTarget) {
+      BasicBlock block, If theIf, BasicBlock target, BasicBlock deadTarget) {
     deadTarget.unlinkSinglePredecessorSiblingsAllowed();
     assert theIf == block.exit();
-    block.replaceLastInstruction(new Goto(), code);
+    block.replaceLastInstruction(new Goto());
     assert block.exit().isGoto();
     assert block.exit().asGoto().getTarget() == target;
   }
 
-  private boolean rewriteIfWithConstZero(IRCode code, BasicBlock block) {
+  private boolean rewriteIfWithConstZero(BasicBlock block) {
     If theIf = block.exit().asIf();
     if (theIf.isZeroTest()) {
       return false;
@@ -614,13 +608,13 @@
       if (leftValue.isConstNumber()) {
         if (leftValue.getConstInstruction().asConstNumber().isZero()) {
           If ifz = new If(theIf.getType().forSwappedOperands(), rightValue);
-          block.replaceLastInstruction(ifz, code);
+          block.replaceLastInstruction(ifz);
           assert block.exit() == ifz;
           return true;
         }
       } else if (rightValue.getConstInstruction().asConstNumber().isZero()) {
         If ifz = new If(theIf.getType(), leftValue);
-        block.replaceLastInstruction(ifz, code);
+        block.replaceLastInstruction(ifz);
         assert block.exit() == ifz;
         return true;
       }
@@ -629,7 +623,7 @@
     return false;
   }
 
-  private boolean rewriteIfWithObjectsIsNullOrNonNull(IRCode code, BasicBlock block) {
+  private boolean rewriteIfWithObjectsIsNullOrNonNull(BasicBlock block) {
     If theIf = block.exit().asIf();
     if (!theIf.isZeroTest() || !theIf.getType().isEqualsOrNotEquals()) {
       return false;
@@ -641,18 +635,18 @@
       DexMethod invokedMethod = invoke.getInvokedMethod();
       if (invokedMethod.isIdenticalTo(dexItemFactory.objectsMethods.isNull)) {
         If ifz = new If(theIf.getType().inverted(), invoke.getFirstArgument());
-        block.replaceLastInstruction(ifz, code);
+        block.replaceLastInstruction(ifz);
         return true;
       } else if (invokedMethod.isIdenticalTo(dexItemFactory.objectsMethods.nonNull)) {
         If ifz = new If(theIf.getType(), invoke.getFirstArgument());
-        block.replaceLastInstruction(ifz, code);
+        block.replaceLastInstruction(ifz);
         return true;
       }
     }
     return false;
   }
 
-  private boolean flipIfBranchesIfNeeded(IRCode code, BasicBlock block) {
+  private boolean flipIfBranchesIfNeeded(BasicBlock block) {
     If theIf = block.exit().asIf();
     BasicBlock trueTarget = theIf.getTrueTarget();
     BasicBlock fallthrough = theIf.fallthroughBlock();
@@ -668,7 +662,7 @@
     // on older Android versions.
     List<Value> inValues = theIf.inValues();
     If newIf = new If(theIf.getType().inverted(), inValues);
-    block.replaceLastInstruction(newIf, code);
+    block.replaceLastInstruction(newIf);
     block.swapSuccessors(trueTarget, fallthrough);
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/KotlinInlineMarkerRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/KotlinInlineMarkerRewriter.java
index 34c0451..909b114 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/KotlinInlineMarkerRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/KotlinInlineMarkerRewriter.java
@@ -51,6 +51,9 @@
         }
       }
     }
+    if (changed) {
+      code.removeRedundantBlocks();
+    }
     return CodeRewriterResult.hasChanged(changed);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/NaturalIntLoopRemover.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/NaturalIntLoopRemover.java
index 69fb225..f72c2c5 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/NaturalIntLoopRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/NaturalIntLoopRemover.java
@@ -48,7 +48,7 @@
     AffectedValues affectedValues = new AffectedValues();
     for (BasicBlock comparisonBlockCandidate : code.blocks) {
       if (isComparisonBlock(comparisonBlockCandidate)) {
-        loopRemoved |= tryRemoveLoop(code, comparisonBlockCandidate.exit().asIf(), affectedValues);
+        loopRemoved |= tryRemoveLoop(comparisonBlockCandidate.exit().asIf(), affectedValues);
       }
     }
     if (loopRemoved) {
@@ -84,7 +84,7 @@
     throw new Unreachable();
   }
 
-  private boolean tryRemoveLoop(IRCode code, If comparison, AffectedValues affectedValues) {
+  private boolean tryRemoveLoop(If comparison, AffectedValues affectedValues) {
     Phi loopPhi = computeLoopPhi(comparison);
     if (loopPhi == null) {
       return false;
@@ -117,7 +117,7 @@
     NaturalIntLoopWithKnowIterations loop = builder.build();
 
     if (loop.has1Iteration()) {
-      loop.remove1IterationLoop(code, affectedValues);
+      loop.remove1IterationLoop(affectedValues);
       return true;
     }
     return false;
@@ -405,19 +405,19 @@
           && target(initCounter + counterIncrement) == loopExit;
     }
 
-    private void remove1IterationLoop(IRCode code, AffectedValues affectedValues) {
+    private void remove1IterationLoop(AffectedValues affectedValues) {
       BasicBlock comparisonBlock = comparison.getBlock();
       updatePhis(comparisonBlock, affectedValues);
-      patchControlFlow(code, comparisonBlock);
+      patchControlFlow(comparisonBlock);
     }
 
-    private void patchControlFlow(IRCode code, BasicBlock comparisonBlock) {
+    private void patchControlFlow(BasicBlock comparisonBlock) {
       assert loopExit.getPhis().isEmpty(); // Edges should be split.
-      comparisonBlock.replaceLastInstruction(new Goto(), code);
+      comparisonBlock.replaceLastInstruction(new Goto());
       comparisonBlock.removeSuccessor(loopExit);
 
       backPredecessor.replaceSuccessor(comparisonBlock, loopExit);
-      backPredecessor.replaceLastInstruction(new Goto(), code);
+      backPredecessor.replaceLastInstruction(new Goto());
       comparisonBlock.removePredecessor(backPredecessor);
       loopExit.replacePredecessor(comparisonBlock, backPredecessor);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/ThrowCatchOptimizer.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/ThrowCatchOptimizer.java
index 23a66a3..4e6eb88 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/ThrowCatchOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/ThrowCatchOptimizer.java
@@ -361,7 +361,9 @@
     }
     if (hasUnlinkedCatchHandlers) {
       code.removeUnreachableBlocks();
+      code.removeRedundantBlocks();
     }
+    assert code.isConsistentSSA(appView);
   }
 
   private boolean isSingleHandlerTrivial(BasicBlock firstBlock, IRCode code) {
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 3438304..9629859 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
@@ -180,6 +180,7 @@
         }
       }
     }
+    code.removeRedundantBlocks();
     assert code.isConsistentSSA(appView);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
index db5c9dc..4633083 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
@@ -617,9 +617,7 @@
     assert insertionPoint.isPhi() || instructionIterator.peekPrevious() == insertionPoint;
     newInstruction.setPosition(
         getPositionForCanonicalizationConstantAtInsertionPoint(insertionPoint, newInstruction));
-    if (newInstruction.instructionTypeCanThrow()
-        && insertionPoint.getBlock().hasCatchHandlers()
-        && insertionPoint.getBlock().canThrow()) {
+    if (newInstruction.instructionTypeCanThrow() && insertionPoint.getBlock().hasCatchHandlers()) {
       // Split the block and rewind the block iterator to the insertion block.
       BasicBlock splitBlock =
           instructionIterator.splitCopyCatchHandlers(
@@ -638,7 +636,6 @@
           assert !splitBlock.canThrow();
           splitBlock.listIterator().add(newInstruction);
         } else {
-          assert splitBlock.canThrow();
           instructionIterator.addBeforeAndPositionBeforeNewInstruction(newInstruction);
         }
         instructionIterator.positionAfterPreviousInstruction(insertionPoint.asInstruction());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RuntimeWorkaroundCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/RuntimeWorkaroundCodeRewriter.java
index 2262154..0756ee0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RuntimeWorkaroundCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RuntimeWorkaroundCodeRewriter.java
@@ -294,12 +294,12 @@
 
   // See comment for InternalOptions.canHaveNumberConversionRegisterAllocationBug().
   public static void workaroundNumberConversionRegisterAllocationBug(
-      IRCode code, InternalOptions options) {
-    if (!options.canHaveNumberConversionRegisterAllocationBug()) {
+      AppView<?> appView, IRCode code) {
+    if (!appView.options().canHaveNumberConversionRegisterAllocationBug()) {
       return;
     }
 
-    DexItemFactory dexItemFactory = options.dexItemFactory();
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
     ListIterator<BasicBlock> blocks = code.listIterator();
     while (blocks.hasNext()) {
       BasicBlock block = blocks.next();
@@ -345,6 +345,8 @@
         }
       }
     }
+    code.removeRedundantBlocks();
+    assert code.isConsistentSSA(appView);
   }
 
   private static void ensureInstructionBefore(
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index 4417b2a..ac6868b 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -155,6 +155,9 @@
         }
       }
     }
+    if (result.hasChanged().isPossiblyTrue()) {
+      code.removeRedundantBlocks();
+    }
     return result;
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
index 550453a..d558a57 100644
--- a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
@@ -128,7 +128,6 @@
     }
 
     public String run() throws IOException {
-      Timing timing = Timing.empty();
       IRConverter converter = new IRConverter(appView);
       code.removeRedundantBlocks();
       converter.replaceCodeForTesting(code);
diff --git a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
index a5032c9..4db7196 100644
--- a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
+++ b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
@@ -99,11 +100,11 @@
       assertEquals(argumentInstructions, test.countArgumentInstructions());
       assertEquals(firstBlockInstructions, block.getInstructions().size());
       InstructionList instructionList = block.getInstructions();
-      assertTrue(!instructionList.getNth(i).isArgument());
+      assertFalse(instructionList.getNth(i).isArgument());
 
       InstructionListIterator iterator = test.listIteratorAt(block, i);
       BasicBlock newBlock = iterator.split(code);
-      assertTrue(code.isConsistentSSA(appView));
+      assertTrue(code.isConsistentSSAAllowingRedundantBlocks(appView));
 
       assertEquals(initialBlockCount + 1, code.blocks.size());
       assertEquals(i + 1, code.entryBlock().getInstructions().size());
@@ -135,11 +136,11 @@
       assertEquals(argumentInstructions, test.countArgumentInstructions());
       assertEquals(firstBlockInstructions, block.getInstructions().size());
       InstructionList instructionList = block.getInstructions();
-      assertTrue(!instructionList.getNth(i).isArgument());
+      assertFalse(instructionList.getNth(i).isArgument());
 
       InstructionListIterator iterator = test.listIteratorAt(block, i);
       BasicBlock newBlock = iterator.split(code, 1);
-      assertTrue(code.isConsistentSSA(appView));
+      assertTrue(code.isConsistentSSAAllowingRedundantBlocks(appView));
 
       assertEquals(initialBlockCount + 2, code.blocks.size());
       assertEquals(i + 1, code.entryBlock().getInstructions().size());
@@ -341,11 +342,11 @@
       assertEquals(argumentInstructions, test.countArgumentInstructions());
       assertEquals(firstBlockInstructions, block.getInstructions().size());
       InstructionList instructionList = block.getInstructions();
-      assertTrue(!instructionList.getNth(i).isArgument());
+      assertFalse(instructionList.getNth(i).isArgument());
 
       InstructionListIterator iterator = test.listIteratorAt(block, i);
       BasicBlock newBlock = iterator.split(code);
-      assertTrue(code.isConsistentSSA(appView));
+      assertTrue(code.isConsistentSSAAllowingRedundantBlocks(appView));
 
       assertEquals(initialBlockCount + 1, code.blocks.size());
       assertEquals(i + 1, code.entryBlock().getInstructions().size());
@@ -464,11 +465,11 @@
       assertEquals(argumentInstructions, test.countArgumentInstructions());
       assertEquals(firstBlockInstructions, block.getInstructions().size());
       InstructionList instructionList = block.getInstructions();
-      assertTrue(!instructionList.getNth(i).isArgument());
+      assertFalse(instructionList.getNth(i).isArgument());
 
       InstructionListIterator iterator = test.listIteratorAt(block, i);
       BasicBlock newBlock = iterator.split(code);
-      assertTrue(code.isConsistentSSA(appView));
+      assertTrue(code.isConsistentSSAAllowingRedundantBlocks(appView));
 
       assertEquals(initialBlockCount + 1, code.blocks.size());
       assertEquals(i + 1, code.entryBlock().getInstructions().size());
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
index da53eb3..b86c119 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -75,7 +75,7 @@
               long paramNullCheckCount =
                   countCall(testMethod, "Intrinsics", "checkParameterIsNotNull");
               // One after Iterator#hasNext, and another in the filter predicate: sinceYear != null.
-              assertEquals(2, ifzCount);
+              assertEquals(testParameters.isCfRuntime() ? 5 : 2, ifzCount);
               assertEquals(0, paramNullCheckCount);
             });
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
index ad41db7..81f6750 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
@@ -106,12 +106,6 @@
         inspector
             .assertIsCompleteMergeGroup(
                 lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testFirst$1"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testFirst$2"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testFirst$3"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
                     getTestName(), "MainKt$testFirst$4"),
                 lambdasInInput.getKStyleLambdaReferenceFromTypeName(
                     getTestName(), "MainKt$testFirst$5"),
@@ -124,12 +118,6 @@
                 lambdasInInput.getKStyleLambdaReferenceFromTypeName(
                     getTestName(), "MainKt$testFirst$9"),
                 lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testSecond$1"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testSecond$2"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testSecond$3"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
                     getTestName(), "MainKt$testSecond$4"),
                 lambdasInInput.getKStyleLambdaReferenceFromTypeName(
                     getTestName(), "MainKt$testSecond$5"),
diff --git a/src/test/java/com/android/tools/r8/repackage/MappingFileAfterRepackagingTest.java b/src/test/java/com/android/tools/r8/repackage/MappingFileAfterRepackagingTest.java
index 1464cff..2c53eb2 100644
--- a/src/test/java/com/android/tools/r8/repackage/MappingFileAfterRepackagingTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/MappingFileAfterRepackagingTest.java
@@ -59,14 +59,18 @@
                               line.contains(
                                   "java.lang.String MappingFileAfterRepackagingTest$A.toString()"))
                       .count();
-              assertEquals(repackage ? 2 : 0, syntheticMatches);
+              assertEquals(
+                  repackage ? 1 + BooleanUtils.intValue(parameters.isDexRuntime()) : 0,
+                  syntheticMatches);
 
               long unqualifiedMatches =
                   StringUtils.splitLines(runResult.proguardMap()).stream()
                       .filter(line -> line.contains("java.lang.String toString()"))
                       .count();
               assertEquals(
-                  (repackage ? 1 : 3) + BooleanUtils.intValue(isPc2pc()), unqualifiedMatches);
+                  (repackage ? 1 : 2 + BooleanUtils.intValue(parameters.isDexRuntime()))
+                      + BooleanUtils.intValue(isPc2pc()),
+                  unqualifiedMatches);
             });
   }
 
@@ -78,7 +82,7 @@
   static class Main {
 
     public static void main(String[] args) {
-      System.out.println((System.currentTimeMillis() > 0 ? new A() : new B()));
+      System.out.println(System.currentTimeMillis() > 0 ? new A() : new B());
     }
   }