Split return rewriter pass

Change-Id: Ib06bb796b6445dca1d6e359669ea0a59b6c498bb
diff --git a/src/main/java/com/android/tools/r8/cf/LoadStoreHelper.java b/src/main/java/com/android/tools/r8/cf/LoadStoreHelper.java
index 0081d41..64dee4d 100644
--- a/src/main/java/com/android/tools/r8/cf/LoadStoreHelper.java
+++ b/src/main/java/com/android/tools/r8/cf/LoadStoreHelper.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
 import com.android.tools.r8.ir.code.ConstClass;
 import com.android.tools.r8.ir.code.ConstInstruction;
 import com.android.tools.r8.ir.code.ConstNumber;
@@ -25,7 +26,6 @@
 import java.util.ArrayList;
 import java.util.IdentityHashMap;
 import java.util.List;
-import java.util.ListIterator;
 import java.util.Map;
 
 public class LoadStoreHelper {
@@ -34,8 +34,12 @@
   private final IRCode code;
   private final TypeVerificationHelper typesHelper;
 
-  private Map<Value, ConstInstruction> clonableConstants = null;
-  private ListIterator<BasicBlock> blockIterator = null;
+  private Map<Value, ConstInstruction> clonableConstants;
+
+  // The active block and instruction iterator used by the pass. These are stored in fields to avoid
+  // needing to pass them back and forth through Instruction#insertLoadAndStores.
+  private BasicBlockIterator blockIterator;
+  private InstructionListIterator instructionIterator;
 
   public LoadStoreHelper(AppView<?> appView, IRCode code, TypeVerificationHelper typesHelper) {
     this.appView = appView;
@@ -99,9 +103,11 @@
     clonableConstants = new IdentityHashMap<>();
     blockIterator = code.listIterator();
     while (blockIterator.hasNext()) {
-      InstructionListIterator it = blockIterator.next().listIterator();
-      while (it.hasNext()) {
-        it.next().insertLoadAndStores(it, this);
+      BasicBlock block = blockIterator.next();
+      instructionIterator = block.listIterator();
+      while (instructionIterator.hasNext()) {
+        Instruction instruction = instructionIterator.next();
+        instruction.insertLoadAndStores(this);
       }
       clonableConstants.clear();
     }
@@ -128,10 +134,10 @@
               moves.add(new PhiMove(phi, value));
             }
           }
-          InstructionListIterator it = pred.listIterator(pred.getInstructions().size());
-          Instruction exit = it.previous();
+          instructionIterator = pred.listIterator(pred.getInstructions().size());
+          Instruction exit = instructionIterator.previous();
           assert pred.exit() == exit;
-          movePhis(moves, it, exit.getPosition());
+          movePhis(moves, exit.getPosition());
         }
         allocator.addToLiveAtEntrySet(block, block.getPhis());
       }
@@ -147,9 +153,9 @@
     return StackValue.create(typesHelper.createInitializedType(type), height, appView);
   }
 
-  public void loadInValues(Instruction instruction, InstructionListIterator it) {
+  public void loadInValues(Instruction instruction) {
     int topOfStack = 0;
-    it.previous();
+    instructionIterator.previous();
     for (int i = 0; i < instruction.inValues().size(); i++) {
       Value value = instruction.inValues().get(i);
       StackValue stackValue = createStackValue(value, topOfStack++);
@@ -158,26 +164,25 @@
       if (constInstruction != null) {
         ConstInstruction clonedConstInstruction =
             ConstInstruction.copyOf(stackValue, constInstruction);
-        add(clonedConstInstruction, instruction, it);
+        add(clonedConstInstruction, instruction);
       } else {
-        add(load(stackValue, value), instruction, it);
+        add(load(stackValue, value), instruction);
       }
       instruction.replaceValue(i, stackValue);
     }
-    it.next();
+    instructionIterator.next();
   }
 
-  public void storeOrPopOutValue(
-      DexType type, Instruction instruction, InstructionListIterator it) {
+  public void storeOrPopOutValue(DexType type, Instruction instruction) {
     if (instruction.hasOutValue()) {
       assert instruction.outValue().isUsed();
-      storeOutValue(instruction, it);
+      storeOutValue(instruction);
     } else {
-      popOutType(type, instruction, it);
+      popOutType(type, instruction);
     }
   }
 
-  public void storeOutValue(Instruction instruction, InstructionListIterator it) {
+  public void storeOutValue(Instruction instruction) {
     assert instruction.hasOutValue();
     assert !(instruction.outValue() instanceof StackValue);
     if (instruction.isConstInstruction()) {
@@ -187,7 +192,7 @@
             || constInstruction.outValue().numberOfUsers() == 1;
         clonableConstants.put(instruction.outValue(), constInstruction);
         instruction.outValue().clearUsers();
-        it.removeOrReplaceByDebugLocalRead();
+        instructionIterator.removeOrReplaceByDebugLocalRead();
         return;
       }
       assert instruction.outValue().isUsed()
@@ -195,7 +200,7 @@
           : "Expected instruction to be removed: " + instruction;
     }
     if (!instruction.outValue().isUsed()) {
-      popOutValue(instruction.outValue(), instruction, it);
+      popOutValue(instruction.outValue(), instruction);
       return;
     }
     StackValue newOutValue = createStackValue(instruction.outValue(), 0);
@@ -208,41 +213,40 @@
     // instruction is throwing, the action should be moved to a new block - otherwise, the store
     // should be inserted and the remaining instructions should be moved along with the handlers to
     // the new block.
-    boolean hasCatchHandlers = instruction.getBlock().hasCatchHandlers();
+    boolean hasCatchHandlers = storeBlock.hasCatchHandlers();
     if (hasCatchHandlers && instruction.instructionTypeCanThrow()) {
-      storeBlock = it.split(this.code, this.blockIterator);
-      it = storeBlock.listIterator();
+      storeBlock = instructionIterator.split(this.code, this.blockIterator);
+      instructionIterator = storeBlock.listIterator();
     }
-    add(store, instruction.getPosition(), it);
+    add(store, instruction.getPosition());
     if (hasCatchHandlers && !instruction.instructionTypeCanThrow()) {
-      splitAfterStoredOutValue(it);
+      splitAfterStoredOutValue();
     }
   }
 
   // DebugLocalWrite encodes a store and it needs to consistently split out the catch range after
   // its store.
-  public void splitAfterStoredOutValue(InstructionListIterator it) {
-    it.split(this.code, this.blockIterator);
-    this.blockIterator.previous();
+  public void splitAfterStoredOutValue() {
+    instructionIterator.split(this.code, this.blockIterator);
+    blockIterator.previous();
   }
 
-  public void popOutType(DexType type, Instruction instruction, InstructionListIterator it) {
-    popOutValue(createStackValue(type, 0), instruction, it);
+  public void popOutType(DexType type, Instruction instruction) {
+    popOutValue(createStackValue(type, 0), instruction);
   }
 
-  private void popOutValue(Value value, Instruction instruction, InstructionListIterator it) {
-    popOutValue(createStackValue(value, 0), instruction, it);
+  private void popOutValue(Value value, Instruction instruction) {
+    popOutValue(createStackValue(value, 0), instruction);
   }
 
-  private void popOutValue(
-      StackValue newOutValue, Instruction instruction, InstructionListIterator it) {
+  private void popOutValue(StackValue newOutValue, Instruction instruction) {
     BasicBlock insertBlock = instruction.getBlock();
     if (insertBlock.hasCatchHandlers() && instruction.instructionTypeCanThrow()) {
-      insertBlock = it.split(this.code, this.blockIterator);
-      it = insertBlock.listIterator();
+      insertBlock = instructionIterator.split(this.code, this.blockIterator);
+      instructionIterator = insertBlock.listIterator();
     }
     instruction.swapOutValue(newOutValue);
-    add(new Pop(newOutValue), instruction.getPosition(), it);
+    add(new Pop(newOutValue), instruction.getPosition());
   }
 
   private static class PhiMove {
@@ -255,13 +259,13 @@
     }
   }
 
-  private void movePhis(List<PhiMove> moves, InstructionListIterator it, Position position) {
+  private void movePhis(List<PhiMove> moves, Position position) {
     // TODO(zerny): Accounting for non-interfering phis would lower the max stack size.
     int topOfStack = 0;
     List<StackValue> temps = new ArrayList<>(moves.size());
     for (PhiMove move : moves) {
       StackValue tmp = createStackValue(move.phi, topOfStack++);
-      add(load(tmp, move.operand), position, it);
+      add(load(tmp, move.operand), position);
       temps.add(tmp);
       move.operand.removePhiUser(move.phi);
     }
@@ -269,7 +273,7 @@
       PhiMove move = moves.get(i);
       StackValue tmp = temps.get(i);
       FixedLocalValue out = new FixedLocalValue(move.phi);
-      add(new Store(out, tmp), position, it);
+      add(new Store(out, tmp), position);
       move.phi.replaceUsers(out);
     }
   }
@@ -294,14 +298,12 @@
     return new Load(stackValue, value);
   }
 
-  private static void add(
-      Instruction newInstruction, Instruction existingInstruction, InstructionListIterator it) {
-    add(newInstruction, existingInstruction.getPosition(), it);
+  private void add(Instruction newInstruction, Instruction existingInstruction) {
+    add(newInstruction, existingInstruction.getPosition());
   }
 
-  private static void add(
-      Instruction newInstruction, Position position, InstructionListIterator it) {
+  private void add(Instruction newInstruction, Position position) {
     newInstruction.setPosition(position);
-    it.add(newInstruction);
+    instructionIterator.add(newInstruction);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingDefinition.java b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingDefinition.java
index 020db76..3a544aa 100644
--- a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingDefinition.java
+++ b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingDefinition.java
@@ -77,7 +77,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     throw new Unreachable();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java
index cd4d6a4..8bfdfcf 100644
--- a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java
@@ -73,7 +73,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     // Nothing to do.
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingUser.java b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingUser.java
index a5cf0e4..3f99c84 100644
--- a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingUser.java
+++ b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingUser.java
@@ -74,7 +74,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     throw new Unreachable();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Argument.java b/src/main/java/com/android/tools/r8/ir/code/Argument.java
index bebe521..d759235 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Argument.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Argument.java
@@ -133,7 +133,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     // Arguments are defined by locals so nothing to load or store.
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
index 1f6ea43..865d83b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -176,9 +176,9 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
index ae761d1..ba579de 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -111,9 +111,9 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index 9419bef..c96f6f6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -238,8 +238,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java
index 279d1b9..e29df9f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Assume.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -214,7 +214,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     throw new Unreachable(ERROR_MESSAGE);
   }
 
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 9bc7905..7afef24 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
@@ -1175,7 +1175,7 @@
   public boolean consistentCatchHandlers() {
     // Check that catch handlers are always the first successors of a block.
     if (hasCatchHandlers()) {
-      assert exit().isGoto() || exit().isThrow();
+      assert exit().isGoto() || exit().isReturn() || exit().isThrow();
       CatchHandlers<Integer> catchHandlers = getCatchHandlersWithSuccessorIndexes();
       // Check that guards are unique.
       assert catchHandlers.getGuards().size()
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 4bd1624..4bccf15 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
@@ -24,6 +24,7 @@
 import com.android.tools.r8.ir.optimize.NestUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.IteratorUtils;
+import com.android.tools.r8.utils.ListUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
@@ -1010,6 +1011,34 @@
 
   private InstructionListIterator ensureSingleReturnInstruction(
       AppView<?> appView, IRCode code, List<BasicBlock> normalExits) {
+    // First ensure that there will be not critical edges after inlining. This is needed since
+    // return blocks are allowed to have catch handlers.
+    if (Iterables.any(normalExits, BasicBlock::hasCatchHandlers)) {
+      normalExits =
+          ListUtils.map(
+              normalExits,
+              exitBlock -> {
+                if (!exitBlock.hasCatchHandlers()) {
+                  return exitBlock;
+                }
+                Return exit = exitBlock.exit().asReturn();
+
+                // Create new exit block.
+                BasicBlock newExitBlock = new BasicBlock(code.metadata());
+                newExitBlock.setNumber(code.getNextBlockNumber());
+                Return newReturn =
+                    exit.isReturnVoid() ? new Return() : new Return(exit.returnValue());
+                newReturn.setPosition(exit.getPosition());
+                newExitBlock.add(newReturn, code.metadata());
+
+                // Fixup old exit.
+                exit.replace(new Goto());
+                exitBlock.link(newExitBlock);
+                newExitBlock.close(null);
+                code.blocks.add(newExitBlock);
+                return newExitBlock;
+              });
+    }
     if (normalExits.size() == 1) {
       InstructionListIterator it = normalExits.get(0).listIterator();
       it.nextUntil(Instruction::isReturn);
diff --git a/src/main/java/com/android/tools/r8/ir/code/Binop.java b/src/main/java/com/android/tools/r8/ir/code/Binop.java
index 12cbc36..207024e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Binop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Binop.java
@@ -137,9 +137,9 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index 9845028..1b1d55c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -249,9 +249,9 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
index fe1c8b3..5273534 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -188,8 +188,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
index 54cca22..514e31f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
@@ -136,8 +136,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
index cd799fe..5f7f2c5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
@@ -128,8 +128,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index 95df669..124b3f7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -166,8 +166,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
index 0b2b5e1..590fa00 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -152,8 +152,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
index 3e8e999..8b3b71f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
@@ -90,7 +90,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     // Non-materializing so no stack values are needed.
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
index 7304858..ea4ef1f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
@@ -64,12 +64,12 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
     // A local-write does not have an outgoing stack value, but in writes directly to the local.
     assert !instructionTypeCanThrow();
     if (getBlock().hasCatchHandlers()) {
-      helper.splitAfterStoredOutValue(it);
+      helper.splitAfterStoredOutValue();
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
index 5852d5e..a8291d6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
@@ -123,7 +123,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     throw new Unreachable();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
index 090040a..174392a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
@@ -74,7 +74,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     // Nothing to do for positions which are not actual instructions.
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
index 6b3072f..d1bea5b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
@@ -144,8 +144,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Dup.java b/src/main/java/com/android/tools/r8/ir/code/Dup.java
index 41dea29..071839b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Dup.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Dup.java
@@ -103,7 +103,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     // Intentionally empty. Dup is a stack operation.
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Dup2.java b/src/main/java/com/android/tools/r8/ir/code/Dup2.java
index eebb405..c05a8af 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Dup2.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Dup2.java
@@ -117,7 +117,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     // Intentionally empty. Dup2 is a stack operation.
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Goto.java b/src/main/java/com/android/tools/r8/ir/code/Goto.java
index 3c5a357..6bb4e8e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Goto.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Goto.java
@@ -84,7 +84,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     // Nothing to do.
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/If.java b/src/main/java/com/android/tools/r8/ir/code/If.java
index fac1512..e19b240 100644
--- a/src/main/java/com/android/tools/r8/ir/code/If.java
+++ b/src/main/java/com/android/tools/r8/ir/code/If.java
@@ -243,8 +243,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Inc.java b/src/main/java/com/android/tools/r8/ir/code/Inc.java
index 3b51d7d..37af806 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Inc.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Inc.java
@@ -71,7 +71,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     // Inc is inserted after load/store insertion, after register allocation.
     throw new Unreachable();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InitClass.java b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
index c9039c1..91df093 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InitClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
@@ -164,8 +164,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index 18401ac..90bf26a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -210,9 +210,9 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
index 19b2929..0ea0546 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
@@ -107,9 +107,9 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index d54a445..4e3f1ce 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -250,8 +250,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index f9072c0..9da772e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -1567,7 +1567,7 @@
   public abstract ConstraintWithTarget inliningConstraint(
       InliningConstraints inliningConstraints, ProgramMethod context);
 
-  public abstract void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper);
+  public abstract void insertLoadAndStores(LoadStoreHelper helper);
 
   public DexType computeVerificationType(AppView<?> appView, TypeVerificationHelper helper) {
     assert outValue == null || !outValue.getType().isReferenceType();
diff --git a/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java b/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java
index de5ce5a..8c2287b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java
@@ -285,8 +285,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index 6aa12e5..b4dc0ec 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -317,5 +317,5 @@
    * Subclasses must implement load and store handling and make sure to deal with a null out-value
    */
   @Override
-  public abstract void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper);
+  public abstract void insertLoadAndStores(LoadStoreHelper helper);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index a3f258c..7e03629 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -187,14 +187,14 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     // Essentially the same as InvokeMethod but with call site's method proto
     // instead of a static called method.
-    helper.loadInValues(this, it);
+    helper.loadInValues(this);
     if (getCallSite().methodProto.returnType.isVoidType()) {
       return;
     }
-    helper.storeOrPopOutValue(getCallSite().methodProto.returnType, this, it);
+    helper.storeOrPopOutValue(getCallSite().methodProto.returnType, this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index c1a5688..e15b67c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -265,12 +265,12 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
     if (getReturnType().isVoidType()) {
       return;
     }
-    helper.storeOrPopOutValue(getReturnType(), this, it);
+    helper.storeOrPopOutValue(getReturnType(), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
index b4256d2..bb1400e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
@@ -101,9 +101,9 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
-    helper.storeOrPopOutValue(type, this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
+    helper.storeOrPopOutValue(type, this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Load.java b/src/main/java/com/android/tools/r8/ir/code/Load.java
index ac3865d..d9d2c52 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Load.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Load.java
@@ -91,7 +91,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     // Nothing to do. This is only hit because loads and stores are insert for phis.
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Monitor.java b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
index 430f8a4..0982912 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Monitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
@@ -120,8 +120,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Move.java b/src/main/java/com/android/tools/r8/ir/code/Move.java
index a1c5475..24a777f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Move.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Move.java
@@ -121,7 +121,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     throw new Unreachable(ERROR_MESSAGE);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/MoveException.java b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
index 4d73577..9d12798 100644
--- a/src/main/java/com/android/tools/r8/ir/code/MoveException.java
+++ b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
@@ -101,8 +101,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
index 1bd40d4..0eb198e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -155,9 +155,9 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilled.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilled.java
index 3b44907..bdaeb21 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilled.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilled.java
@@ -139,7 +139,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     throw cfUnsupported();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
index cd21fbc..33a0b7d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
@@ -114,7 +114,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     throw new Unreachable(ERROR_MESSAGE);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index f946940..a3f05e0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -125,8 +125,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
index cafceff..bc3d425 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
@@ -130,8 +130,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Not.java b/src/main/java/com/android/tools/r8/ir/code/Not.java
index 483aa87..2a98b81 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Not.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Not.java
@@ -96,7 +96,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     // JVM has no Not instruction, they should be replaced by "Load -1, Xor" before building CF.
     throw new Unreachable();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Pop.java b/src/main/java/com/android/tools/r8/ir/code/Pop.java
index 00554e8..92c7745 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Pop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Pop.java
@@ -121,7 +121,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     throw new Unreachable("This IR must not be inserted before load and store insertion.");
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/RecordFieldValues.java b/src/main/java/com/android/tools/r8/ir/code/RecordFieldValues.java
index 7ab1c92..0876c64 100644
--- a/src/main/java/com/android/tools/r8/ir/code/RecordFieldValues.java
+++ b/src/main/java/com/android/tools/r8/ir/code/RecordFieldValues.java
@@ -114,9 +114,9 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ResourceConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ResourceConstNumber.java
index d18a410..053384e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ResourceConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ResourceConstNumber.java
@@ -56,7 +56,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     throw new Unreachable("We never write cf code with resource numbers");
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Return.java b/src/main/java/com/android/tools/r8/ir/code/Return.java
index 4e89ab8..b263cb4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Return.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Return.java
@@ -58,6 +58,10 @@
     return !isReturnVoid();
   }
 
+  public Value getReturnValueOrNull() {
+    return hasReturnValue() ? returnValue() : null;
+  }
+
   public Value returnValue() {
     assert !isReturnVoid();
     return inValues.get(0);
@@ -129,13 +133,18 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     if (!isReturnVoid()) {
-      helper.loadInValues(this, it);
+      helper.loadInValues(this);
     }
   }
 
   @Override
+  public boolean isAllowedAfterThrowingInstruction() {
+    return true;
+  }
+
+  @Override
   public void buildCf(CfBuilder builder) {
     builder.add(
         isReturnVoid() ? new CfReturnVoid() : new CfReturn(ValueType.fromType(getReturnType())),
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index b7a26d9..aed2b5e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -213,8 +213,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index b201864..dc29a40 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -230,8 +230,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Store.java b/src/main/java/com/android/tools/r8/ir/code/Store.java
index 0ace979..552466d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Store.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Store.java
@@ -92,7 +92,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     throw new Unreachable("This IR must not be inserted before load and store insertion.");
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/StringSwitch.java b/src/main/java/com/android/tools/r8/ir/code/StringSwitch.java
index 506cb80..9968963 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StringSwitch.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StringSwitch.java
@@ -130,7 +130,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     throw new Unreachable();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Swap.java b/src/main/java/com/android/tools/r8/ir/code/Swap.java
index 3e826c8..097dda3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Swap.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Swap.java
@@ -98,7 +98,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     // Intentionally empty. Swap is a stack operation.
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Throw.java b/src/main/java/com/android/tools/r8/ir/code/Throw.java
index ab5db40..06bfeaa 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Throw.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Throw.java
@@ -83,8 +83,8 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/UninitializedThisLocalRead.java b/src/main/java/com/android/tools/r8/ir/code/UninitializedThisLocalRead.java
index 3561840..b01312c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/UninitializedThisLocalRead.java
+++ b/src/main/java/com/android/tools/r8/ir/code/UninitializedThisLocalRead.java
@@ -82,7 +82,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     // Non-materializing so no stack values are needed.
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Unop.java b/src/main/java/com/android/tools/r8/ir/code/Unop.java
index 9c65c4e..daf2e77 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Unop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Unop.java
@@ -52,9 +52,9 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
-    helper.loadInValues(this, it);
-    helper.storeOutValue(this, it);
+  public void insertLoadAndStores(LoadStoreHelper helper) {
+    helper.loadInValues(this);
+    helper.storeOutValue(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/UnusedArgument.java b/src/main/java/com/android/tools/r8/ir/code/UnusedArgument.java
index e9ded3f..bd527d6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/UnusedArgument.java
+++ b/src/main/java/com/android/tools/r8/ir/code/UnusedArgument.java
@@ -88,7 +88,7 @@
   }
 
   @Override
-  public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+  public void insertLoadAndStores(LoadStoreHelper helper) {
     throw new Unreachable();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPassCollection.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPassCollection.java
index a6bbd64..24ab29b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPassCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPassCollection.java
@@ -53,11 +53,14 @@
     passes.add(new BranchSimplifier(appView));
     passes.add(new SplitBranch(appView));
     passes.add(new RedundantConstNumberRemover(appView));
-    if (!appView.options().debug) {
+    if (appView.options().isRelease()) {
       passes.add(new RedundantFieldLoadAndStoreElimination(appView));
     }
     passes.add(new BinopRewriter(appView));
     passes.add(new ServiceLoaderRewriter(appView));
+    if (appView.options().isRelease()) {
+      passes.add(new SplitReturnRewriter(appView));
+    }
     return new CodeRewriterPassCollection(passes);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/SplitReturnRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/SplitReturnRewriter.java
new file mode 100644
index 0000000..21199d1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/SplitReturnRewriter.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.conversion.passes;
+
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Return;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
+import com.android.tools.r8.ir.optimize.AffectedValues;
+import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Set;
+
+public class SplitReturnRewriter extends CodeRewriterPass<AppInfo> {
+
+  public SplitReturnRewriter(AppView<?> appView) {
+    super(appView);
+  }
+
+  @Override
+  protected String getRewriterId() {
+    return "SplitReturnRewriter";
+  }
+
+  @Override
+  protected boolean shouldRewriteCode(IRCode code, MethodProcessor methodProcessor) {
+    // Disable in tests that need dead switches to be left behind.
+    assert options.isRelease();
+    return appView.options().getTestingOptions().enableDeadSwitchCaseElimination;
+  }
+
+  @Override
+  protected CodeRewriterResult rewriteCode(IRCode code) {
+    boolean changed = false;
+    boolean hasUnreachableBlocks = false;
+    Set<BasicBlock> blocksToRemove = Sets.newIdentityHashSet();
+    Deque<BasicBlock> worklist = new ArrayDeque<>(code.computeNormalExitBlocks());
+    while (!worklist.isEmpty()) {
+      BasicBlock block = worklist.removeFirst();
+      Return returnInstruction = block.entry().asReturn();
+      if (returnInstruction == null) {
+        continue;
+      }
+      Value returnValue = returnInstruction.getReturnValueOrNull();
+      IntList predecessorsToRemove = new IntArrayList();
+      for (int predecessorIndex = 0;
+          predecessorIndex < block.getPredecessors().size();
+          predecessorIndex++) {
+        BasicBlock predecessor = block.getPredecessor(predecessorIndex);
+        if (!predecessor.exit().isGoto()) {
+          continue;
+        }
+        if (predecessor.exit().asGoto().getTarget() != block) {
+          assert predecessor.hasCatchSuccessor(block);
+          continue;
+        }
+        if (block.hasCatchHandlers()) {
+          for (BasicBlock catchHandlerBlock : block.getSuccessors()) {
+            catchHandlerBlock.getMutablePredecessors().clear();
+          }
+          block.getMutableSuccessors().clear();
+          block.clearCatchHandlers();
+          hasUnreachableBlocks = true;
+        } else {
+          assert block.getSuccessors().isEmpty();
+        }
+        Value newReturnValue;
+        if (returnValue != null && returnValue.isPhi() && returnValue.getBlock() == block) {
+          newReturnValue = returnValue.asPhi().getOperand(predecessorIndex);
+        } else {
+          newReturnValue = returnValue;
+        }
+        Return newReturnInstruction =
+            Return.builder().setReturnValue(newReturnValue).setPosition(returnInstruction).build();
+        predecessor.exit().replace(newReturnInstruction);
+        predecessor.removeAllNormalSuccessors();
+        predecessorsToRemove.add(predecessorIndex);
+        worklist.add(predecessor);
+      }
+      if (!predecessorsToRemove.isEmpty()) {
+        if (predecessorsToRemove.size() == block.getPredecessors().size()) {
+          blocksToRemove.add(block);
+          if (returnValue != null) {
+            if (returnValue.isPhi() && returnValue.getBlock() == block) {
+              for (Value operand : returnValue.asPhi().getOperands()) {
+                operand.removePhiUser(returnValue.asPhi());
+              }
+            } else {
+              returnValue.removeUser(returnInstruction);
+            }
+          }
+        } else {
+          block.removePredecessorsByIndex(predecessorsToRemove);
+          block.removePhisByIndex(predecessorsToRemove);
+        }
+        changed = true;
+      }
+    }
+    code.removeBlocks(blocksToRemove);
+    if (hasUnreachableBlocks) {
+      AffectedValues affectedValues = code.removeUnreachableBlocks();
+      affectedValues.narrowingWithAssumeRemoval(appView, code);
+    }
+    if (changed) {
+      code.removeRedundantBlocks();
+    }
+    return CodeRewriterResult.hasChanged(changed);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
index 10988dc..471dbf6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
@@ -7,11 +7,13 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.JumpInstruction;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.AssumeInfoCollection;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -38,6 +40,11 @@
         returnedTypes.add(returnValue.getDynamicType(appView));
       }
     }
-    return DynamicType.join(appView, returnedTypes);
+    DynamicType dynamicReturnType = DynamicType.join(appView, returnedTypes);
+    AssumeInfoCollection assumeInfoCollection = appView.getAssumeInfoCollection();
+    if (assumeInfoCollection.get(method).getAssumeType().getNullability().isDefinitelyNotNull()) {
+      return dynamicReturnType.withNullability(Nullability.definitelyNotNull());
+    }
+    return dynamicReturnType;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 17eb9a8..b58048e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -746,17 +746,20 @@
           monitorExitBlock.close(null);
         }
 
-        for (BasicBlock block : code.blocks) {
+        BasicBlockIterator blockIterator = code.listIterator();
+        while (blockIterator.hasNext()) {
+          BasicBlock block = blockIterator.next();
           if (block.exit().isReturn()) {
-            // Since return instructions are not allowed after a throwing instruction in a block
-            // with catch handlers, the call to prepareBlocksForCatchHandlers() has already taken
-            // care of ensuring that all return blocks have no throwing instructions.
-            assert !block.canThrow();
-
             InstructionListIterator instructionIterator =
                 block.listIterator(block.getInstructions().size() - 1);
-            instructionIterator.setInsertionPosition(Position.syntheticNone());
-            instructionIterator.add(new Monitor(MonitorType.EXIT, lockValue));
+            if (block.canThrow()) {
+              BasicBlock splitBlock =
+                  instructionIterator.splitCopyCatchHandlers(code, blockIterator, options);
+              instructionIterator = splitBlock.listIterator();
+            }
+            Monitor monitorExit = new Monitor(MonitorType.EXIT, lockValue);
+            monitorExit.setPosition(Position.syntheticNone());
+            instructionIterator.add(monitorExit);
           }
         }
       }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index 12e89e5..818ddcf 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -341,10 +341,7 @@
     assertCounters(ALWAYS_INLINABLE, ALWAYS_INLINABLE, countInvokes(inspector, m));
 
     m = clazz.method("int", "notInlinableDueToSideEffect", ImmutableList.of("inlining.A"));
-    assertCounters(
-        parameters.isCfRuntime() ? ALWAYS_INLINABLE : NEVER_INLINABLE,
-        NEVER_INLINABLE,
-        countInvokes(inspector, m));
+    assertCounters(ALWAYS_INLINABLE, NEVER_INLINABLE, countInvokes(inspector, m));
 
     m =
         clazz.method(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java
index ade059d..89000f3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java
@@ -5,11 +5,9 @@
 package com.android.tools.r8.ir.optimize.inliner;
 
 import static com.android.tools.r8.ir.optimize.inliner.testclasses.InliningIntoVisibilityBridgeTestClasses.getClassA;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 
 import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.R8TestRunResult;
@@ -64,27 +62,23 @@
             .run(parameters.getRuntime(), TestClass.class)
             .assertSuccessWithOutput(expectedOutput);
 
-    // Verify that A.method() is only there if there is an explicit -neverinline rule.
+    // Verify that A.method() is removed unless there is an explicit -neverinline rule.
     {
       ClassSubject classSubject = result.inspector().clazz(getClassA());
-      assertThat(classSubject, isPresent());
+      assertThat(classSubject, isPresentIf(neverInline || parameters.isDexRuntime()));
 
       MethodSubject methodSubject = classSubject.uniqueMethodWithOriginalName("method");
       assertEquals(neverInline, methodSubject.isPresent());
     }
 
-    // Verify that B.method() is still there, and that B.method() is neither a bridge nor a
-    // synthetic method unless there is an explicit -neverinline rule.
+    // Verify that B.method() is removed.
     {
       ClassSubject classSubject =
           result.inspector().clazz(InliningIntoVisibilityBridgeTestClassB.class);
-      assertThat(classSubject, isPresent());
+      assertThat(classSubject, isPresentIf(neverInline || parameters.isDexRuntime()));
 
-      MethodSubject methodSubject = classSubject.uniqueMethodWithOriginalName("method");
-      if (!neverInline) {
-        assertThat(methodSubject, isPresentAndRenamed());
-        assertFalse(methodSubject.isBridge());
-        assertFalse(methodSubject.isSynthetic());
+      if (neverInline) {
+        assertEquals(0, classSubject.allMethods().size());
       }
     }
   }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/B135542760.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/B135542760.java
index 83d1acb..810baae 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/switches/B135542760.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/B135542760.java
@@ -34,7 +34,6 @@
         .addOptionsModification(
             options -> {
               options.testing.enableSwitchToIfRewriting = false;
-              options.testing.enableDeadSwitchCaseElimination = true;
             })
         .enableInliningAnnotations()
         .setMinApi(parameters)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchCaseRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchCaseRemovalTest.java
index d3f2fed..319f2f0 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchCaseRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchCaseRemovalTest.java
@@ -55,7 +55,6 @@
         .addOptionsModification(
             options -> {
               options.testing.enableSwitchToIfRewriting = false;
-              options.testing.enableDeadSwitchCaseElimination = true;
             })
         .enableInliningAnnotations()
         .enableMemberValuePropagationAnnotations()
diff --git a/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingTest.java b/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingTest.java
index 03275f0..3059040 100644
--- a/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingTest.java
@@ -150,9 +150,7 @@
     );
 
     Consumer<InternalOptions> optionsConsumer =
-        options -> {
-          options.testing.enableDeadSwitchCaseElimination = false;
-        };
+        options -> options.testing.enableDeadSwitchCaseElimination = false;
     AndroidApp originalApplication = buildApplication(builder);
     AndroidApp processedApplication = processApplication(originalApplication, optionsConsumer);
     DexEncodedMethod method = getMethod(processedApplication, signature);
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsForJavaLangClassTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsForJavaLangClassTest.java
index 9b61b25..f251a9c 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsForJavaLangClassTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsForJavaLangClassTest.java
@@ -70,19 +70,14 @@
   private void inspectMethod(
       MethodSubject methodSubject, boolean maybeNullReceiver, boolean maybeSubtype) {
     assertThat(methodSubject, isPresent());
-    assertThat(
-        methodSubject, onlyIf(maybeNullReceiver || maybeSubtype, invokesMethodWithName("equals")));
-    assertThat(
-        methodSubject,
-        onlyIf(maybeNullReceiver || maybeSubtype, invokesMethodWithName("hashCode")));
+    assertThat(methodSubject, onlyIf(maybeSubtype, invokesMethodWithName("equals")));
+    assertThat(methodSubject, onlyIf(maybeSubtype, invokesMethodWithName("hashCode")));
     assertThat(
         methodSubject,
         onlyIf(
             maybeNullReceiver,
             anyOf(invokesMethodWithName("getClass"), invokesMethodWithName("requireNonNull"))));
-    assertThat(
-        methodSubject,
-        onlyIf(maybeNullReceiver || maybeSubtype, invokesMethodWithName("toString")));
+    assertThat(methodSubject, onlyIf(maybeSubtype, invokesMethodWithName("toString")));
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
index 9642e4e..237753d 100644
--- a/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
+++ b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
@@ -450,8 +450,8 @@
     );
     DexCode code = method.getCode().asDexCode();
     // TODO(sgjesse): Maybe this test is too fragile, as it leaves quite a lot of code, so the
-    // expectation might need changing with other optimizations.
+    //  expectation might need changing with other optimizations.
     // TODO(zerny): Consider optimizing the fallthrough branch of conditionals to not be return.
-    assertEquals(26, code.instructions.length);
+    assertEquals(27, code.instructions.length);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index 25e8d89..edacb15 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -15,7 +15,6 @@
 import com.android.tools.r8.dex.code.DexConstWideHigh16;
 import com.android.tools.r8.dex.code.DexDivInt;
 import com.android.tools.r8.dex.code.DexDivInt2Addr;
-import com.android.tools.r8.dex.code.DexGoto;
 import com.android.tools.r8.dex.code.DexInstruction;
 import com.android.tools.r8.dex.code.DexInvokeStatic;
 import com.android.tools.r8.dex.code.DexInvokeStaticRange;
@@ -1294,7 +1293,7 @@
     assertTrue(code.instructions[1] instanceof DexInvokeStatic);
     assertTrue(code.instructions[2] instanceof DexMoveResult);
     assertTrue(code.instructions[3] instanceof DexDivInt2Addr);
-    assertTrue(code.instructions[4] instanceof DexGoto);
+    assertTrue(code.instructions[4] instanceof DexReturn);
     assertTrue(code.instructions[5] instanceof DexConst4);
     assertTrue(code.instructions[6] instanceof DexReturn);
     DexInvokeStatic invoke = (DexInvokeStatic) code.instructions[1];