Version 1.6.33

Cherry-pick: Avoid that force inlining marks are overridden by delayed
optimization feedback
CL: https://r8-review.googlesource.com/c/r8/+/43662

Cherry-pick: Move ServiceLoader desugaring to CfCode to support catch
handler
CL: https://r8-review.googlesource.com/c/r8/+/43744

Bug: 141735091
Bug: 141290856
Change-Id: I800bbd7582845a1ee10fb730c130533348c15d97
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 02abab8..753d1b1 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.6.32";
+  public static final String LABEL = "1.6.33";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 36e4cc8..e52c246 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -213,6 +213,8 @@
       createString("Ljava/lang/reflect/InvocationHandler;");
   public final DexString proxyDescriptor = createString("Ljava/lang/reflect/Proxy;");
   public final DexString serviceLoaderDescriptor = createString("Ljava/util/ServiceLoader;");
+  public final DexString serviceLoaderConfigurationErrorDescriptor =
+      createString("Ljava/util/ServiceConfigurationError;");
   public final DexString listDescriptor = createString("Ljava/util/List;");
   public final DexString comparatorDescriptor = createString("Ljava/util/Comparator;");
   public final DexString callableDescriptor = createString("Ljava/util/concurrent/Callable;");
@@ -301,6 +303,8 @@
   public final DexType invocationHandlerType = createType(invocationHandlerDescriptor);
   public final DexType proxyType = createType(proxyDescriptor);
   public final DexType serviceLoaderType = createType(serviceLoaderDescriptor);
+  public final DexType serviceLoaderConfigurationErrorType =
+      createType(serviceLoaderConfigurationErrorDescriptor);
   public final DexType listType = createType(listDescriptor);
   public final DexType comparatorType = createType(comparatorDescriptor);
   public final DexType callableType = createType(callableDescriptor);
@@ -574,6 +578,7 @@
   public class ThrowableMethods {
 
     public final DexMethod addSuppressed;
+    public final DexMethod getMessage;
     public final DexMethod getSuppressed;
     public final DexMethod initCause;
 
@@ -584,6 +589,12 @@
           createString("getSuppressed"), throwableArrayDescriptor, DexString.EMPTY_ARRAY);
       initCause = createMethod(throwableDescriptor, createString("initCause"), throwableDescriptor,
           new DexString[] { throwableDescriptor });
+      getMessage =
+          createMethod(
+              throwableDescriptor,
+              createString("getMessage"),
+              stringDescriptor,
+              DexString.EMPTY_ARRAY);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 83ecf82..055b120 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
 import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
 import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
+import com.android.tools.r8.ir.optimize.ServiceLoaderRewriter;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -258,7 +259,8 @@
         || name.contains(OutlineOptions.CLASS_NAME)
         || name.contains(TwrCloseResourceRewriter.UTILITY_CLASS_NAME)
         || name.contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME)
-        || name.contains(BackportedMethodRewriter.UTILITY_CLASS_NAME_PREFIX);
+        || name.contains(BackportedMethodRewriter.UTILITY_CLASS_NAME_PREFIX)
+        || name.contains(ServiceLoaderRewriter.SERVICE_LOADER_CLASS_NAME);
   }
 
   public boolean isProgramType(DexDefinitionSupplier definitions) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index e370192..9c4215e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -161,6 +161,7 @@
   private final UninstantiatedTypeOptimization uninstantiatedTypeOptimization;
   private final TypeChecker typeChecker;
   private final DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
+  private final ServiceLoaderRewriter serviceLoaderRewriter;
 
   // Assumers that will insert Assume instructions.
   private final AliasIntroducer aliasIntroducer;
@@ -243,6 +244,7 @@
       this.d8NestBasedAccessDesugaring = null;
       this.stringSwitchRemover = null;
       this.desugaredLibraryAPIConverter = null;
+      this.serviceLoaderRewriter = null;
       return;
     }
     this.lambdaRewriter = options.enableDesugaring ? new LambdaRewriter(appView, this) : null;
@@ -309,6 +311,10 @@
               : null;
       this.typeChecker = new TypeChecker(appView.withLiveness());
       this.d8NestBasedAccessDesugaring = null;
+      this.serviceLoaderRewriter =
+          options.enableServiceLoaderRewriting
+              ? new ServiceLoaderRewriter(appView.withLiveness())
+              : null;
     } else {
       this.classInliner = null;
       this.classStaticizer = null;
@@ -326,6 +332,7 @@
       this.typeChecker = null;
       this.d8NestBasedAccessDesugaring =
           options.shouldDesugarNests() ? new D8NestBasedAccessDesugaring(appView) : null;
+      this.serviceLoaderRewriter = null;
     }
     this.stringSwitchRemover =
         options.isStringSwitchConversionEnabled()
@@ -717,6 +724,12 @@
     printPhase("Lambda merging finalization");
     finalizeLambdaMerging(application, feedback, builder, executorService);
 
+    if (serviceLoaderRewriter != null && serviceLoaderRewriter.getSynthesizedClass() != null) {
+      forEachSynthesizedServiceLoaderMethod(
+          executorService, serviceLoaderRewriter.getSynthesizedClass());
+      builder.addSynthesizedClass(serviceLoaderRewriter.getSynthesizedClass(), true);
+    }
+
     if (outliner != null) {
       printPhase("Outlining");
       timing.begin("IR conversion phase 3");
@@ -842,6 +855,24 @@
     ThreadUtils.awaitFutures(futures);
   }
 
+  private void forEachSynthesizedServiceLoaderMethod(
+      ExecutorService executorService, DexClass synthesizedClass) throws ExecutionException {
+    List<Future<?>> futures = new ArrayList<>();
+    for (DexEncodedMethod method : synthesizedClass.methods()) {
+      futures.add(
+          executorService.submit(
+              () -> {
+                IRCode code =
+                    method.buildIR(appView, appView.appInfo().originFor(method.method.holder));
+                assert code != null;
+                codeRewriter.rewriteMoveResult(code);
+                finalizeIR(method, code, OptimizationFeedbackIgnore.getInstance());
+                return null;
+              }));
+    }
+    ThreadUtils.awaitFutures(futures);
+  }
+
   private void collectLambdaMergingCandidates(DexApplication application) {
     if (lambdaMerger != null) {
       lambdaMerger.collectGroupCandidates(application, appView.withLiveness());
@@ -1092,9 +1123,9 @@
     // we will return with finalizeEmptyThrowingCode() above.
     assert code.verifyTypes(appView);
 
-    if (appView.enableWholeProgramOptimizations() && options.enableServiceLoaderRewriting) {
+    if (serviceLoaderRewriter != null) {
       assert appView.appInfo().hasLiveness();
-      ServiceLoaderRewriter.rewrite(code, appView.withLiveness());
+      serviceLoaderRewriter.rewrite(code);
     }
 
     if (classStaticizer != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
index 376e186..08aefe9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -19,6 +19,8 @@
 
 public interface MethodOptimizationFeedback {
 
+  void markForceInline(DexEncodedMethod method);
+
   void markInlinedIntoSingleCallSite(DexEncodedMethod method);
 
   void markMethodCannotBeKept(DexEncodedMethod method);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java
new file mode 100644
index 0000000..74b1631
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2019, 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 static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKESTATIC;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+
+import com.android.tools.r8.cf.code.CfArrayStore;
+import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLabel;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfNewArray;
+import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStore;
+import com.android.tools.r8.cf.code.CfThrow;
+import com.android.tools.r8.cf.code.CfTryCatch;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.ValueType;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+
+public class ServiceLoaderSourceCode {
+
+  // This is building the following implementation for service-loader rewriting:
+  // public static <S> Iterator<S> loadS() {
+  //   try {
+  //     return Arrays.asList(X, Y, Z).iterator();
+  //   } catch (Throwable t) {
+  //     throw new ServiceConfigurationError(t.getMessage(), t);
+  //   }
+  // }
+  public static CfCode generate(
+      DexType serviceType, List<DexClass> classes, DexItemFactory factory) {
+    ImmutableList.Builder<CfInstruction> builder = ImmutableList.builder();
+
+    CfLabel tryCatchStart = new CfLabel();
+    CfLabel tryCatchEnd = new CfLabel();
+
+    builder.add(
+        tryCatchStart,
+        new CfConstNumber(classes.size(), ValueType.INT),
+        new CfNewArray(factory.createArrayType(1, serviceType)));
+
+    for (int i = 0; i < classes.size(); i++) {
+      builder.add(
+          new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+          new CfConstNumber(i, ValueType.INT),
+          new CfNew(classes.get(i).type),
+          new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+          new CfInvoke(
+              INVOKESPECIAL,
+              factory.createMethod(
+                  classes.get(i).type,
+                  factory.createProto(factory.voidType),
+                  factory.constructorMethodName),
+              false),
+          new CfArrayStore(MemberType.OBJECT));
+    }
+
+    builder.add(
+        new CfInvoke(INVOKESTATIC, factory.utilArraysMethods.asList, false),
+        new CfInvoke(
+            INVOKEINTERFACE,
+            factory.createMethod(
+                factory.listType,
+                factory.createProto(factory.iteratorType),
+                factory.createString("iterator")),
+            true),
+        tryCatchEnd,
+        new CfReturn(ValueType.OBJECT));
+
+    // Build the exception handler.
+    CfLabel tryCatchHandler = new CfLabel();
+    builder.add(
+        tryCatchHandler,
+        new CfStore(ValueType.OBJECT, 0),
+        new CfNew(factory.serviceLoaderConfigurationErrorType),
+        new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+        new CfLoad(ValueType.OBJECT, 0),
+        new CfInvoke(INVOKEVIRTUAL, factory.throwableMethods.getMessage, false),
+        new CfLoad(ValueType.OBJECT, 0),
+        new CfInvoke(
+            INVOKESPECIAL,
+            factory.createMethod(
+                factory.serviceLoaderConfigurationErrorType,
+                factory.createProto(factory.voidType, factory.stringType, factory.throwableType),
+                factory.constructorMethodName),
+            false),
+        new CfThrow());
+
+    CfTryCatch cfTryCatch =
+        new CfTryCatch(
+            tryCatchStart,
+            tryCatchEnd,
+            ImmutableList.of(factory.throwableType),
+            ImmutableList.of(tryCatchHandler));
+
+    return new CfCode(
+        null, 5, 1, builder.build(), ImmutableList.of(cfTryCatch), ImmutableList.of());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index 89e0f2d..7830f6e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -4,33 +4,36 @@
 
 package com.android.tools.r8.ir.optimize;
 
-import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
-
+import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+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.DexProto;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
-import com.android.tools.r8.ir.code.ArrayPut;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.ir.code.ConstClass;
-import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.code.InvokeInterface;
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.InvokeVirtual;
-import com.android.tools.r8.ir.code.MemberType;
-import com.android.tools.r8.ir.code.NewArrayEmpty;
-import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.desugar.ServiceLoaderSourceCode;
+import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * ServiceLoaderRewriter will attempt to rewrite calls on the form of: ServiceLoader.load(X.class,
@@ -61,7 +64,24 @@
  */
 public class ServiceLoaderRewriter {
 
-  public static void rewrite(IRCode code, AppView<? extends AppInfoWithLiveness> appView) {
+  public static final String SERVICE_LOADER_CLASS_NAME = "$$ServiceLoaderMethods";
+  private static final String SERVICE_LOADER_METHOD_PREFIX_NAME = "$load";
+
+  private DexProgramClass synthesizedClass;
+  private ConcurrentHashMap<DexType, DexEncodedMethod> synthesizedServiceLoaders =
+      new ConcurrentHashMap<>();
+
+  private final AppView<? extends AppInfoWithLiveness> appView;
+
+  public ServiceLoaderRewriter(AppView<? extends AppInfoWithLiveness> appView) {
+    this.appView = appView;
+  }
+
+  public DexProgramClass getSynthesizedClass() {
+    return synthesizedClass;
+  }
+
+  public void rewrite(IRCode code) {
     DexItemFactory factory = appView.dexItemFactory();
     InstructionListIterator instructionIterator = code.instructionListIterator();
     while (instructionIterator.hasNext()) {
@@ -144,71 +164,106 @@
       }
 
       // We can perform the rewrite of the ServiceLoader.load call.
-      new Rewriter(appView, code, instructionIterator, serviceLoaderLoad)
-          .perform(classLoaderInvoke, constClass.getValue(), classes);
+      DexEncodedMethod synthesizedMethod =
+          synthesizedServiceLoaders.computeIfAbsent(
+              constClass.getValue(),
+              service -> {
+                DexEncodedMethod addedMethod = createSynthesizedMethod(service, classes);
+                if (appView.options().isGeneratingClassFiles()) {
+                  addedMethod.upgradeClassFileVersion(code.method.getClassFileVersion());
+                }
+                return addedMethod;
+              });
+
+      new Rewriter(code, instructionIterator, serviceLoaderLoad)
+          .perform(classLoaderInvoke, synthesizedMethod.method);
     }
   }
 
+  private DexEncodedMethod createSynthesizedMethod(DexType serviceType, List<DexClass> classes) {
+    DexType serviceLoaderType =
+        appView.dexItemFactory().createType("L" + SERVICE_LOADER_CLASS_NAME + ";");
+    if (synthesizedClass == null) {
+      synthesizedClass =
+          new DexProgramClass(
+              serviceLoaderType,
+              null,
+              new SynthesizedOrigin("Service Loader desugaring", getClass()),
+              ClassAccessFlags.fromDexAccessFlags(
+                  Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
+              appView.dexItemFactory().objectType,
+              DexTypeList.empty(),
+              appView.dexItemFactory().createString("ServiceLoader"),
+              null,
+              Collections.emptyList(),
+              null,
+              Collections.emptyList(),
+              DexAnnotationSet.empty(),
+              DexEncodedField.EMPTY_ARRAY, // Static fields.
+              DexEncodedField.EMPTY_ARRAY, // Instance fields.
+              DexEncodedMethod.EMPTY_ARRAY,
+              DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
+              appView.dexItemFactory().getSkipNameValidationForTesting());
+      appView.appInfo().addSynthesizedClass(synthesizedClass);
+    }
+    DexProto proto = appView.dexItemFactory().createProto(appView.dexItemFactory().iteratorType);
+    DexMethod method =
+        appView
+            .dexItemFactory()
+            .createMethod(
+                serviceLoaderType,
+                proto,
+                SERVICE_LOADER_METHOD_PREFIX_NAME + synthesizedServiceLoaders.size());
+    MethodAccessFlags methodAccess =
+        MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_STATIC, false);
+    DexEncodedMethod encodedMethod =
+        new DexEncodedMethod(
+            method,
+            methodAccess,
+            DexAnnotationSet.empty(),
+            ParameterAnnotationsList.empty(),
+            ServiceLoaderSourceCode.generate(serviceType, classes, appView.dexItemFactory()));
+    synthesizedClass.addDirectMethod(encodedMethod);
+    return encodedMethod;
+  }
+
   /**
-   * Rewriter will look assume that code is on the form:
+   * Rewriter assumes that the code is of the form:
    *
    * <pre>
    * ConstClass         v1 <- X
-   * ConstClass         v2 <- X
+   * ConstClass         v2 <- X or NULL
    * Invoke-Virtual     v3 <- v2; method: java.lang.ClassLoader java.lang.Class.getClassLoader()
    * Invoke-Static      v4 <- v1, v3; method: java.util.ServiceLoader java.util.ServiceLoader
    *     .load(java.lang.Class, java.lang.ClassLoader)
    * Invoke-Virtual     v5 <- v4; method: java.util.Iterator java.util.ServiceLoader.iterator()
    * </pre>
    *
-   * and rewrites it to for classes impl(X) defined in META-INF/services/X:
+   * and rewrites it to:
    *
    * <pre>
-   * ConstClass         v1 <- X
-   * ConstClass         v2 <- X
-   * ConstNumber        va <-  impl(X).size() (INT)
-   * NewArrayEmpty      vb <- va X[]
-   * for i = 0 to C - 1:
-   *   ConstNumber        vc(i) <-  i (INT)
-   *   NewInstance        vd <-  impl(X).get(i)
-   *   Invoke-Direct      vd; method: void impl(X).get(i).<init>()
-   *   ArrayPut           vb, vc(i), vd
-   * end for
-   * Invoke-Static      ve <- vb; method: java.util.List java.util.Arrays.asList(java.lang.Object[])
-   * Invoke-Interface   v5 <- ve; method: java.util.Iterator java.util.List.iterator()
+   * Invoke-Static      v5 <- ; method: java.util.Iterator syn(X)()
    * </pre>
    *
-   * We rely on the DeadCodeRemover to remove the ConstClasses and any aliased values no longer
+   * where syn(X) is the synthesized method generated for the service class.
+   *
+   * <p>We rely on the DeadCodeRemover to remove the ConstClasses and any aliased values no longer
    * used.
    */
   private static class Rewriter {
 
-    private final AppView appView;
-    private final DexItemFactory factory;
     private final IRCode code;
     private final InvokeStatic serviceLoaderLoad;
 
     private InstructionListIterator iterator;
-    private MemberType memberType;
-    private Value valueArray;
-    private int index = 0;
 
-    public Rewriter(
-        AppView appView,
-        IRCode code,
-        InstructionListIterator iterator,
-        InvokeStatic serviceLoaderLoad) {
-      this.appView = appView;
-      this.factory = appView.dexItemFactory();
+    Rewriter(IRCode code, InstructionListIterator iterator, InvokeStatic serviceLoaderLoad) {
       this.iterator = iterator;
       this.code = code;
       this.serviceLoaderLoad = serviceLoaderLoad;
     }
 
-    public void perform(InvokeVirtual classLoaderInvoke, DexType dexType, List<DexClass> classes) {
-      assert valueArray == null;
-      assert memberType == null;
-
+    public void perform(InvokeVirtual classLoaderInvoke, DexMethod method) {
       // Remove the ClassLoader call since this can throw and will not be removed otherwise.
       if (classLoaderInvoke != null) {
         clearGetClassLoader(classLoaderInvoke);
@@ -220,69 +275,11 @@
           serviceLoaderLoad.outValue().singleUniqueUser().asInvokeVirtual();
       iterator.replaceCurrentInstruction(code.createConstNull());
 
-      // Build the array for the "loaded" classes.
-      ConstNumber arrayLength = code.createIntConstant(classes.size());
-      arrayLength.setPosition(serviceLoaderLoad.getPosition());
-      iterator.add(arrayLength);
-
-      DexType arrayType = factory.createArrayType(1, dexType);
-      TypeLatticeElement arrayLatticeElement =
-          TypeLatticeElement.fromDexType(arrayType, definitelyNotNull(), appView);
-      valueArray = code.createValue(arrayLatticeElement);
-      NewArrayEmpty newArrayEmpty =
-          new NewArrayEmpty(valueArray, arrayLength.outValue(), arrayType);
-      newArrayEmpty.setPosition(serviceLoaderLoad.getPosition());
-      iterator.add(newArrayEmpty);
-
-      this.memberType = MemberType.fromDexType(dexType);
-
-      // Add all new instances to the array.
-      classes.forEach(this::addNewServiceAndPutInArray);
-
-      // Build the Arrays.asList(...) instruction.
-      Value vArrayAsList =
-          code.createValue(
-              TypeLatticeElement.fromDexType(factory.listType, definitelyNotNull(), appView));
-      InvokeStatic arraysAsList =
-          new InvokeStatic(
-              factory.utilArraysMethods.asList, vArrayAsList, ImmutableList.of(valueArray));
-      arraysAsList.setPosition(serviceLoaderLoad.getPosition());
-      iterator.add(arraysAsList);
-
       // Find the iterator instruction and replace it.
       iterator.nextUntil(x -> x == serviceLoaderIterator);
-
-      DexMethod method =
-          factory.createMethod(
-              factory.listType, factory.createProto(factory.iteratorType), "iterator");
-      InvokeInterface arrayIterator =
-          new InvokeInterface(
-              method, serviceLoaderIterator.outValue(), ImmutableList.of(vArrayAsList));
-      iterator.replaceCurrentInstruction(arrayIterator);
-    }
-
-    private void addNewServiceAndPutInArray(DexClass clazz) {
-      ConstNumber indexInArray = code.createIntConstant(index++);
-      indexInArray.setPosition(serviceLoaderLoad.getPosition());
-      iterator.add(indexInArray);
-
-      TypeLatticeElement clazzLatticeElement =
-          TypeLatticeElement.fromDexType(clazz.type, definitelyNotNull(), appView);
-      Value vInstance = code.createValue(clazzLatticeElement);
-      NewInstance newInstance = new NewInstance(clazz.type, vInstance);
-      newInstance.setPosition(serviceLoaderLoad.getPosition());
-      iterator.add(newInstance);
-
-      DexMethod method = clazz.getDefaultInitializer().method;
-      assert method.getArity() == 0;
-      InvokeDirect invokeDirect =
-          new InvokeDirect(method, null, Collections.singletonList(vInstance));
-      invokeDirect.setPosition(serviceLoaderLoad.getPosition());
-      iterator.add(invokeDirect);
-
-      ArrayPut put = new ArrayPut(memberType, valueArray, indexInArray.outValue(), vInstance);
-      put.setPosition(serviceLoaderLoad.getPosition());
-      iterator.add(put);
+      InvokeStatic synthesizedInvoke =
+          new InvokeStatic(method, serviceLoaderIterator.outValue(), ImmutableList.of());
+      iterator.replaceCurrentInstruction(synthesizedInvoke);
     }
 
     private void clearGetClassLoader(InvokeVirtual classLoaderInvoke) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 162642c..d185a37 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -107,6 +107,11 @@
   // METHOD OPTIMIZATION INFO:
 
   @Override
+  public void markForceInline(DexEncodedMethod method) {
+    getMethodOptimizationInfoForUpdating(method).markForceInline();
+  }
+
+  @Override
   public synchronized void markInlinedIntoSingleCallSite(DexEncodedMethod method) {
     getMethodOptimizationInfoForUpdating(method).markInlinedIntoSingleCallSite();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
index ea5afa0..79b2e7f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -44,6 +44,9 @@
   // METHOD OPTIMIZATION INFO:
 
   @Override
+  public void markForceInline(DexEncodedMethod method) {}
+
+  @Override
   public void markInlinedIntoSingleCallSite(DexEncodedMethod method) {}
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 537cbd1..9119b4b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -52,6 +52,11 @@
   // METHOD OPTIMIZATION INFO.
 
   @Override
+  public void markForceInline(DexEncodedMethod method) {
+    // Ignored.
+  }
+
+  @Override
   public synchronized void markInlinedIntoSingleCallSite(DexEncodedMethod method) {
     method.getMutableOptimizationInfo().markInlinedIntoSingleCallSite();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
index a90efc7..4ce3033 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
@@ -79,6 +79,10 @@
     }
   }
 
+  public final boolean allLambdas(Predicate<LambdaInfo> predicate) {
+    return !anyLambda(lambda -> !predicate.test(lambda));
+  }
+
   public final boolean anyLambda(Predicate<LambdaInfo> predicate) {
     assert verifyLambdaIds(false);
     for (LambdaInfo info : lambdas.values()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index f399ad6..89451c9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -228,6 +228,11 @@
     // sequential lambda ids, create group lambda classes.
     Map<LambdaGroup, DexProgramClass> lambdaGroupsClasses = finalizeLambdaGroups();
 
+    // Mark all the implementation methods for force inlining.
+    for (LambdaGroup group : lambdaGroupsClasses.keySet()) {
+      group.forEachLambda(info -> info.clazz.virtualMethods().forEach(feedback::markForceInline));
+    }
+
     // Fixup optimization info to ensure that the optimization info does not refer to any merged
     // lambdas.
     LambdaMergerOptimizationInfoFixer optimizationInfoFixer =
@@ -238,7 +243,6 @@
     this.strategyFactory = (method, code) -> new ApplyStrategy(method, code, optimizationInfoFixer);
 
     // Add synthesized lambda group classes to the builder.
-
     for (Entry<LambdaGroup, DexProgramClass> entry : lambdaGroupsClasses.entrySet()) {
       DexProgramClass synthesizedClass = entry.getValue();
       appView.appInfo().addSynthesizedClass(synthesizedClass);
@@ -254,6 +258,18 @@
       synthesizedClass.forEachMethod(
           encodedMethod -> encodedMethod.markProcessed(ConstraintWithTarget.NEVER));
     }
+
+    // Verify that all implementation methods are marked for force inlining (i.e., check that the
+    // delayed optimization feedback has been flushed).
+    assert lambdaGroupsClasses.keySet().stream()
+        .allMatch(
+            group ->
+                group.allLambdas(
+                    lambda ->
+                        lambda.clazz.virtualMethods().stream()
+                            .map(DexEncodedMethod::getOptimizationInfo)
+                            .allMatch(MethodOptimizationInfo::forceInline)));
+
     converter.optimizeSynthesizedClasses(lambdaGroupsClasses.values(), executorService);
 
     // Rewrite lambda class references into lambda group class
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
index 4226daa..15d4160 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
@@ -105,14 +105,6 @@
         // anyways and our new method is a product of inlining.
         MethodAccessFlags accessFlags = MAIN_METHOD_FLAGS.copy();
 
-        // Mark all the impl methods for force inlining
-        // LambdaGroupVirtualMethodSourceCode relies on.
-        for (DexEncodedMethod implMethod : implMethods) {
-          if (implMethod != null) {
-            implMethod.getMutableOptimizationInfo().markForceInline();
-          }
-        }
-
         DexMethod method = factory.createMethod(group.getGroupClassType(), methodProto, methodName);
         result.add(
             new DexEncodedMethod(