Move rewriting of invokevirtual to private method in self

The rewriting is moved from a desugaring step to determining
the invoke instruction to use in the DEX writer.

This is in preparation for also handling invokevirtual to private
methods within the same nest this way.

Bug: b/236125275
Change-Id: I08f38fe25fb1d9e9f1314fd7f3c018d46513c24a
diff --git a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
index b9ef649..6f1fde5 100644
--- a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.cf.TypeVerificationHelper.TypeInfo;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.DebugLocalWrite;
 import com.android.tools.r8.ir.code.Dup;
@@ -119,6 +120,11 @@
   }
 
   @Override
+  public ProgramMethod getProgramMethod() {
+    return code.context();
+  }
+
+  @Override
   public int registersUsed() {
     return maxRegisterNumber + 1;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 5e6c879..34d5646 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -31,6 +31,7 @@
 import com.google.common.collect.Iterables;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
@@ -148,6 +149,29 @@
         null);
   }
 
+  public static DexProgramClass createMockClassForTesting(DexItemFactory dexItemFactory) {
+    return new DexProgramClass(
+        dexItemFactory.createType("LMock;"),
+        null,
+        Origin.unknown(),
+        ClassAccessFlags.fromSharedAccessFlags(0),
+        dexItemFactory.objectType,
+        DexTypeList.empty(),
+        null,
+        null,
+        Collections.emptyList(),
+        Collections.emptyList(),
+        null,
+        Collections.emptyList(),
+        ClassSignature.noSignature(),
+        DexAnnotationSet.empty(),
+        DexEncodedField.EMPTY_ARRAY,
+        DexEncodedField.EMPTY_ARRAY,
+        MethodCollectionFactory.empty(),
+        false,
+        DexProgramClass::invalidChecksumRequest);
+  }
+
   @Override
   public void accept(
       Consumer<DexProgramClass> programClassConsumer,
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index be6eac1..43ec7af 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -7,6 +7,8 @@
 
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.dex.code.DexInstruction;
+import com.android.tools.r8.dex.code.DexInvokeDirect;
+import com.android.tools.r8.dex.code.DexInvokeDirectRange;
 import com.android.tools.r8.dex.code.DexInvokeInterface;
 import com.android.tools.r8.dex.code.DexInvokeInterfaceRange;
 import com.android.tools.r8.graph.AppView;
@@ -66,20 +68,37 @@
     if (needsRangedInvoke(builder)) {
       assert argumentsConsecutive(builder);
       int firstRegister = argumentRegisterValue(0, builder);
-      instruction =
-          new DexInvokeInterfaceRange(firstRegister, argumentRegisters, getInvokedMethod());
+      if (isPrivateMethodInvokedOnSelf(builder)) {
+        instruction =
+            new DexInvokeDirectRange(firstRegister, argumentRegisters, getInvokedMethod());
+      } else {
+        instruction =
+            new DexInvokeInterfaceRange(firstRegister, argumentRegisters, getInvokedMethod());
+      }
     } else {
       int[] individualArgumentRegisters = new int[5];
       int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
-      instruction =
-          new DexInvokeInterface(
-              argumentRegistersCount,
-              getInvokedMethod(),
-              individualArgumentRegisters[0], // C
-              individualArgumentRegisters[1], // D
-              individualArgumentRegisters[2], // E
-              individualArgumentRegisters[3], // F
-              individualArgumentRegisters[4]); // G
+      if (isPrivateMethodInvokedOnSelf(builder)) {
+        instruction =
+            new DexInvokeDirect(
+                argumentRegistersCount,
+                getInvokedMethod(),
+                individualArgumentRegisters[0], // C
+                individualArgumentRegisters[1], // D
+                individualArgumentRegisters[2], // E
+                individualArgumentRegisters[3], // F
+                individualArgumentRegisters[4]); // G
+      } else {
+        instruction =
+            new DexInvokeInterface(
+                argumentRegistersCount,
+                getInvokedMethod(),
+                individualArgumentRegisters[0], // C
+                individualArgumentRegisters[1], // D
+                individualArgumentRegisters[2], // E
+                individualArgumentRegisters[3], // F
+                individualArgumentRegisters[4]); // G
+      }
     }
     addInvokeAndMoveResult(instruction, builder);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index f5e7b03..ead2849 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.DefaultInliningOracle;
 import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
@@ -83,6 +84,32 @@
         lookupSingleTarget(appView, context, dynamicReceiverType));
   }
 
+  /**
+   * If an invoke-virtual targets a private method in the current class overriding will not apply
+   * (see JVM 11 spec on method selection 5.4.6. In previous jvm specs this was not explicitly
+   * stated, but derived from method resolution 5.4.3.3 and overriding 5.4.5).
+   *
+   * <p>An invoke-interface can in the same way target a private method.
+   *
+   * <p>For desugaring we use invoke-direct instead. We need to do this as the Android Runtime will
+   * not allow invoke-virtual of a private method.
+   */
+  protected boolean isPrivateMethodInvokedOnSelf(DexBuilder builder) {
+    DexMethod method = getInvokedMethod();
+    if (method.getHolderType()
+        != builder.getRegisterAllocator().getProgramMethod().getHolderType()) {
+      return false;
+    }
+    DexEncodedMethod directTarget =
+        builder.getRegisterAllocator().getProgramMethod().getHolder().lookupDirectMethod(method);
+    if (directTarget != null && !directTarget.isStatic()) {
+      assert method.holder == directTarget.getHolderType();
+      assert directTarget.getReference() == method;
+      return true;
+    }
+    return false;
+  }
+
   @Override
   public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) {
     return value == getReceiver() || super.throwsNpeIfValueIsNull(value, appView, context);
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 0cbf370..bfcb485 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -7,6 +7,8 @@
 
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.dex.code.DexInstruction;
+import com.android.tools.r8.dex.code.DexInvokeDirect;
+import com.android.tools.r8.dex.code.DexInvokeDirectRange;
 import com.android.tools.r8.dex.code.DexInvokeVirtual;
 import com.android.tools.r8.dex.code.DexInvokeVirtualRange;
 import com.android.tools.r8.graph.AppView;
@@ -72,19 +74,37 @@
     if (needsRangedInvoke(builder)) {
       assert argumentsConsecutive(builder);
       int firstRegister = argumentRegisterValue(0, builder);
-      instruction = new DexInvokeVirtualRange(firstRegister, argumentRegisters, getInvokedMethod());
+      if (isPrivateMethodInvokedOnSelf(builder)) {
+        instruction =
+            new DexInvokeDirectRange(firstRegister, argumentRegisters, getInvokedMethod());
+      } else {
+        instruction =
+            new DexInvokeVirtualRange(firstRegister, argumentRegisters, getInvokedMethod());
+      }
     } else {
       int[] individualArgumentRegisters = new int[5];
       int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
-      instruction =
-          new DexInvokeVirtual(
-              argumentRegistersCount,
-              getInvokedMethod(),
-              individualArgumentRegisters[0], // C
-              individualArgumentRegisters[1], // D
-              individualArgumentRegisters[2], // E
-              individualArgumentRegisters[3], // F
-              individualArgumentRegisters[4]); // G
+      if (isPrivateMethodInvokedOnSelf(builder)) {
+        instruction =
+            new DexInvokeDirect(
+                argumentRegistersCount,
+                getInvokedMethod(),
+                individualArgumentRegisters[0], // C
+                individualArgumentRegisters[1], // D
+                individualArgumentRegisters[2], // E
+                individualArgumentRegisters[3], // F
+                individualArgumentRegisters[4]); // G
+      } else {
+        instruction =
+            new DexInvokeVirtual(
+                argumentRegistersCount,
+                getInvokedMethod(),
+                individualArgumentRegisters[0], // C
+                individualArgumentRegisters[1], // D
+                individualArgumentRegisters[2], // E
+                individualArgumentRegisters[3], // F
+                individualArgumentRegisters[4]); // G
+      }
     }
     addInvokeAndMoveResult(instruction, builder);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InvokeToPrivateRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InvokeToPrivateRewriter.java
deleted file mode 100644
index 65f23dc..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/InvokeToPrivateRewriter.java
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) 2021, 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.desugar;
-
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.google.common.collect.ImmutableList;
-import java.util.Collection;
-import org.objectweb.asm.Opcodes;
-
-/**
- * If an invoke-virtual targets a private method in the current class overriding will not apply (see
- * JVM 11 spec on method selection 5.4.6. In previous jvm specs this was not explicitly stated, but
- * derived from method resolution 5.4.3.3 and overriding 5.4.5).
- *
- * <p>An invoke-interface can in the same way target a private method.
- *
- * <p>For desugaring we use invoke-direct instead. We need to do this as the Android Runtime will
- * not allow invoke-virtual of a private method.
- */
-public class InvokeToPrivateRewriter implements CfInstructionDesugaring {
-
-  @Override
-  public Collection<CfInstruction> desugarInstruction(
-      CfInstruction instruction,
-      FreshLocalProvider freshLocalProvider,
-      LocalStackAllocator localStackAllocator,
-      CfInstructionDesugaringEventConsumer eventConsumer,
-      ProgramMethod context,
-      MethodProcessingContext methodProcessingContext,
-      CfInstructionDesugaringCollection desugaringCollection,
-      DexItemFactory dexItemFactory) {
-    if (!instruction.isInvokeVirtual() && !instruction.isInvokeInterface()) {
-      return null;
-    }
-    CfInvoke invoke = instruction.asInvoke();
-    DexMethod method = invoke.getMethod();
-    DexEncodedMethod privateMethod = privateMethodInvokedOnSelf(invoke, context);
-    if (privateMethod == null) {
-      return null;
-    }
-    return ImmutableList.of(new CfInvoke(Opcodes.INVOKESPECIAL, method, invoke.isInterface()));
-  }
-
-  @Override
-  public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
-    if (!instruction.isInvokeVirtual() && !instruction.isInvokeInterface()) {
-      return false;
-    }
-    return isInvokingPrivateMethodOnSelf(instruction.asInvoke(), context);
-  }
-
-  private DexEncodedMethod privateMethodInvokedOnSelf(CfInvoke invoke, ProgramMethod context) {
-    DexMethod method = invoke.getMethod();
-    if (method.getHolderType() != context.getHolderType()) {
-      return null;
-    }
-    DexEncodedMethod directTarget = context.getHolder().lookupDirectMethod(method);
-    if (directTarget != null && !directTarget.isStatic()) {
-      assert method.holder == directTarget.getHolderType();
-      return directTarget;
-    }
-    return null;
-  }
-
-  private boolean isInvokingPrivateMethodOnSelf(CfInvoke invoke, ProgramMethod context) {
-    return privateMethodInvokedOnSelf(invoke, context) != null;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index f68df08..4ee8f70 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -121,7 +121,6 @@
     desugarings.add(new LambdaInstructionDesugaring(appView));
     desugarings.add(new ConstantDynamicInstructionDesugaring(appView));
     desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
-    desugarings.add(new InvokeToPrivateRewriter());
     desugarings.add(new StringConcatInstructionDesugaring(appView));
     desugarings.add(new BufferCovariantReturnTypeRewriter(appView));
     if (backportedMethodRewriter.hasBackports()) {
@@ -154,7 +153,6 @@
     NonEmptyCfInstructionDesugaringCollection desugaringCollection =
         new NonEmptyCfInstructionDesugaringCollection(appView, noAndroidApiLevelCompute());
     desugaringCollection.desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
-    desugaringCollection.desugarings.add(new InvokeToPrivateRewriter());
     desugaringCollection.yieldingDesugarings.add(
         new UnrepresentableInDexInstructionRemover(appView));
     return desugaringCollection;
@@ -360,8 +358,7 @@
           //  identification is explicitly non-overlapping and remove the exceptions below.
           assert !alsoApplicable
                   || (appliedDesugaring instanceof InterfaceMethodRewriter
-                      && (desugaring instanceof InvokeToPrivateRewriter
-                          || desugaring instanceof NestBasedAccessDesugaring))
+                      && desugaring instanceof NestBasedAccessDesugaring)
                   || (appliedDesugaring instanceof TwrInstructionDesugaring
                       && desugaring instanceof InterfaceMethodRewriter)
               : "Desugaring of "
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index af1ed03..90eb8a9 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.Add;
 import com.android.tools.r8.ir.code.And;
@@ -200,6 +201,11 @@
     numberOfArgumentRegisters = argumentRegisters;
   }
 
+  @Override
+  public ProgramMethod getProgramMethod() {
+    return code.context();
+  }
+
   /**
    * Perform register allocation for the IRCode.
    */
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
index 9cdc715..28fe1f9 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
@@ -3,12 +3,15 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.regalloc;
 
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.utils.InternalOptions;
 import java.util.List;
 
 public interface RegisterAllocator {
+  ProgramMethod getProgramMethod();
+
   void allocateRegisters();
   int registersUsed();
   int getRegisterForValue(Value value, int instructionNumber);
diff --git a/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java b/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java
index 1d88df8..679772d 100644
--- a/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java
@@ -12,17 +12,22 @@
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
 import java.io.IOException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
 import org.objectweb.asm.Opcodes;
 
 @RunWith(Parameterized.class)
 public class InterfaceInvokePrivateTest extends TestBase implements Opcodes {
 
-  private final TestParameters parameters;
-  private final CfVersion inputCfVersion;
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameter(1)
+  public CfVersion inputCfVersion;
 
   @Parameterized.Parameters(name = "{0}, Input CfVersion = {1}")
   public static Iterable<?> data() {
@@ -31,16 +36,7 @@
         CfVersion.rangeInclusive(CfVersion.V1_8, CfVersion.V15));
   }
 
-  public InterfaceInvokePrivateTest(TestParameters parameters, CfVersion inputCfVersion) {
-    this.parameters = parameters;
-    this.inputCfVersion = inputCfVersion;
-  }
-
-  private boolean isNotDesugaredAndCfRuntimeOlderThanJDK11(DesugarTestConfiguration configuration) {
-    return DesugarTestConfiguration.isNotDesugared(configuration)
-        && parameters.getRuntime().isCf()
-        && parameters.getRuntime().asCf().isOlderThan(CfVm.JDK11);
-  }
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!", "21", "6");
 
   private boolean isInputCfVersionSupported() {
     return inputCfVersion.isLessThanOrEqualTo(
@@ -68,7 +64,7 @@
             parameters.getRuntime().asCf().isOlderThan(CfVm.JDK11),
             r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class),
             // Succeeds on VMs from JDK 11 regardless of the CF version of the input.
-            r -> r.assertSuccessWithOutputLines("Hello, world!"));
+            r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
   }
 
   @Test
@@ -88,12 +84,17 @@
                     containsString(
                         "more recent version of the Java Runtime (class file version "
                             + inputCfVersion.toString())),
-            // Running un-desugared on a JVM with a supported CF version fails on pre JVM 11. On
-            // JVM 11 and above this succeeds even of the input CF version is below 55.
-            this::isNotDesugaredAndCfRuntimeOlderThanJDK11,
+            // Running without interface method desugaring on a JVM with a supported CF version
+            // fails on pre JVM 11. On JVM 11 and above this succeeds even of the input CF version
+            // is below 55.
+            c ->
+                parameters.getRuntime().isCf()
+                    && parameters.getRuntime().asCf().isOlderThan(CfVm.JDK11)
+                    && (DesugarTestConfiguration.isNotDesugared(c)
+                        || parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)),
             r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class),
             // All other conditions succeed.
-            r -> r.assertSuccessWithOutputLines("Hello, world!"));
+            r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
   }
 
   @Test
@@ -121,7 +122,7 @@
                 && parameters.getRuntime().asCf().isOlderThan(CfVm.JDK11),
             r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class),
             // All other conditions succeed.
-            r -> r.assertSuccessWithOutputLines("Hello, world!"));
+            r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
   }
 
   private byte[] transformIToPrivate(CfVersion version) throws NoSuchMethodException, IOException {
@@ -145,10 +146,20 @@
     /* private */ default String privateHello() {
       return "Hello, world!";
     }
+    /* private */ default String privateHello(int i1, int i2, int i3, int i4, int i5, int i6) {
+      return "" + (i1 + i2 + i3 + i4 + i5 + i6);
+    }
+    /* private */ default String privateHello(long l1, long l2, long l3) {
+      return "" + (l1 + l2 + l3);
+    }
 
     default String hello() {
       // The private method "privateHello" is called with invokeinterface.
-      return privateHello();
+      return privateHello()
+          + "\n"
+          + privateHello(1, 2, 3, 4, 5, 6)
+          + "\n"
+          + privateHello(1L, 2L, 3L);
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/VirtualInvokePrivateTest.java b/src/test/java/com/android/tools/r8/desugar/VirtualInvokePrivateTest.java
index 55de73b..7b1eea2 100644
--- a/src/test/java/com/android/tools/r8/desugar/VirtualInvokePrivateTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/VirtualInvokePrivateTest.java
@@ -3,29 +3,26 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.desugar;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assume.assumeTrue;
 
-import com.android.tools.r8.DesugarTestConfiguration;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.StringUtils;
 import java.io.IOException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
 import org.objectweb.asm.Opcodes;
 
 @RunWith(Parameterized.class)
 public class VirtualInvokePrivateTest extends TestBase implements Opcodes {
 
-  private final TestParameters parameters;
+  @Parameter(0)
+  public TestParameters parameters;
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
@@ -36,26 +33,7 @@
         .build();
   }
 
-  public VirtualInvokePrivateTest(TestParameters parameters) {
-    this.parameters = parameters;
-  }
-
-  private boolean isNotDesugaredAndCfRuntimeNewerThanOrEqualToJDK11(
-      DesugarTestConfiguration configuration) {
-    return DesugarTestConfiguration.isNotDesugared(configuration)
-        && parameters.getRuntime().isCf()
-        && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11);
-  }
-
-  private void inspectNotDesugared(CodeInspector inspector) {
-    MethodSubject main = inspector.clazz(TestRunner.class).uniqueMethodWithName("main");
-    assertEquals(2, main.streamInstructions().filter(InstructionSubject::isInvokeVirtual).count());
-  }
-
-  private void inspectDesugared(CodeInspector inspector) {
-    MethodSubject main = inspector.clazz(TestRunner.class).uniqueMethodWithName("main");
-    assertEquals(1, main.streamInstructions().filter(InstructionSubject::isInvokeVirtual).count());
-  }
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!", "21", "6");
 
   @Test
   public void testReference() throws Exception {
@@ -65,18 +43,7 @@
     testForJvm()
         .addProgramClassFileData(transformInvokeSpecialToInvokeVirtual())
         .run(parameters.getRuntime(), TestRunner.class)
-        .assertSuccessWithOutputLines("Hello, world!");
-  }
-
-  @Test
-  public void testDesugar() throws Exception {
-    testForDesugaring(parameters)
-        .addProgramClassFileData(transformInvokeSpecialToInvokeVirtual())
-        .run(parameters.getRuntime(), TestRunner.class)
-        .inspectIf(
-            this::isNotDesugaredAndCfRuntimeNewerThanOrEqualToJDK11, this::inspectNotDesugared)
-        .inspectIf(DesugarTestConfiguration::isDesugared, this::inspectDesugared)
-        .assertSuccessWithOutputLines("Hello, world!");
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
 
   @Test
@@ -86,7 +53,7 @@
         .addKeepMainRule(TestRunner.class)
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), TestRunner.class)
-        .assertSuccessWithOutputLines("Hello, world!");
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
 
   private byte[] transformInvokeSpecialToInvokeVirtual() throws IOException {
@@ -110,9 +77,19 @@
       return "Hello, world!";
     }
 
+    private String hello(int i1, int i2, int i3, int i4, int i5, int i6) {
+      return "" + (i1 + i2 + i3 + i4 + i5 + i6);
+    }
+
+    private String hello(long l1, long l2, long l3) {
+      return "" + (l1 + l2 + l3);
+    }
+
     public static void main(String[] args) {
-      // The private method "hello" is called with invokevirtual.
+      // The 3 private methods "hello" are called with invokevirtual.
       System.out.println(new TestRunner().hello());
+      System.out.println(new TestRunner().hello(1, 2, 3, 4, 5, 6));
+      System.out.println(new TestRunner().hello(1L, 2L, 3L));
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
index 4319780..c8760d8 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
@@ -6,6 +6,12 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.Add;
 import com.android.tools.r8.ir.code.BasicBlock;
@@ -21,6 +27,31 @@
 public class IdenticalAfterRegisterAllocationTest {
 
   private static class MockRegisterAllocator implements RegisterAllocator {
+    private final ProgramMethod mockMethod;
+
+    MockRegisterAllocator() {
+      DexItemFactory dexItemFactory = new DexItemFactory();
+      DexProgramClass clazz = DexProgramClass.createMockClassForTesting(new DexItemFactory());
+      DexMethod signature =
+          dexItemFactory.createMethod(
+              clazz.type,
+              dexItemFactory.createProto(dexItemFactory.voidType),
+              dexItemFactory.createString("mock"));
+      mockMethod =
+          new ProgramMethod(
+              clazz,
+              DexEncodedMethod.builder()
+                  .setMethod(signature)
+                  .setAccessFlags(MethodAccessFlags.fromDexAccessFlags(0))
+                  .disableAndroidApiLevelCheck()
+                  .build());
+    }
+
+    @Override
+    public ProgramMethod getProgramMethod() {
+      return mockMethod;
+    }
+
     @Override
     public void allocateRegisters() {
     }
diff --git a/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java b/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java
index 81f261c..9dc3d7f 100644
--- a/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java
+++ b/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java
@@ -4,11 +4,6 @@
 
 package com.android.tools.r8.regress.b191296688;
 
-import static com.android.tools.r8.utils.codeinspector.CodeMatchers.isInvokeWithTarget;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
 import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
 import com.android.tools.r8.KotlinTestBase;
@@ -17,10 +12,6 @@
 import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.utils.DescriptorUtils;
-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.MethodSubject;
 import java.nio.file.Path;
 import java.util.Collection;
 import org.junit.Test;
@@ -67,7 +58,6 @@
             .addProgramClasses(A.class)
             .setMinApi(parameters.getApiLevel())
             .compile()
-            .inspect(this::verifyDirectCallToPrivate)
             .writeToZip();
     testForD8()
         .addProgramFiles(desugaredJar)
@@ -76,18 +66,4 @@
         .run(parameters.getRuntime(), PKG + ".BKt")
         .assertSuccessWithOutputLines("hep");
   }
-
-  private void verifyDirectCallToPrivate(CodeInspector inspector) {
-    ClassSubject bClassSubject = inspector.clazz(PKG + ".B");
-    MethodSubject proceedMethodSubject = bClassSubject.uniqueMethodWithName("proceed");
-    assertThat(proceedMethodSubject, isPresent());
-    assertTrue(
-        bClassSubject.allMethods().stream()
-            .anyMatch(
-                method ->
-                    method
-                        .streamInstructions()
-                        .filter(InstructionSubject::isInvokeSpecialOrDirect)
-                        .anyMatch(isInvokeWithTarget(proceedMethodSubject))));
-  }
 }