Reland "Don't use accessors for lambdas to interface companion methods."

This reverts commit a4aa2f9bf90f60e878bc02b3a5ac67e6b1318eb0.

Change-Id: I036ef3468ff368d6c911bc502f4d6551ff4f8e73
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index 71bc104..227270d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -28,6 +28,7 @@
     INVOKE_DIRECT((short) 0x07),
     INVOKE_INTERFACE((short) 0x08),
     // Internal method handle needed by lambda desugaring.
+    // TODO(b/200254463): Remove this now that lambda desugaring is CF/CF.
     INVOKE_SUPER((short) 0x09);
 
     private final short value;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 9b2a145..2e9fcab 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -75,6 +75,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (!instruction.isInvoke()) {
       return null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BufferCovariantReturnTypeRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BufferCovariantReturnTypeRewriter.java
index ea1cd46..4d9ec0e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BufferCovariantReturnTypeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BufferCovariantReturnTypeRewriter.java
@@ -38,6 +38,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (!isInvokeCandidate(instruction)) {
       return null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
index 513a9d5..810812a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
@@ -40,6 +40,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory);
 
   /**
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
index ada494f..5053f86 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.desugar;
 
 import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -15,6 +16,7 @@
 import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
 import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
 import com.android.tools.r8.utils.ThrowingConsumer;
+import java.util.Collection;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -56,6 +58,15 @@
       MethodProcessingContext methodProcessingContext,
       CfInstructionDesugaringEventConsumer eventConsumer);
 
+  /** Selective desugaring of a single invoke instruction assuming a given context. */
+  public abstract Collection<CfInstruction> desugarInstruction(
+      CfInstruction instruction,
+      FreshLocalProvider freshLocalProvider,
+      LocalStackAllocator localStackAllocator,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramMethod context,
+      MethodProcessingContext methodProcessingContext);
+
   public boolean isEmpty() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
index 027b27c..ef793c4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.desugar;
 
+import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
@@ -13,6 +14,7 @@
 import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
 import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
 import com.android.tools.r8.utils.ThrowingConsumer;
+import java.util.Collection;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -47,6 +49,18 @@
   }
 
   @Override
+  public Collection<CfInstruction> desugarInstruction(
+      CfInstruction instruction,
+      FreshLocalProvider freshLocalProvider,
+      LocalStackAllocator localStackAllocator,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramMethod context,
+      MethodProcessingContext methodProcessingContext) {
+    // Nothing to desugar.
+    return null;
+  }
+
+  @Override
   public boolean isEmpty() {
     return true;
   }
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
index 7fd31a8..65f23dc 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InvokeToPrivateRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InvokeToPrivateRewriter.java
@@ -35,6 +35,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (!instruction.isInvokeVirtual() && !instruction.isInvokeInterface()) {
       return null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index afedd24..2c815a1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -33,6 +33,7 @@
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.desugar.lambda.ForcefullyMovedLambdaMethodConsumer;
 import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring.DesugarInvoke;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
@@ -40,6 +41,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.function.Consumer;
+import org.objectweb.asm.Opcodes;
 
 /**
  * Represents lambda class generated for a lambda descriptor in context of lambda instantiation
@@ -81,7 +83,8 @@
       AppView<?> appView,
       LambdaInstructionDesugaring desugaring,
       ProgramMethod accessedFrom,
-      LambdaDescriptor descriptor) {
+      LambdaDescriptor descriptor,
+      DesugarInvoke desugarInvoke) {
     assert desugaring != null;
     assert descriptor != null;
     this.type = builder.getType();
@@ -105,7 +108,7 @@
         !stateless ? null : factory.createField(type, type, factory.lambdaInstanceFieldName);
 
     // Synthesize the program class once all fields are set.
-    synthesizeLambdaClass(builder);
+    synthesizeLambdaClass(builder, desugarInvoke);
   }
 
   public final DexProgramClass getLambdaProgramClass() {
@@ -124,12 +127,13 @@
     this.clazz = clazz;
   }
 
-  private void synthesizeLambdaClass(SyntheticProgramClassBuilder builder) {
+  private void synthesizeLambdaClass(
+      SyntheticProgramClassBuilder builder, DesugarInvoke desugarInvoke) {
     builder.setInterfaces(descriptor.interfaces);
     synthesizeStaticFields(builder);
     synthesizeInstanceFields(builder);
     synthesizeDirectMethods(builder);
-    synthesizeVirtualMethods(builder);
+    synthesizeVirtualMethods(builder, desugarInvoke);
   }
 
   final DexField getCaptureField(int index) {
@@ -146,7 +150,8 @@
   }
 
   // Synthesize virtual methods.
-  private void synthesizeVirtualMethods(SyntheticProgramClassBuilder builder) {
+  private void synthesizeVirtualMethods(
+      SyntheticProgramClassBuilder builder, DesugarInvoke desugarInvoke) {
     DexMethod mainMethod =
         appView.dexItemFactory().createMethod(type, descriptor.erasedProto, descriptor.name);
 
@@ -159,7 +164,7 @@
             .setAccessFlags(
                 MethodAccessFlags.fromSharedAccessFlags(
                     Constants.ACC_PUBLIC | Constants.ACC_FINAL, false))
-            .setCode(LambdaMainMethodSourceCode.build(this, mainMethod))
+            .setCode(LambdaMainMethodSourceCode.build(this, mainMethod, desugarInvoke))
             // The api level is computed when tracing.
             .disableAndroidApiLevelCheck()
             .build());
@@ -261,6 +266,21 @@
     }
   }
 
+  public static int getAsmOpcodeForInvokeType(MethodHandleType type) {
+    switch (type) {
+      case INVOKE_INTERFACE:
+        return Opcodes.INVOKEINTERFACE;
+      case INVOKE_STATIC:
+        return Opcodes.INVOKESTATIC;
+      case INVOKE_DIRECT:
+        return Opcodes.INVOKESPECIAL;
+      case INVOKE_INSTANCE:
+        return Opcodes.INVOKEVIRTUAL;
+      default:
+        throw new Unreachable("Unexpected method handle type: " + type);
+    }
+  }
+
   // Creates a delegation target for this particular lambda class. Note that we
   // should always be able to create targets for the lambdas we support.
   private Target createTarget(ProgramMethod accessedFrom) {
@@ -287,12 +307,22 @@
   }
 
   private boolean doesNotNeedAccessor(ProgramMethod accessedFrom) {
-    return canAccessModifyLambdaImplMethod() || !descriptor.needsAccessor(accessedFrom);
+    return canAccessModifyLambdaImplMethod()
+        || isPrivateOrStaticInterfaceMethodInvokeThatWillBeDesugared()
+        || !descriptor.needsAccessor(accessedFrom);
+  }
+
+  private boolean isPrivateOrStaticInterfaceMethodInvokeThatWillBeDesugared() {
+    return appView.options().isInterfaceMethodDesugaringEnabled()
+        && descriptor.implHandle.isInterface
+        && (descriptor.implHandle.type.isInvokeDirect()
+            || descriptor.implHandle.type.isInvokeStatic());
   }
 
   private boolean canAccessModifyLambdaImplMethod() {
     MethodHandleType invokeType = descriptor.implHandle.type;
     return appView.options().canAccessModifyLambdaImplementationMethods(appView)
+        && !isPrivateOrStaticInterfaceMethodInvokeThatWillBeDesugared()
         && (invokeType.isInvokeDirect() || invokeType.isInvokeStatic())
         && descriptor.delegatesToLambdaImplMethod(appView.dexItemFactory())
         && !desugaring.isDirectTargetedLambdaImplementationMethod(descriptor.implHandle);
@@ -328,7 +358,7 @@
 
     assert implHandle.type.isInvokeDirect();
     // If the lambda$ method is an instance-private method on an interface we convert it into a
-    // public static method as it will be placed on the companion class.
+    // public static method so it is accessible.
     if (appView.definitionFor(implMethod.holder).isInterface()) {
       DexProto implProto = implMethod.proto;
       DexType[] implParams = implProto.parameters.values;
@@ -367,7 +397,11 @@
 
     if (doesNotNeedAccessor(accessedFrom)) {
       return new NoAccessorMethodTarget(
-          descriptor.implHandle.asMethod(), Type.VIRTUAL, descriptor.implHandle.isInterface);
+          descriptor.implHandle.asMethod(),
+          descriptor.implHandle.type.isInvokeDirect()
+              ? Type.DIRECT
+              : descriptor.implHandle.type.isInvokeStatic() ? Type.STATIC : Type.VIRTUAL,
+          descriptor.implHandle.isInterface);
     }
     // We need to generate an accessor method in `accessedFrom` class/interface
     // for accessing the original instance impl-method. Note that impl-method's
@@ -523,7 +557,7 @@
   }
 
   // Used for targeting methods referenced directly without creating accessors.
-  private static final class NoAccessorMethodTarget extends Target {
+  public static final class NoAccessorMethodTarget extends Target {
 
     NoAccessorMethodTarget(DexMethod method, Type invokeType, boolean isInterface) {
       super(method, invokeType, isInterface);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
index e0a9c07..acfee7b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -25,13 +25,18 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.LambdaClass.InvalidLambdaImplTarget;
+import com.android.tools.r8.ir.desugar.LambdaClass.NoAccessorMethodTarget;
+import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring.DesugarInvoke;
+import com.android.tools.r8.utils.IntBox;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
 import com.google.common.collect.Lists;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import org.objectweb.asm.Opcodes;
 
@@ -166,7 +171,8 @@
     }
   }
 
-  public static CfCode build(LambdaClass lambda, DexMethod mainMethod) {
+  public static CfCode build(
+      LambdaClass lambda, DexMethod mainMethod, DesugarInvoke desugarInvoke) {
     DexItemFactory factory = lambda.appView.dexItemFactory();
     LambdaClass.Target target = lambda.target;
     if (target instanceof InvalidLambdaImplTarget) {
@@ -184,11 +190,13 @@
 
     // Only constructor call should use direct invoke type since super
     // and private methods require accessor methods.
-    boolean constructorTarget = target.invokeType == Invoke.Type.DIRECT;
-    assert !constructorTarget || methodToCall.name == factory.constructorMethodName;
+    boolean constructorTarget = methodToCall.name == factory.constructorMethodName;
+    assert !constructorTarget || target.invokeType == Type.DIRECT;
 
     boolean targetWithReceiver =
-        target.invokeType == Invoke.Type.VIRTUAL || target.invokeType == Invoke.Type.INTERFACE;
+        target.invokeType == Invoke.Type.VIRTUAL
+            || target.invokeType == Invoke.Type.INTERFACE
+            || (target.invokeType == Type.DIRECT && !constructorTarget);
     List<DexType> implReceiverAndArgs = new ArrayList<>();
     if (targetWithReceiver) {
       implReceiverAndArgs.add(methodToCall.holder);
@@ -241,8 +249,24 @@
               erasedParams[i], enforcedParams[i], expectedParamType, instructions, factory);
     }
 
-    instructions.add(
-        new CfInvoke(target.invokeType.getCfOpcode(), methodToCall, target.isInterface()));
+    CfInvoke invoke =
+        new CfInvoke(target.invokeType.getCfOpcode(), methodToCall, target.isInterface());
+    if (target instanceof NoAccessorMethodTarget) {
+      IntBox locals = new IntBox();
+      IntBox stack = new IntBox();
+      Collection<CfInstruction> is =
+          desugarInvoke.desugarInvoke(invoke, locals::getAndIncrement, stack::getAndIncrement);
+      if (is != null) {
+        instructions.addAll(is);
+        maxLocals += locals.get();
+        maxStack += stack.get();
+      } else {
+        instructions.add(invoke);
+      }
+    } else {
+      instructions.add(invoke);
+    }
+
     DexType methodToCallReturnType = methodToCall.getReturnType();
     if (!methodToCallReturnType.isVoidType()) {
       maxStack =
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 da0a96a..596f4cc 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
@@ -254,7 +254,8 @@
     return true;
   }
 
-  private Collection<CfInstruction> desugarInstruction(
+  @Override
+  public Collection<CfInstruction> desugarInstruction(
       CfInstruction instruction,
       FreshLocalProvider freshLocalProvider,
       LocalStackAllocator localStackAllocator,
@@ -273,6 +274,7 @@
               eventConsumer,
               context,
               methodProcessingContext,
+              this,
               appView.dexItemFactory());
       if (replacement != null) {
         assert desugaring.needsDesugaring(instruction, context);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java
index 8ef2e18..1d55e7e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
@@ -46,6 +47,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (instruction.isConstDynamic()) {
       return desugarConstDynamicInstruction(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
index 4634977..2b6c9d4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
@@ -97,6 +98,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (needsDesugaring(instruction, context)) {
       assert instruction.isInvoke();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java
index 73d7f8f..5c6d6c1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
@@ -69,6 +70,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     InvokeRetargetingResult invokeRetargetingResult = computeNewInvokeTarget(instruction, context);
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
index 28b6981..5c89290 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.DesugarDescription;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
@@ -47,6 +48,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     return computeDesugarDescription(instruction)
         .desugarInstruction(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
index dfb797c..428bdd0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
@@ -73,6 +74,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (instruction.isInvokeSpecial()) {
       return desugarInvokeInstruction(instruction.asInvoke(), eventConsumer, context);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 87e0d65..db52706 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.DesugarDescription;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
@@ -248,6 +249,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     assert !isSyntheticMethodThatShouldNotBeDoubleProcessed(context);
     return computeDescription(instruction, context)
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
index 71010c3..206c276 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LambdaClass;
@@ -75,6 +76,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (instruction.isInvokeDynamic()) {
       return desugarInvokeDynamicInstruction(
@@ -83,19 +85,36 @@
           localStackAllocator,
           eventConsumer,
           context,
-          methodProcessingContext);
+          methodProcessingContext,
+          (invoke, localProvider, stackAllocator) ->
+              desugaringCollection.desugarInstruction(
+                  invoke,
+                  localProvider,
+                  stackAllocator,
+                  eventConsumer,
+                  context,
+                  methodProcessingContext));
     }
     return null;
   }
 
+  public interface DesugarInvoke {
+    Collection<CfInstruction> desugarInvoke(
+        CfInvoke invoke,
+        FreshLocalProvider freshLocalProvider,
+        LocalStackAllocator localStackAllocator);
+  }
+
   private Collection<CfInstruction> desugarInvokeDynamicInstruction(
       CfInvokeDynamic invoke,
       FreshLocalProvider freshLocalProvider,
       LocalStackAllocator localStackAllocator,
       LambdaDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
-      MethodProcessingContext methodProcessingContext) {
-    LambdaClass lambdaClass = createLambdaClass(invoke, context, methodProcessingContext);
+      MethodProcessingContext methodProcessingContext,
+      DesugarInvoke desugarInvoke) {
+    LambdaClass lambdaClass =
+        createLambdaClass(invoke, context, methodProcessingContext, desugarInvoke);
     if (lambdaClass == null) {
       return null;
     }
@@ -134,7 +153,8 @@
   private LambdaClass createLambdaClass(
       CfInvokeDynamic invoke,
       ProgramMethod context,
-      MethodProcessingContext methodProcessingContext) {
+      MethodProcessingContext methodProcessingContext,
+      DesugarInvoke desugarInvoke) {
     LambdaDescriptor descriptor =
         LambdaDescriptor.tryInfer(invoke.getCallSite(), appView.appInfoForDesugaring(), context);
     if (descriptor == null) {
@@ -149,7 +169,10 @@
                 SyntheticNaming.SyntheticKind.LAMBDA,
                 methodProcessingContext.createUniqueContext(),
                 appView,
-                builder -> box.set(new LambdaClass(builder, appView, this, context, descriptor)));
+                builder ->
+                    box.set(
+                        new LambdaClass(
+                            builder, appView, this, context, descriptor, desugarInvoke)));
     // Immediately set the actual program class on the lambda.
     LambdaClass lambdaClass = box.get();
     lambdaClass.setClass(clazz);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
index 87adb53..f9ca2e9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
@@ -31,6 +31,7 @@
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
@@ -268,6 +269,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (instruction.isFieldInstruction()) {
       return desugarFieldInstruction(instruction.asFieldInstruction(), context, eventConsumer);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
index babb6f7..f508ca6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
@@ -40,6 +40,7 @@
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
@@ -167,6 +168,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     assert !instruction.isInitClass();
     if (!needsDesugaring(instruction, context)) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/stringconcat/StringConcatInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/stringconcat/StringConcatInstructionDesugaring.java
index 607b088..065fc4c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/stringconcat/StringConcatInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/stringconcat/StringConcatInstructionDesugaring.java
@@ -29,6 +29,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
@@ -76,6 +77,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (instruction.isInvokeDynamic()) {
       // We are interested in bootstrap methods StringConcatFactory::makeConcat
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
index ee71bc0..716b6e2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
@@ -57,6 +58,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (!instruction.isInvoke()) {
       return null;
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaPrivateInstanceInterfaceMethodWithNonLambdaCallSiteTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaPrivateInstanceInterfaceMethodWithNonLambdaCallSiteTest.java
index 7d0b2fa..6961fe9 100644
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaPrivateInstanceInterfaceMethodWithNonLambdaCallSiteTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaPrivateInstanceInterfaceMethodWithNonLambdaCallSiteTest.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar.lambdas;
 
+import static org.junit.Assert.assertFalse;
+
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -41,7 +43,21 @@
         .addProgramClasses(Main.class, A.class, FunctionalInterface.class)
         .addProgramClassFileData(getProgramClassFileData())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("Hello world!", "Hello world!");
+        .assertSuccessWithOutputLines("Hello world!", "Hello world!")
+        .inspect(
+            inspector -> {
+              if (parameters.isDexRuntime()
+                  && !parameters.canUseDefaultAndStaticInterfaceMethods()) {
+                inspector
+                    .clazz(I.class)
+                    .toCompanionClass()
+                    .forAllMethods(
+                        m ->
+                            // We don't expect any synthetic accessors to be needed for the private
+                            // interface method.
+                            assertFalse("Unexpected synthetic method: " + m, m.isSynthetic()));
+              }
+            });
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaWithPrivateInterfaceInvokeTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaWithPrivateInterfaceInvokeTest.java
new file mode 100644
index 0000000..d66b1d2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaWithPrivateInterfaceInvokeTest.java
@@ -0,0 +1,114 @@
+// 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.desugar.lambdas;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class LambdaWithPrivateInterfaceInvokeTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello world");
+
+  private final TestParameters parameters;
+  private final boolean useInvokeSpecial;
+
+  @Parameterized.Parameters(name = "{0}, invokespecial:{1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+  }
+
+  public LambdaWithPrivateInterfaceInvokeTest(TestParameters parameters, boolean useInvokeSpecial) {
+    this.parameters = parameters;
+    this.useInvokeSpecial = useInvokeSpecial;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(TestClass.class, MyFun.class, A.class)
+        .addProgramClassFileData(getTransformForI())
+        .run(parameters.getRuntime(), TestClass.class)
+        // On JDK 8 and 9 the VM will fail if not targeting with invoke special.
+        .applyIf(
+            !useInvokeSpecial
+                && parameters.isCfRuntime()
+                && parameters.asCfRuntime().isOlderThan(CfVm.JDK11),
+            r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class),
+            r -> r.assertSuccessWithOutput(EXPECTED));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(TestClass.class, MyFun.class, A.class)
+        .addProgramClassFileData(getTransformForI())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  private byte[] getTransformForI() throws Exception {
+    return transformer(I.class)
+        .setPrivate(I.class.getDeclaredMethod("bar"))
+        .transformMethodInsnInMethod(
+            "lambda$foo$0",
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              if (name.equals("bar")) {
+                assertEquals(Opcodes.INVOKEINTERFACE, opcode);
+                visitor.visitMethodInsn(
+                    useInvokeSpecial ? Opcodes.INVOKESPECIAL : Opcodes.INVOKEINTERFACE,
+                    owner,
+                    name,
+                    descriptor,
+                    isInterface);
+              } else {
+                visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+              }
+            })
+        .transform();
+  }
+
+  interface I {
+    /* private */ default String bar() {
+      return "Hello world";
+    }
+
+    default void foo() {
+      TestClass.run(
+          () -> {
+            System.out.println(bar());
+          });
+    }
+  }
+
+  interface MyFun {
+    void run();
+  }
+
+  static class A implements I {}
+
+  static class TestClass {
+
+    public static void run(MyFun fn) {
+      fn.run();
+    }
+
+    public static void main(String[] args) {
+      new A().foo();
+    }
+  }
+}