Merge commit '7899e0ce754ad72ce6e99559dcb4143a04470687' into dev-release
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
index fa105e6..6731b5e 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -211,6 +211,10 @@
     return TraversalContinuation.CONTINUE;
   }
 
+  public Set<DexType> getInstantiatedLambdaInterfaces() {
+    return instantiatedLambdas.keySet();
+  }
+
   public static class Builder extends ObjectAllocationInfoCollectionImpl {
 
     private static class Data {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraproceduralDataflowAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraproceduralDataflowAnalysis.java
index f290cf1..a8482bb 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraproceduralDataflowAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraproceduralDataflowAnalysis.java
@@ -59,7 +59,7 @@
       // Update the block exit state, and re-enqueue all successor blocks if the abstract state
       // changed.
       if (setBlockExitState(block, state)) {
-        worklist.addAll(block.getSuccessors());
+        worklist.addAllIgnoringSeenSet(block.getSuccessors());
       }
     }
     return new SuccessfulDataflowAnalysisResult();
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 8db8c01..100131c 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
@@ -1132,13 +1132,9 @@
       return false;
     }
     boolean didDesugar = false;
-    Supplier<AppInfoWithClassHierarchy> lazyAppInfo =
-        Suppliers.memoize(() -> appView.appInfoForDesugaring());
     if (lambdaRewriter != null) {
-      didDesugar |= lambdaRewriter.desugarLambdas(method, lazyAppInfo.get()) > 0;
-    }
-    if (backportedMethodRewriter != null) {
-      didDesugar |= backportedMethodRewriter.desugar(method, lazyAppInfo.get());
+      AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
+      didDesugar |= lambdaRewriter.desugarLambdas(method, appInfo) > 0;
     }
     return didDesugar;
   }
@@ -1424,6 +1420,12 @@
       timing.end();
     }
 
+    if (backportedMethodRewriter != null) {
+      timing.begin("Rewrite backport methods");
+      backportedMethodRewriter.desugar(code);
+      timing.end();
+    }
+
     timing.begin("Desugar string concat");
     stringConcatRewriter.desugarStringConcats(method.method, code);
     timing.end();
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 1f41aa4..da1a799 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
@@ -4,25 +4,27 @@
 
 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.dex.ApplicationReader;
 import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexApplication;
 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.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
+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.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.desugar.backports.BackportedMethods;
 import com.android.tools.r8.ir.desugar.backports.BooleanMethodRewrites;
@@ -38,19 +40,19 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.DesugarState;
 import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.Sets;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.IdentityHashMap;
 import java.util.List;
-import java.util.ListIterator;
 import java.util.Map;
 import java.util.Queue;
+import java.util.Set;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.function.Consumer;
-import org.objectweb.asm.Opcodes;
 
 public final class BackportedMethodRewriter {
 
@@ -109,34 +111,29 @@
     BackportedMethods.registerSynthesizedCodeReferences(options.itemFactory);
   }
 
-  public boolean desugar(ProgramMethod method, AppInfoWithClassHierarchy appInfo) {
+  public void desugar(IRCode code) {
     if (!enabled) {
-      return false;
+      return; // Nothing to do!
     }
-    CfCode code = method.getDefinition().getCode().asCfCode();
-    ListIterator<CfInstruction> iterator = code.getInstructions().listIterator();
-    boolean replaced = false;
+    Set<Value> affectedValues = Sets.newIdentityHashSet();
+    InstructionListIterator iterator = code.instructionListIterator();
     while (iterator.hasNext()) {
-      CfInvoke invoke = iterator.next().asInvoke();
-      if (invoke == null) {
+      Instruction instruction = iterator.next();
+      if (!instruction.isInvokeMethod()) {
         continue;
       }
-      DexMethod invokedMethod = invoke.getMethod();
+
+      InvokeMethod invoke = instruction.asInvokeMethod();
+      DexMethod invokedMethod = invoke.getInvokedMethod();
       MethodProvider provider = getMethodProviderOrNull(invokedMethod);
       if (provider != null) {
-        if (!replaced) {
-          // Create mutable instructions on first write.
-          ArrayList<CfInstruction> mutableInstructions = new ArrayList<>(code.getInstructions());
-          code.setInstructions(mutableInstructions);
-          iterator = mutableInstructions.listIterator(iterator.previousIndex());
-          iterator.next();
-        }
         provider.rewriteInvoke(
-            invoke, iterator, method.getHolder(), appInfo, synthesizedMethods::add);
-        replaced = true;
+            invoke, iterator, code, appView, affectedValues, synthesizedMethods::add);
       }
     }
-    return replaced;
+    if (!affectedValues.isEmpty()) {
+      new TypeAnalysis(appView).narrowing(affectedValues);
+    }
   }
 
   public void processSynthesizedClasses(IRConverter converter, ExecutorService executor)
@@ -251,7 +248,7 @@
       name = factory.createString("compare");
       proto = factory.createProto(factory.intType, factory.longType, factory.longType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, LongMethodRewrites.rewriteCompare()));
+      addProvider(new InvokeRewriter(method, LongMethodRewrites::rewriteCompare));
 
       // Boolean
       type = factory.boxedBooleanType;
@@ -296,7 +293,7 @@
       name = factory.createString("hash");
       proto = factory.createProto(factory.intType, factory.objectArrayType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, ObjectsMethodRewrites.rewriteToArraysHashCode()));
+      addProvider(new InvokeRewriter(method, ObjectsMethodRewrites::rewriteToArraysHashCode));
 
       // int Objects.hashCode(Object o)
       name = factory.createString("hashCode");
@@ -306,7 +303,7 @@
 
       // T Objects.requireNonNull(T obj)
       method = factory.objectsMethods.requireNonNull;
-      addProvider(new InvokeRewriter(method, ObjectsMethodRewrites.rewriteRequireNonNull()));
+      addProvider(new InvokeRewriter(method, ObjectsMethodRewrites::rewriteRequireNonNull));
 
       // T Objects.requireNonNull(T obj, String message)
       name = factory.createString("requireNonNull");
@@ -363,7 +360,7 @@
       DexString name = factory.createString("hashCode");
       DexProto proto = factory.createProto(factory.intType, factory.byteType);
       DexMethod method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteAsIdentity()));
+      addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteAsIdentity));
 
       // Short
       type = factory.boxedShortType;
@@ -371,7 +368,7 @@
       name = factory.createString("hashCode");
       proto = factory.createProto(factory.intType, factory.shortType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteAsIdentity()));
+      addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteAsIdentity));
 
       // Integer
       type = factory.boxedIntType;
@@ -380,25 +377,25 @@
       name = factory.createString("hashCode");
       proto = factory.createProto(factory.intType, factory.intType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteAsIdentity()));
+      addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteAsIdentity));
 
       // int Integer.max(int a, int b)
       name = factory.createString("max");
       proto = factory.createProto(factory.intType, factory.intType, factory.intType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath()));
+      addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToInvokeMath));
 
       // int Integer.min(int a, int b)
       name = factory.createString("min");
       proto = factory.createProto(factory.intType, factory.intType, factory.intType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath()));
+      addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToInvokeMath));
 
       // int Integer.sum(int a, int b)
       name = factory.createString("sum");
       proto = factory.createProto(factory.intType, factory.intType, factory.intType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToAddInstruction()));
+      addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToAddInstruction));
 
       // Double
       type = factory.boxedDoubleType;
@@ -413,19 +410,19 @@
       name = factory.createString("max");
       proto = factory.createProto(factory.doubleType, factory.doubleType, factory.doubleType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath()));
+      addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToInvokeMath));
 
       // double Double.min(double a, double b)
       name = factory.createString("min");
       proto = factory.createProto(factory.doubleType, factory.doubleType, factory.doubleType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath()));
+      addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToInvokeMath));
 
       // double Double.sum(double a, double b)
       name = factory.createString("sum");
       proto = factory.createProto(factory.doubleType, factory.doubleType, factory.doubleType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToAddInstruction()));
+      addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToAddInstruction));
 
       // boolean Double.isFinite(double a)
       name = factory.createString("isFinite");
@@ -440,25 +437,25 @@
       name = factory.createString("hashCode");
       proto = factory.createProto(factory.intType, factory.floatType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, FloatMethodRewrites.rewriteHashCode()));
+      addProvider(new InvokeRewriter(method, FloatMethodRewrites::rewriteHashCode));
 
       // float Float.max(float a, float b)
       name = factory.createString("max");
       proto = factory.createProto(factory.floatType, factory.floatType, factory.floatType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath()));
+      addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToInvokeMath));
 
       // float Float.min(float a, float b)
       name = factory.createString("min");
       proto = factory.createProto(factory.floatType, factory.floatType, factory.floatType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath()));
+      addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToInvokeMath));
 
       // float Float.sum(float a, float b)
       name = factory.createString("sum");
       proto = factory.createProto(factory.floatType, factory.floatType, factory.floatType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToAddInstruction()));
+      addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToAddInstruction));
 
       // boolean Float.isFinite(float a)
       name = factory.createString("isFinite");
@@ -479,19 +476,19 @@
       name = factory.createString("logicalAnd");
       proto = factory.createProto(factory.booleanType, factory.booleanType, factory.booleanType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, BooleanMethodRewrites.rewriteLogicalAnd()));
+      addProvider(new InvokeRewriter(method, BooleanMethodRewrites::rewriteLogicalAnd));
 
       // boolean Boolean.logicalOr(boolean a, boolean b)
       name = factory.createString("logicalOr");
       proto = factory.createProto(factory.booleanType, factory.booleanType, factory.booleanType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, BooleanMethodRewrites.rewriteLogicalOr()));
+      addProvider(new InvokeRewriter(method, BooleanMethodRewrites::rewriteLogicalOr));
 
       // boolean Boolean.logicalXor(boolean a, boolean b)
       name = factory.createString("logicalXor");
       proto = factory.createProto(factory.booleanType, factory.booleanType, factory.booleanType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, BooleanMethodRewrites.rewriteLogicalXor()));
+      addProvider(new InvokeRewriter(method, BooleanMethodRewrites::rewriteLogicalXor));
 
       // Long
       type = factory.boxedLongType;
@@ -506,19 +503,19 @@
       name = factory.createString("max");
       proto = factory.createProto(factory.longType, factory.longType, factory.longType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath()));
+      addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToInvokeMath));
 
       // long Long.min(long a, long b)
       name = factory.createString("min");
       proto = factory.createProto(factory.longType, factory.longType, factory.longType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath()));
+      addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToInvokeMath));
 
       // long Long.sum(long a, long b)
       name = factory.createString("sum");
       proto = factory.createProto(factory.longType, factory.longType, factory.longType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToAddInstruction()));
+      addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToAddInstruction));
 
       // Character
       type = factory.boxedCharType;
@@ -527,7 +524,7 @@
       name = factory.createString("hashCode");
       proto = factory.createProto(factory.intType, factory.charType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteAsIdentity()));
+      addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteAsIdentity));
 
       // Objects
       type = factory.objectsType;
@@ -919,7 +916,7 @@
         method = factory.createMethod(type, proto, name);
         addProvider(
             i == 0
-                ? new InvokeRewriter(method, CollectionMethodRewrites.rewriteListOfEmpty())
+                ? new InvokeRewriter(method, CollectionMethodRewrites::rewriteListOfEmpty)
                 : new MethodGenerator(
                     method,
                     (options, methodArg) ->
@@ -940,7 +937,7 @@
         method = factory.createMethod(type, proto, name);
         addProvider(
             i == 0
-                ? new InvokeRewriter(method, CollectionMethodRewrites.rewriteSetOfEmpty())
+                ? new InvokeRewriter(method, CollectionMethodRewrites::rewriteSetOfEmpty)
                 : new MethodGenerator(
                     method,
                     (options, methodArg) ->
@@ -960,7 +957,7 @@
         method = factory.createMethod(type, proto, name);
         addProvider(
             i == 0
-                ? new InvokeRewriter(method, CollectionMethodRewrites.rewriteMapOfEmpty())
+                ? new InvokeRewriter(method, CollectionMethodRewrites::rewriteMapOfEmpty)
                 : new MethodGenerator(
                     method,
                     (options, methodArg) ->
@@ -1227,10 +1224,10 @@
           };
       MethodInvokeRewriter[] rewriters =
           new MethodInvokeRewriter[] {
-            OptionalMethodRewrites.rewriteOrElseGet(),
-            OptionalMethodRewrites.rewriteDoubleOrElseGet(),
-            OptionalMethodRewrites.rewriteLongOrElseGet(),
-            OptionalMethodRewrites.rewriteIntOrElseGet(),
+            OptionalMethodRewrites::rewriteOrElseGet,
+            OptionalMethodRewrites::rewriteDoubleOrElseGet,
+            OptionalMethodRewrites::rewriteLongOrElseGet,
+            OptionalMethodRewrites::rewriteIntOrElseGet,
           };
       DexString name = factory.createString("orElseThrow");
       for (int i = 0; i < optionalTypes.length; i++) {
@@ -1297,10 +1294,11 @@
     }
 
     public abstract void rewriteInvoke(
-        CfInvoke invoke,
-        ListIterator<CfInstruction> iterator,
-        DexProgramClass context,
-        AppInfoWithClassHierarchy appInfo,
+        InvokeMethod invoke,
+        InstructionListIterator iterator,
+        IRCode code,
+        AppView<?> appView,
+        Set<Value> affectedValues,
         Consumer<ProgramMethod> registerSynthesizedMethod);
   }
 
@@ -1315,12 +1313,14 @@
 
     @Override
     public void rewriteInvoke(
-        CfInvoke invoke,
-        ListIterator<CfInstruction> iterator,
-        DexProgramClass context,
-        AppInfoWithClassHierarchy appInfo,
+        InvokeMethod invoke,
+        InstructionListIterator iterator,
+        IRCode code,
+        AppView<?> appView,
+        Set<Value> affectedValues,
         Consumer<ProgramMethod> registerSynthesizedMethod) {
-      rewriter.rewrite(invoke, iterator, appInfo.dexItemFactory());
+      rewriter.rewrite(invoke, iterator, appView.dexItemFactory(), affectedValues);
+      assert code.isConsistentSSA();
     }
   }
 
@@ -1341,33 +1341,34 @@
 
     @Override
     public void rewriteInvoke(
-        CfInvoke invoke,
-        ListIterator<CfInstruction> iterator,
-        DexProgramClass context,
-        AppInfoWithClassHierarchy appInfo,
+        InvokeMethod invoke,
+        InstructionListIterator iterator,
+        IRCode code,
+        AppView<?> appView,
+        Set<Value> affectedValues,
         Consumer<ProgramMethod> registerSynthesizedMethod) {
-      ProgramMethod method = getSyntheticMethod(context, appInfo);
-      registerSynthesizedMethod.accept(method);
-      iterator.remove();
-      iterator.add(new CfInvoke(Opcodes.INVOKESTATIC, method.getReference(), false));
-    }
+      ProgramMethod method =
+          appView
+              .getSyntheticItems()
+              .createMethod(
+                  code.context().getHolder(),
+                  appView.dexItemFactory(),
+                  builder ->
+                      builder
+                          .setProto(getProto(appView.dexItemFactory()))
+                          .setAccessFlags(
+                              MethodAccessFlags.fromSharedAccessFlags(
+                                  Constants.ACC_PUBLIC
+                                      | Constants.ACC_STATIC
+                                      | Constants.ACC_SYNTHETIC,
+                                  false))
+                          .setCode(
+                              methodSig -> generateTemplateMethod(appView.options(), methodSig)));
 
-    private ProgramMethod getSyntheticMethod(
-        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
-      return appInfo
-          .getSyntheticItems()
-          .createMethod(
-              context,
-              appInfo.dexItemFactory(),
-              builder ->
-                  builder
-                      .setProto(getProto(appInfo.dexItemFactory()))
-                      .setAccessFlags(
-                          MethodAccessFlags.fromSharedAccessFlags(
-                              Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC,
-                              false))
-                      .setCode(
-                          methodSig -> generateTemplateMethod(appInfo.app().options, methodSig)));
+      iterator.replaceCurrentInstruction(
+          new InvokeStatic(method.getReference(), invoke.outValue(), invoke.inValues()));
+
+      registerSynthesizedMethod.accept(method);
     }
 
     public DexProto getProto(DexItemFactory itemFactory) {
@@ -1399,30 +1400,16 @@
   }
 
   private interface TemplateMethodFactory {
+
     Code create(InternalOptions options, DexMethod method);
   }
 
-  public interface MethodInvokeRewriter {
+  private interface MethodInvokeRewriter {
 
-    CfInstruction rewriteSingle(CfInvoke invoke, DexItemFactory factory);
-
-    // Convenience wrapper since most rewrites are to a single instruction.
-    default void rewrite(
-        CfInvoke invoke, ListIterator<CfInstruction> iterator, DexItemFactory factory) {
-      iterator.remove();
-      iterator.add(rewriteSingle(invoke, factory));
-    }
-  }
-
-  public abstract static class FullMethodInvokeRewriter implements MethodInvokeRewriter {
-
-    @Override
-    public final CfInstruction rewriteSingle(CfInvoke invoke, DexItemFactory factory) {
-      throw new Unreachable();
-    }
-
-    @Override
-    public abstract void rewrite(
-        CfInvoke invoke, ListIterator<CfInstruction> iterator, DexItemFactory factory);
+    void rewrite(
+        InvokeMethod invoke,
+        InstructionListIterator iterator,
+        DexItemFactory factory,
+        Set<Value> affectedValues);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BooleanMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BooleanMethodRewrites.java
index ec0a554..90654b2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BooleanMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BooleanMethodRewrites.java
@@ -4,27 +4,52 @@
 
 package com.android.tools.r8.ir.desugar.backports;
 
-import com.android.tools.r8.cf.code.CfLogicalBinop;
-import com.android.tools.r8.cf.code.CfLogicalBinop.Opcode;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.code.And;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.NumericType;
-import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import com.android.tools.r8.ir.code.Or;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.Xor;
+import java.util.List;
+import java.util.Set;
 
 public final class BooleanMethodRewrites {
+  public static void rewriteLogicalAnd(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    List<Value> inValues = invoke.inValues();
+    assert inValues.size() == 2;
 
-  private static MethodInvokeRewriter createRewriter(CfLogicalBinop.Opcode op) {
-    return (invoke, factory) -> new CfLogicalBinop(op, NumericType.INT);
+    iterator.replaceCurrentInstruction(
+        new And(NumericType.INT, invoke.outValue(), inValues.get(0), inValues.get(1)));
   }
 
-  public static MethodInvokeRewriter rewriteLogicalAnd() {
-    return createRewriter(Opcode.And);
+  public static void rewriteLogicalOr(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    List<Value> inValues = invoke.inValues();
+    assert inValues.size() == 2;
+
+    iterator.replaceCurrentInstruction(
+        new Or(NumericType.INT, invoke.outValue(), inValues.get(0), inValues.get(1)));
   }
 
-  public static MethodInvokeRewriter rewriteLogicalOr() {
-    return createRewriter(Opcode.Or);
-  }
+  public static void rewriteLogicalXor(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    List<Value> inValues = invoke.inValues();
+    assert inValues.size() == 2;
 
-  public static MethodInvokeRewriter rewriteLogicalXor() {
-    return createRewriter(Opcode.Xor);
+    iterator.replaceCurrentInstruction(
+        new Xor(NumericType.INT, invoke.outValue(), inValues.get(0), inValues.get(1)));
   }
 
   private BooleanMethodRewrites() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodRewrites.java
index 64c94c5..354040f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodRewrites.java
@@ -4,31 +4,54 @@
 
 package com.android.tools.r8.ir.desugar.backports;
 
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
-import org.objectweb.asm.Opcodes;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.Value;
+import java.util.Collections;
+import java.util.Set;
 
 public final class CollectionMethodRewrites {
 
   private CollectionMethodRewrites() {}
 
-  public static MethodInvokeRewriter rewriteListOfEmpty() {
-    return rewriteToCollectionMethod("emptyList");
+  public static void rewriteListOfEmpty(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    rewriteToCollectionMethod(invoke, iterator, factory, "emptyList");
   }
 
-  public static MethodInvokeRewriter rewriteSetOfEmpty() {
-    return rewriteToCollectionMethod("emptySet");
+  public static void rewriteSetOfEmpty(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    rewriteToCollectionMethod(invoke, iterator, factory, "emptySet");
   }
 
-  public static MethodInvokeRewriter rewriteMapOfEmpty() {
-    return rewriteToCollectionMethod("emptyMap");
+  public static void rewriteMapOfEmpty(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    rewriteToCollectionMethod(invoke, iterator, factory, "emptyMap");
   }
 
-  private static MethodInvokeRewriter rewriteToCollectionMethod(String methodName) {
-    return (invoke, factory) ->
-        new CfInvoke(
-            Opcodes.INVOKESTATIC,
-            factory.createMethod(factory.collectionsType, invoke.getMethod().proto, methodName),
-            false);
+  private static void rewriteToCollectionMethod(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      String methodName) {
+    assert invoke.inValues().isEmpty();
+
+    DexMethod collectionsEmptyList =
+        factory.createMethod(factory.collectionsType, invoke.getInvokedMethod().proto, methodName);
+    InvokeStatic newInvoke =
+        new InvokeStatic(collectionsEmptyList, invoke.outValue(), Collections.emptyList());
+    iterator.replaceCurrentInstruction(newInvoke);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/FloatMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/FloatMethodRewrites.java
index 8670d3b..3601c51 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/FloatMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/FloatMethodRewrites.java
@@ -4,20 +4,29 @@
 
 package com.android.tools.r8.ir.desugar.backports;
 
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
-import org.objectweb.asm.Opcodes;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.Value;
+import java.util.Set;
 
 public final class FloatMethodRewrites {
 
   private FloatMethodRewrites() {}
 
-  public static MethodInvokeRewriter rewriteHashCode() {
-    return (invoke, factory) ->
-        new CfInvoke(
-            Opcodes.INVOKESTATIC,
+  public static void rewriteHashCode(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    InvokeStatic mathInvoke =
+        new InvokeStatic(
             factory.createMethod(
-                factory.boxedFloatType, invoke.getMethod().proto, "floatToIntBits"),
+                factory.boxedFloatType, invoke.getInvokedMethod().proto, "floatToIntBits"),
+            invoke.outValue(),
+            invoke.inValues(),
             false);
+    iterator.replaceCurrentInstruction(mathInvoke);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/LongMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/LongMethodRewrites.java
index 5c29f81..348194b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/LongMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/LongMethodRewrites.java
@@ -4,16 +4,28 @@
 
 package com.android.tools.r8.ir.desugar.backports;
 
-import com.android.tools.r8.cf.code.CfCmp;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.code.Cmp;
 import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.NumericType;
-import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import com.android.tools.r8.ir.code.Value;
+import java.util.List;
+import java.util.Set;
 
 public final class LongMethodRewrites {
 
   private LongMethodRewrites() {}
 
-  public static MethodInvokeRewriter rewriteCompare() {
-    return (invoke, factory) -> new CfCmp(Bias.NONE, NumericType.LONG);
+  public static void rewriteCompare(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    List<Value> inValues = invoke.inValues();
+    assert inValues.size() == 2;
+    iterator.replaceCurrentInstruction(
+        new Cmp(NumericType.LONG, Bias.NONE, invoke.outValue(), inValues.get(0), inValues.get(1)));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/NumericMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/NumericMethodRewrites.java
index 050aefa..4a413bc 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/NumericMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/NumericMethodRewrites.java
@@ -1,44 +1,56 @@
 package com.android.tools.r8.ir.desugar.backports;
 
-import com.android.tools.r8.cf.code.CfArithmeticBinop;
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.Add;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.NumericType;
-import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.FullMethodInvokeRewriter;
-import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
-import java.util.ListIterator;
-import org.objectweb.asm.Opcodes;
+import com.android.tools.r8.ir.code.Value;
+import java.util.List;
+import java.util.Set;
 
 public final class NumericMethodRewrites {
-
-  public static MethodInvokeRewriter rewriteToInvokeMath() {
-    return (invoke, factory) -> {
-      DexMethod method = invoke.getMethod();
-      return new CfInvoke(
-          Opcodes.INVOKESTATIC,
-          factory.createMethod(factory.mathType, method.proto, method.name),
-          false);
-    };
+  public static void rewriteToInvokeMath(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    InvokeStatic mathInvoke =
+        new InvokeStatic(
+            factory.createMethod(
+                factory.mathType, invoke.getInvokedMethod().proto, invoke.getInvokedMethod().name),
+            invoke.outValue(),
+            invoke.inValues(),
+            false);
+    iterator.replaceCurrentInstruction(mathInvoke);
   }
 
-  public static MethodInvokeRewriter rewriteToAddInstruction() {
-    return (invoke, factory) -> {
-      NumericType numericType = NumericType.fromDexType(invoke.getMethod().getReturnType());
-      return new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, numericType);
-    };
+  public static void rewriteToAddInstruction(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    List<Value> values = invoke.inValues();
+    assert values.size() == 2;
+
+    NumericType numericType = NumericType.fromDexType(invoke.getReturnType());
+    Add add = new Add(numericType, invoke.outValue(), values.get(0), values.get(1));
+    iterator.replaceCurrentInstruction(add);
   }
 
-  public static MethodInvokeRewriter rewriteAsIdentity() {
-    return new FullMethodInvokeRewriter() {
-      @Override
-      public void rewrite(
-          CfInvoke invoke, ListIterator<CfInstruction> iterator, DexItemFactory factory) {
-        // The invoke consumes the stack value and pushes another assumed to be the same.
-        iterator.remove();
-      }
-    };
+  public static void rewriteAsIdentity(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    List<Value> values = invoke.inValues();
+    assert values.size() == 1;
+    if (invoke.hasOutValue()) {
+      invoke.outValue().replaceUsers(values.get(0));
+    }
+    // TODO(b/152853271): Debugging information is lost here (DebugLocalWrite may be required).
+    iterator.removeOrReplaceByDebugLocalRead();
   }
 
   private NumericMethodRewrites() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethodRewrites.java
index 69253be..de16e98 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethodRewrites.java
@@ -4,41 +4,43 @@
 
 package com.android.tools.r8.ir.desugar.backports;
 
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.cf.code.CfStackInstruction;
-import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.FullMethodInvokeRewriter;
-import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
-import java.util.ListIterator;
-import org.objectweb.asm.Opcodes;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.Value;
+import java.util.Set;
 
 public final class ObjectsMethodRewrites {
 
-  public static MethodInvokeRewriter rewriteToArraysHashCode() {
-    return (invoke, factory) -> {
-      DexType arraysType = factory.createType(factory.arraysDescriptor);
-      return new CfInvoke(
-          Opcodes.INVOKESTATIC,
-          factory.createMethod(arraysType, invoke.getMethod().proto, "hashCode"),
-          false);
-    };
+  public static void rewriteToArraysHashCode(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    DexType arraysType = factory.createType(factory.arraysDescriptor);
+    DexMethod hashCodeMethod =
+        factory.createMethod(arraysType, invoke.getInvokedMethod().proto, "hashCode");
+    InvokeStatic arraysHashCode =
+        new InvokeStatic(hashCodeMethod, invoke.outValue(), invoke.inValues(), false);
+    iterator.replaceCurrentInstruction(arraysHashCode);
   }
 
-  public static MethodInvokeRewriter rewriteRequireNonNull() {
-    return new FullMethodInvokeRewriter() {
-
-      @Override
-      public void rewrite(
-          CfInvoke invoke, ListIterator<CfInstruction> iterator, DexItemFactory factory) {
-        iterator.remove();
-        // requireNonNull returns the operand, so dup top-of-stack, do getClass and pop the class.
-        iterator.add(new CfStackInstruction(Opcode.Dup));
-        iterator.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, factory.objectMembers.getClass, false));
-        iterator.add(new CfStackInstruction(Opcode.Pop));
-      }
-    };
+  public static void rewriteRequireNonNull(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    InvokeVirtual getClass =
+        new InvokeVirtual(factory.objectMembers.getClass, null, invoke.inValues());
+    if (invoke.hasOutValue()) {
+      affectedValues.addAll(invoke.outValue().affectedValues());
+      invoke.outValue().replaceUsers(invoke.inValues().get(0));
+      invoke.setOutValue(null);
+    }
+    iterator.replaceCurrentInstruction(getClass);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/OptionalMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/OptionalMethodRewrites.java
index d66d7f5..1334c62 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/OptionalMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/OptionalMethodRewrites.java
@@ -4,40 +4,69 @@
 
 package com.android.tools.r8.ir.desugar.backports;
 
-import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
-import java.util.function.Function;
-import org.objectweb.asm.Opcodes;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.Value;
+import java.util.Set;
 
 public final class OptionalMethodRewrites {
 
   private OptionalMethodRewrites() {}
 
-  private static MethodInvokeRewriter createRewriter(
-      Function<DexItemFactory, DexType> holderTypeSupplier, String methodName) {
-    return (invoke, factory) ->
-        new CfInvoke(
-            Opcodes.INVOKEVIRTUAL,
+  public static void rewriteOrElseGet(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    InvokeVirtual getInvoke =
+        new InvokeVirtual(
+            factory.createMethod(factory.optionalType, invoke.getInvokedMethod().proto, "get"),
+            invoke.outValue(),
+            invoke.inValues());
+    iterator.replaceCurrentInstruction(getInvoke);
+  }
+
+  public static void rewriteDoubleOrElseGet(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    InvokeVirtual getInvoke =
+        new InvokeVirtual(
             factory.createMethod(
-                holderTypeSupplier.apply(factory), invoke.getMethod().proto, methodName),
-            false);
+                factory.optionalDoubleType, invoke.getInvokedMethod().proto, "getAsDouble"),
+            invoke.outValue(),
+            invoke.inValues());
+    iterator.replaceCurrentInstruction(getInvoke);
   }
 
-  public static MethodInvokeRewriter rewriteOrElseGet() {
-    return createRewriter(factory -> factory.optionalType, "get");
+  public static void rewriteIntOrElseGet(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    InvokeVirtual getInvoke =
+        new InvokeVirtual(
+            factory.createMethod(
+                factory.optionalIntType, invoke.getInvokedMethod().proto, "getAsInt"),
+            invoke.outValue(),
+            invoke.inValues());
+    iterator.replaceCurrentInstruction(getInvoke);
   }
 
-  public static MethodInvokeRewriter rewriteDoubleOrElseGet() {
-    return createRewriter(factory -> factory.optionalDoubleType, "getAsDouble");
-  }
-
-  public static MethodInvokeRewriter rewriteIntOrElseGet() {
-    return createRewriter(factory -> factory.optionalIntType, "getAsInt");
-  }
-
-  public static MethodInvokeRewriter rewriteLongOrElseGet() {
-    return createRewriter(factory -> factory.optionalLongType, "getAsLong");
+  public static void rewriteLongOrElseGet(
+      InvokeMethod invoke,
+      InstructionListIterator iterator,
+      DexItemFactory factory,
+      Set<Value> affectedValues) {
+    InvokeVirtual getInvoke =
+        new InvokeVirtual(
+            factory.createMethod(
+                factory.optionalLongType, invoke.getInvokedMethod().proto, "getAsLong"),
+            invoke.outValue(),
+            invoke.inValues());
+    iterator.replaceCurrentInstruction(getInvoke);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 311e10d..6c28f2e 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -55,6 +55,7 @@
 import com.android.tools.r8.utils.PredicateSet;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.Visibility;
+import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -613,21 +614,13 @@
 
   public Collection<DexClass> computeReachableInterfaces() {
     Set<DexClass> interfaces = Sets.newIdentityHashSet();
-    Set<DexType> seen = Sets.newIdentityHashSet();
-    Deque<DexType> worklist = new ArrayDeque<>();
+    WorkList<DexType> worklist = WorkList.newIdentityWorkList();
+    worklist.addIfNotSeen(objectAllocationInfoCollection.getInstantiatedLambdaInterfaces());
     for (DexProgramClass clazz : classes()) {
-      worklist.add(clazz.type);
+      worklist.addIfNotSeen(clazz.type);
     }
-    for (DexCallSite callSite : callSites.keySet()) {
-      for (DexEncodedMethod method : lookupLambdaImplementedMethods(callSite)) {
-        worklist.add(method.holder());
-      }
-    }
-    while (!worklist.isEmpty()) {
-      DexType type = worklist.pop();
-      if (!seen.add(type)) {
-        continue;
-      }
+    while (worklist.hasNext()) {
+      DexType type = worklist.next();
       DexClass definition = definitionFor(type);
       if (definition == null) {
         continue;
@@ -636,9 +629,9 @@
         interfaces.add(definition);
       }
       if (definition.superType != null) {
-        worklist.add(definition.superType);
+        worklist.addIfNotSeen(definition.superType);
       }
-      Collections.addAll(worklist, definition.interfaces.values);
+      worklist.addIfNotSeen(definition.interfaces.values);
     }
     return interfaces;
   }
@@ -1247,18 +1240,21 @@
    */
   public void forEachTypeInHierarchyOfLiveProgramClasses(Consumer<DexClass> fn) {
     forEachTypeInHierarchyOfLiveProgramClasses(
-        fn, ListUtils.map(liveTypes, t -> definitionFor(t).asProgramClass()), callSites, this);
+        fn,
+        ListUtils.map(liveTypes, t -> definitionFor(t).asProgramClass()),
+        objectAllocationInfoCollection.getInstantiatedLambdaInterfaces(),
+        this);
   }
 
   // Split in a static method so it can be used during construction.
   static void forEachTypeInHierarchyOfLiveProgramClasses(
       Consumer<DexClass> fn,
       Collection<DexProgramClass> liveProgramClasses,
-      Map<DexCallSite, ProgramMethodSet> callSites,
+      Set<DexType> lambdaInterfaces,
       AppInfoWithClassHierarchy appInfo) {
     Set<DexType> seen = Sets.newIdentityHashSet();
-    Deque<DexType> worklist = new ArrayDeque<>();
     liveProgramClasses.forEach(c -> seen.add(c.type));
+    Deque<DexType> worklist = new ArrayDeque<>(lambdaInterfaces);
     for (DexProgramClass liveProgramClass : liveProgramClasses) {
       fn.accept(liveProgramClass);
       DexType superType = liveProgramClass.superType;
@@ -1271,16 +1267,6 @@
         }
       }
     }
-    for (DexCallSite callSite : callSites.keySet()) {
-      List<DexType> interfaces = LambdaDescriptor.getInterfaces(callSite, appInfo);
-      if (interfaces != null) {
-        for (DexType iface : interfaces) {
-          if (seen.add(iface)) {
-            worklist.add(iface);
-          }
-        }
-      }
-    }
     while (!worklist.isEmpty()) {
       DexType type = worklist.pop();
       DexClass clazz = appInfo.definitionFor(type);
diff --git a/src/main/java/com/android/tools/r8/utils/WorkList.java b/src/main/java/com/android/tools/r8/utils/WorkList.java
index 2e74be4..8ee6e30 100644
--- a/src/main/java/com/android/tools/r8/utils/WorkList.java
+++ b/src/main/java/com/android/tools/r8/utils/WorkList.java
@@ -50,7 +50,7 @@
     }
   }
 
-  public void addAll(Iterable<T> items) {
+  public void addAllIgnoringSeenSet(Iterable<T> items) {
     items.forEach(workingList::addLast);
   }
 
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 9fe1539..700b3f0 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -642,6 +642,7 @@
               "530-checker-lse2",
               TestCondition.match(
                   TestCondition.tools(DexTool.DX),
+                  TestCondition.compilers(CompilerUnderTest.R8, CompilerUnderTest.D8),
                   TestCondition.runtimesUpTo(DexVm.Version.V6_0_1)))
           .put(
               "534-checker-bce-deoptimization",