Reland "Backport methods on CF code."

Bug: 147485959
Bug: 169305577
Change-Id: I10d31650a1386fbcf3ecf767234e574e4a589302
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 b695024..f475e89 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
@@ -1135,9 +1135,13 @@
       return false;
     }
     boolean didDesugar = false;
+    Supplier<AppInfoWithClassHierarchy> lazyAppInfo =
+        Suppliers.memoize(() -> appView.appInfoForDesugaring());
     if (lambdaRewriter != null) {
-      AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
-      didDesugar |= lambdaRewriter.desugarLambdas(method, appInfo) > 0;
+      didDesugar |= lambdaRewriter.desugarLambdas(method, lazyAppInfo.get()) > 0;
+    }
+    if (backportedMethodRewriter != null) {
+      didDesugar |= backportedMethodRewriter.desugar(method, lazyAppInfo.get());
     }
     return didDesugar;
   }
@@ -1423,12 +1427,6 @@
       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 99bad27..b45fe42 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,28 +4,25 @@
 
 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;
@@ -41,19 +38,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 {
 
@@ -108,29 +105,34 @@
     BackportedMethods.registerSynthesizedCodeReferences(options.itemFactory);
   }
 
-  public void desugar(IRCode code) {
+  public boolean desugar(ProgramMethod method, AppInfoWithClassHierarchy appInfo) {
     if (!enabled) {
-      return; // Nothing to do!
+      return false;
     }
-    Set<Value> affectedValues = Sets.newIdentityHashSet();
-    InstructionListIterator iterator = code.instructionListIterator();
+    CfCode code = method.getDefinition().getCode().asCfCode();
+    ListIterator<CfInstruction> iterator = code.getInstructions().listIterator();
+    boolean replaced = false;
     while (iterator.hasNext()) {
-      Instruction instruction = iterator.next();
-      if (!instruction.isInvokeMethod()) {
+      CfInvoke invoke = iterator.next().asInvoke();
+      if (invoke == null) {
         continue;
       }
-
-      InvokeMethod invoke = instruction.asInvokeMethod();
-      DexMethod invokedMethod = invoke.getInvokedMethod();
+      DexMethod invokedMethod = invoke.getMethod();
       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, code, appView, affectedValues, synthesizedMethods::add);
+            invoke, iterator, method.getHolder(), appInfo, synthesizedMethods::add);
+        replaced = true;
       }
     }
-    if (!affectedValues.isEmpty()) {
-      new TypeAnalysis(appView).narrowing(affectedValues);
-    }
+    return replaced;
   }
 
   public void processSynthesizedClasses(IRConverter converter, ExecutorService executor)
@@ -245,7 +247,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;
@@ -290,7 +292,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");
@@ -300,7 +302,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");
@@ -357,7 +359,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;
@@ -365,7 +367,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;
@@ -374,25 +376,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;
@@ -407,19 +409,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");
@@ -434,25 +436,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");
@@ -473,19 +475,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;
@@ -500,19 +502,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;
@@ -521,7 +523,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;
@@ -913,7 +915,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) ->
@@ -934,7 +936,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) ->
@@ -954,7 +956,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) ->
@@ -1221,10 +1223,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++) {
@@ -1291,11 +1293,10 @@
     }
 
     public abstract void rewriteInvoke(
-        InvokeMethod invoke,
-        InstructionListIterator iterator,
-        IRCode code,
-        AppView<?> appView,
-        Set<Value> affectedValues,
+        CfInvoke invoke,
+        ListIterator<CfInstruction> iterator,
+        DexProgramClass context,
+        AppInfoWithClassHierarchy appInfo,
         Consumer<ProgramMethod> registerSynthesizedMethod);
   }
 
@@ -1310,14 +1311,12 @@
 
     @Override
     public void rewriteInvoke(
-        InvokeMethod invoke,
-        InstructionListIterator iterator,
-        IRCode code,
-        AppView<?> appView,
-        Set<Value> affectedValues,
+        CfInvoke invoke,
+        ListIterator<CfInstruction> iterator,
+        DexProgramClass context,
+        AppInfoWithClassHierarchy appInfo,
         Consumer<ProgramMethod> registerSynthesizedMethod) {
-      rewriter.rewrite(invoke, iterator, appView.dexItemFactory(), affectedValues);
-      assert code.isConsistentSSA();
+      rewriter.rewrite(invoke, iterator, appInfo.dexItemFactory());
     }
   }
 
@@ -1338,34 +1337,33 @@
 
     @Override
     public void rewriteInvoke(
-        InvokeMethod invoke,
-        InstructionListIterator iterator,
-        IRCode code,
-        AppView<?> appView,
-        Set<Value> affectedValues,
+        CfInvoke invoke,
+        ListIterator<CfInstruction> iterator,
+        DexProgramClass context,
+        AppInfoWithClassHierarchy appInfo,
         Consumer<ProgramMethod> registerSynthesizedMethod) {
-      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)));
-
-      iterator.replaceCurrentInstruction(
-          new InvokeStatic(method.getReference(), invoke.outValue(), invoke.inValues()));
-
+      ProgramMethod method = getSyntheticMethod(context, appInfo);
       registerSynthesizedMethod.accept(method);
+      iterator.remove();
+      iterator.add(new CfInvoke(Opcodes.INVOKESTATIC, method.getReference(), false));
+    }
+
+    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)));
     }
 
     public DexProto getProto(DexItemFactory itemFactory) {
@@ -1401,12 +1399,27 @@
     CfCode create(InternalOptions options, DexMethod method);
   }
 
-  private interface MethodInvokeRewriter {
+  public interface MethodInvokeRewriter {
 
-    void rewrite(
-        InvokeMethod invoke,
-        InstructionListIterator iterator,
-        DexItemFactory factory,
-        Set<Value> affectedValues);
+    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);
   }
 }
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 90654b2..ec0a554 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,52 +4,27 @@
 
 package com.android.tools.r8.ir.desugar.backports;
 
-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.cf.code.CfLogicalBinop;
+import com.android.tools.r8.cf.code.CfLogicalBinop.Opcode;
 import com.android.tools.r8.ir.code.NumericType;
-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;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
 
 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;
 
-    iterator.replaceCurrentInstruction(
-        new And(NumericType.INT, invoke.outValue(), inValues.get(0), inValues.get(1)));
+  private static MethodInvokeRewriter createRewriter(CfLogicalBinop.Opcode op) {
+    return (invoke, factory) -> new CfLogicalBinop(op, NumericType.INT);
   }
 
-  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 rewriteLogicalAnd() {
+    return createRewriter(Opcode.And);
   }
 
-  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 rewriteLogicalOr() {
+    return createRewriter(Opcode.Or);
+  }
 
-    iterator.replaceCurrentInstruction(
-        new Xor(NumericType.INT, invoke.outValue(), inValues.get(0), inValues.get(1)));
+  public static MethodInvokeRewriter rewriteLogicalXor() {
+    return createRewriter(Opcode.Xor);
   }
 
   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 354040f..64c94c5 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,54 +4,31 @@
 
 package com.android.tools.r8.ir.desugar.backports;
 
-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;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import org.objectweb.asm.Opcodes;
 
 public final class CollectionMethodRewrites {
 
   private CollectionMethodRewrites() {}
 
-  public static void rewriteListOfEmpty(
-      InvokeMethod invoke,
-      InstructionListIterator iterator,
-      DexItemFactory factory,
-      Set<Value> affectedValues) {
-    rewriteToCollectionMethod(invoke, iterator, factory, "emptyList");
+  public static MethodInvokeRewriter rewriteListOfEmpty() {
+    return rewriteToCollectionMethod("emptyList");
   }
 
-  public static void rewriteSetOfEmpty(
-      InvokeMethod invoke,
-      InstructionListIterator iterator,
-      DexItemFactory factory,
-      Set<Value> affectedValues) {
-    rewriteToCollectionMethod(invoke, iterator, factory, "emptySet");
+  public static MethodInvokeRewriter rewriteSetOfEmpty() {
+    return rewriteToCollectionMethod("emptySet");
   }
 
-  public static void rewriteMapOfEmpty(
-      InvokeMethod invoke,
-      InstructionListIterator iterator,
-      DexItemFactory factory,
-      Set<Value> affectedValues) {
-    rewriteToCollectionMethod(invoke, iterator, factory, "emptyMap");
+  public static MethodInvokeRewriter rewriteMapOfEmpty() {
+    return rewriteToCollectionMethod("emptyMap");
   }
 
-  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);
+  private static MethodInvokeRewriter rewriteToCollectionMethod(String methodName) {
+    return (invoke, factory) ->
+        new CfInvoke(
+            Opcodes.INVOKESTATIC,
+            factory.createMethod(factory.collectionsType, invoke.getMethod().proto, methodName),
+            false);
   }
 }
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 3601c51..8670d3b 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,29 +4,20 @@
 
 package com.android.tools.r8.ir.desugar.backports;
 
-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;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import org.objectweb.asm.Opcodes;
 
 public final class FloatMethodRewrites {
 
   private FloatMethodRewrites() {}
 
-  public static void rewriteHashCode(
-      InvokeMethod invoke,
-      InstructionListIterator iterator,
-      DexItemFactory factory,
-      Set<Value> affectedValues) {
-    InvokeStatic mathInvoke =
-        new InvokeStatic(
+  public static MethodInvokeRewriter rewriteHashCode() {
+    return (invoke, factory) ->
+        new CfInvoke(
+            Opcodes.INVOKESTATIC,
             factory.createMethod(
-                factory.boxedFloatType, invoke.getInvokedMethod().proto, "floatToIntBits"),
-            invoke.outValue(),
-            invoke.inValues(),
+                factory.boxedFloatType, invoke.getMethod().proto, "floatToIntBits"),
             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 348194b..5c29f81 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,28 +4,16 @@
 
 package com.android.tools.r8.ir.desugar.backports;
 
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.ir.code.Cmp;
+import com.android.tools.r8.cf.code.CfCmp;
 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.code.Value;
-import java.util.List;
-import java.util.Set;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
 
 public final class LongMethodRewrites {
 
   private LongMethodRewrites() {}
 
-  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)));
+  public static MethodInvokeRewriter rewriteCompare() {
+    return (invoke, factory) -> new CfCmp(Bias.NONE, NumericType.LONG);
   }
 }
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 4a413bc..050aefa 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,56 +1,44 @@
 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.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.graph.DexMethod;
 import com.android.tools.r8.ir.code.NumericType;
-import com.android.tools.r8.ir.code.Value;
-import java.util.List;
-import java.util.Set;
+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;
 
 public final class NumericMethodRewrites {
-  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 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 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 rewriteToAddInstruction() {
+    return (invoke, factory) -> {
+      NumericType numericType = NumericType.fromDexType(invoke.getMethod().getReturnType());
+      return new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, numericType);
+    };
   }
 
-  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();
+  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();
+      }
+    };
   }
 
   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 de16e98..69253be 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,43 +4,41 @@
 
 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.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;
+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;
 
 public final class ObjectsMethodRewrites {
 
-  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 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 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);
+  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));
+      }
+    };
   }
 }
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 1334c62..d66d7f5 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,69 +4,40 @@
 
 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.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;
+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;
 
 public final class OptionalMethodRewrites {
 
   private OptionalMethodRewrites() {}
 
-  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);
+  private static MethodInvokeRewriter createRewriter(
+      Function<DexItemFactory, DexType> holderTypeSupplier, String methodName) {
+    return (invoke, factory) ->
+        new CfInvoke(
+            Opcodes.INVOKEVIRTUAL,
+            factory.createMethod(
+                holderTypeSupplier.apply(factory), invoke.getMethod().proto, methodName),
+            false);
   }
 
-  public static void rewriteDoubleOrElseGet(
-      InvokeMethod invoke,
-      InstructionListIterator iterator,
-      DexItemFactory factory,
-      Set<Value> affectedValues) {
-    InvokeVirtual getInvoke =
-        new InvokeVirtual(
-            factory.createMethod(
-                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 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);
+  public static MethodInvokeRewriter rewriteIntOrElseGet() {
+    return createRewriter(factory -> factory.optionalIntType, "getAsInt");
+  }
+
+  public static MethodInvokeRewriter rewriteLongOrElseGet() {
+    return createRewriter(factory -> factory.optionalLongType, "getAsLong");
   }
 }
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index f5c75aa..67721e4 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -642,7 +642,7 @@
               "530-checker-lse2",
               TestCondition.match(
                   TestCondition.tools(DexTool.DX),
-                  TestCondition.compilers(CompilerUnderTest.D8),
+                  TestCondition.compilers(CompilerUnderTest.R8, CompilerUnderTest.D8),
                   TestCondition.runtimesUpTo(DexVm.Version.V6_0_1)))
           .put(
               "534-checker-bce-deoptimization",