Merge "Introduce relaxed method signature equivalence."
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 5bed061..3cf9b8c 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
@@ -581,7 +581,7 @@
     assert predecessors.size() == 1;
     assert predecessors.get(0).successors.size() == 1;
     BasicBlock unlinkedBlock = predecessors.get(0);
-    predecessors.get(0).successors.clear();
+    unlinkedBlock.successors.clear();
     predecessors.clear();
     return unlinkedBlock;
   }
@@ -604,7 +604,7 @@
     assert successors.size() == 1;
     assert successors.get(0).predecessors.size() == 1;
     BasicBlock unlinkedBlock = successors.get(0);
-    successors.get(0).predecessors.clear();
+    unlinkedBlock.predecessors.clear();
     successors.clear();
     return unlinkedBlock;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
index 1adcd11..81e79e6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
@@ -291,21 +291,19 @@
 
   private void appendCatchHandlers(IRCode code, BasicBlock invokeBlock,
       IRCode inlinee, ListIterator<BasicBlock> blocksIterator) {
-    BasicBlock inlineeBlock = null;
-    // Move back through the inlinee blocks added (they are now in the basic blocks list).
+    // Position right after the empty invoke block, by moving back through the newly added inlinee
+    // blocks (they are now in the basic blocks list).
     for (int i = 0; i < inlinee.blocks.size(); i++) {
-      inlineeBlock = blocksIterator.previous();
+      blocksIterator.previous();
     }
-    assert inlineeBlock == inlinee.blocks.getFirst();
-    // Position right after the empty invoke block.
-    inlineeBlock = blocksIterator.next();
-    assert inlineeBlock == inlinee.blocks.getFirst();
+    assert IteratorUtils.peekNext(blocksIterator) == inlinee.blocks.getFirst();
 
-    // Iterate through the inlined blocks (they are now in the basic blocks list).
+    // Iterate through the inlined blocks.
     Iterator<BasicBlock> inlinedBlocksIterator = inlinee.blocks.iterator();
     while (inlinedBlocksIterator.hasNext()) {
       BasicBlock inlinedBlock = inlinedBlocksIterator.next();
-      assert inlineeBlock == inlinedBlock;  // Iterators must be in sync.
+      BasicBlock expected = blocksIterator.next();
+      assert inlinedBlock == expected; // Iterators must be in sync.
       if (inlinedBlock.hasCatchHandlers()) {
         // The block already has catch handlers, so it has only one throwing instruction, and no
         // splitting is required.
@@ -316,23 +314,17 @@
         // handlers must be added to each of these blocks.
         splitBlockAndCopyCatchHandlers(code, invokeBlock, inlinedBlock, blocksIterator);
       }
-      // Iterate to the next inlined block (if more inlined blocks).
-      inlineeBlock = blocksIterator.next();
     }
   }
 
-  private void removeArgumentInstructions(IRCode inlinee) {
-    int index = 0;
-    InstructionListIterator inlineeIterator = inlinee.blocks.getFirst().listIterator();
-    List<Value> arguments = inlinee.collectArguments();
-    while (inlineeIterator.hasNext()) {
-      Instruction instruction = inlineeIterator.next();
-      if (instruction.isArgument()) {
-        assert !instruction.outValue().isUsed();
-        assert instruction.outValue() == arguments.get(index++);
-        inlineeIterator.remove();
-      }
-    }
+  private static void removeArgumentInstruction(
+      InstructionListIterator iterator, Value expectedArgument) {
+    assert iterator.hasNext();
+    Instruction instruction = iterator.next();
+    assert instruction.isArgument();
+    assert !instruction.outValue().isUsed();
+    assert instruction.outValue() == expectedArgument;
+    iterator.remove();
   }
 
   @Override
@@ -341,47 +333,77 @@
       List<BasicBlock> blocksToRemove, DexType downcast) {
     assert blocksToRemove != null;
     boolean inlineeCanThrow = canThrow(inlinee);
+
+    // Split the block with the invocation into three blocks, where the first block contains all
+    // instructions before the invocation, the second block contains only the invocation, and the
+    // third block contains all instructions that follow the invocation.
     BasicBlock invokeBlock = split(code, 1, blocksIterator);
     assert invokeBlock.getInstructions().size() == 2;
     assert invokeBlock.getInstructions().getFirst().isInvoke();
 
+    Invoke invoke = invokeBlock.getInstructions().getFirst().asInvoke();
+    BasicBlock invokePredecessor = invokeBlock.getPredecessors().get(0);
+    BasicBlock invokeSuccessor = invokeBlock.getSuccessors().get(0);
+
     // Invalidate position-on-throwing-instructions property if it does not hold for the inlinee.
     if (!inlinee.doAllThrowingInstructionsHavePositions()) {
       code.setAllThrowingInstructionsHavePositions(false);
     }
 
-    // Split the invoke instruction into a separate block.
-    Invoke invoke = invokeBlock.getInstructions().getFirst().asInvoke();
-    BasicBlock invokePredecessor = invokeBlock.getPredecessors().get(0);
-    BasicBlock invokeSuccessor = invokeBlock.getSuccessors().get(0);
-
-    CheckCast castInstruction = null;
-    // Map all argument values, and remove the arguments instructions in the inlinee.
+    // Map all argument values. The first one needs special handling if there is a downcast type.
     List<Value> arguments = inlinee.collectArguments();
     assert invoke.inValues().size() == arguments.size();
-    for (int i = 0; i < invoke.inValues().size(); i++) {
+
+    BasicBlock entryBlock = inlinee.blocks.getFirst();
+    InstructionListIterator entryBlockIterator;
+
+    int i = 0;
+    if (downcast != null) {
+      CheckCast castInstruction =
+          new CheckCast(code.createValue(ValueType.OBJECT), invoke.inValues().get(0), downcast);
+      castInstruction.setPosition(invoke.getPosition());
+
+      // Splice in the check cast operation.
+      if (entryBlock.canThrow() || invokeBlock.hasCatchHandlers()) {
+        // Since the cast-instruction may also fail we need to create a new block for the cast.
+        //
+        // Note that the downcast of the receiver is made at the call site, so we need to copy the
+        // catch handlers from the invoke block to the block with the cast. This is already being
+        // done when we copy the catch handlers of the invoke block (if any) to all the blocks in
+        // the inlinee (by the call to appendCatchHandlers() later in this method), so we don't
+        // need to do anything about that here.
+        BasicBlock inlineEntry = entryBlock;
+        entryBlock = entryBlock.listIterator().split(inlinee);
+        entryBlockIterator = entryBlock.listIterator();
+        // Insert cast instruction into the new block.
+        inlineEntry.getInstructions().addFirst(castInstruction);
+        castInstruction.setBlock(inlineEntry);
+        assert castInstruction.getBlock().getInstructions().size() == 2;
+      } else {
+        castInstruction.setBlock(entryBlock);
+        entryBlockIterator = entryBlock.listIterator();
+        entryBlockIterator.add(castInstruction);
+      }
+
+      // Map the argument value that has been cast.
+      Value argument = arguments.get(i);
+      argument.replaceUsers(castInstruction.outValue);
+      removeArgumentInstruction(entryBlockIterator, argument);
+      i++;
+    } else {
+      entryBlockIterator = entryBlock.listIterator();
+    }
+
+    // Map the remaining argument values.
+    for (; i < invoke.inValues().size(); i++) {
       // TODO(zerny): Support inlining in --debug mode.
       assert !arguments.get(i).hasLocalInfo();
-      if ((i == 0) && (downcast != null)) {
-        Value invokeValue = invoke.inValues().get(0);
-        Value receiverValue = arguments.get(0);
-        Value value = code.createValue(ValueType.OBJECT);
-        castInstruction = new CheckCast(value, invokeValue, downcast);
-        castInstruction.setPosition(invoke.getPosition());
-        receiverValue.replaceUsers(value);
-      } else {
-        arguments.get(i).replaceUsers(invoke.inValues().get(i));
-      }
+      Value argument = arguments.get(i);
+      argument.replaceUsers(invoke.inValues().get(i));
+      removeArgumentInstruction(entryBlockIterator, argument);
     }
-    removeArgumentInstructions(inlinee);
-    if (castInstruction != null) {
-      // Splice in the check cast operation.
-      inlinee.blocks.getFirst().listIterator().split(inlinee);
-      BasicBlock newBlock = inlinee.blocks.getFirst();
-      assert newBlock.getInstructions().size() == 1;
-      newBlock.getInstructions().addFirst(castInstruction);
-      castInstruction.setBlock(newBlock);
-    }
+
+    assert entryBlock.getInstructions().stream().noneMatch(Instruction::isArgument);
 
     // The inline entry is the first block now the argument instructions are gone.
     BasicBlock inlineEntry = inlinee.blocks.getFirst();
@@ -421,6 +443,7 @@
       invokeBlockIterator.next();
       invokeBlockIterator.remove();
       invokeSuccessor = invokeBlock;
+      assert invokeBlock.getInstructions().getFirst().isGoto();
     }
 
     // Link the inlinee into the graph.
@@ -433,15 +456,14 @@
     if (blocksIterator == null) {
       // If no block iterator was passed create one for the insertion of the inlinee blocks.
       blocksIterator = code.blocks.listIterator(code.blocks.indexOf(invokeBlock));
-      blocksIterator.next();
     } else {
-      // If a blocks iterator was passed, back up to the block with the invoke instruction and
-      // remove it.
+      // If a block iterator was passed, back up to the block with the invoke instruction.
       blocksIterator.previous();
       blocksIterator.previous();
     }
+    assert IteratorUtils.peekNext(blocksIterator) == invokeBlock;
 
-    // Insert inlinee blocks into the IR code.
+    // Insert inlinee blocks into the IR code of the callee, before the invoke block.
     int blockNumber = code.getHighestBlockNumber() + 1;
     for (BasicBlock bb : inlinee.blocks) {
       bb.setNumber(blockNumber++);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index 5f62c6c..8e91a62 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -192,8 +192,6 @@
   private boolean inPrelude;
   private Int2ObjectMap<DebugLocalInfo> incomingLocals;
   private Int2ObjectMap<DebugLocalInfo> outgoingLocals;
-  private Int2ReferenceMap<Int2ObjectMap<DebugLocalInfo>> definitelyLiveIncomingLocals =
-      new Int2ReferenceOpenHashMap<>();
   private Int2ReferenceMap<CfState.Snapshot> incomingState = new Int2ReferenceOpenHashMap<>();
   private final CanonicalPositions canonicalPositions;
 
@@ -371,38 +369,21 @@
     if (predecessorOffset == IRBuilder.INITIAL_BLOCK_OFFSET) {
       return;
     }
-    if (currentInstructionIndex != predecessorOffset) {
-      // If transfer is not still in the same block, then update the state to that of the successor.
-      // The builder's lookup of local variables relies on this state for starting locals.
-      currentInstructionIndex = successorOffset;
-      state.reset(incomingState.get(currentInstructionIndex), false);
-      setLocalVariableLists();
-      // The transfer has not yet taken place, so the current position is that of the predecessor.
-      int positionOffset = predecessorOffset;
-      List<CfInstruction> instructions = code.getInstructions();
-      CfInstruction instruction = instructions.get(positionOffset);
-      while (0 < positionOffset && !(instruction instanceof CfPosition)) {
-        instruction = instructions.get(--positionOffset);
-      }
-      if (instruction instanceof CfPosition) {
-        CfPosition position = (CfPosition) instruction;
-        state.setPosition(getCanonicalPosition(position.getPosition()));
-      } else {
-        state.setPosition(canonicalPositions.getPreamblePosition());
-      }
-    }
+    // The transfer has not yet taken place, so the current position is that of the predecessor.
+    state.setPosition(getCanonicalDebugPositionAtOffset(predecessorOffset));
+
     // Manually compute the local variable change for the block transfer.
-    LocalVariableList atSource = getLocalVariables(predecessorOffset);
-    LocalVariableList atTarget = getLocalVariables(successorOffset);
+    Int2ObjectMap<DebugLocalInfo> atSource = getLocalVariables(predecessorOffset).locals;
+    Int2ObjectMap<DebugLocalInfo> atTarget = getLocalVariables(successorOffset).locals;
     if (!isExceptional) {
-      for (Entry<DebugLocalInfo> entry : atSource.locals.int2ObjectEntrySet()) {
-        if (atTarget.locals.get(entry.getIntKey()) != entry.getValue()) {
+      for (Entry<DebugLocalInfo> entry : atSource.int2ObjectEntrySet()) {
+        if (atTarget.get(entry.getIntKey()) != entry.getValue()) {
           builder.addDebugLocalEnd(entry.getIntKey(), entry.getValue());
         }
       }
     }
-    for (Entry<DebugLocalInfo> entry : atTarget.locals.int2ObjectEntrySet()) {
-      if (atSource.locals.get(entry.getIntKey()) != entry.getValue()) {
+    for (Entry<DebugLocalInfo> entry : atTarget.int2ObjectEntrySet()) {
+      if (atSource.get(entry.getIntKey()) != entry.getValue()) {
         builder.addDebugLocalStart(entry.getIntKey(), entry.getValue());
       }
     }
@@ -562,33 +543,15 @@
 
   private void setLocalVariableLists() {
     incomingLocals = getLocalVariables(currentInstructionIndex).locals;
-    CfInstruction currentInstruction = code.getInstructions().get(currentInstructionIndex);
     if (inPrelude) {
       outgoingLocals = incomingLocals;
       return;
     }
-    if (currentInstruction.isReturn() || currentInstruction instanceof CfThrow) {
-      outgoingLocals = emptyMap();
-      return;
-    }
-    if (isControlFlow(currentInstruction)) {
-      // We need to read all locals that are not live on all successors to ensure liveness.
-      // Determine outgoingLocals as the intersection of all successors' locals.
-      outgoingLocals = null;
-      int[] targets = getTargets(currentInstructionIndex);
-      for (int target : targets) {
-        Int2ObjectMap<DebugLocalInfo> locals = getLocalVariables(target).locals;
-        outgoingLocals = intersectMaps(outgoingLocals, locals);
-      }
-      assert outgoingLocals != null;
-      // Pass outgoingLocals to all successors.
-      for (int target : targets) {
-        Int2ObjectMap<DebugLocalInfo> existing = definitelyLiveIncomingLocals.get(target);
-        definitelyLiveIncomingLocals.put(target, intersectMaps(existing, outgoingLocals));
-      }
-    } else {
-      outgoingLocals = getLocalVariables(currentInstructionIndex + 1).locals;
-    }
+    CfInstruction currentInstruction = code.getInstructions().get(currentInstructionIndex);
+    outgoingLocals =
+        !isControlFlow(currentInstruction)
+            ? getLocalVariables(currentInstructionIndex + 1).locals
+            : emptyMap();
   }
 
   private boolean localsChanged() {
@@ -613,27 +576,6 @@
     }
   }
 
-  private Int2ObjectMap<DebugLocalInfo> intersectMaps(
-      Int2ObjectMap<DebugLocalInfo> existing, Int2ObjectMap<DebugLocalInfo> update) {
-    assert update != null;
-    if (existing == null) {
-      return update;
-    }
-    if (existing.size() > update.size()) {
-      return intersectMaps(update, existing);
-    }
-    if (existing.equals(update)) {
-      return existing;
-    }
-    Int2ObjectOpenHashMap<DebugLocalInfo> result = new Int2ObjectOpenHashMap<>();
-    for (Entry<DebugLocalInfo> local : existing.int2ObjectEntrySet()) {
-      if (local.getValue().equals(update.get(local.getIntKey()))) {
-        result.put(local.getIntKey(), local.getValue());
-      }
-    }
-    return result;
-  }
-
   private boolean isControlFlow(CfInstruction currentInstruction) {
     return currentInstruction.isReturn()
         || currentInstruction.getTarget() != null
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 5c4d581..8fe972b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -706,14 +706,6 @@
     addInstruction(new Argument(value));
   }
 
-  private void addDebugLocalWrite(ValueType type, int dest, Value in) {
-    assert options.debug;
-    Value out = writeRegister(dest, type, ThrowingInfo.NO_THROW);
-    DebugLocalWrite write = new DebugLocalWrite(out, in);
-    assert !write.instructionTypeCanThrow();
-    addInstruction(write);
-  }
-
   private Value getIncomingLocalValue(int register, DebugLocalInfo local) {
     assert options.debug;
     assert local != null;
@@ -729,26 +721,26 @@
   }
 
   public void addDebugLocalStart(int register, DebugLocalInfo local) {
+    assert local != null;
     if (!options.debug) {
       return;
     }
-    assert local != null;
-    assert local == getOutgoingLocal(register) :
-        "local-start mismatch: " + local + " != " + getOutgoingLocal(register)
-            + " at " + currentInstructionOffset
-            + " for source\n" + source.toString();
-    // TODO(b/111251032): Here we lookup a value with type based on debug info. That's just wrong!
-    ValueType valueType = ValueType.fromDexType(local.type);
-    Value incomingValue = readRegisterIgnoreLocal(register, valueType, local);
     // If the local was not introduced by the previous instruction, start it here.
+    Value incomingValue = getIncomingLocalValue(register, local);
     if (incomingValue.getLocalInfo() != local
         || currentBlock.isEmpty()
         || currentBlock.getInstructions().getLast().outValue() != incomingValue) {
-      addDebugLocalWrite(ValueType.fromDexType(local.type), register, incomingValue);
+      // Note that the write register must not lookup outgoing local information and the local is
+      // never considered clobbered by a start (if the in value has local info it must have been
+      // marked ended elsewhere).
+      Value out = writeRegister(register, incomingValue.outType(), ThrowingInfo.NO_THROW, local);
+      DebugLocalWrite write = new DebugLocalWrite(out, incomingValue);
+      addInstruction(write);
     }
   }
 
   public void addDebugLocalEnd(int register, DebugLocalInfo local) {
+    assert local != null;
     if (!options.debug) {
       return;
     }
@@ -951,7 +943,8 @@
       // If the move is writing to a different local we must construct a new value.
       DebugLocalInfo destLocal = getOutgoingLocal(dest);
       if (destLocal != null && destLocal != in.getLocalInfo()) {
-        addDebugLocalWrite(type, dest, in);
+        Value out = writeRegister(dest, type, ThrowingInfo.NO_THROW);
+        addInstruction(new DebugLocalWrite(out, in));
         return;
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index e528a90..1039817 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -476,6 +476,7 @@
         || successorOffset == EXCEPTIONAL_SYNC_EXIT_OFFSET) {
       return;
     }
+    // The transfer has not yet taken place, so the current position is that of the predecessor.
     currentPosition = getCanonicalDebugPositionAtOffset(predecessorOffset);
 
     LocalChangeAtOffset localChange = state.getLocalChange(predecessorOffset, successorOffset);
@@ -484,12 +485,8 @@
         builder.addDebugLocalEnd(toClose.slot.register, toClose.info);
       }
     }
-    List<Local> localsToOpen = localChange.getLocalsToOpen();
-    if (!localsToOpen.isEmpty()) {
-      state.restoreState(successorOffset);
-      for (Local toOpen : localsToOpen) {
-        builder.addDebugLocalStart(toOpen.slot.register, toOpen.info);
-      }
+    for (Local toOpen : localChange.getLocalsToOpen()) {
+      builder.addDebugLocalStart(toOpen.slot.register, toOpen.info);
     }
   }
 
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 04cd083..106d537 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
@@ -605,7 +605,7 @@
               if (!strategy.isValidTarget(invoke, target, inlinee)) {
                 continue;
               }
-              DexType downcast = createDowncastIfNeeded(strategy, invoke, target);
+              DexType downcast = getDowncastTypeIfNeeded(strategy, invoke, target);
               // Inline the inlinee code in place of the invoke instruction
               // Back up before the invoke instruction.
               iterator.previous();
@@ -635,7 +635,7 @@
     assert code.isConsistentSSA();
   }
 
-  private DexType createDowncastIfNeeded(
+  private static DexType getDowncastTypeIfNeeded(
       InliningStrategy strategy, InvokeMethod invoke, DexEncodedMethod target) {
     if (invoke.isInvokeMethodWithReceiver()) {
       // If the invoke has a receiver but the actual type of the receiver is different
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java
index dc94a71..4451910 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java
@@ -25,10 +25,16 @@
 
     List<String[]> fullTestList = new ArrayList<>(tests.length * 2);
     for (String test : tests) {
-      fullTestList.add(makeTest(Input.JAVAC, CompilerUnderTest.D8, CompilationMode.DEBUG, test));
-      fullTestList.add(makeTest(Input.JAVAC, CompilerUnderTest.D8, CompilationMode.RELEASE, test));
+      fullTestList.add(
+          makeTest(Input.JAVAC, CompilerUnderTest.D8, CompilationMode.DEBUG, test, Output.DEX));
+      fullTestList.add(
+          makeTest(Input.JAVAC, CompilerUnderTest.D8, CompilationMode.RELEASE, test, Output.DEX));
       fullTestList.add(makeTest(Input.DX, CompilerUnderTest.R8, CompilationMode.DEBUG, test));
       fullTestList.add(makeTest(Input.DX, CompilerUnderTest.R8, CompilationMode.RELEASE, test));
+      fullTestList.add(
+          makeTest(Input.JAVAC, CompilerUnderTest.R8, CompilationMode.DEBUG, test, Output.CF));
+      fullTestList.add(
+          makeTest(Input.JAVAC, CompilerUnderTest.R8, CompilationMode.RELEASE, test, Output.CF));
     }
     return fullTestList;
   }
diff --git a/src/test/java/com/android/tools/r8/debug/PostIncrementTestRunner.java b/src/test/java/com/android/tools/r8/debug/PostIncrementTestRunner.java
index 4cfb71a..6ddc9db 100644
--- a/src/test/java/com/android/tools/r8/debug/PostIncrementTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/PostIncrementTestRunner.java
@@ -5,18 +5,24 @@
 
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.VmTestRunner.IgnoreIfVmOlderOrEqualThan;
 import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.DebuggeeState;
 import java.util.stream.Stream;
 import org.junit.Assume;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 // See b/80385846
+@RunWith(VmTestRunner.class)
 public class PostIncrementTestRunner extends DebugTestBase {
 
   private static final Class CLASS = PostIncrementTest.class;
   private static final String NAME = CLASS.getCanonicalName();
 
   @Test
+  @IgnoreIfVmOlderOrEqualThan(Version.V5_1_1)
   public void test() throws Exception {
     Assume.assumeTrue("Older runtimes cause some kind of debug streaming issues",
         ToolHelper.getDexVm().isNewerThan(DexVm.ART_5_1_1_HOST));
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/CompositionalLenseTest.java b/src/test/java/com/android/tools/r8/memberrebinding/CompositionalLenseTest.java
index 6c6e69d..4903304 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/CompositionalLenseTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/CompositionalLenseTest.java
@@ -11,19 +11,24 @@
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.code.InvokeVirtual;
-import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.InvokeInstructionSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
 // Base -> X:
 class Base {
@@ -47,10 +52,22 @@
   }
 }
 
+@RunWith(Parameterized.class)
 public class CompositionalLenseTest extends TestBase {
   private final static List<Class> CLASSES =
       ImmutableList.of(Base.class, Sub.class, TestMain.class);
 
+  private Backend backend;
+
+  @Parameterized.Parameters(name = "Backend: {0}")
+  public static Collection<Backend> data() {
+    return Arrays.asList(Backend.values());
+  }
+
+  public CompositionalLenseTest(Backend backend) {
+    this.backend = backend;
+  }
+
   @Test
   public void test() throws Exception {
     Path mapPath = temp.newFile("test-mapping.txt").toPath();
@@ -63,26 +80,38 @@
     FileUtils.writeTextFile(mapPath, pgMap);
 
     AndroidApp app = readClasses(CLASSES);
-    R8Command.Builder builder = ToolHelper.prepareR8CommandBuilder(app);
-    builder.addProguardConfiguration(
-        ImmutableList.of(
-            keepMainProguardConfiguration(TestMain.class),
-            "-applymapping " + mapPath,
-            "-dontobfuscate"),  // to use the renamed names in test-mapping.txt
-        Origin.unknown());
+    R8Command.Builder builder = ToolHelper.prepareR8CommandBuilder(app, emptyConsumer(backend));
+    builder
+        .addProguardConfiguration(
+            ImmutableList.of(
+                keepMainProguardConfiguration(TestMain.class),
+                "-applymapping " + mapPath,
+                "-dontobfuscate"), // to use the renamed names in test-mapping.txt
+            Origin.unknown())
+        .addLibraryFiles(runtimeJar(backend));
     AndroidApp processedApp = ToolHelper.runR8(builder.build(), options -> {
       options.enableInlining = false;
       options.enableClassMerging = false;
     });
-    CodeInspector codeInspector = new CodeInspector(processedApp);
+    CodeInspector codeInspector = new CodeInspector(processedApp, o -> o.enableCfFrontend = true);
     ClassSubject classSubject = codeInspector.clazz(TestMain.class);
     assertThat(classSubject, isPresent());
     MethodSubject methodSubject = classSubject.method(CodeInspector.MAIN);
     assertThat(methodSubject, isPresent());
-    DexCode dexCode = methodSubject.getMethod().getCode().asDexCode();
-    assertTrue(dexCode.instructions[2] instanceof InvokeVirtual);
-    InvokeVirtual invoke = (InvokeVirtual) dexCode.instructions[2];
-    DexMethod invokedMethod = invoke.getMethod();
+    Iterator<InstructionSubject> iterator = methodSubject.iterateInstructions();
+
+    InstructionSubject instruction = null;
+    boolean found = false;
+    while (iterator.hasNext()) {
+      instruction = iterator.next();
+      if (instruction.isInvokeVirtual()) {
+        found = true;
+        break;
+      }
+    }
+    assertTrue(found);
+
+    DexMethod invokedMethod = ((InvokeInstructionSubject) instruction).invokedMethod();
     assertEquals("bar", invokedMethod.name.toString());
     assertEquals("X", invokedMethod.getHolder().getName());
   }
diff --git a/src/test/java/com/android/tools/r8/regress/b63935662/Regress63935662.java b/src/test/java/com/android/tools/r8/regress/b63935662/Regress63935662.java
index 01b0a17..86852ef 100644
--- a/src/test/java/com/android/tools/r8/regress/b63935662/Regress63935662.java
+++ b/src/test/java/com/android/tools/r8/regress/b63935662/Regress63935662.java
@@ -7,28 +7,53 @@
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.OffOrAuto;
 import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collection;
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
+@RunWith(Parameterized.class)
 public class Regress63935662 extends TestBase {
 
+  private Backend backend;
+
+  @Parameterized.Parameters(name = "Backend: {0}")
+  public static Collection<Backend> data() {
+    return Arrays.asList(Backend.values());
+  }
+
+  public Regress63935662(Backend backend) {
+    this.backend = backend;
+  }
+
   void run(AndroidApp app, Class mainClass) throws Exception {
     Path proguardConfig =
         writeTextToTempFile(keepMainProguardConfiguration(mainClass, true, false));
-    R8Command command =
-        ToolHelper.prepareR8CommandBuilder(app)
-            .addProguardConfigurationFiles(proguardConfig)
-            .setMinApiLevel(AndroidApiLevel.L.getLevel())
-            .build();
+    R8Command.Builder builder =
+        ToolHelper.prepareR8CommandBuilder(app, emptyConsumer(backend))
+            .addLibraryFiles(runtimeJar(backend))
+            .addProguardConfigurationFiles(proguardConfig);
+    if (backend == Backend.DEX) {
+      builder.setMinApiLevel(AndroidApiLevel.L.getLevel());
+    }
     String resultFromJava = runOnJava(mainClass);
-    app = ToolHelper.runR8(command, options -> options.interfaceMethodDesugaring = OffOrAuto.Auto);
-    String resultFromArt = runOnArt(app, mainClass);
-    Assert.assertEquals(resultFromJava, resultFromArt);
+    app =
+        ToolHelper.runR8(
+            builder.build(), options -> options.interfaceMethodDesugaring = OffOrAuto.Auto);
+    String result;
+    if (backend == Backend.DEX) {
+      result = runOnArt(app, mainClass);
+    } else {
+      assert backend == Backend.CF;
+      result = runOnJava(app, mainClass);
+    }
+    Assert.assertEquals(resultFromJava, result);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java b/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
index b2bcb4e..ee99b38 100644
--- a/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.rewrite.switchmaps;
 
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
@@ -13,12 +12,28 @@
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
+@RunWith(Parameterized.class)
 public class RewriteSwitchMapsTest extends TestBase {
 
+  private Backend backend;
+
+  @Parameterized.Parameters(name = "Backend: {0}")
+  public static Collection<Backend> data() {
+    return Arrays.asList(Backend.values());
+  }
+
+  public RewriteSwitchMapsTest(Backend backend) {
+    this.backend = backend;
+  }
+
   private static final String JAR_FILE = "switchmaps.jar";
   private static final String SWITCHMAP_CLASS_NAME = "switchmaps.Switches$1";
   private static final List<String> PG_CONFIG = ImmutableList.of(
@@ -37,15 +52,16 @@
   }
 
   private void run(CompilationMode compilationMode) throws Exception {
-    R8Command command = R8Command.builder()
-        .addProgramFiles(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR).resolve(JAR_FILE))
-        .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
-        .addProguardConfiguration(PG_CONFIG, Origin.unknown())
-        .setMode(compilationMode)
-        .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
-        .build();
+    R8Command command =
+        R8Command.builder()
+            .addProgramFiles(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR).resolve(JAR_FILE))
+            .addLibraryFiles(runtimeJar(backend))
+            .addProguardConfiguration(PG_CONFIG, Origin.unknown())
+            .setMode(compilationMode)
+            .setProgramConsumer(emptyConsumer(backend))
+            .build();
     AndroidApp result = ToolHelper.runR8(command);
-    CodeInspector inspector = new CodeInspector(result);
+    CodeInspector inspector = new CodeInspector(result, o -> o.enableCfFrontend = true);
     Assert.assertFalse(inspector.clazz(SWITCHMAP_CLASS_NAME).isPresent());
   }
 }