Merge commit '75d3c2a9584996d5ba82250273eb506cab57adf1' into dev-release
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index 2906011..6c9025d 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -33,7 +33,6 @@
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.LazyLoadedDexApplication;
 import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfigurationParser;
@@ -153,18 +152,13 @@
         code = buildEmptyThrowingCfCode(method.getReference());
       }
       DexEncodedMethod throwingMethod =
-          new DexEncodedMethod(
-              method.getReference(),
-              method.accessFlags,
-              MethodTypeSignature.noSignature(),
-              DexAnnotationSet.empty(),
-              ParameterAnnotationsList.empty(),
-              code,
-              false,
-              CfVersion.V1_6,
-              AndroidApiLevel.UNKNOWN,
-              AndroidApiLevel.UNKNOWN,
-              false);
+          DexEncodedMethod.builder()
+              .setMethod(method.getReference())
+              .setAccessFlags(method.accessFlags)
+              .setGenericSignature(MethodTypeSignature.noSignature())
+              .setCode(code)
+              .setClassFileVersion(CfVersion.V1_6)
+              .build();
       if (method.isStatic() || method.isDirectMethod()) {
         directMethods.add(throwingMethod);
       } else {
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index b279333..9336265 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -651,8 +651,13 @@
         }
       }
       fields[i] =
-          new DexEncodedField(
-              field, accessFlags, fieldTypeSignature, fieldAnnotations, staticValue);
+          DexEncodedField.builder()
+              .setField(field)
+              .setAccessFlags(accessFlags)
+              .setGenericSignature(fieldTypeSignature)
+              .setAnnotations(fieldAnnotations)
+              .setStaticValue(staticValue)
+              .build();
     }
     return fields;
   }
@@ -696,13 +701,14 @@
         }
       }
       methods[i] =
-          new DexEncodedMethod(
-              method,
-              accessFlags,
-              methodTypeSignature,
-              methodAnnotations,
-              parameterAnnotationsIterator.getNextFor(method),
-              code);
+          DexEncodedMethod.builder()
+              .setMethod(method)
+              .setAccessFlags(accessFlags)
+              .setGenericSignature(methodTypeSignature)
+              .setAnnotations(methodAnnotations)
+              .setParameterAnnotations(parameterAnnotationsIterator.getNextFor(method))
+              .setCode(code)
+              .build();
     }
     return methods;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index ebbcd93..72cddc2 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -31,6 +31,7 @@
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.KeepInfoCollection;
+import com.android.tools.r8.shaking.KeepMethodInfo;
 import com.android.tools.r8.shaking.LibraryModeledPredicate;
 import com.android.tools.r8.shaking.MainDexInfo;
 import com.android.tools.r8.shaking.ProguardCompatibilityActions;
@@ -508,6 +509,10 @@
     return keepInfo;
   }
 
+  public KeepMethodInfo getKeepInfo(ProgramMethod method) {
+    return getKeepInfo().getMethodInfo(method);
+  }
+
   public boolean hasProguardCompatibilityActions() {
     return proguardCompatibilityActions != null;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 5efaba3..c89d8ed 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -32,9 +32,6 @@
 public class DexEncodedField extends DexEncodedMember<DexEncodedField, DexField>
     implements StructuralItem<DexEncodedField> {
 
-  public static final boolean D8_R8_SYNTHESIZED = true;
-  public static final boolean NOT_DEPRECATED = false;
-  public static final DexValue NO_STATIC_VALUE = null;
   public static final DexEncodedField[] EMPTY_ARRAY = {};
 
   public final FieldAccessFlags accessFlags;
@@ -56,38 +53,7 @@
     // TODO(b/171867022): Should the optimization info and member info be part of the definition?
   }
 
-  public DexEncodedField(DexField field, FieldAccessFlags accessFlags) {
-    this(field, accessFlags, FieldTypeSignature.noSignature(), DexAnnotationSet.empty(), null);
-  }
-
-  public DexEncodedField(
-      DexField field,
-      FieldAccessFlags accessFlags,
-      FieldTypeSignature genericSignature,
-      DexAnnotationSet annotations,
-      DexValue staticValue) {
-    this(field, accessFlags, genericSignature, annotations, staticValue, false);
-  }
-
-  public DexEncodedField(
-      DexField field,
-      FieldAccessFlags accessFlags,
-      FieldTypeSignature genericSignature,
-      DexAnnotationSet annotations,
-      DexValue staticValue,
-      boolean deprecated) {
-    this(
-        field,
-        accessFlags,
-        genericSignature,
-        annotations,
-        staticValue,
-        deprecated,
-        false,
-        AndroidApiLevel.UNKNOWN);
-  }
-
-  public DexEncodedField(
+  private DexEncodedField(
       DexField field,
       FieldAccessFlags accessFlags,
       FieldTypeSignature genericSignature,
@@ -375,11 +341,15 @@
   }
 
   public static Builder builder() {
-    return new Builder();
+    return new Builder(false);
   }
 
   private static Builder builder(DexEncodedField from) {
-    return new Builder(from);
+    return new Builder(from.isD8R8Synthesized(), from);
+  }
+
+  public static Builder syntheticBuilder() {
+    return new Builder(true);
   }
 
   public static class Builder {
@@ -388,16 +358,18 @@
     private DexAnnotationSet annotations = DexAnnotationSet.empty();
     private FieldAccessFlags accessFlags;
     private FieldTypeSignature genericSignature = FieldTypeSignature.noSignature();
-    private DexValue staticValue;
-    private AndroidApiLevel apiLevel;
+    private DexValue staticValue = null;
+    private AndroidApiLevel apiLevel = AndroidApiLevel.UNKNOWN;
     private FieldOptimizationInfo optimizationInfo = DefaultFieldOptimizationInfo.getInstance();
     private boolean deprecated;
-    private boolean d8R8Synthesized;
+    private final boolean d8R8Synthesized;
     private Consumer<DexEncodedField> buildConsumer = ConsumerUtils.emptyConsumer();
 
-    Builder() {}
+    private Builder(boolean d8R8Synthesized) {
+      this.d8R8Synthesized = d8R8Synthesized;
+    }
 
-    Builder(DexEncodedField from) {
+    private Builder(boolean d8R8Synthesized, DexEncodedField from) {
       // Copy all the mutable state of a DexEncodedField here.
       field = from.getReference();
       accessFlags = from.accessFlags.copy();
@@ -411,7 +383,7 @@
               ? from.optimizationInfo.asMutableFieldOptimizationInfo().mutableCopy()
               : from.optimizationInfo;
       deprecated = from.isDeprecated();
-      d8R8Synthesized = from.isD8R8Synthesized();
+      this.d8R8Synthesized = d8R8Synthesized;
     }
 
     public Builder apply(Consumer<Builder> consumer) {
@@ -456,13 +428,23 @@
       return this;
     }
 
-    public Builder setD8R8Synthesized() {
-      this.d8R8Synthesized = true;
+    public Builder setApiLevel(AndroidApiLevel apiLevel) {
+      this.apiLevel = apiLevel;
       return this;
     }
 
-    public Builder setApiLevel(AndroidApiLevel apiLevel) {
-      this.apiLevel = apiLevel;
+    public Builder setGenericSignature(FieldTypeSignature genericSignature) {
+      this.genericSignature = genericSignature;
+      return this;
+    }
+
+    public Builder setStaticValue(DexValue staticValue) {
+      this.staticValue = staticValue;
+      return this;
+    }
+
+    public Builder setDeprecated(boolean deprecated) {
+      this.deprecated = deprecated;
       return this;
     }
 
@@ -471,7 +453,6 @@
       assert accessFlags != null;
       assert genericSignature != null;
       assert annotations != null;
-      assert apiLevel != null;
       DexEncodedField dexEncodedField =
           new DexEncodedField(
               field,
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 6cbb223..c68adac 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -97,8 +97,6 @@
 public class DexEncodedMethod extends DexEncodedMember<DexEncodedMethod, DexMethod>
     implements StructuralItem<DexEncodedMethod> {
 
-  public static final boolean D8_R8_SYNTHESIZED = true;
-
   public static final String CONFIGURATION_DEBUGGING_PREFIX = "Shaking error: Missing method in ";
 
   /**
@@ -145,7 +143,12 @@
           MethodTypeSignature.noSignature(),
           DexAnnotationSet.empty(),
           ParameterAnnotationsList.empty(),
-          null);
+          null,
+          false,
+          null,
+          AndroidApiLevel.UNKNOWN,
+          AndroidApiLevel.UNKNOWN,
+          false);
   public static final Int2ReferenceMap<DebugLocalInfo> NO_PARAMETER_INFO =
       new Int2ReferenceArrayMap<>(0);
 
@@ -201,6 +204,10 @@
     return getReference().getArgumentType(argumentIndex, isStatic());
   }
 
+  public int getFirstNonReceiverArgumentIndex() {
+    return isStatic() ? 0 : 1;
+  }
+
   public int getNumberOfArguments() {
     return getReference().getArity() + BooleanUtils.intValue(isInstance());
   }
@@ -222,73 +229,7 @@
     obsolete = false;
   }
 
-  public DexEncodedMethod(
-      DexMethod method,
-      MethodAccessFlags accessFlags,
-      MethodTypeSignature genericSignature,
-      DexAnnotationSet annotations,
-      ParameterAnnotationsList parameterAnnotationsList,
-      Code code) {
-    this(
-        method,
-        accessFlags,
-        genericSignature,
-        annotations,
-        parameterAnnotationsList,
-        code,
-        false,
-        null,
-        AndroidApiLevel.UNKNOWN,
-        AndroidApiLevel.UNKNOWN);
-  }
-
-  public DexEncodedMethod(
-      DexMethod method,
-      MethodAccessFlags accessFlags,
-      MethodTypeSignature genericSignature,
-      DexAnnotationSet annotations,
-      ParameterAnnotationsList parameterAnnotationsList,
-      Code code,
-      boolean d8R8Synthesized) {
-    this(
-        method,
-        accessFlags,
-        genericSignature,
-        annotations,
-        parameterAnnotationsList,
-        code,
-        d8R8Synthesized,
-        null,
-        AndroidApiLevel.UNKNOWN,
-        AndroidApiLevel.UNKNOWN);
-  }
-
-  public DexEncodedMethod(
-      DexMethod method,
-      MethodAccessFlags accessFlags,
-      MethodTypeSignature genericSignature,
-      DexAnnotationSet annotations,
-      ParameterAnnotationsList parameterAnnotationsList,
-      Code code,
-      boolean d8R8Synthesized,
-      CfVersion classFileVersion,
-      AndroidApiLevel apiLevelForDefinition,
-      AndroidApiLevel apiLevelForCode) {
-    this(
-        method,
-        accessFlags,
-        genericSignature,
-        annotations,
-        parameterAnnotationsList,
-        code,
-        d8R8Synthesized,
-        classFileVersion,
-        apiLevelForDefinition,
-        apiLevelForCode,
-        false);
-  }
-
-  public DexEncodedMethod(
+  private DexEncodedMethod(
       DexMethod method,
       MethodAccessFlags accessFlags,
       MethodTypeSignature genericSignature,
@@ -1316,18 +1257,19 @@
     // Some debuggers (like IntelliJ) automatically skip synthetic methods on single step.
     newFlags.setSynthetic();
     newFlags.unsetAbstract();
-    return new DexEncodedMethod(
-        newMethod,
-        newFlags,
-        MethodTypeSignature.noSignature(),
-        DexAnnotationSet.empty(),
-        ParameterAnnotationsList.empty(),
-        ForwardMethodBuilder.builder(factory)
-            .setNonStaticSource(newMethod)
-            // Holder is companion class, or retarget method, not an interface.
-            .setStaticTarget(forwardMethod, false)
-            .build(),
-        true);
+    // Holder is companion class, or retarget method, not an interface.
+    return syntheticBuilder()
+        .setMethod(newMethod)
+        .setAccessFlags(newFlags)
+        .setGenericSignature(MethodTypeSignature.noSignature())
+        .setAnnotations(DexAnnotationSet.empty())
+        .setCode(
+            ForwardMethodBuilder.builder(factory)
+                .setNonStaticSource(newMethod)
+                // Holder is companion class, or retarget method, not an interface.
+                .setStaticTarget(forwardMethod, false)
+                .build())
+        .build();
   }
 
   public DexEncodedMethod toStaticMethodWithoutThis(AppView<AppInfoWithLiveness> appView) {
@@ -1469,16 +1411,20 @@
     this.genericSignature = MethodTypeSignature.noSignature();
   }
 
+  public static Builder syntheticBuilder() {
+    return new Builder(true);
+  }
+
   private static Builder syntheticBuilder(DexEncodedMethod from) {
-    return new Builder(from, true);
+    return new Builder(true, from);
   }
 
   public static Builder builder() {
-    return new Builder();
+    return new Builder(false);
   }
 
   private static Builder builder(DexEncodedMethod from) {
-    return new Builder(from);
+    return new Builder(from.isD8R8Synthesized(), from);
   }
 
   public static class Builder {
@@ -1486,7 +1432,6 @@
     private MethodAccessFlags accessFlags;
     private Code code;
     private DexMethod method;
-
     private MethodTypeSignature genericSignature = MethodTypeSignature.noSignature();
     private DexAnnotationSet annotations = DexAnnotationSet.empty();
     private OptionalBool isLibraryMethodOverride = OptionalBool.UNKNOWN;
@@ -1495,19 +1440,23 @@
     private MethodOptimizationInfo optimizationInfo = DefaultMethodOptimizationInfo.getInstance();
     private KotlinMethodLevelInfo kotlinInfo = getNoKotlinInfo();
     private CfVersion classFileVersion = null;
-    private AndroidApiLevel apiLevelForDefinition = null;
-    private AndroidApiLevel apiLevelForCode = null;
-    private boolean d8R8Synthesized = false;
+    private AndroidApiLevel apiLevelForDefinition = AndroidApiLevel.UNKNOWN;
+    private AndroidApiLevel apiLevelForCode = AndroidApiLevel.UNKNOWN;
+    private final boolean d8R8Synthesized;
+    private boolean deprecated = false;
+
+    // Checks to impose on the built method. They should always be active to start with and be
+    // lowered on the use site.
+    private boolean checkMethodNotNull = true;
+    private boolean checkParameterAnnotationList = true;
 
     private Consumer<DexEncodedMethod> buildConsumer = ConsumerUtils.emptyConsumer();
 
-    private Builder() {}
-
-    private Builder(DexEncodedMethod from) {
-      this(from, from.isD8R8Synthesized());
+    private Builder(boolean d8R8Synthesized) {
+      this.d8R8Synthesized = d8R8Synthesized;
     }
 
-    private Builder(DexEncodedMethod from, boolean d8R8Synthesized) {
+    private Builder(boolean d8R8Synthesized, DexEncodedMethod from) {
       // Copy all the mutable state of a DexEncodedMethod here.
       method = from.getReference();
       accessFlags = from.getAccessFlags().copy();
@@ -1523,6 +1472,7 @@
       kotlinInfo = from.getKotlinInfo();
       classFileVersion = from.classFileVersion;
       this.d8R8Synthesized = d8R8Synthesized;
+      deprecated = from.isDeprecated();
 
       if (from.getParameterAnnotations().isEmpty()
           || from.getParameterAnnotations().size() == from.getParameters().size()) {
@@ -1574,11 +1524,6 @@
       return this;
     }
 
-    public Builder setD8R8Synthesized() {
-      this.d8R8Synthesized = true;
-      return this;
-    }
-
     public Builder setMethod(DexMethod method) {
       this.method = method;
       return this;
@@ -1699,12 +1644,48 @@
       return setCode(null);
     }
 
+    public Builder setGenericSignature(MethodTypeSignature methodSignature) {
+      this.genericSignature = methodSignature;
+      return this;
+    }
+
+    public Builder setApiLevelForDefinition(AndroidApiLevel apiLevelForDefinition) {
+      this.apiLevelForDefinition = apiLevelForDefinition;
+      return this;
+    }
+
+    public Builder setApiLevelForCode(AndroidApiLevel apiLevelForCode) {
+      this.apiLevelForCode = apiLevelForCode;
+      return this;
+    }
+
+    public Builder setDeprecated(boolean deprecated) {
+      this.deprecated = deprecated;
+      return this;
+    }
+
+    public Builder setClassFileVersion(CfVersion version) {
+      classFileVersion = version;
+      return this;
+    }
+
+    public Builder disableMethodNotNullCheck() {
+      checkMethodNotNull = false;
+      return this;
+    }
+
+    public Builder disableParameterAnnotationListCheck() {
+      checkParameterAnnotationList = false;
+      return this;
+    }
+
     public DexEncodedMethod build() {
-      assert method != null;
+      assert !checkMethodNotNull || method != null;
       assert accessFlags != null;
       assert annotations != null;
       assert parameterAnnotations != null;
-      assert parameterAnnotations.isEmpty()
+      assert !checkParameterAnnotationList
+          || parameterAnnotations.isEmpty()
           || parameterAnnotations.size() == method.proto.parameters.size();
       assert apiLevelForDefinition != null;
       assert apiLevelForCode != null;
@@ -1719,7 +1700,8 @@
               d8R8Synthesized,
               classFileVersion,
               apiLevelForDefinition,
-              apiLevelForCode);
+              apiLevelForCode,
+              deprecated);
       result.setKotlinMemberInfo(kotlinInfo);
       result.compilationState = compilationState;
       result.optimizationInfo = optimizationInfo;
@@ -1729,20 +1711,5 @@
       buildConsumer.accept(result);
       return result;
     }
-
-    public Builder setGenericSignature(MethodTypeSignature methodSignature) {
-      this.genericSignature = methodSignature;
-      return this;
-    }
-
-    public Builder setApiLevelForDefinition(AndroidApiLevel apiLevelForDefinition) {
-      this.apiLevelForDefinition = apiLevelForDefinition;
-      return this;
-    }
-
-    public Builder setApiLevelForCode(AndroidApiLevel apiLevelForCode) {
-      this.apiLevelForCode = apiLevelForCode;
-      return this;
-    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 2f052aa..2164d54 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -668,13 +668,14 @@
             createAnnotationSet(annotations, parent.application.options);
         DexValue staticValue = flags.isStatic() ? getStaticValue(value, dexField.type) : null;
         DexEncodedField field =
-            new DexEncodedField(
-                dexField,
-                flags,
-                fieldSignature,
-                annotationSet,
-                staticValue,
-                AsmUtils.isDeprecated(access));
+            DexEncodedField.builder()
+                .setField(dexField)
+                .setAccessFlags(flags)
+                .setGenericSignature(fieldSignature)
+                .setAnnotations(annotationSet)
+                .setStaticValue(staticValue)
+                .setDeprecated(AsmUtils.isDeprecated(access))
+                .build();
         if (flags.isStatic()) {
           parent.staticFields.add(field);
         } else {
@@ -902,18 +903,19 @@
             parent.application.getFactory()));
       }
       DexEncodedMethod dexMethod =
-          new DexEncodedMethod(
-              method,
-              flags,
-              genericSignature,
-              createAnnotationSet(annotations, options),
-              parameterAnnotationsList,
-              code,
-              false,
-              parent.version,
-              AndroidApiLevel.UNKNOWN,
-              AndroidApiLevel.UNKNOWN,
-              deprecated);
+          DexEncodedMethod.builder()
+              .setMethod(method)
+              .setAccessFlags(flags)
+              .setGenericSignature(genericSignature)
+              .setAnnotations(createAnnotationSet(annotations, options))
+              .setParameterAnnotations(parameterAnnotationsList)
+              .setCode(code)
+              .setClassFileVersion(parent.version)
+              .setApiLevelForDefinition(AndroidApiLevel.UNKNOWN)
+              .setApiLevelForCode(AndroidApiLevel.UNKNOWN)
+              .setDeprecated(deprecated)
+              .disableParameterAnnotationListCheck()
+              .build();
       Wrapper<DexMethod> signature = MethodSignatureEquivalence.get().wrap(method);
       if (parent.methodSignatures.add(signature)) {
         parent.hasReachabilitySensitiveMethod |= isReachabilitySensitive();
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
index c016016..4304fdd 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.analysis.value.SingleValue;
 import com.android.tools.r8.ir.code.ConstInstruction;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Position;
@@ -11,12 +13,13 @@
 import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.BooleanUtils;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Ordering;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceRBTreeMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
 import it.unimi.dsi.fastutil.ints.IntBidirectionalIterator;
+import it.unimi.dsi.fastutil.ints.IntSortedSet;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.function.Consumer;
@@ -68,11 +71,11 @@
 
     public static class Builder {
 
-      private boolean isAlwaysNull = false;
-      private DexType type = null;
+      private SingleValue singleValue;
+      private DexType type;
 
-      public Builder setIsAlwaysNull() {
-        this.isAlwaysNull = true;
+      public Builder setSingleValue(SingleValue singleValue) {
+        this.singleValue = singleValue;
         return this;
       }
 
@@ -83,15 +86,15 @@
 
       public RemovedArgumentInfo build() {
         assert type != null;
-        return new RemovedArgumentInfo(isAlwaysNull, type);
+        return new RemovedArgumentInfo(singleValue, type);
       }
     }
 
-    private final boolean isAlwaysNull;
+    private final SingleValue singleValue;
     private final DexType type;
 
-    private RemovedArgumentInfo(boolean isAlwaysNull, DexType type) {
-      this.isAlwaysNull = isAlwaysNull;
+    private RemovedArgumentInfo(SingleValue singleValue, DexType type) {
+      this.singleValue = singleValue;
       this.type = type;
     }
 
@@ -99,16 +102,20 @@
       return new Builder();
     }
 
+    public boolean hasSingleValue() {
+      return singleValue != null;
+    }
+
+    public SingleValue getSingleValue() {
+      return singleValue;
+    }
+
     public DexType getType() {
       return type;
     }
 
-    public boolean isAlwaysNull() {
-      return isAlwaysNull;
-    }
-
     public boolean isNeverUsed() {
-      return !isAlwaysNull;
+      return !hasSingleValue();
     }
 
     @Override
@@ -142,6 +149,16 @@
       this.newType = newType;
     }
 
+    public RewrittenTypeInfo combine(RewrittenPrototypeDescription other) {
+      return other.hasRewrittenReturnInfo() ? combine(other.getRewrittenReturnInfo()) : this;
+    }
+
+    public RewrittenTypeInfo combine(RewrittenTypeInfo other) {
+      assert !getNewType().isVoidType();
+      assert getNewType() == other.getOldType();
+      return new RewrittenTypeInfo(getOldType(), other.getNewType());
+    }
+
     public DexType getNewType() {
       return newType;
     }
@@ -197,6 +214,10 @@
       return EMPTY;
     }
 
+    public IntSortedSet getKeys() {
+      return argumentInfos.keySet();
+    }
+
     public boolean isEmpty() {
       return this == EMPTY;
     }
@@ -224,6 +245,10 @@
       return argumentInfos.getOrDefault(argumentIndex, ArgumentInfo.NO_INFO);
     }
 
+    public int size() {
+      return argumentInfos.size();
+    }
+
     public static Builder builder() {
       return new Builder();
     }
@@ -248,6 +273,24 @@
       }
     }
 
+    public DexMethod rewriteMethod(ProgramMethod method, DexItemFactory dexItemFactory) {
+      if (isEmpty()) {
+        return method.getReference();
+      }
+      DexProto rewrittenProto = rewriteProto(method, dexItemFactory);
+      return method.getReference().withProto(rewrittenProto, dexItemFactory);
+    }
+
+    public DexProto rewriteProto(ProgramMethod method, DexItemFactory dexItemFactory) {
+      return isEmpty()
+          ? method.getProto()
+          : dexItemFactory.createProto(method.getReturnType(), rewriteParameters(method));
+    }
+
+    public DexType[] rewriteParameters(ProgramMethod method) {
+      return rewriteParameters(method.getDefinition());
+    }
+
     public DexType[] rewriteParameters(DexEncodedMethod encodedMethod) {
       // Currently not allowed to remove the receiver of an instance method. This would involve
       // changing invoke-direct/invoke-virtual into invoke-static.
@@ -382,13 +425,38 @@
     return NONE;
   }
 
+  public RewrittenPrototypeDescription combine(RewrittenPrototypeDescription other) {
+    if (isEmpty()) {
+      return other;
+    }
+    if (other.isEmpty()) {
+      return this;
+    }
+    // We currently don't have any passes that remove extra parameters inserted by previous passes.
+    // If the input prototype changes have removed some of the extra parameters, we would need to
+    // adapt the merging of prototype changes below.
+    List<ExtraParameter> newExtraParameters =
+        ImmutableList.<ExtraParameter>builder()
+            .addAll(getExtraParameters())
+            .addAll(other.getExtraParameters())
+            .build();
+    RewrittenTypeInfo newRewrittenTypeInfo =
+        hasRewrittenReturnInfo()
+            ? getRewrittenReturnInfo().combine(other)
+            : other.getRewrittenReturnInfo();
+    ArgumentInfoCollection newArgumentInfoCollection =
+        getArgumentInfoCollection().combine(other.getArgumentInfoCollection());
+    return new RewrittenPrototypeDescription(
+        newExtraParameters, newRewrittenTypeInfo, newArgumentInfoCollection);
+  }
+
   public boolean isEmpty() {
     return extraParameters.isEmpty()
         && rewrittenReturnInfo == null
         && argumentInfoCollection.isEmpty();
   }
 
-  public Collection<ExtraParameter> getExtraParameters() {
+  public List<ExtraParameter> getExtraParameters() {
     return extraParameters;
   }
 
@@ -465,6 +533,18 @@
         extraParameters, rewrittenReturnInfo, argumentInfoCollection.combine(other));
   }
 
+  public RewrittenPrototypeDescription withRewrittenReturnInfo(
+      RewrittenTypeInfo rewrittenReturnInfo) {
+    if (rewrittenReturnInfo == null) {
+      return this;
+    }
+    if (!hasRewrittenReturnInfo()) {
+      return new RewrittenPrototypeDescription(
+          extraParameters, rewrittenReturnInfo, argumentInfoCollection);
+    }
+    throw new Unreachable();
+  }
+
   public RewrittenPrototypeDescription withExtraUnusedNullParameter() {
     return withExtraUnusedNullParameters(1);
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index 43a2c8a..b1055ef 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -9,7 +9,6 @@
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -21,10 +20,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.FieldAccessFlags;
-import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMember;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
@@ -131,17 +127,14 @@
 
     AndroidApiLevel apiReferenceLevel = classInitializerMerger.getApiReferenceLevel(appView);
     DexEncodedMethod definition =
-        new DexEncodedMethod(
-            newMethodReference,
-            MethodAccessFlags.createForClassInitializer(),
-            MethodTypeSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            ParameterAnnotationsList.empty(),
-            classInitializerMerger.getCode(syntheticMethodReference),
-            DexEncodedMethod.D8_R8_SYNTHESIZED,
-            classInitializerMerger.getCfVersion(),
-            apiReferenceLevel,
-            apiReferenceLevel);
+        DexEncodedMethod.syntheticBuilder()
+            .setMethod(newMethodReference)
+            .setAccessFlags(MethodAccessFlags.createForClassInitializer())
+            .setCode(classInitializerMerger.getCode(syntheticMethodReference))
+            .setClassFileVersion(classInitializerMerger.getCfVersion())
+            .setApiLevelForDefinition(apiReferenceLevel)
+            .setApiLevelForCode(apiReferenceLevel)
+            .build();
     classMethodsBuilder.addDirectMethod(definition);
 
     // In case we didn't synthesize CF code, we register the class initializer for conversion to dex
@@ -218,18 +211,12 @@
     assert appView.hasLiveness();
     assert mode.isInitial();
 
-    boolean deprecated = false;
-    boolean d8R8Synthesized = true;
     DexEncodedField classIdField =
-        new DexEncodedField(
-            group.getClassIdField(),
-            FieldAccessFlags.createPublicFinalSynthetic(),
-            FieldTypeSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            null,
-            deprecated,
-            d8R8Synthesized,
-            minApiLevelIfEnabledOrUnknown(appView));
+        DexEncodedField.syntheticBuilder()
+            .setField(group.getClassIdField())
+            .setAccessFlags(FieldAccessFlags.createPublicFinalSynthetic())
+            .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
+            .build();
 
     // For the $r8$classId synthesized fields, we try to over-approximate the set of values it may
     // have. For example, for a merge group of size 4, we may compute the set {0, 2, 3}, if the
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
index d6ae971..2ab6c46 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -11,15 +11,12 @@
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeUtils;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.code.ConstructorEntryPointSynthesizedCode;
@@ -362,17 +359,15 @@
 
     DexEncodedMethod representativeMethod = representative.getDefinition();
     DexEncodedMethod newInstanceInitializer =
-        new DexEncodedMethod(
-            newMethodReference,
-            getNewAccessFlags(),
-            MethodTypeSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            ParameterAnnotationsList.empty(),
-            getNewCode(newMethodReference, syntheticMethodReference, needsClassId, extraNulls),
-            true,
-            getNewClassFileVersion(),
-            representativeMethod.getApiLevelForDefinition(),
-            representativeMethod.getApiLevelForCode());
+        DexEncodedMethod.syntheticBuilder()
+            .setMethod(newMethodReference)
+            .setAccessFlags(getNewAccessFlags())
+            .setCode(
+                getNewCode(newMethodReference, syntheticMethodReference, needsClassId, extraNulls))
+            .setClassFileVersion(getNewClassFileVersion())
+            .setApiLevelForDefinition(representativeMethod.getApiLevelForDefinition())
+            .setApiLevelForCode(representativeMethod.getApiLevelForCode())
+            .build();
     classMethodsBuilder.addDirectMethod(newInstanceInitializer);
 
     if (mode.isFinal()) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index be6a5a2..62b09a6 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -7,16 +7,13 @@
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.horizontalclassmerging.code.VirtualMethodEntryPointSynthesizedCode;
 import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
@@ -278,17 +275,14 @@
             bridgeMethodReference,
             appView.dexItemFactory());
     DexEncodedMethod newMethod =
-        new DexEncodedMethod(
-            newMethodReference,
-            getAccessFlags(),
-            MethodTypeSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            ParameterAnnotationsList.empty(),
-            synthesizedCode,
-            true,
-            classFileVersion,
-            representativeMethod.getApiLevelForDefinition(),
-            representativeMethod.getApiLevelForCode());
+        DexEncodedMethod.syntheticBuilder()
+            .setMethod(newMethodReference)
+            .setAccessFlags(getAccessFlags())
+            .setCode(synthesizedCode)
+            .setClassFileVersion(classFileVersion)
+            .setApiLevelForDefinition(representativeMethod.getApiLevelForDefinition())
+            .setApiLevelForCode(representativeMethod.getApiLevelForCode())
+            .build();
     if (!representative.getDefinition().isLibraryMethodOverride().isUnknown()) {
       newMethod.setLibraryMethodOverride(representative.getDefinition().isLibraryMethodOverride());
     }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
index 8f4baa6..c42a3bb 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
@@ -17,8 +17,8 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.ConstClass;
-import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -64,7 +64,8 @@
   @Override
   public Instruction createMaterializingInstruction(
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      IRCode code,
+      ProgramMethod context,
+      NumberGenerator valueNumberGenerator,
       TypeAndLocalInfoSupplier info) {
     TypeElement typeLattice = info.getOutType();
     DebugLocalInfo debugLocalInfo = info.getLocalInfo();
@@ -73,9 +74,12 @@
         .isSubtype(appView.dexItemFactory().classType, typeLattice.asClassType().getClassType())
         .isTrue();
     Value returnedValue =
-        code.createValue(classClassType(appView, definitelyNotNull()), debugLocalInfo);
+        new Value(
+            valueNumberGenerator.next(),
+            classClassType(appView, definitelyNotNull()),
+            debugLocalInfo);
     ConstClass instruction = new ConstClass(returnedValue, type);
-    assert !instruction.instructionMayHaveSideEffects(appView, code.context());
+    assert !instruction.instructionMayHaveSideEffects(appView, context);
     return instruction;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java
index 13df457..a537863 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java
@@ -15,8 +15,8 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.DexItemBasedConstString;
-import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
@@ -74,7 +74,8 @@
   @Override
   public Instruction createMaterializingInstruction(
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      IRCode code,
+      ProgramMethod context,
+      NumberGenerator valueNumberGenerator,
       TypeAndLocalInfoSupplier info) {
     TypeElement typeLattice = info.getOutType();
     DebugLocalInfo debugLocalInfo = info.getLocalInfo();
@@ -83,7 +84,10 @@
         .isSubtype(appView.dexItemFactory().stringType, typeLattice.asClassType().getClassType())
         .isTrue();
     Value returnedValue =
-        code.createValue(stringClassType(appView, definitelyNotNull()), debugLocalInfo);
+        new Value(
+            valueNumberGenerator.next(),
+            stringClassType(appView, definitelyNotNull()),
+            debugLocalInfo);
     DexItemBasedConstString instruction =
         new DexItemBasedConstString(returnedValue, item, nameComputationInfo);
     assert !instruction.instructionInstanceCanThrow();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
index 911174b..8769ab2 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -17,8 +17,8 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.StaticGet;
 import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
 import com.android.tools.r8.ir.code.Value;
@@ -69,11 +69,12 @@
   @Override
   public Instruction createMaterializingInstruction(
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      IRCode code,
+      ProgramMethod context,
+      NumberGenerator valueNumberGenerator,
       TypeAndLocalInfoSupplier info) {
     TypeElement type = TypeElement.fromDexType(field.type, maybeNull(), appView);
     assert type.lessThanOrEqual(info.getOutType(), appView);
-    Value outValue = code.createValue(type, info.getLocalInfo());
+    Value outValue = new Value(valueNumberGenerator.next(), type, info.getLocalInfo());
     return new StaticGet(outValue, field);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
index d51b94a..c01133e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
@@ -11,8 +11,8 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.ConstNumber;
-import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -126,14 +126,17 @@
   @Override
   public Instruction createMaterializingInstruction(
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      IRCode code,
+      ProgramMethod context,
+      NumberGenerator valueNumberGenerator,
       TypeAndLocalInfoSupplier info) {
     TypeElement typeLattice = info.getOutType();
     DebugLocalInfo debugLocalInfo = info.getLocalInfo();
     assert !typeLattice.isReferenceType() || value == 0;
     Value returnedValue =
-        code.createValue(
-            typeLattice.isReferenceType() ? TypeElement.getNull() : typeLattice, debugLocalInfo);
+        new Value(
+            valueNumberGenerator.next(),
+            typeLattice.isReferenceType() ? TypeElement.getNull() : typeLattice,
+            debugLocalInfo);
     return new ConstNumber(returnedValue, value);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
index cd0c9e9..35acae4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
@@ -15,8 +15,8 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.ConstString;
-import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -62,7 +62,8 @@
   @Override
   public Instruction createMaterializingInstruction(
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      IRCode code,
+      ProgramMethod context,
+      NumberGenerator valueNumberGenerator,
       TypeAndLocalInfoSupplier info) {
     TypeElement typeLattice = info.getOutType();
     DebugLocalInfo debugLocalInfo = info.getLocalInfo();
@@ -71,7 +72,10 @@
         .isSubtype(appView.dexItemFactory().stringType, typeLattice.asClassType().getClassType())
         .isTrue();
     Value returnedValue =
-        code.createValue(stringClassType(appView, definitelyNotNull()), debugLocalInfo);
+        new Value(
+            valueNumberGenerator.next(),
+            stringClassType(appView, definitelyNotNull()),
+            debugLocalInfo);
     ConstString instruction = new ConstString(returnedValue, string);
     assert !instruction.instructionInstanceCanThrow();
     return instruction;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
index 4a3a11c..94cd837 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -35,9 +36,17 @@
    * Note that calls to this method should generally be guarded by {@link
    * #isMaterializableInContext}.
    */
-  public abstract Instruction createMaterializingInstruction(
+  public final Instruction createMaterializingInstruction(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       IRCode code,
+      TypeAndLocalInfoSupplier info) {
+    return createMaterializingInstruction(appView, code.context(), code.valueNumberGenerator, info);
+  }
+
+  public abstract Instruction createMaterializingInstruction(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      ProgramMethod context,
+      NumberGenerator valueNumberGenerator,
       TypeAndLocalInfoSupplier info);
 
   public abstract boolean isMaterializableInContext(
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index e06df9e..90ca265 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -123,6 +123,10 @@
     public static ThrowingInfo defaultForConstString(InternalOptions options) {
       return options.isGeneratingClassFiles() ? NO_THROW : CAN_THROW;
     }
+
+    public static ThrowingInfo defaultForInstruction(Instruction instruction) {
+      return instruction.instructionTypeCanThrow() ? CAN_THROW : NO_THROW;
+    }
   }
 
   public enum EdgeType {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
index 1e5a344..2be47ca 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
@@ -144,10 +144,11 @@
         // We don't care about calls to native methods.
         return;
       }
-      if (appView.appInfo().isPinned(callee.getReference())) {
-        // Since the callee is kept, we cannot inline it into the caller, and we also cannot collect
-        // any optimization info for the method. Therefore, we drop the call edge to reduce the
-        // total number of call graph edges, which should lead to fewer call graph cycles.
+      if (!appView.getKeepInfo(callee).isInliningAllowed(appView.options())) {
+        // Since the callee is kept and optimizations are disallowed, we cannot inline it into the
+        // caller, and we also cannot collect any optimization info for the method. Therefore, we
+        // drop the call edge to reduce the total number of call graph edges, which should lead to
+        // fewer call graph cycles.
         return;
       }
       getOrCreateNode(callee).addCallerConcurrently(currentMethod, likelySpuriousCallEdge);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index 9f464b8..2b0cc34 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -848,6 +848,8 @@
   @Override
   public boolean verifyCurrentInstructionCanThrow() {
     return isCurrentlyGeneratingMethodSynchronization()
+        // In the prelude we may be materializing arguments from call sites in R8.
+        || inPrelude
         || code.getInstructions().get(currentInstructionIndex).canThrow();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index 6c0279a..211f77b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -66,6 +66,7 @@
   private Try currentTryRange = null;
   private CatchHandlers<Integer> currentCatchHandlers = null;
   private Instruction currentDexInstruction = null;
+  private boolean isBuildingPrelude;
 
   private Position currentPosition = null;
   private final CanonicalPositions canonicalPositions;
@@ -134,14 +135,16 @@
 
   @Override
   public void buildPrelude(IRBuilder builder) {
+    assert !isBuildingPrelude;
+    isBuildingPrelude = true;
     currentPosition = canonicalPositions.getPreamblePosition();
-    if (code.incomingRegisterSize == 0) {
-      return;
+    if (code.incomingRegisterSize > 0) {
+      builder.buildArgumentsWithRewrittenPrototypeChanges(
+          code.registerSize - code.incomingRegisterSize,
+          method.getDefinition(),
+          DexSourceCode::doNothingWriteConsumer);
     }
-    builder.buildArgumentsWithRewrittenPrototypeChanges(
-        code.registerSize - code.incomingRegisterSize,
-        method.getDefinition(),
-        DexSourceCode::doNothingWriteConsumer);
+    isBuildingPrelude = false;
   }
 
   public static void doNothingWriteConsumer(Integer register, DexType type) {
@@ -198,7 +201,8 @@
 
   @Override
   public boolean verifyCurrentInstructionCanThrow() {
-    return currentDexInstruction.canThrow();
+    // In the prelude we may be materializing arguments from call sites in R8.
+    return isBuildingPrelude || currentDexInstruction.canThrow();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 3911614..b35e2ca 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -43,6 +43,7 @@
 import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.value.SingleValue;
 import com.android.tools.r8.ir.code.Add;
 import com.android.tools.r8.ir.code.And;
 import com.android.tools.r8.ir.code.Argument;
@@ -110,6 +111,7 @@
 import com.android.tools.r8.ir.code.StaticPut;
 import com.android.tools.r8.ir.code.Sub;
 import com.android.tools.r8.ir.code.Throw;
+import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
 import com.android.tools.r8.ir.code.Ushr;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.code.ValueType;
@@ -1037,13 +1039,27 @@
   private void handleConstantOrUnusedArgument(
       int register, RemovedArgumentInfo removedArgumentInfo) {
     assert removedArgumentInfo != null;
-    if (removedArgumentInfo.isAlwaysNull()) {
+    if (removedArgumentInfo.hasSingleValue()) {
       if (pendingArgumentInstructions == null) {
         pendingArgumentInstructions = new ArrayList<>();
       }
       DebugLocalInfo local = getOutgoingLocal(register);
-      Value value = writeRegister(register, getNull(), ThrowingInfo.NO_THROW, local);
-      pendingArgumentInstructions.add(new ConstNumber(value, 0));
+      SingleValue singleValue = removedArgumentInfo.getSingleValue();
+      TypeElement type =
+          removedArgumentInfo.getType().isReferenceType() && singleValue.isNull()
+              ? getNull()
+              : removedArgumentInfo.getType().toTypeElement(appView);
+      Instruction materializingInstruction =
+          singleValue.createMaterializingInstruction(
+              appView.withClassHierarchy(),
+              method,
+              valueNumberGenerator,
+              TypeAndLocalInfoSupplier.create(type, local));
+      writeRegister(
+          register,
+          materializingInstruction.outValue(),
+          ThrowingInfo.defaultForInstruction(materializingInstruction));
+      pendingArgumentInstructions.add(materializingInstruction);
     } else {
       assert removedArgumentInfo.isNeverUsed();
     }
@@ -2339,8 +2355,12 @@
   // See addDebugLocalStart and addDebugLocalEnd.
   private Value writeRegister(
       int register, TypeElement typeLattice, ThrowingInfo throwing, DebugLocalInfo local) {
+    return writeRegister(
+        register, new Value(valueNumberGenerator.next(), typeLattice, local), throwing);
+  }
+
+  private Value writeRegister(int register, Value value, ThrowingInfo throwing) {
     checkRegister(register);
-    Value value = new Value(valueNumberGenerator.next(), typeLattice, local);
     currentBlock.writeCurrentDefinition(register, value, throwing);
     return value;
   }
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 8f6f293..e1c1ba6 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
@@ -56,7 +56,6 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
 import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceApplicationRewriter;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
-import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
 import com.android.tools.r8.ir.desugar.lambda.LambdaDeserializationMethodRemover;
 import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
 import com.android.tools.r8.ir.optimize.AssertionsRewriter;
@@ -100,6 +99,7 @@
 import com.android.tools.r8.shaking.LibraryMethodOverrideAnalysis;
 import com.android.tools.r8.utils.Action;
 import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.ConsumerUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -136,7 +136,6 @@
   private final StringOptimizer stringOptimizer;
   private final StringBuilderOptimizer stringBuilderOptimizer;
   private final IdempotentFunctionCallCanonicalizer idempotentFunctionCallCanonicalizer;
-  private final InterfaceMethodRewriter interfaceMethodRewriter;
   private final ClassInliner classInliner;
   private final ClassStaticizer classStaticizer;
   private final InternalOptions options;
@@ -220,7 +219,6 @@
       // - invoke-special desugaring.
       assert options.desugarState.isOn();
       this.instructionDesugaring = CfInstructionDesugaringCollection.create(appView);
-      this.interfaceMethodRewriter = null;
       this.covariantReturnTypeAnnotationTransformer = null;
       this.dynamicTypeOptimization = null;
       this.classInliner = null;
@@ -246,10 +244,6 @@
         appView.enableWholeProgramOptimizations()
             ? CfInstructionDesugaringCollection.empty()
             : CfInstructionDesugaringCollection.create(appView);
-    this.interfaceMethodRewriter =
-        options.isInterfaceMethodDesugaringEnabled() && appView.enableWholeProgramOptimizations()
-            ? new InterfaceMethodRewriter(appView, this)
-            : null;
     this.covariantReturnTypeAnnotationTransformer =
         options.processCovariantReturnTypeAnnotations
             ? new CovariantReturnTypeAnnotationTransformer(this, appView.dexItemFactory())
@@ -346,17 +340,10 @@
         D8NestBasedAccessDesugaring::clearNestAttributes);
   }
 
-  private void staticizeClasses(
-      OptimizationFeedback feedback, ExecutorService executorService, GraphLens applied)
+  private void staticizeClasses(OptimizationFeedback feedback, ExecutorService executorService)
       throws ExecutionException {
     if (classStaticizer != null) {
-      classStaticizer.staticizeCandidates(feedback, executorService, applied);
-    }
-  }
-
-  private void collectStaticizerCandidates(DexApplication application) {
-    if (classStaticizer != null) {
-      classStaticizer.collectCandidates(application);
+      classStaticizer.staticizeCandidates(feedback, executorService);
     }
   }
 
@@ -627,7 +614,6 @@
     DexApplication application = appView.appInfo().app();
 
     computeReachabilitySensitivity(application);
-    collectStaticizerCandidates(application);
     workaroundAbstractMethodOnNonAbstractClassVerificationBug(
         executorService, simpleOptimizationFeedback);
 
@@ -639,7 +625,9 @@
 
     printPhase("Primary optimization pass");
 
-    // Setup the argument propagator for the primary optimization pass.
+    GraphLens graphLensForPrimaryOptimizationPass = appView.graphLens();
+
+    // Setup optimizations for the primary optimization pass.
     appView.withArgumentPropagator(
         argumentPropagator -> argumentPropagator.initializeCodeScanner(executorService, timing));
     appView.withCallSiteOptimizationInfoPropagator(
@@ -649,16 +637,26 @@
           optimization.abandonCallSitePropagationForPinnedMethodsAndOverrides(
               executorService, timing);
         });
+    ConsumerUtils.acceptIfNotNull(
+        enumUnboxer,
+        enumUnboxer ->
+            enumUnboxer.initializeEnumUnboxingCandidates(graphLensForPrimaryOptimizationPass));
+    ConsumerUtils.acceptIfNotNull(
+        classStaticizer,
+        classStaticizer ->
+            classStaticizer.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass));
+    ConsumerUtils.acceptIfNotNull(
+        inliner,
+        inliner -> inliner.initializeDoubleInlineCallers(graphLensForPrimaryOptimizationPass));
 
     if (fieldAccessAnalysis != null) {
       fieldAccessAnalysis.fieldAssignmentTracker().initialize();
     }
 
     // Process the application identifying outlining candidates.
-    GraphLens initialGraphLensForIR = appView.graphLens();
-    GraphLens graphLensForIR = initialGraphLensForIR;
     OptimizationFeedbackDelayed feedback = delayedOptimizationFeedback;
-    PostMethodProcessor.Builder postMethodProcessorBuilder = new PostMethodProcessor.Builder();
+    PostMethodProcessor.Builder postMethodProcessorBuilder =
+        new PostMethodProcessor.Builder(graphLensForPrimaryOptimizationPass);
     {
       timing.begin("Build primary method processor");
       PrimaryMethodProcessor primaryMethodProcessor =
@@ -669,6 +667,7 @@
       if (outliner != null) {
         outliner.createOutlineMethodIdentifierGenerator();
       }
+      assert appView.graphLens() == graphLensForPrimaryOptimizationPass;
       primaryMethodProcessor.forEachMethod(
           (method, methodProcessingContext) ->
               processDesugaredMethod(
@@ -677,8 +676,8 @@
           this::waveDone,
           timing,
           executorService);
+      assert appView.graphLens() == graphLensForPrimaryOptimizationPass;
       timing.end();
-      assert graphLensForIR == appView.graphLens();
     }
 
     // The field access info collection is not maintained during IR processing.
@@ -696,42 +695,54 @@
     // Commit synthetics from the primary optimization pass.
     commitPendingSyntheticItemsR8(appView);
 
+    // Post processing:
+    //   1) Second pass for methods whose collected call site information become more precise.
+    //   2) Second inlining pass for dealing with double inline callers.
+    printPhase("Post optimization pass");
+
     // Analyze the data collected by the argument propagator, use the analysis result to update
     // the parameter optimization infos, and rewrite the application.
     appView.withArgumentPropagator(
         argumentPropagator ->
             argumentPropagator.tearDownCodeScanner(
                 postMethodProcessorBuilder, executorService, timing));
+    appView.withCallSiteOptimizationInfoPropagator(
+        callSiteOptimizationInfoPropagator ->
+            callSiteOptimizationInfoPropagator.enqueueMethodsForReprocessing(
+                postMethodProcessorBuilder));
 
     if (libraryMethodOverrideAnalysis != null) {
       libraryMethodOverrideAnalysis.finish();
     }
 
-    // Post processing:
-    //   1) Second pass for methods whose collected call site information become more precise.
-    //   2) Second inlining pass for dealing with double inline callers.
-    printPhase("Post optimization pass");
-    appView.withCallSiteOptimizationInfoPropagator(
-        optimization ->
-            postMethodProcessorBuilder.put(appView.callSiteOptimizationInfoPropagator()));
-    if (inliner != null) {
-      postMethodProcessorBuilder.put(inliner);
-    }
+    ConsumerUtils.acceptIfNotNull(
+        inliner, inliner -> inliner.enqueueMethodsForReprocessing(postMethodProcessorBuilder));
+
     if (!options.debug) {
       new TrivialFieldAccessReprocessor(appView.withLiveness(), postMethodProcessorBuilder)
           .run(executorService, feedback, timing);
     }
+
     if (enumUnboxer != null) {
       enumUnboxer.unboxEnums(this, postMethodProcessorBuilder, executorService, feedback);
     } else {
       appView.setUnboxedEnums(EnumDataMap.empty());
     }
+
+    GraphLens graphLensForSecondaryOptimizationPass = appView.graphLens();
+
+    ConsumerUtils.acceptIfNotNull(
+        classStaticizer,
+        classStaticizer ->
+            classStaticizer.prepareForSecondaryOptimizationPass(
+                graphLensForSecondaryOptimizationPass));
+
     timing.begin("IR conversion phase 2");
-    graphLensForIR = appView.graphLens();
     PostMethodProcessor postMethodProcessor =
         postMethodProcessorBuilder.build(appView, executorService, timing);
     if (postMethodProcessor != null) {
       assert !options.debug;
+      assert appView.graphLens() == graphLensForSecondaryOptimizationPass;
       postMethodProcessor.forEachMethod(
           (method, methodProcessingContext) ->
               processDesugaredMethod(
@@ -739,7 +750,7 @@
           feedback,
           executorService);
       feedback.updateVisibleOptimizationInfo();
-      assert graphLensForIR == appView.graphLens();
+      assert appView.graphLens() == graphLensForSecondaryOptimizationPass;
     }
     timing.end();
 
@@ -756,7 +767,7 @@
     if (!options.isGeneratingClassFiles()) {
       printPhase("Class staticizer post processing");
       // TODO(b/127694949): Adapt to PostOptimization.
-      staticizeClasses(feedback, executorService, initialGraphLensForIR);
+      staticizeClasses(feedback, executorService);
       feedback.updateVisibleOptimizationInfo();
       // The class staticizer lens shall not be applied through lens code rewriting or it breaks
       // the lambda merger.
@@ -1302,6 +1313,7 @@
           .libraryMethodOptimizer()
           .optimize(code, feedback, methodProcessor, methodProcessingContext);
       timing.end();
+      previous = printMethod(code, "IR after class library method optimizer (SSA)", previous);
       assert code.isConsistentSSA();
     }
 
@@ -1312,6 +1324,7 @@
       timing.begin("Devirtualize invoke interface");
       devirtualizer.devirtualizeInvokeInterface(code);
       timing.end();
+      previous = printMethod(code, "IR after devirtualizer (SSA)", previous);
     }
 
     assert code.verifyTypes(appView);
@@ -1388,12 +1401,14 @@
       timing.begin("Rewrite throw NPE");
       codeRewriter.rewriteThrowNullPointerException(code);
       timing.end();
+      previous = printMethod(code, "IR after rewrite throw null (SSA)", previous);
     }
 
     timing.begin("Optimize class initializers");
     ClassInitializerDefaultsResult classInitializerDefaultsResult =
         classInitializerDefaultsOptimization.optimize(code, feedback);
     timing.end();
+    previous = printMethod(code, "IR after class initializer optimisation (SSA)", previous);
 
     if (Log.ENABLED) {
       Log.debug(getClass(), "Intermediate (SSA) flow graph for %s:\n%s",
@@ -1405,7 +1420,7 @@
     deadCodeRemover.run(code, timing);
     assert code.isConsistentSSA();
 
-    previous = printMethod(code, "IR after lambda desugaring (SSA)", previous);
+    previous = printMethod(code, "IR after dead code removal (SSA)", previous);
 
     assert code.verifyTypes(appView);
 
@@ -1591,7 +1606,7 @@
       timing.end();
     }
 
-    if (appView.getKeepInfo().getMethodInfo(code.context()).isPinned(options)) {
+    if (appView.getKeepInfo(code.context()).isPinned(options)) {
       return;
     }
 
@@ -1713,8 +1728,7 @@
         || definition.getOptimizationInfo().isReachabilitySensitive()) {
       return false;
     }
-    if (appView.appInfo().hasLiveness()
-        && appView.appInfo().withLiveness().isPinned(method.getReference())) {
+    if (!appView.getKeepInfo(method).isInliningAllowed(options)) {
       return false;
     }
     return true;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index a8e5508..36c8dc6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -55,15 +55,22 @@
 
   public static class Builder {
 
-    private final LongLivedProgramMethodSetBuilder<?> methodsToReprocessBuilder =
-        LongLivedProgramMethodSetBuilder.createForIdentitySet();
+    private final LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsToReprocessBuilder;
 
-    Builder() {}
+    Builder(GraphLens graphLensForPrimaryOptimizationPass) {
+      this.methodsToReprocessBuilder =
+          LongLivedProgramMethodSetBuilder.createForIdentitySet(
+              graphLensForPrimaryOptimizationPass);
+    }
 
     public void add(ProgramMethod method) {
       methodsToReprocessBuilder.add(method);
     }
 
+    public LongLivedProgramMethodSetBuilder<ProgramMethodSet> getMethodsToReprocessBuilder() {
+      return methodsToReprocessBuilder;
+    }
+
     public void put(ProgramMethodSet methodsToRevisit) {
       methodsToRevisit.forEach(this::add);
     }
@@ -72,15 +79,11 @@
       put(postOptimization.methodsToRevisit());
     }
 
-    public void removePrunedMethods(Iterable<DexMethod> prunedMethod) {
-      methodsToReprocessBuilder.removeAll(prunedMethod);
-    }
-
     // Some optimizations may change methods, creating new instances of the encoded methods with a
     // new signature. The compiler needs to update the set of methods that must be reprocessed
     // according to the graph lens.
-    public void rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens applied) {
-      methodsToReprocessBuilder.rewrittenWithLens(appView, applied);
+    public void rewrittenWithLens(AppView<AppInfoWithLiveness> appView) {
+      methodsToReprocessBuilder.rewrittenWithLens(appView);
     }
 
     PostMethodProcessor build(
@@ -103,8 +106,7 @@
         // Nothing to revisit.
         return null;
       }
-      ProgramMethodSet methodsToReprocess =
-          methodsToReprocessBuilder.build(appView, appView.graphLens());
+      ProgramMethodSet methodsToReprocess = methodsToReprocessBuilder.build(appView);
       CallGraph callGraph =
           new PartialCallGraphBuilder(appView, methodsToReprocess).build(executorService, timing);
       return new PostMethodProcessor(appView, callGraph);
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 197ab34..64c4cd2 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
@@ -88,11 +88,8 @@
 
   @Override
   public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
-    return instruction.isInvoke() && methodIsBackport(instruction.asInvoke().getMethod());
-  }
-
-  public boolean methodIsBackport(DexMethod method) {
-    return getMethodProviderOrNull(method) != null;
+    return instruction.isInvoke()
+        && getMethodProviderOrNull(instruction.asInvoke().getMethod()) != null;
   }
 
   public static List<DexMethod> generateListOfBackportedMethods(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
index e4a34c4..8f5178e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
@@ -6,14 +6,17 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.conversion.D8MethodProcessor;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterPostProcessingEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryAPICallbackSynthesizorEventConsumer;
+import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
 import com.android.tools.r8.ir.desugar.itf.InterfaceProcessingDesugaringEventConsumer;
 import com.android.tools.r8.shaking.Enqueuer.SyntheticAdditions;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import java.util.concurrent.ExecutionException;
+import java.util.function.BiConsumer;
 
 /**
  * Specialized Event consumer for desugaring finalization. During finalization, it is not possible
@@ -31,8 +34,10 @@
   }
 
   public static R8PostProcessingDesugaringEventConsumer createForR8(
-      SyntheticAdditions additions, CfInstructionDesugaringCollection desugaring) {
-    return new R8PostProcessingDesugaringEventConsumer(additions, desugaring);
+      SyntheticAdditions additions,
+      CfInstructionDesugaringCollection desugaring,
+      BiConsumer<DexProgramClass, DexType> missingClassConsumer) {
+    return new R8PostProcessingDesugaringEventConsumer(additions, desugaring, missingClassConsumer);
   }
 
   public abstract void finalizeDesugaring() throws ExecutionException;
@@ -60,6 +65,12 @@
     }
 
     @Override
+    public void warnMissingInterface(
+        DexProgramClass context, DexType missing, InterfaceDesugaringSyntheticHelper helper) {
+      helper.warnMissingInterface(context, context, missing);
+    }
+
+    @Override
     public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
       // Intentionally empty.
     }
@@ -114,11 +125,21 @@
 
     private final SyntheticAdditions additions;
     private final CfInstructionDesugaringCollection desugaring;
+    private final BiConsumer<DexProgramClass, DexType> missingClassConsumer;
 
     R8PostProcessingDesugaringEventConsumer(
-        SyntheticAdditions additions, CfInstructionDesugaringCollection desugaring) {
+        SyntheticAdditions additions,
+        CfInstructionDesugaringCollection desugaring,
+        BiConsumer<DexProgramClass, DexType> missingClassConsumer) {
       this.additions = additions;
       this.desugaring = desugaring;
+      this.missingClassConsumer = missingClassConsumer;
+    }
+
+    @Override
+    public void warnMissingInterface(
+        DexProgramClass context, DexType missing, InterfaceDesugaringSyntheticHelper helper) {
+      missingClassConsumer.accept(context, missing);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index 5760bd2..a18d9b9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -164,16 +164,18 @@
             .setVirtualTarget(methodReference, methodHolder.isInterface())
             .setCastResult();
     DexEncodedMethod newVirtualMethod =
-        new DexEncodedMethod(
-            newMethod,
-            newAccessFlags,
-            methodDefinition.getGenericSignature(),
-            methodDefinition
-                .annotations()
-                .keepIf(x -> !isCovariantReturnTypeAnnotation(x.annotation)),
-            methodDefinition.parameterAnnotationsList.keepIf(Predicates.alwaysTrue()),
-            forwardMethodBuilder.build(),
-            true);
+        DexEncodedMethod.syntheticBuilder()
+            .setMethod(newMethod)
+            .setAccessFlags(newAccessFlags)
+            .setGenericSignature(methodDefinition.getGenericSignature())
+            .setAnnotations(
+                methodDefinition
+                    .annotations()
+                    .keepIf(x -> !isCovariantReturnTypeAnnotation(x.annotation)))
+            .setParameterAnnotations(
+                methodDefinition.parameterAnnotationsList.keepIf(Predicates.alwaysTrue()))
+            .setCode(forwardMethodBuilder.build())
+            .build();
     // Optimize to generate DexCode instead of CfCode.
     ProgramMethod programMethod = new ProgramMethod(methodHolder, newVirtualMethod);
     converter.optimizeSynthesizedMethod(programMethod);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index dd815a3..c7f5ec8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
@@ -26,12 +25,9 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue.DexValueNull;
 import com.android.tools.r8.graph.FieldAccessFlags;
-import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.ir.code.Invoke.Type;
@@ -159,34 +155,30 @@
 
     // Synthesize main method.
     methods.add(
-        new DexEncodedMethod(
-            mainMethod,
-            MethodAccessFlags.fromSharedAccessFlags(
-                Constants.ACC_PUBLIC | Constants.ACC_FINAL, false),
-            MethodTypeSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            ParameterAnnotationsList.empty(),
-            LambdaMainMethodSourceCode.build(this, mainMethod),
-            true));
+        DexEncodedMethod.syntheticBuilder()
+            .setMethod(mainMethod)
+            .setAccessFlags(
+                MethodAccessFlags.fromSharedAccessFlags(
+                    Constants.ACC_PUBLIC | Constants.ACC_FINAL, false))
+            .setCode(LambdaMainMethodSourceCode.build(this, mainMethod))
+            .build());
 
     // Synthesize bridge methods.
     for (DexProto bridgeProto : descriptor.bridges) {
       DexMethod bridgeMethod =
           appView.dexItemFactory().createMethod(type, bridgeProto, descriptor.name);
       methods.add(
-          new DexEncodedMethod(
-              bridgeMethod,
-              MethodAccessFlags.fromSharedAccessFlags(
-                  Constants.ACC_PUBLIC
-                      | Constants.ACC_FINAL
-                      | Constants.ACC_SYNTHETIC
-                      | Constants.ACC_BRIDGE,
-                  false),
-              MethodTypeSignature.noSignature(),
-              DexAnnotationSet.empty(),
-              ParameterAnnotationsList.empty(),
-              LambdaBridgeMethodSourceCode.build(this, bridgeMethod, mainMethod),
-              true));
+          DexEncodedMethod.syntheticBuilder()
+              .setMethod(bridgeMethod)
+              .setAccessFlags(
+                  MethodAccessFlags.fromSharedAccessFlags(
+                      Constants.ACC_PUBLIC
+                          | Constants.ACC_FINAL
+                          | Constants.ACC_SYNTHETIC
+                          | Constants.ACC_BRIDGE,
+                      false))
+              .setCode(LambdaBridgeMethodSourceCode.build(this, bridgeMethod, mainMethod))
+              .build());
     }
     builder.setVirtualMethods(methods);
   }
@@ -197,31 +189,27 @@
     List<DexEncodedMethod> methods = new ArrayList<>(stateless ? 2 : 1);
 
     // Constructor.
+    MethodAccessFlags accessFlags =
+        MethodAccessFlags.fromSharedAccessFlags(
+            (stateless ? Constants.ACC_PRIVATE : Constants.ACC_PUBLIC) | Constants.ACC_SYNTHETIC,
+            true);
     methods.add(
-        new DexEncodedMethod(
-            constructor,
-            MethodAccessFlags.fromSharedAccessFlags(
-                (stateless ? Constants.ACC_PRIVATE : Constants.ACC_PUBLIC)
-                    | Constants.ACC_SYNTHETIC,
-                true),
-            MethodTypeSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            ParameterAnnotationsList.empty(),
-            LambdaConstructorSourceCode.build(this),
-            true));
+        DexEncodedMethod.syntheticBuilder()
+            .setMethod(constructor)
+            .setAccessFlags(accessFlags)
+            .setCode(LambdaConstructorSourceCode.build(this))
+            .build());
 
     // Class constructor for stateless lambda classes.
     if (stateless) {
       methods.add(
-          new DexEncodedMethod(
-              classConstructor,
-              MethodAccessFlags.fromSharedAccessFlags(
-                  Constants.ACC_SYNTHETIC | Constants.ACC_STATIC, true),
-              MethodTypeSignature.noSignature(),
-              DexAnnotationSet.empty(),
-              ParameterAnnotationsList.empty(),
-              LambdaClassConstructorSourceCode.build(this),
-              true));
+          DexEncodedMethod.syntheticBuilder()
+              .setMethod(classConstructor)
+              .setAccessFlags(
+                  MethodAccessFlags.fromSharedAccessFlags(
+                      Constants.ACC_SYNTHETIC | Constants.ACC_STATIC, true))
+              .setCode(LambdaClassConstructorSourceCode.build(this))
+              .build());
       feedback.classInitializerMayBePostponed(methods.get(1));
     }
     builder.setDirectMethods(methods);
@@ -233,19 +221,13 @@
     int fieldCount = fieldTypes.length;
     List<DexEncodedField> fields = new ArrayList<>(fieldCount);
     for (int i = 0; i < fieldCount; i++) {
-      boolean deprecated = false;
-      boolean d8R8Synthesized = true;
       fields.add(
-          new DexEncodedField(
-              getCaptureField(i),
-              FieldAccessFlags.createPublicFinalSynthetic(),
-              FieldTypeSignature.noSignature(),
-              DexAnnotationSet.empty(),
-              null,
-              deprecated,
-              d8R8Synthesized,
+          DexEncodedField.syntheticBuilder()
+              .setField(getCaptureField(i))
+              .setAccessFlags(FieldAccessFlags.createPublicFinalSynthetic())
               // The api level is computed when tracing.
-              AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView)));
+              .setApiLevel(AndroidApiLevel.UNKNOWN)
+              .build());
     }
     builder.setInstanceFields(fields);
   }
@@ -255,24 +237,20 @@
     if (isStateless()) {
       // Create instance field for stateless lambda.
       assert this.lambdaField != null;
-      boolean deprecated = false;
-      boolean d8R8Synthesized = true;
       builder.setStaticFields(
           Collections.singletonList(
-              new DexEncodedField(
-                  this.lambdaField,
-                  FieldAccessFlags.fromSharedAccessFlags(
-                      Constants.ACC_PUBLIC
-                          | Constants.ACC_FINAL
-                          | Constants.ACC_SYNTHETIC
-                          | Constants.ACC_STATIC),
-                  FieldTypeSignature.noSignature(),
-                  DexAnnotationSet.empty(),
-                  DexValueNull.NULL,
-                  deprecated,
-                  d8R8Synthesized,
+              DexEncodedField.syntheticBuilder()
+                  .setField(this.lambdaField)
+                  .setAccessFlags(
+                      FieldAccessFlags.fromSharedAccessFlags(
+                          Constants.ACC_PUBLIC
+                              | Constants.ACC_FINAL
+                              | Constants.ACC_SYNTHETIC
+                              | Constants.ACC_STATIC))
+                  .setStaticValue(DexValueNull.NULL)
                   // The api level is computed when tracing.
-                  AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView))));
+                  .setApiLevel(AndroidApiLevel.UNKNOWN)
+                  .build()));
     }
   }
 
@@ -579,14 +557,14 @@
                     // Always make the method public to provide access.
                     newAccessFlags.setPublic();
                     DexEncodedMethod newMethod =
-                        new DexEncodedMethod(
-                            callTarget,
-                            newAccessFlags,
-                            encodedMethod.getGenericSignature(),
-                            encodedMethod.annotations(),
-                            encodedMethod.parameterAnnotationsList,
-                            encodedMethod.getCode(),
-                            true);
+                        DexEncodedMethod.syntheticBuilder()
+                            .setMethod(callTarget)
+                            .setAccessFlags(newAccessFlags)
+                            .setGenericSignature(encodedMethod.getGenericSignature())
+                            .setAnnotations(encodedMethod.annotations())
+                            .setParameterAnnotations(encodedMethod.parameterAnnotationsList)
+                            .setCode(encodedMethod.getCode())
+                            .build();
                     newMethod.copyMetadata(encodedMethod);
                     forcefullyMovedLambdaMethodConsumer.acceptForcefullyMovedLambdaMethod(
                         encodedMethod.getReference(), callTarget);
@@ -661,16 +639,15 @@
                     // its accessibility and make it virtual.
                     MethodAccessFlags newAccessFlags = encodedMethod.accessFlags.copy();
                     newAccessFlags.unsetPrivate();
-                    newAccessFlags.setPublic();
                     DexEncodedMethod newMethod =
-                        new DexEncodedMethod(
-                            callTarget,
-                            newAccessFlags,
-                            encodedMethod.getGenericSignature(),
-                            encodedMethod.annotations(),
-                            encodedMethod.parameterAnnotationsList,
-                            encodedMethod.getCode(),
-                            true);
+                        DexEncodedMethod.syntheticBuilder()
+                            .setMethod(callTarget)
+                            .setAccessFlags(newAccessFlags)
+                            .setGenericSignature(encodedMethod.getGenericSignature())
+                            .setAnnotations(encodedMethod.annotations())
+                            .setParameterAnnotations(encodedMethod.parameterAnnotationsList)
+                            .setCode(encodedMethod.getCode())
+                            .build();
                     newMethod.copyMetadata(encodedMethod);
                     forcefullyMovedLambdaMethodConsumer.acceptForcefullyMovedLambdaMethod(
                         encodedMethod.getReference(), callTarget);
@@ -727,14 +704,11 @@
       ProgramMethod accessorMethod =
           new ProgramMethod(
               accessorClass,
-              new DexEncodedMethod(
-                  callTarget,
-                  MethodAccessFlags.createPublicStaticSynthetic(),
-                  MethodTypeSignature.noSignature(),
-                  DexAnnotationSet.empty(),
-                  ParameterAnnotationsList.empty(),
-                  AccessorMethodSourceCode.build(LambdaClass.this, callTarget),
-                  true));
+              DexEncodedMethod.syntheticBuilder()
+                  .setMethod(callTarget)
+                  .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+                  .setCode(AccessorMethodSourceCode.build(LambdaClass.this, callTarget))
+                  .build());
       accessorClass.addDirectMethod(accessorMethod.getDefinition());
       if (appView.options().isDesugaredLibraryCompilation()
           || appView.options().isGeneratingDex()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
index bd2e603..22654d6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
@@ -124,28 +124,25 @@
   private void synthesizeStaticFields(SyntheticProgramClassBuilder builder) {
     builder.setStaticFields(
         ImmutableList.of(
-            DexEncodedField.builder()
+            DexEncodedField.syntheticBuilder()
                 .setField(this.initializedValueField)
                 .setAccessFlags(FieldAccessFlags.createPrivateStaticSynthetic())
                 .setApiLevel(AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView))
-                .setD8R8Synthesized()
                 .build(),
-            DexEncodedField.builder()
+            DexEncodedField.syntheticBuilder()
                 .setField(this.constantValueField)
                 .setAccessFlags(FieldAccessFlags.createPrivateStaticSynthetic())
                 .setApiLevel(AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView))
-                .setD8R8Synthesized()
                 .build()));
   }
 
   private void synthesizeDirectMethods(SyntheticProgramClassBuilder builder) {
     builder.setDirectMethods(
         ImmutableList.of(
-            DexEncodedMethod.builder()
+            DexEncodedMethod.syntheticBuilder()
                 .setMethod(getConstMethod)
                 .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
                 .setCode(generateGetterCode(builder))
-                .setD8R8Synthesized()
                 .setApiLevelForDefinition(AndroidApiLevel.S)
                 .setApiLevelForCode(AndroidApiLevel.S)
                 .build()));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
index be8ac84..50aa6b2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
@@ -26,7 +26,6 @@
 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.code.Invoke.Type;
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
@@ -126,9 +125,7 @@
     if (isAPIConversionSyntheticType(context.getHolderType(), wrapperSynthesizor, appView)) {
       return false;
     }
-    CfInvoke invoke = instruction.asInvoke();
-    return shouldRewriteInvoke(
-        invoke.getMethod(), invoke.getInvokeType(context), invoke.isInterface(), context);
+    return shouldRewriteInvoke(instruction.asInvoke(), context);
   }
 
   static boolean isAPIConversionSyntheticType(
@@ -141,26 +138,20 @@
     return type.descriptor.toString().startsWith(DESCRIPTOR_VIVIFIED_PREFIX);
   }
 
-  private DexClassAndMethod getMethodForDesugaring(
-      DexMethod invokedMethod, boolean isInvokeSuper, boolean isInterface, ProgramMethod context) {
+  private DexClassAndMethod getMethodForDesugaring(CfInvoke invoke, ProgramMethod context) {
+    DexMethod invokedMethod = invoke.getMethod();
     // TODO(b/191656218): Use lookupInvokeSpecial instead when this is all to Cf.
-    return isInvokeSuper
+    return invoke.isInvokeSuper(context.getHolderType())
         ? appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context)
         : appView
             .appInfoForDesugaring()
-            .resolveMethod(invokedMethod, isInterface)
+            .resolveMethod(invokedMethod, invoke.isInterface())
             .getResolutionPair();
   }
 
   // TODO(b/191656218): Consider caching the result.
-  private boolean shouldRewriteInvoke(
-      DexMethod unresolvedInvokedMethod,
-      Type invokeType,
-      boolean isInterface,
-      ProgramMethod context) {
-    DexClassAndMethod invokedMethod =
-        getMethodForDesugaring(
-            unresolvedInvokedMethod, invokeType == Type.SUPER, isInterface, context);
+  private boolean shouldRewriteInvoke(CfInvoke invoke, ProgramMethod context) {
+    DexClassAndMethod invokedMethod = getMethodForDesugaring(invoke, context);
     if (invokedMethod == null) {
       // Implies a resolution/look-up failure, we do not convert to keep the runtime error.
       return false;
@@ -176,7 +167,7 @@
     if (isEmulatedInterfaceOverride(invokedMethod)) {
       return false;
     }
-    if (isAlreadyDesugared(unresolvedInvokedMethod, invokeType, isInterface, context)) {
+    if (isAlreadyDesugared(invoke, context)) {
       return false;
     }
     return appView.rewritePrefix.hasRewrittenTypeInSignature(invokedMethod.getProto(), appView);
@@ -203,22 +194,16 @@
             .containsKey(interfaceResult.getHolderType());
   }
 
-  private boolean isAlreadyDesugared(
-      DexMethod unresolvedInvokedMethod,
-      Type invokeType,
-      boolean isInterface,
-      ProgramMethod context) {
+  private boolean isAlreadyDesugared(CfInvoke invoke, ProgramMethod context) {
     if (interfaceMethodRewriter != null
-        && interfaceMethodRewriter.needsRewriting(unresolvedInvokedMethod, invokeType, context)) {
+        && interfaceMethodRewriter.needsDesugaring(invoke, context)) {
       return true;
     }
-    if (retargeter != null
-        && retargeter.hasNewInvokeTarget(
-            unresolvedInvokedMethod, isInterface, invokeType == Type.SUPER, context)) {
+    if (retargeter != null && retargeter.needsDesugaring(invoke, context)) {
       return true;
     }
     if (backportedMethodRewriter != null
-        && backportedMethodRewriter.methodIsBackport(unresolvedInvokedMethod)) {
+        && backportedMethodRewriter.needsDesugaring(invoke, context)) {
       return true;
     }
     return false;
@@ -378,8 +363,7 @@
     if (invoke.getMethod().isInstanceInitializer(appView.dexItemFactory())) {
       return false;
     }
-    DexClassAndMethod methodForDesugaring =
-        getMethodForDesugaring(invoke.getMethod(), false, invoke.isInterface(), context);
+    DexClassAndMethod methodForDesugaring = getMethodForDesugaring(invoke, context);
     assert methodForDesugaring != null;
     return methodForDesugaring.getAccessFlags().isPublic();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java
index 8a59f14..73d7f8f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java
@@ -19,11 +19,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
-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.desugar.CfInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
@@ -91,34 +86,6 @@
     return computeNewInvokeTarget(instruction, context).hasNewInvokeTarget();
   }
 
-  @Deprecated // Use Cf to Cf desugaring instead.
-  public void desugar(IRCode code) {
-    if (retargetLibraryMember.isEmpty()) {
-      return;
-    }
-
-    InstructionListIterator iterator = code.instructionListIterator();
-    while (iterator.hasNext()) {
-      Instruction instruction = iterator.next();
-      if (!instruction.isInvokeMethod()) {
-        continue;
-      }
-
-      InvokeMethod invoke = instruction.asInvokeMethod();
-      DexMethod invokedMethod = invoke.getInvokedMethod();
-      boolean isInterface = invoke.getInterfaceBit();
-
-      InvokeRetargetingResult invokeRetargetingResult =
-          computeNewInvokeTarget(
-              invokedMethod, isInterface, invoke.isInvokeSuper(), code.context());
-      if (invokeRetargetingResult.hasNewInvokeTarget()) {
-        DexMethod newInvokeTarget = invokeRetargetingResult.getNewInvokeTarget(null);
-        iterator.replaceCurrentInstruction(
-            new InvokeStatic(newInvokeTarget, invoke.outValue(), invoke.inValues()));
-      }
-    }
-  }
-
   static class InvokeRetargetingResult {
 
     static InvokeRetargetingResult NO_REWRITING =
@@ -154,32 +121,20 @@
     }
   }
 
-  public boolean hasNewInvokeTarget(
-      DexMethod invokedMethod, boolean isInterface, boolean isInvokeSuper, ProgramMethod context) {
-    return computeNewInvokeTarget(invokedMethod, isInterface, isInvokeSuper, context)
-        .hasNewInvokeTarget();
-  }
-
   private InvokeRetargetingResult computeNewInvokeTarget(
       CfInstruction instruction, ProgramMethod context) {
     if (retargetLibraryMember.isEmpty() || !instruction.isInvoke()) {
       return NO_REWRITING;
     }
     CfInvoke cfInvoke = instruction.asInvoke();
-    return computeNewInvokeTarget(
-        cfInvoke.getMethod(),
-        cfInvoke.isInterface(),
-        cfInvoke.isInvokeSuper(context.getHolderType()),
-        context);
-  }
-
-  private InvokeRetargetingResult computeNewInvokeTarget(
-      DexMethod invokedMethod, boolean isInterface, boolean isInvokeSuper, ProgramMethod context) {
-    InvokeRetargetingResult retarget = computeRetargetedMethod(invokedMethod, isInterface);
+    DexMethod invokedMethod = cfInvoke.getMethod();
+    InvokeRetargetingResult retarget =
+        computeRetargetedMethod(invokedMethod, cfInvoke.isInterface());
     if (!retarget.hasNewInvokeTarget()) {
       return NO_REWRITING;
     }
-    if (isInvokeSuper && matchesNonFinalHolderRewrite(invokedMethod)) {
+    if (cfInvoke.isInvokeSuper(context.getHolderType())
+        && matchesNonFinalHolderRewrite(invokedMethod)) {
       DexClassAndMethod superTarget =
           appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
       // Final methods can be rewritten as a normal invoke.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterLibraryTypeSynthesizor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterLibraryTypeSynthesizor.java
index 8c5a747..9e50613 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterLibraryTypeSynthesizor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterLibraryTypeSynthesizor.java
@@ -22,12 +22,10 @@
 import com.android.tools.r8.graph.DirectMappedDexApplication;
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
 import com.android.tools.r8.graph.GenericSignature.ClassSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.NestHostClassAttribute;
 import com.android.tools.r8.graph.NestMemberClassAttribute;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.utils.StringDiagnostic;
 import java.util.Comparator;
@@ -140,15 +138,13 @@
                       newClass,
                       ignore -> new TreeSet<>(Comparator.comparing(DexEncodedMethod::getReference)))
                   .add(
-                      new DexEncodedMethod(
-                          retargetMethod,
-                          MethodAccessFlags.fromCfAccessFlags(
-                              Constants.ACC_PUBLIC | Constants.ACC_STATIC, false),
-                          MethodTypeSignature.noSignature(),
-                          DexAnnotationSet.empty(),
-                          ParameterAnnotationsList.empty(),
-                          null,
-                          true));
+                      DexEncodedMethod.syntheticBuilder()
+                          .setMethod(retargetMethod)
+                          .setAccessFlags(
+                              MethodAccessFlags.fromCfAccessFlags(
+                                  Constants.ACC_PUBLIC | Constants.ACC_STATIC, false))
+                          .setCode(null)
+                          .build());
             }
           });
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
index aa69845..9bc1a8e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.ClasspathOrLibraryClass;
 import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -21,10 +20,7 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldAccessFlags;
-import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryClasspathWrapperSynthesizeEventConsumer;
@@ -561,14 +557,11 @@
     // TODO(b/146114533): Fix inlining in synthetic methods and remove unsetBridge.
     newFlags.unsetBridge();
     newFlags.setSynthetic();
-    return new DexEncodedMethod(
-        methodToInstall,
-        newFlags,
-        MethodTypeSignature.noSignature(),
-        DexAnnotationSet.empty(),
-        ParameterAnnotationsList.empty(),
-        code,
-        true);
+    return DexEncodedMethod.syntheticBuilder()
+        .setMethod(methodToInstall)
+        .setAccessFlags(newFlags)
+        .setCode(code)
+        .build();
   }
 
   private List<DexEncodedMethod> allImplementedMethods(DexClass clazz) {
@@ -624,8 +617,10 @@
     // Field is package private to be accessible from convert methods without a getter.
     FieldAccessFlags fieldAccessFlags =
         FieldAccessFlags.fromCfAccessFlags(Constants.ACC_FINAL | Constants.ACC_SYNTHETIC);
-    return new DexEncodedField(
-        field, fieldAccessFlags, FieldTypeSignature.noSignature(), DexAnnotationSet.empty(), null);
+    return DexEncodedField.syntheticBuilder()
+        .setField(field)
+        .setAccessFlags(fieldAccessFlags)
+        .build();
   }
 
   // Program wrappers are harder to deal with than classpath wrapper because generating a method's
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 8661587..2b8e3c7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -13,7 +13,6 @@
 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.DexAnnotationSet;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMember;
 import com.android.tools.r8.graph.DexClassAndMethod;
@@ -25,11 +24,9 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GenericSignature;
 import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.LibraryMethod;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodResolutionResult;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.utils.BooleanBox;
@@ -51,6 +48,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -295,10 +293,15 @@
 
     final DexClass directSubClass;
     final DexProgramClass closestProgramSubClass;
+    final BiConsumer<DexProgramClass, DexType> reportMissingTypeCallback;
 
-    public ReportingContext(DexClass directSubClass, DexProgramClass closestProgramSubClass) {
+    public ReportingContext(
+        DexClass directSubClass,
+        DexProgramClass closestProgramSubClass,
+        BiConsumer<DexProgramClass, DexType> reportMissingTypeCallback) {
       this.directSubClass = directSubClass;
       this.closestProgramSubClass = closestProgramSubClass;
+      this.reportMissingTypeCallback = reportMissingTypeCallback;
     }
 
     ReportingContext forClass(DexClass directSubClass) {
@@ -306,15 +309,16 @@
           directSubClass,
           directSubClass.isProgramClass()
               ? directSubClass.asProgramClass()
-              : closestProgramSubClass);
+              : closestProgramSubClass,
+          reportMissingTypeCallback);
     }
 
     public DexClass definitionFor(DexType type, AppView<?> appView) {
       return appView.appInfo().definitionForDesugarDependency(directSubClass, type);
     }
 
-    public void reportMissingType(DexType missingType, InterfaceDesugaringSyntheticHelper helper) {
-      helper.warnMissingInterface(closestProgramSubClass, closestProgramSubClass, missingType);
+    public void reportMissingType(DexType missingType) {
+      reportMissingTypeCallback.accept(closestProgramSubClass, missingType);
     }
   }
 
@@ -324,7 +328,7 @@
     static final LibraryReportingContext LIBRARY_CONTEXT = new LibraryReportingContext();
 
     LibraryReportingContext() {
-      super(null, null);
+      super(null, null, null);
     }
 
     @Override
@@ -338,7 +342,7 @@
     }
 
     @Override
-    public void reportMissingType(DexType missingType, InterfaceDesugaringSyntheticHelper helper) {
+    public void reportMissingType(DexType missingType) {
       // Ignore missing types in the library.
     }
   }
@@ -400,7 +404,12 @@
   public void process(
       DexProgramClass clazz, InterfaceProcessingDesugaringEventConsumer eventConsumer) {
     if (!clazz.isInterface()) {
-      visitClassInfo(clazz, new ReportingContext(clazz, clazz));
+      visitClassInfo(
+          clazz,
+          new ReportingContext(
+              clazz,
+              clazz,
+              (context, missing) -> eventConsumer.warnMissingInterface(context, missing, helper)));
     }
   }
 
@@ -788,14 +797,12 @@
     MethodAccessFlags accessFlags = MethodAccessFlags.builder().setPublic().build();
     DexMethod newMethod = method.withHolder(clazz.getType(), dexItemFactory);
     DexEncodedMethod newEncodedMethod =
-        new DexEncodedMethod(
-            newMethod,
-            accessFlags,
-            MethodTypeSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            ParameterAnnotationsList.empty(),
-            createExceptionThrowingCfCode(newMethod, accessFlags, errorType, dexItemFactory),
-            true);
+        DexEncodedMethod.syntheticBuilder()
+            .setMethod(newMethod)
+            .setAccessFlags(accessFlags)
+            .setCode(
+                createExceptionThrowingCfCode(newMethod, accessFlags, errorType, dexItemFactory))
+            .build();
     addSyntheticMethod(clazz.asProgramClass(), newEncodedMethod);
   }
 
@@ -864,7 +871,7 @@
     }
     DexClass clazz = context.definitionFor(type, appView);
     if (clazz == null) {
-      context.reportMissingType(type, helper);
+      context.reportMissingType(type);
       return null;
     }
     return clazz;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
index 9ee3606..a2a2bf9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.ClasspathOrLibraryClass;
-import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexClasspathClass;
@@ -28,7 +27,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue.DexValueInt;
 import com.android.tools.r8.graph.FieldAccessFlags;
-import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
 import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.InvalidCode;
 import com.android.tools.r8.graph.MethodAccessFlags;
@@ -456,12 +454,12 @@
             dexItemFactory.intType,
             "$desugar$clinit",
             candidate -> iface.lookupField(candidate) == null);
-    return new DexEncodedField(
-        clinitFieldReference,
-        FieldAccessFlags.builder().setPackagePrivate().setStatic().setSynthetic().build(),
-        FieldTypeSignature.noSignature(),
-        DexAnnotationSet.empty(),
-        DexValueInt.DEFAULT);
+    return DexEncodedField.syntheticBuilder()
+        .setField(clinitFieldReference)
+        .setAccessFlags(
+            FieldAccessFlags.builder().setPackagePrivate().setStatic().setSynthetic().build())
+        .setStaticValue(DexValueInt.DEFAULT)
+        .build();
   }
 
   private void createCompanionClassInitializer(
@@ -526,7 +524,8 @@
     return shouldIgnoreFromReportsPredicate.test(missing);
   }
 
-  void warnMissingInterface(DexClass classToDesugar, DexClass implementing, DexType missing) {
+  public void warnMissingInterface(
+      DexClass classToDesugar, DexClass implementing, DexType missing) {
     // We use contains() on non hashed collection, but we know it's a 8 cases collection.
     // j$ interfaces won't be missing, they are in the desugared library.
     if (shouldIgnoreFromReports(missing)) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 77f2c98..7dc9de3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -243,7 +243,7 @@
                         + InterfaceDesugaringSyntheticHelper.COMPANION_CLASS_NAME_SUFFIX)));
   }
 
-  public boolean needsRewriting(DexMethod method, Type invokeType, ProgramMethod context) {
+  private boolean needsRewriting(DexMethod method, Type invokeType, ProgramMethod context) {
     return !isSyntheticMethodThatShouldNotBeDoubleProcessed(context)
         && invokeNeedsRewriting(method, invokeType);
   }
@@ -273,15 +273,14 @@
     return true;
   }
 
-  private boolean isAlreadyRewritten(
-      DexMethod method, boolean itfBit, boolean isSuper, ProgramMethod context) {
-
+  private boolean isAlreadyDesugared(CfInvoke invoke, ProgramMethod context) {
     // In Cf to Cf it is forbidden to desugar twice the same instruction, if the backported
     // method rewriter or the desugared library retargeter already desugar the instruction, they
     // take precedence and nothing has to be done here.
-    return (backportedMethodRewriter != null && backportedMethodRewriter.methodIsBackport(method))
+    return (backportedMethodRewriter != null
+            && backportedMethodRewriter.needsDesugaring(invoke, context))
         || (desugaredLibraryRetargeter != null
-            && desugaredLibraryRetargeter.hasNewInvokeTarget(method, itfBit, isSuper, context));
+            && desugaredLibraryRetargeter.needsDesugaring(invoke, context));
   }
 
   @Override
@@ -310,11 +309,7 @@
       }
       if (instruction.isInvoke()) {
         CfInvoke cfInvoke = instruction.asInvoke();
-        if (isAlreadyRewritten(
-            cfInvoke.getMethod(),
-            cfInvoke.isInterface(),
-            cfInvoke.isInvokeSuper(context.getHolderType()),
-            context)) {
+        if (isAlreadyDesugared(cfInvoke, context)) {
           continue;
         }
         if (cfInvoke.isInvokeStatic()) {
@@ -385,11 +380,7 @@
   public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
     if (instruction.isInvoke()) {
       CfInvoke cfInvoke = instruction.asInvoke();
-      if (isAlreadyRewritten(
-          cfInvoke.getMethod(),
-          cfInvoke.isInterface(),
-          cfInvoke.isInvokeSuper(context.getHolderType()),
-          context)) {
+      if (isAlreadyDesugared(cfInvoke, context)) {
         return false;
       }
       return needsRewriting(cfInvoke.getMethod(), cfInvoke.getInvokeType(context), context);
@@ -410,11 +401,7 @@
       return null;
     }
     CfInvoke invoke = instruction.asInvoke();
-    if (isAlreadyRewritten(
-        invoke.getMethod(),
-        invoke.isInterface(),
-        invoke.isInvokeSuper(context.getHolderType()),
-        context)) {
+    if (isAlreadyDesugared(invoke, context)) {
       return null;
     }
 
@@ -461,8 +448,7 @@
           };
       // TODO(b/192439456): Make a test to prove resolution is needed here and fix it.
       return rewriteInvokeStatic(
-          invoke.getMethod(),
-          invoke.isInterface(),
+          invoke,
           methodProcessingContext,
           context,
           staticOutliningMethodConsumer,
@@ -485,11 +471,7 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext) {
-    if (isAlreadyRewritten(
-        invoke.getMethod(),
-        invoke.isInterface(),
-        invoke.isInvokeSuper(context.getHolderType()),
-        context)) {
+    if (isAlreadyDesugared(invoke, context)) {
       return null;
     }
 
@@ -672,14 +654,15 @@
   }
 
   private Collection<CfInstruction> rewriteInvokeStatic(
-      DexMethod invokedMethod,
-      boolean interfaceBit,
+      CfInvoke invoke,
       MethodProcessingContext methodProcessingContext,
       ProgramMethod context,
       Consumer<ProgramMethod> staticOutliningMethodConsumer,
       Function<DexMethod, Collection<CfInstruction>> rewriteInvoke,
       Function<SingleResolutionResult, Collection<CfInstruction>> rewriteToThrow,
       CfInstructionDesugaringEventConsumer eventConsumer) {
+    DexMethod invokedMethod = invoke.getMethod();
+    boolean interfaceBit = invoke.isInterface();
     if (appView.getSyntheticItems().isPendingSynthetic(invokedMethod.holder)) {
       // We did not create this code yet, but it will not require rewriting.
       return null;
@@ -727,7 +710,7 @@
           // to outline again the invoke-static. Just do nothing instead.
           return null;
         }
-        if (isAlreadyRewritten(invokedMethod, interfaceBit, false, context)) {
+        if (isAlreadyDesugared(invoke, context)) {
           return null;
         }
         ProgramMethod newProgramMethod =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java
index e16f254..49d843d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 
 // TODO(b/183998768): Consider forcing the processing of interface methods in D8 akin to R8.
@@ -18,4 +19,7 @@
 
   void acceptEmulatedInterfaceMarkerInterface(
       DexProgramClass clazz, DexClasspathClass newInterface);
+
+  void warnMissingInterface(
+      DexProgramClass context, DexType missing, InterfaceDesugaringSyntheticHelper helper);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/AccessBridgeFactory.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/AccessBridgeFactory.java
index 8814c2e..b81ea84 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/AccessBridgeFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/AccessBridgeFactory.java
@@ -21,7 +21,7 @@
     assert field.getAccessFlags().isPrivate();
     return new ProgramMethod(
         field.getHolder(),
-        DexEncodedMethod.builder()
+        DexEncodedMethod.syntheticBuilder()
             .setAccessFlags(
                 MethodAccessFlags.builder()
                     .setBridge()
@@ -37,7 +37,6 @@
                     .setSourceMethod(bridgeMethodReference)
                     .build())
             .setMethod(bridgeMethodReference)
-            .setD8R8Synthesized()
             .setApiLevelForDefinition(field.getDefinition().getApiLevel())
             .setApiLevelForCode(field.getDefinition().getApiLevel())
             .build());
@@ -51,7 +50,7 @@
     assert !method.getHolder().isInterface();
     return new ProgramMethod(
         method.getHolder(),
-        DexEncodedMethod.builder()
+        DexEncodedMethod.syntheticBuilder()
             // Not setting the 'bridge' flag as this fails verification.
             .setAccessFlags(MethodAccessFlags.builder().setConstructor().setSynthetic().build())
             .setCode(
@@ -60,7 +59,6 @@
                     .setConstructorTarget(method.getReference())
                     .build())
             .setMethod(bridgeMethodReference)
-            .setD8R8Synthesized()
             .setApiLevelForDefinition(method.getDefinition().getApiLevelForDefinition())
             .setApiLevelForCode(method.getDefinition().getApiLevelForCode())
             .build());
@@ -74,7 +72,7 @@
     boolean isInterface = method.getHolder().isInterface();
     return new ProgramMethod(
         method.getHolder(),
-        DexEncodedMethod.builder()
+        DexEncodedMethod.syntheticBuilder()
             .setAccessFlags(
                 MethodAccessFlags.builder()
                     .setBridge()
@@ -93,7 +91,6 @@
             .setMethod(bridgeMethodReference)
             .setApiLevelForDefinition(method.getDefinition().getApiLevelForDefinition())
             .setApiLevelForCode(method.getDefinition().getApiLevelForDefinition())
-            .setD8R8Synthesized()
             .build());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
index 7c26d30..627f8af 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
@@ -20,7 +20,6 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -35,9 +34,7 @@
 import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.graph.DexValue.DexValueType;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
@@ -289,14 +286,11 @@
         MethodAccessFlags.fromSharedAccessFlags(
             Constants.ACC_SYNTHETIC | Constants.ACC_PRIVATE, false);
     DexEncodedMethod encodedMethod =
-        new DexEncodedMethod(
-            method,
-            methodAccessFlags,
-            MethodTypeSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            ParameterAnnotationsList.empty(),
-            null,
-            true);
+        DexEncodedMethod.syntheticBuilder()
+            .setMethod(method)
+            .setAccessFlags(methodAccessFlags)
+            .setCode(null)
+            .build();
     encodedMethod.setCode(provider.generateCfCode(), appView);
     return new ProgramMethod(clazz, encodedMethod);
   }
@@ -589,14 +583,11 @@
         MethodAccessFlags.fromSharedAccessFlags(
             Constants.ACC_SYNTHETIC | Constants.ACC_PROTECTED, true);
     DexEncodedMethod init =
-        new DexEncodedMethod(
-            factory.recordMembers.init,
-            methodAccessFlags,
-            MethodTypeSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            ParameterAnnotationsList.empty(),
-            null,
-            true);
+        DexEncodedMethod.syntheticBuilder()
+            .setMethod(factory.recordMembers.init)
+            .setAccessFlags(methodAccessFlags)
+            .setCode(null)
+            .build();
     init.setCode(
         new CallObjectInitCfCodeProvider(appView, factory.recordTagType).generateCfCode(), appView);
     return init;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
index ce0646d..d39f509 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.LookupResult;
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -19,7 +20,7 @@
 import com.android.tools.r8.ir.code.InvokeCustom;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
-import com.android.tools.r8.ir.conversion.PostOptimization;
+import com.android.tools.r8.ir.conversion.PostMethodProcessor;
 import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
 import com.android.tools.r8.logging.Log;
@@ -31,6 +32,7 @@
 import com.android.tools.r8.utils.LazyBox;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.Sets;
 import java.util.Set;
@@ -38,7 +40,7 @@
 import java.util.concurrent.ExecutorService;
 import java.util.function.Consumer;
 
-public class CallSiteOptimizationInfoPropagator implements PostOptimization {
+public class CallSiteOptimizationInfoPropagator {
 
   // TODO(b/139246447): should we revisit new targets over and over again?
   //   Maybe piggy-back on MethodProcessor's wave/batch processing?
@@ -61,16 +63,17 @@
   private final AppView<AppInfoWithLiveness> appView;
   private final InternalOptions options;
   private final CallSiteOptimizationOptions optimizationOptions;
-  private ProgramMethodSet revisitedMethods = null;
   private Mode mode = Mode.COLLECT;
 
+  private ProgramMethodSet revisitedMethodsForTesting = null;
+
   public CallSiteOptimizationInfoPropagator(AppView<AppInfoWithLiveness> appView) {
     assert appView.enableWholeProgramOptimizations();
     this.appView = appView;
     this.options = appView.options();
     this.optimizationOptions = appView.options().callSiteOptimizationOptions();
     if (Log.isLoggingEnabledFor(CallSiteOptimizationInfoPropagator.class)) {
-      revisitedMethods = ProgramMethodSet.create();
+      revisitedMethodsForTesting = ProgramMethodSet.create();
     }
   }
 
@@ -80,9 +83,9 @@
 
   public void logResults() {
     assert Log.ENABLED;
-    if (revisitedMethods != null) {
-      Log.info(getClass(), "# of methods to revisit: %s", revisitedMethods.size());
-      for (ProgramMethod m : revisitedMethods) {
+    if (revisitedMethodsForTesting != null) {
+      Log.info(getClass(), "# of methods to revisit: %s", revisitedMethodsForTesting.size());
+      for (ProgramMethod m : revisitedMethodsForTesting) {
         Log.info(
             getClass(),
             "%s: %s",
@@ -379,10 +382,19 @@
     return callSiteOptimizationInfo;
   }
 
-  @Override
-  public ProgramMethodSet methodsToRevisit() {
+  public void enqueueMethodsForReprocessing(
+      PostMethodProcessor.Builder postMethodProcessorBuilder) {
+    postMethodProcessorBuilder
+        .getMethodsToReprocessBuilder()
+        .rewrittenWithLens(appView.graphLens())
+        .merge(methodsToRevisit());
+  }
+
+  private LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsToRevisit() {
     mode = Mode.REVISIT;
-    ProgramMethodSet targetsToRevisit = ProgramMethodSet.create();
+    GraphLens currentGraphLens = appView.graphLens();
+    LongLivedProgramMethodSetBuilder<ProgramMethodSet> builder =
+        LongLivedProgramMethodSetBuilder.createForIdentitySet(currentGraphLens);
     for (DexProgramClass clazz : appView.appInfo().classes()) {
       clazz.forEachProgramMethodMatching(
           definition -> {
@@ -398,14 +410,14 @@
             return callSiteOptimizationInfo.hasUsefulOptimizationInfo(appView, definition);
           },
           method -> {
-            targetsToRevisit.add(method);
+            builder.add(method, currentGraphLens);
             appView.options().testing.callSiteOptimizationInfoInspector.accept(method);
           });
     }
-    if (revisitedMethods != null) {
-      revisitedMethods.addAll(targetsToRevisit);
+    if (revisitedMethodsForTesting != null) {
+      revisitedMethodsForTesting.addAll(builder.build(appView));
     }
-    return targetsToRevisit;
+    return builder;
   }
 
   private synchronized boolean verifyAllProgramDispatchTargetsHaveBeenAbandoned(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index fdc9208..7bf0e38 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -48,7 +48,7 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.LensCodeRewriter;
 import com.android.tools.r8.ir.conversion.MethodProcessor;
-import com.android.tools.r8.ir.conversion.PostOptimization;
+import com.android.tools.r8.ir.conversion.PostMethodProcessor;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
 import com.android.tools.r8.ir.optimize.inliner.DefaultInliningReasonStrategy;
@@ -64,6 +64,7 @@
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -77,7 +78,7 @@
 import java.util.Map;
 import java.util.Set;
 
-public class Inliner implements PostOptimization {
+public class Inliner {
 
   protected final AppView<AppInfoWithLiveness> appView;
   private final Set<DexMethod> extraNeverInlineMethods;
@@ -86,7 +87,7 @@
 
   // State for inlining methods which are known to be called twice.
   private boolean applyDoubleInlining = false;
-  private final ProgramMethodSet doubleInlineCallers = ProgramMethodSet.create();
+  private LongLivedProgramMethodSetBuilder<ProgramMethodSet> doubleInlineCallers;
   private final ProgramMethodSet doubleInlineSelectedTargets = ProgramMethodSet.create();
   private final Map<DexEncodedMethod, ProgramMethod> doubleInlineeCandidates =
       new IdentityHashMap<>();
@@ -117,7 +118,7 @@
       WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod singleTargetReference = singleTarget.getReference();
-    if (appInfo.isPinned(singleTargetReference)) {
+    if (!appView.getKeepInfo(singleTarget).isInliningAllowed(appView.options())) {
       whyAreYouNotInliningReporter.reportPinned();
       return true;
     }
@@ -226,9 +227,10 @@
 
     if (doubleInlineeCandidates.containsKey(target.getDefinition())) {
       // Both calls can be inlined.
+      GraphLens currentGraphLens = appView.graphLens();
       ProgramMethod doubleInlineeCandidate = doubleInlineeCandidates.get(target.getDefinition());
-      doubleInlineCallers.add(doubleInlineeCandidate);
-      doubleInlineCallers.add(method);
+      doubleInlineCallers.add(doubleInlineeCandidate, currentGraphLens);
+      doubleInlineCallers.add(method, currentGraphLens);
       doubleInlineSelectedTargets.add(target);
     } else {
       // First call can be inlined.
@@ -236,10 +238,20 @@
     }
   }
 
-  @Override
-  public ProgramMethodSet methodsToRevisit() {
+  public void initializeDoubleInlineCallers(GraphLens graphLensForPrimaryOptimizationPass) {
+    assert appView.graphLens() == graphLensForPrimaryOptimizationPass;
+    doubleInlineCallers =
+        LongLivedProgramMethodSetBuilder.createForIdentitySet(graphLensForPrimaryOptimizationPass);
+  }
+
+  public void enqueueMethodsForReprocessing(
+      PostMethodProcessor.Builder postMethodProcessorBuilder) {
+    // The double inline callers are always rewritten up until the graph lens of the primary
+    // optimization pass, so we can safely merge them into the methods to reprocess (which may be
+    // rewritten with a newer graph lens).
+    postMethodProcessorBuilder.getMethodsToReprocessBuilder().merge(doubleInlineCallers);
+    doubleInlineCallers = null;
     applyDoubleInlining = true;
-    return doubleInlineCallers;
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index df30aac..b2e8366 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -310,7 +310,10 @@
       DexType type = proto.parameters.values[i];
       if (type.isAlwaysNull(appView)) {
         RemovedArgumentInfo removedArg =
-            RemovedArgumentInfo.builder().setIsAlwaysNull().setType(type).build();
+            RemovedArgumentInfo.builder()
+                .setSingleValue(appView.abstractValueFactory().createNullValue())
+                .setType(type)
+                .build();
         argInfosBuilder.addArgumentInfo(i + offset, removedArg);
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 2e74bb2..705ce93 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMember;
@@ -101,6 +102,7 @@
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
+import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
 import com.android.tools.r8.utils.collections.ProgramMethodMap;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.HashMultiset;
@@ -135,18 +137,20 @@
   private final DexItemFactory factory;
   // Map the enum candidates with their dependencies, i.e., the methods to reprocess for the given
   // enum if the optimization eventually decides to unbox it.
-  private final EnumUnboxingCandidateInfoCollection enumUnboxingCandidatesInfo;
+  private EnumUnboxingCandidateInfoCollection enumUnboxingCandidatesInfo;
   private final Set<DexProgramClass> candidatesToRemoveInWave = Sets.newConcurrentHashSet();
   private final Map<DexType, EnumStaticFieldValues> staticFieldValuesMap =
       new ConcurrentHashMap<>();
-  private final ProgramMethodSet methodsDependingOnLibraryModelisation =
-      ProgramMethodSet.createConcurrent();
+
+  // Methods depending on library modelisation need to be reprocessed so they are peephole
+  // optimized.
+  private LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsDependingOnLibraryModelisation;
 
   // Map from checkNotNull() methods to the enums that use the given method.
   private final ProgramMethodMap<Set<DexProgramClass>> checkNotNullMethods =
       ProgramMethodMap.createConcurrent();
 
-  private final DexEncodedField ordinalField;
+  private final DexClassAndField ordinalField;
 
   private EnumUnboxingRewriter enumUnboxerRewriter;
 
@@ -164,21 +168,18 @@
       debugLogs = null;
     }
     assert !appView.options().debug;
-    enumUnboxingCandidatesInfo = new EnumUnboxingCandidateAnalysis(appView, this).findCandidates();
-
     ordinalField =
-        appView.appInfo().resolveField(factory.enumMembers.ordinalField).getResolvedField();
-    if (ordinalField == null) {
-      // This can happen when compiling for non standard libraries, in that case, this effectively
-      // disables the enum unboxer.
-      enumUnboxingCandidatesInfo.clear();
-    }
+        appView.appInfo().resolveField(factory.enumMembers.ordinalField).getResolutionPair();
   }
 
   public static int ordinalToUnboxedInt(int ordinal) {
     return ordinal + 1;
   }
 
+  public DexClassAndField getOrdinalField() {
+    return ordinalField;
+  }
+
   public void updateEnumUnboxingCandidatesInfo() {
     for (DexProgramClass candidate : candidatesToRemoveInWave) {
       enumUnboxingCandidatesInfo.removeCandidate(candidate);
@@ -203,7 +204,7 @@
   }
 
   private void markMethodDependsOnLibraryModelisation(ProgramMethod method) {
-    methodsDependingOnLibraryModelisation.add(method);
+    methodsDependingOnLibraryModelisation.add(method, appView.graphLens());
   }
 
   private DexProgramClass getEnumUnboxingCandidateOrNull(TypeElement lattice) {
@@ -298,7 +299,7 @@
         enumUnboxingCandidatesInfo.addMethodDependency(eligibleEnum, code.context());
       }
     }
-    if (methodsDependingOnLibraryModelisation.contains(code.context())) {
+    if (methodsDependingOnLibraryModelisation.contains(code.context(), appView.graphLens())) {
       conversionOptions.disablePeepholeOptimizations();
     }
   }
@@ -555,6 +556,16 @@
     return result;
   }
 
+  public void initializeEnumUnboxingCandidates(GraphLens graphLensForPrimaryOptimizationPass) {
+    assert enumUnboxingCandidatesInfo == null;
+    enumUnboxingCandidatesInfo =
+        new EnumUnboxingCandidateAnalysis(appView, this)
+            .findCandidates(graphLensForPrimaryOptimizationPass);
+    methodsDependingOnLibraryModelisation =
+        LongLivedProgramMethodSetBuilder.createConcurrentForIdentitySet(
+            graphLensForPrimaryOptimizationPass);
+  }
+
   public void unboxEnums(
       IRConverter converter,
       PostMethodProcessor.Builder postBuilder,
@@ -577,7 +588,8 @@
     ImmutableSet<DexType> enumsToUnbox = enumUnboxingCandidatesInfo.candidates();
     ImmutableSet<DexProgramClass> enumClassesToUnbox =
         enumUnboxingCandidatesInfo.candidateClasses();
-    ProgramMethodSet dependencies = enumUnboxingCandidatesInfo.allMethodDependencies();
+    LongLivedProgramMethodSetBuilder<ProgramMethodSet> dependencies =
+        enumUnboxingCandidatesInfo.allMethodDependencies();
     enumUnboxingCandidatesInfo.clear();
     // Update keep info on any of the enum methods of the removed classes.
     updateKeepInfo(enumsToUnbox);
@@ -587,11 +599,32 @@
             .synthesizeEnumUnboxingUtilityClasses(enumClassesToUnbox, enumDataMap)
             .build(converter, executorService);
 
+    // Fixup the application.
     EnumUnboxingTreeFixer.Result treeFixerResult =
         new EnumUnboxingTreeFixer(
                 appView, checkNotNullMethods, enumDataMap, enumClassesToUnbox, utilityClasses)
             .fixupTypeReferences(converter, executorService);
     EnumUnboxingLens enumUnboxingLens = treeFixerResult.getLens();
+    appView.setUnboxedEnums(enumDataMap);
+
+    // Update the graph lens.
+    appView.rewriteWithLens(enumUnboxingLens);
+
+    // Enqueue the (lens rewritten) methods that require reprocessing.
+    //
+    // Note that the reprocessing set must be rewritten to the new enum unboxing lens before pruning
+    // the builders with the methods removed by the tree fixer (since these methods references are
+    // already fully lens rewritten).
+    postBuilder
+        .getMethodsToReprocessBuilder()
+        .rewrittenWithLens(appView)
+        .merge(dependencies)
+        .merge(methodsDependingOnLibraryModelisation)
+        .removeAll(treeFixerResult.getPrunedItems().getRemovedMethods());
+    methodsDependingOnLibraryModelisation.clear();
+
+    updateOptimizationInfos(executorService, feedback, treeFixerResult.getPrunedItems());
+
     enumUnboxerRewriter =
         new EnumUnboxingRewriter(
             appView,
@@ -600,17 +633,6 @@
             enumUnboxingLens,
             enumDataMap,
             utilityClasses);
-    appView.setUnboxedEnums(enumDataMap);
-    GraphLens previousLens = appView.graphLens();
-    appView.rewriteWithLens(enumUnboxingLens);
-    updateOptimizationInfos(executorService, feedback, treeFixerResult.getPrunedItems());
-    postBuilder.put(dependencies);
-    // Methods depending on library modelisation need to be reprocessed so they are peephole
-    // optimized.
-    postBuilder.put(methodsDependingOnLibraryModelisation);
-    methodsDependingOnLibraryModelisation.clear();
-    postBuilder.removePrunedMethods(treeFixerResult.getPrunedItems().getRemovedMethods());
-    postBuilder.rewrittenWithLens(appView, previousLens);
   }
 
   private void updateOptimizationInfos(
@@ -842,7 +864,7 @@
   }
 
   private OptionalInt getOrdinal(ObjectState state) {
-    AbstractValue field = state.getAbstractFieldValue(ordinalField);
+    AbstractValue field = state.getAbstractFieldValue(getOrdinalField().getDefinition());
     if (field.isSingleNumberValue()) {
       return OptionalInt.of(field.asSingleNumberValue().getIntValue());
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
index a675735..4225447 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.ir.optimize.enums.eligibility.Reason;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.KeepInfoCollection;
@@ -35,10 +36,16 @@
     factory = appView.dexItemFactory();
   }
 
-  EnumUnboxingCandidateInfoCollection findCandidates() {
+  EnumUnboxingCandidateInfoCollection findCandidates(
+      GraphLens graphLensForPrimaryOptimizationPass) {
+    if (enumUnboxer.getOrdinalField() == null || enumUnboxer.getOrdinalField().isProgramField()) {
+      // This can happen when compiling for non standard libraries, in that case, this effectively
+      // disables the enum unboxer.
+      return enumToUnboxCandidates;
+    }
     for (DexProgramClass clazz : appView.appInfo().classes()) {
       if (isEnumUnboxingCandidate(clazz)) {
-        enumToUnboxCandidates.addCandidate(clazz);
+        enumToUnboxCandidates.addCandidate(appView, clazz, graphLensForPrimaryOptimizationPass);
       }
     }
     removeEnumsInAnnotations();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
index 7d85b8d..386bcb0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
@@ -4,13 +4,18 @@
 
 package com.android.tools.r8.ir.optimize.enums;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -21,9 +26,14 @@
 
   private final Map<DexType, EnumUnboxingCandidateInfo> enumTypeToInfo = new ConcurrentHashMap<>();
 
-  public void addCandidate(DexProgramClass enumClass) {
+  public void addCandidate(
+      AppView<AppInfoWithLiveness> appView,
+      DexProgramClass enumClass,
+      GraphLens graphLensForPrimaryOptimizationPass) {
     assert !enumTypeToInfo.containsKey(enumClass.type);
-    enumTypeToInfo.put(enumClass.type, new EnumUnboxingCandidateInfo(enumClass));
+    enumTypeToInfo.put(
+        enumClass.type,
+        new EnumUnboxingCandidateInfo(appView, enumClass, graphLensForPrimaryOptimizationPass));
   }
 
   public void removeCandidate(DexProgramClass enumClass) {
@@ -62,10 +72,13 @@
     return info.enumClass;
   }
 
-  public ProgramMethodSet allMethodDependencies() {
-    ProgramMethodSet allMethodDependencies = ProgramMethodSet.create();
-    for (EnumUnboxingCandidateInfo info : enumTypeToInfo.values()) {
-      allMethodDependencies.addAll(info.methodDependencies);
+  public LongLivedProgramMethodSetBuilder<ProgramMethodSet> allMethodDependencies() {
+    Iterator<EnumUnboxingCandidateInfo> candidateInfoIterator = enumTypeToInfo.values().iterator();
+    assert candidateInfoIterator.hasNext();
+    LongLivedProgramMethodSetBuilder<ProgramMethodSet> allMethodDependencies =
+        candidateInfoIterator.next().methodDependencies;
+    while (candidateInfoIterator.hasNext()) {
+      allMethodDependencies.merge(candidateInfoIterator.next().methodDependencies);
     }
     return allMethodDependencies;
   }
@@ -111,20 +124,27 @@
   private static class EnumUnboxingCandidateInfo {
 
     private final DexProgramClass enumClass;
-    private final ProgramMethodSet methodDependencies = ProgramMethodSet.createConcurrent();
+    private final LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodDependencies;
     private final Set<DexField> requiredInstanceFieldData = Sets.newConcurrentHashSet();
 
-    public EnumUnboxingCandidateInfo(DexProgramClass enumClass) {
+    public EnumUnboxingCandidateInfo(
+        AppView<AppInfoWithLiveness> appView,
+        DexProgramClass enumClass,
+        GraphLens graphLensForPrimaryOptimizationPass) {
       assert enumClass != null;
+      assert appView.graphLens() == graphLensForPrimaryOptimizationPass;
       this.enumClass = enumClass;
+      this.methodDependencies =
+          LongLivedProgramMethodSetBuilder.createConcurrentForIdentitySet(
+              graphLensForPrimaryOptimizationPass);
     }
 
     public DexProgramClass getEnumClass() {
       return enumClass;
     }
 
-    public void addMethodDependency(ProgramMethod programMethod) {
-      methodDependencies.add(programMethod);
+    public void addMethodDependency(ProgramMethod method) {
+      methodDependencies.add(method);
     }
 
     public void addRequiredInstanceFieldData(DexField field) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index a5978fa..c9c7c40 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -46,8 +46,9 @@
     // During the second IR processing enum unboxing is the only optimization rewriting
     // prototype description, if this does not hold, remove the assertion and merge
     // the two prototype changes.
-    assert prototypeChanges.isEmpty();
-    return prototypeChangesPerMethod.getOrDefault(method, RewrittenPrototypeDescription.none());
+    RewrittenPrototypeDescription enumUnboxingPrototypeChanges =
+        prototypeChangesPerMethod.getOrDefault(method, RewrittenPrototypeDescription.none());
+    return prototypeChanges.combine(enumUnboxingPrototypeChanges);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
index 9614ede..a239ebb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
@@ -22,7 +22,6 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -31,10 +30,7 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldAccessFlags;
-import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.ValueType;
@@ -228,16 +224,13 @@
 
     private DexEncodedField createValuesField(DexType sharedUtilityClassType) {
       DexEncodedField valuesField =
-          new DexEncodedField(
-              dexItemFactory.createField(
-                  sharedUtilityClassType, dexItemFactory.intArrayType, "$VALUES"),
-              FieldAccessFlags.createPublicStaticFinalSynthetic(),
-              FieldTypeSignature.noSignature(),
-              DexAnnotationSet.empty(),
-              DexEncodedField.NO_STATIC_VALUE,
-              DexEncodedField.NOT_DEPRECATED,
-              DexEncodedField.D8_R8_SYNTHESIZED,
-              minApiLevelIfEnabledOrUnknown(appView));
+          DexEncodedField.syntheticBuilder()
+              .setField(
+                  dexItemFactory.createField(
+                      sharedUtilityClassType, dexItemFactory.intArrayType, "$VALUES"))
+              .setAccessFlags(FieldAccessFlags.createPublicStaticFinalSynthetic())
+              .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
+              .build();
       fieldAccessInfoCollectionModifierBuilder
           .recordFieldReadInUnknownContext(valuesField.getReference())
           .recordFieldWriteInUnknownContext(valuesField.getReference());
@@ -248,17 +241,14 @@
 
     private DexEncodedMethod createClassInitializer(
         DexType sharedUtilityClassType, DexEncodedField valuesField) {
-      return new DexEncodedMethod(
-          dexItemFactory.createClassInitializer(sharedUtilityClassType),
-          MethodAccessFlags.createForClassInitializer(),
-          MethodTypeSignature.noSignature(),
-          DexAnnotationSet.empty(),
-          ParameterAnnotationsList.empty(),
-          createClassInitializerCode(sharedUtilityClassType, valuesField),
-          DexEncodedMethod.D8_R8_SYNTHESIZED,
-          CfVersion.V1_6,
-          minApiLevelIfEnabledOrUnknown(appView),
-          minApiLevelIfEnabledOrUnknown(appView));
+      return DexEncodedMethod.syntheticBuilder()
+          .setMethod(dexItemFactory.createClassInitializer(sharedUtilityClassType))
+          .setAccessFlags(MethodAccessFlags.createForClassInitializer())
+          .setCode(createClassInitializerCode(sharedUtilityClassType, valuesField))
+          .setClassFileVersion(CfVersion.V1_6)
+          .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
+          .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+          .build();
     }
 
     private CfCode createClassInitializerCode(
@@ -292,20 +282,19 @@
     private DexEncodedMethod createValuesMethod(
         DexType sharedUtilityClassType, DexEncodedField valuesField) {
       DexEncodedMethod valuesMethod =
-          new DexEncodedMethod(
-              dexItemFactory.createMethod(
-                  sharedUtilityClassType,
-                  dexItemFactory.createProto(dexItemFactory.intArrayType, dexItemFactory.intType),
-                  "values"),
-              MethodAccessFlags.createPublicStaticSynthetic(),
-              MethodTypeSignature.noSignature(),
-              DexAnnotationSet.empty(),
-              ParameterAnnotationsList.empty(),
-              createValuesMethodCode(sharedUtilityClassType, valuesField),
-              DexEncodedMethod.D8_R8_SYNTHESIZED,
-              CfVersion.V1_6,
-              minApiLevelIfEnabledOrUnknown(appView),
-              minApiLevelIfEnabledOrUnknown(appView));
+          DexEncodedMethod.syntheticBuilder()
+              .setMethod(
+                  dexItemFactory.createMethod(
+                      sharedUtilityClassType,
+                      dexItemFactory.createProto(
+                          dexItemFactory.intArrayType, dexItemFactory.intType),
+                      "values"))
+              .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+              .setCode(createValuesMethodCode(sharedUtilityClassType, valuesField))
+              .setClassFileVersion(CfVersion.V1_6)
+              .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
+              .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+              .build();
       this.valuesMethod = valuesMethod;
       return valuesMethod;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
index c411d08..2c012f0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
@@ -8,12 +8,10 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexLibraryClass;
 import com.android.tools.r8.graph.FieldAccessFlags;
-import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
 
 /**
  * This class synthesizes library fields that we rely on for modeling.
@@ -35,13 +33,12 @@
             DexEncodedField definition = enumClass.lookupField(field);
             if (definition == null) {
               enumClass.appendInstanceField(
-                  new DexEncodedField(
-                      field,
-                      FieldAccessFlags.fromCfAccessFlags(
-                          Constants.ACC_PRIVATE | Constants.ACC_FINAL),
-                      FieldTypeSignature.noSignature(),
-                      DexAnnotationSet.empty(),
-                      null));
+                  DexEncodedField.syntheticBuilder()
+                      .setField(field)
+                      .setAccessFlags(
+                          FieldAccessFlags.fromCfAccessFlags(
+                              Constants.ACC_PRIVATE | Constants.ACC_FINAL))
+                      .build());
             }
           });
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
index 1f6c00a..0fbb615 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -7,7 +7,6 @@
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndField;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -58,6 +57,8 @@
   private final DexItemFactory factory;
   private final IRConverter converter;
 
+  private GraphLens graphLensForOptimizationPass;
+
   // Represents a staticizing candidate with all information
   // needed for staticizing.
   final class CandidateInfo {
@@ -113,14 +114,36 @@
     this.converter = converter;
   }
 
+  public void prepareForPrimaryOptimizationPass(GraphLens graphLensForPrimaryOptimizationPass) {
+    collectCandidates();
+    this.graphLensForOptimizationPass = graphLensForPrimaryOptimizationPass;
+  }
+
+  public void prepareForSecondaryOptimizationPass(GraphLens graphLensForSecondaryOptimizationPass) {
+    // Rewrite all the referenced from sets such that they are all rewritten up until the lens of
+    // the second optimization pass. This is needed to ensure all elements in the referenced from
+    // sets are rewritten up until the same graph lens, in case any referenced from sets are
+    // extended during the secondary optimization pass.
+    assert appView.graphLens() == graphLensForSecondaryOptimizationPass;
+    referencedFrom
+        .values()
+        .forEach(
+            referencedFromBuilder ->
+                referencedFromBuilder.rewrittenWithLens(graphLensForSecondaryOptimizationPass));
+    this.graphLensForOptimizationPass = graphLensForSecondaryOptimizationPass;
+  }
+
   // Before doing any usage-based analysis we collect a set of classes that can be
   // candidates for staticizing. This analysis is very simple, but minimizes the
   // set of eligible classes staticizer tracks and thus time and memory it needs.
-  public final void collectCandidates(DexApplication app) {
+  public final void collectCandidates() {
     Set<DexType> notEligible = Sets.newIdentityHashSet();
     Map<DexType, DexEncodedField> singletonFields = new HashMap<>();
 
-    app.classes()
+    assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
+    appView
+        .appInfo()
+        .classes()
         .forEach(
             cls -> {
               // We only consider classes eligible for staticizing if there is just
@@ -169,20 +192,26 @@
             });
 
     // Finalize the set of the candidates.
-    app.classes().forEach(cls -> {
-      DexType type = cls.type;
-      if (!notEligible.contains(type)) {
-        DexEncodedField field = singletonFields.get(type);
-        if (field != null && // Singleton field found
-            !field.accessFlags.isVolatile() && // Don't remove volatile fields.
-            !isPinned(cls, field)) { // Don't remove pinned objects.
-          assert field.accessFlags.isStatic();
-          // Note: we don't check that the field is final, since we will analyze
-          //       later how and where it is initialized.
-          new CandidateInfo(cls, field); // will self-register
-        }
-      }
-    });
+    appView
+        .appInfo()
+        .classes()
+        .forEach(
+            cls -> {
+              DexType type = cls.type;
+              if (!notEligible.contains(type)) {
+                DexEncodedField field = singletonFields.get(type);
+                if (field != null
+                    && // Singleton field found
+                    !field.accessFlags.isVolatile()
+                    && // Don't remove volatile fields.
+                    !isPinned(cls, field)) { // Don't remove pinned objects.
+                  assert field.accessFlags.isStatic();
+                  // Note: we don't check that the field is final, since we will analyze
+                  //       later how and where it is initialized.
+                  new CandidateInfo(cls, field); // will self-register
+                }
+              }
+            });
   }
 
   private void markNotEligible(DexType type, Set<DexType> notEligible) {
@@ -374,9 +403,13 @@
   }
 
   private void addReferencedFrom(CandidateInfo info, ProgramMethod context) {
+    GraphLens currentGraphLens = appView.graphLens();
+    assert currentGraphLens == graphLensForOptimizationPass;
     LongLivedProgramMethodSetBuilder<?> builder =
         referencedFrom.computeIfAbsent(
-            info, ignore -> LongLivedProgramMethodSetBuilder.createConcurrentForIdentitySet());
+            info,
+            ignore ->
+                LongLivedProgramMethodSetBuilder.createConcurrentForIdentitySet(currentGraphLens));
     builder.add(context);
   }
 
@@ -689,9 +722,8 @@
   //  3. Rewrite methods referencing staticized members, also remove instance creation
   //
   public final void staticizeCandidates(
-      OptimizationFeedback feedback, ExecutorService executorService, GraphLens applied)
-      throws ExecutionException {
-    new StaticizingProcessor(appView, this, converter, applied).run(feedback, executorService);
+      OptimizationFeedback feedback, ExecutorService executorService) throws ExecutionException {
+    new StaticizingProcessor(appView, this, converter).run(feedback, executorService);
   }
 
   private class CallSiteReferencesInvalidator extends UseRegistry {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 3e59aa8..2fbaa44 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -19,7 +19,6 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.IRCode;
@@ -87,17 +86,14 @@
   private final Map<DexField, CandidateInfo> singletonFields = new IdentityHashMap<>();
   private final Map<DexMethod, CandidateInfo> singletonGetters = new IdentityHashMap<>();
   private final Map<DexType, DexType> candidateToHostMapping = new IdentityHashMap<>();
-  private final GraphLens applied;
 
   StaticizingProcessor(
       AppView<AppInfoWithLiveness> appView,
       ClassStaticizer classStaticizer,
-      IRConverter converter,
-      GraphLens applied) {
+      IRConverter converter) {
     this.appView = appView;
     this.classStaticizer = classStaticizer;
     this.converter = converter;
-    this.applied = applied;
   }
 
   final void run(OptimizationFeedback feedback, ExecutorService executorService)
@@ -238,7 +234,7 @@
         LongLivedProgramMethodSetBuilder<?> referencedFromBuilder =
             classStaticizer.referencedFrom.remove(info);
         assert referencedFromBuilder != null;
-        referencedFrom = referencedFromBuilder.build(appView, applied);
+        referencedFrom = referencedFromBuilder.build(appView);
         materializedReferencedFromCollections.put(info, referencedFrom);
       } else {
         referencedFrom = ProgramMethodSet.empty();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
index 2761296..f81b135 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -7,7 +7,6 @@
 import static com.android.tools.r8.optimize.argumentpropagation.utils.StronglyConnectedProgramClasses.computeStronglyConnectedProgramClasses;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -15,7 +14,6 @@
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.conversion.MethodProcessor;
 import com.android.tools.r8.ir.conversion.PostMethodProcessor;
-import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.VirtualRootMethodsAnalysis;
@@ -128,15 +126,30 @@
       throws ExecutionException {
     assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
     timing.begin("Argument propagator");
+
+    // Compute the strongly connected program components for parallel execution.
     ImmediateProgramSubtypingInfo immediateSubtypingInfo =
         ImmediateProgramSubtypingInfo.create(appView);
     List<Set<DexProgramClass>> stronglyConnectedProgramComponents =
         computeStronglyConnectedProgramClasses(appView, immediateSubtypingInfo);
+
+    // Set the optimization info on each method.
     populateParameterOptimizationInfo(
         immediateSubtypingInfo, stronglyConnectedProgramComponents, executorService, timing);
-    optimizeMethodParameters(
-        immediateSubtypingInfo, stronglyConnectedProgramComponents, executorService);
-    enqueueMethodsForProcessing(postMethodProcessorBuilder);
+
+    // Using the computed optimization info, build a graph lens that describes the mapping from
+    // methods with constant parameters to methods with the constant parameters removed.
+    ArgumentPropagatorGraphLens graphLens =
+        optimizeMethodParameters(stronglyConnectedProgramComponents, executorService);
+
+    // Find all the code objects that need reprocessing.
+    new ArgumentPropagatorMethodReprocessingEnqueuer(appView)
+        .enqueueMethodForReprocessing(graphLens, postMethodProcessorBuilder, executorService);
+
+    // Finally, apply the graph lens to the program (i.e., remove constant parameters from method
+    // definitions).
+    new ArgumentPropagatorApplicationFixer(appView, graphLens).fixupApplication(executorService);
+
     timing.end();
   }
 
@@ -169,47 +182,20 @@
   }
 
   /** Called by {@link IRConverter} to optimize method definitions. */
-  private void optimizeMethodParameters(
-      ImmediateProgramSubtypingInfo immediateSubtypingInfo,
+  private ArgumentPropagatorGraphLens optimizeMethodParameters(
       List<Set<DexProgramClass>> stronglyConnectedProgramComponents,
       ExecutorService executorService)
       throws ExecutionException {
     Collection<ArgumentPropagatorGraphLens.Builder> partialGraphLensBuilders =
         ThreadUtils.processItemsWithResults(
             stronglyConnectedProgramComponents,
-            classes ->
-                new ArgumentPropagatorProgramOptimizer(appView, immediateSubtypingInfo)
-                    .optimize(classes),
+            classes -> new ArgumentPropagatorProgramOptimizer(appView).optimize(classes),
             executorService);
 
     // Merge all the partial, disjoint graph lens builders into a single graph lens.
     ArgumentPropagatorGraphLens.Builder graphLensBuilder =
         ArgumentPropagatorGraphLens.builder(appView);
     partialGraphLensBuilders.forEach(graphLensBuilder::mergeDisjoint);
-
-    ArgumentPropagatorGraphLens graphLens = graphLensBuilder.build();
-    if (graphLens != null) {
-      appView.setGraphLens(graphLens);
-    }
-  }
-
-  /**
-   * Called by {@link IRConverter} to add all methods that require reprocessing to {@param
-   * postMethodProcessorBuilder}.
-   */
-  private void enqueueMethodsForProcessing(PostMethodProcessor.Builder postMethodProcessorBuilder) {
-    for (DexProgramClass clazz : appView.appInfo().classes()) {
-      clazz.forEachProgramMethodMatching(
-          DexEncodedMethod::hasCode,
-          method -> {
-            CallSiteOptimizationInfo callSiteOptimizationInfo =
-                method.getDefinition().getCallSiteOptimizationInfo();
-            if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo()
-                && !appView.appInfo().isNeverReprocessMethod(method.getReference())) {
-              postMethodProcessorBuilder.add(method);
-              appView.testing().callSiteOptimizationInfoInspector.accept(method);
-            }
-          });
-    }
+    return graphLensBuilder.build();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
new file mode 100644
index 0000000..346e3fc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.MethodCollection;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ThreadUtils;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * Takes as input a mapping from old method signatures to new method signatures (with parameters
+ * removed), and rewrites all method definitions in the application to their new method signatures.
+ */
+public class ArgumentPropagatorApplicationFixer {
+
+  private final AppView<AppInfoWithLiveness> appView;
+  private final ArgumentPropagatorGraphLens graphLens;
+
+  public ArgumentPropagatorApplicationFixer(
+      AppView<AppInfoWithLiveness> appView, ArgumentPropagatorGraphLens graphLens) {
+    this.appView = appView;
+    this.graphLens = graphLens;
+  }
+
+  public void fixupApplication(ExecutorService executorService) throws ExecutionException {
+    // If the graph lens is null, argument propagation did not lead to any parameter removals. In
+    // this case there is no needed to fixup the program.
+    if (graphLens == null) {
+      return;
+    }
+
+    // TODO(b/190154391): Do not naively visit all classes, when only few require changes.
+    ThreadUtils.processItems(appView.appInfo().classes(), this::fixupClass, executorService);
+    appView.setGraphLens(graphLens);
+  }
+
+  private void fixupClass(DexProgramClass clazz) {
+    MethodCollection methodCollection = clazz.getMethodCollection();
+    methodCollection.replaceMethods(
+        method -> {
+          DexMethod methodReferenceBeforeParameterRemoval = method.getReference();
+          DexMethod methodReferenceAfterParameterRemoval =
+              graphLens.internalGetNextMethodSignature(methodReferenceBeforeParameterRemoval);
+          if (methodReferenceAfterParameterRemoval == methodReferenceBeforeParameterRemoval) {
+            return method;
+          }
+
+          return method.toTypeSubstitutedMethod(
+              methodReferenceAfterParameterRemoval,
+              builder -> {
+                // TODO(b/190154391): fixup parameter annotations, if any.
+              });
+        });
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
index 0110197..c60640c 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
@@ -5,18 +5,27 @@
 package com.android.tools.r8.optimize.argumentpropagation;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.graph.NestedGraphLens;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
 
-public class ArgumentPropagatorGraphLens extends NonIdentityGraphLens {
+public class ArgumentPropagatorGraphLens extends NestedGraphLens {
 
-  ArgumentPropagatorGraphLens(AppView<AppInfoWithLiveness> appView) {
-    super(appView);
+  private final Map<DexMethod, ArgumentInfoCollection> removedParameters;
+
+  ArgumentPropagatorGraphLens(
+      AppView<AppInfoWithLiveness> appView,
+      BidirectionalOneToOneMap<DexMethod, DexMethod> methodMap,
+      Map<DexMethod, ArgumentInfoCollection> removedParameters) {
+    super(appView, EMPTY_FIELD_MAP, methodMap, EMPTY_TYPE_MAP);
+    this.removedParameters = removedParameters;
   }
 
   public static Builder builder(AppView<AppInfoWithLiveness> appView) {
@@ -24,85 +33,67 @@
   }
 
   @Override
-  public DexType getOriginalType(DexType type) {
-    return getPrevious().getOriginalType(type);
+  protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
+      RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
+    DexMethod previous = internalGetPreviousMethodSignature(method);
+    if (previous == method) {
+      assert !removedParameters.containsKey(method);
+      return prototypeChanges;
+    }
+    return prototypeChanges.withRemovedArguments(
+        removedParameters.getOrDefault(method, ArgumentInfoCollection.empty()));
   }
 
   @Override
-  public Iterable<DexType> getOriginalTypes(DexType type) {
-    return getPrevious().getOriginalTypes(type);
+  public DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+    return super.internalGetPreviousMethodSignature(method);
   }
 
   @Override
-  public DexField getOriginalFieldSignature(DexField field) {
-    return getPrevious().getOriginalFieldSignature(field);
-  }
-
-  @Override
-  public DexField getRenamedFieldSignature(DexField originalField) {
-    return getPrevious().getRenamedFieldSignature(originalField);
-  }
-
-  @Override
-  public DexMethod getOriginalMethodSignature(DexMethod method) {
-    return getPrevious().getOriginalMethodSignature(method);
-  }
-
-  @Override
-  public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
-    return applied != this
-        ? getPrevious().getRenamedMethodSignature(originalMethod, applied)
-        : originalMethod;
-  }
-
-  @Override
-  protected DexType internalDescribeLookupClassType(DexType previous) {
-    return previous;
-  }
-
-  @Override
-  protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
-    return previous;
-  }
-
-  @Override
-  protected MethodLookupResult internalDescribeLookupMethod(
-      MethodLookupResult previous, DexMethod context) {
-    return previous;
-  }
-
-  @Override
-  protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
-    return method;
-  }
-
-  @Override
-  public boolean isContextFreeForMethods() {
-    return getPrevious().isContextFreeForMethods();
-  }
-
-  @Override
-  public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) {
-    return getPrevious().lookupPrototypeChangesForMethodDefinition(method);
+  public DexMethod internalGetNextMethodSignature(DexMethod method) {
+    return super.internalGetNextMethodSignature(method);
   }
 
   public static class Builder {
 
     private final AppView<AppInfoWithLiveness> appView;
+    private final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> newMethodSignatures =
+        new BidirectionalOneToOneHashMap<>();
+    private final Map<DexMethod, ArgumentInfoCollection> removedParameters =
+        new IdentityHashMap<>();
 
     Builder(AppView<AppInfoWithLiveness> appView) {
       this.appView = appView;
     }
 
+    public boolean isEmpty() {
+      return newMethodSignatures.isEmpty();
+    }
+
     public ArgumentPropagatorGraphLens.Builder mergeDisjoint(
         ArgumentPropagatorGraphLens.Builder partialGraphLensBuilder) {
-      // TODO(b/190154391): Implement.
+      newMethodSignatures.putAll(partialGraphLensBuilder.newMethodSignatures);
+      removedParameters.putAll(partialGraphLensBuilder.removedParameters);
+      return this;
+    }
+
+    public Builder recordMove(
+        DexMethod from, DexMethod to, ArgumentInfoCollection removedParametersForMethod) {
+      if (from != to) {
+        newMethodSignatures.put(from, to);
+        if (!removedParametersForMethod.isEmpty()) {
+          removedParameters.put(to, removedParametersForMethod);
+        }
+      } else {
+        assert removedParametersForMethod.isEmpty();
+      }
       return this;
     }
 
     public ArgumentPropagatorGraphLens build() {
-      // TODO(b/190154391): Implement.
-      return null;
+      return isEmpty()
+          ? null
+          : new ArgumentPropagatorGraphLens(appView, newMethodSignatures, removedParameters);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
new file mode 100644
index 0000000..3c732df
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
@@ -0,0 +1,189 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.PostMethodProcessor;
+import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+/** Finds the methods in the program that should be reprocessed due to argument propagation. */
+public class ArgumentPropagatorMethodReprocessingEnqueuer {
+
+  private final AppView<AppInfoWithLiveness> appView;
+
+  public ArgumentPropagatorMethodReprocessingEnqueuer(AppView<AppInfoWithLiveness> appView) {
+    this.appView = appView;
+  }
+
+  /**
+   * Called indirectly from {@link IRConverter} to add all methods that require reprocessing to
+   * {@param postMethodProcessorBuilder}.
+   */
+  public void enqueueMethodForReprocessing(
+      ArgumentPropagatorGraphLens graphLens,
+      PostMethodProcessor.Builder postMethodProcessorBuilder,
+      ExecutorService executorService)
+      throws ExecutionException {
+    // Bring the methods to reprocess set up-to-date with the current graph lens (i.e., the one
+    // prior to the argument propagator lens, which has not yet been installed!).
+    LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsToReprocessBuilder =
+        postMethodProcessorBuilder
+            .getMethodsToReprocessBuilder()
+            .rewrittenWithLens(appView.graphLens());
+    enqueueMethodsWithNonTrivialOptimizationInfo(methodsToReprocessBuilder);
+    if (graphLens != null) {
+      enqueueAffectedCallers(graphLens, methodsToReprocessBuilder, executorService);
+    }
+  }
+
+  private void enqueueMethodsWithNonTrivialOptimizationInfo(
+      LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsToReprocessBuilder) {
+    GraphLens currentGraphLens = appView.graphLens();
+    for (DexProgramClass clazz : appView.appInfo().classes()) {
+      clazz.forEachProgramMethodMatching(
+          DexEncodedMethod::hasCode,
+          method -> {
+            CallSiteOptimizationInfo callSiteOptimizationInfo =
+                method.getDefinition().getCallSiteOptimizationInfo();
+            if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo()
+                && !appView.appInfo().isNeverReprocessMethod(method.getReference())) {
+              methodsToReprocessBuilder.add(method, currentGraphLens);
+              appView.testing().callSiteOptimizationInfoInspector.accept(method);
+            }
+          });
+    }
+  }
+
+  // TODO(b/190154391): This could invalidate the @NeverReprocessMethod testing annotations (non
+  //  critical). If @NeverReprocessMethod is used, we would need to scan the application to mark
+  //  methods as unoptimizable prior to removing parameters from the application.
+  private void enqueueAffectedCallers(
+      ArgumentPropagatorGraphLens graphLens,
+      LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsToReprocessBuilder,
+      ExecutorService executorService)
+      throws ExecutionException {
+    Collection<List<ProgramMethod>> methodsToReprocess =
+        ThreadUtils.processItemsWithResults(
+            appView.appInfo().classes(),
+            clazz -> {
+              List<ProgramMethod> methodsToReprocessInClass = new ArrayList<>();
+              clazz.forEachProgramMethodMatching(
+                  DexEncodedMethod::hasCode,
+                  method -> {
+                    AffectedMethodUseRegistry registry =
+                        new AffectedMethodUseRegistry(appView, graphLens);
+                    method.registerCodeReferences(registry);
+                    if (registry.isAffected()) {
+                      methodsToReprocessInClass.add(method);
+                    }
+                  });
+              return methodsToReprocessInClass;
+            },
+            executorService);
+    GraphLens currentGraphLens = appView.graphLens();
+    methodsToReprocess.forEach(
+        methodsToReprocessForClass ->
+            methodsToReprocessBuilder.addAll(methodsToReprocessForClass, currentGraphLens));
+  }
+
+  static class AffectedMethodUseRegistry extends UseRegistry {
+
+    private final AppView<AppInfoWithLiveness> appView;
+    private final ArgumentPropagatorGraphLens graphLens;
+
+    // Set to true if the given piece of code resolves to a method that needs rewriting according to
+    // the graph lens.
+    private boolean affected;
+
+    AffectedMethodUseRegistry(
+        AppView<AppInfoWithLiveness> appView, ArgumentPropagatorGraphLens graphLens) {
+      super(appView.dexItemFactory());
+      this.appView = appView;
+      this.graphLens = graphLens;
+    }
+
+    boolean isAffected() {
+      return affected;
+    }
+
+    @Override
+    public void registerInvokeDirect(DexMethod method) {
+      registerInvokeMethod(method);
+    }
+
+    @Override
+    public void registerInvokeInterface(DexMethod method) {
+      registerInvokeMethod(method);
+    }
+
+    @Override
+    public void registerInvokeStatic(DexMethod method) {
+      registerInvokeMethod(method);
+    }
+
+    @Override
+    public void registerInvokeSuper(DexMethod method) {
+      registerInvokeMethod(method);
+    }
+
+    @Override
+    public void registerInvokeVirtual(DexMethod method) {
+      registerInvokeMethod(method);
+    }
+
+    private void registerInvokeMethod(DexMethod method) {
+      SingleResolutionResult resolutionResult =
+          appView.appInfo().unsafeResolveMethodDueToDexFormat(method).asSingleResolution();
+      if (resolutionResult == null || !resolutionResult.getResolvedHolder().isProgramClass()) {
+        return;
+      }
+
+      ProgramMethod resolvedMethod = resolutionResult.getResolvedProgramMethod();
+      DexMethod rewrittenMethodReference =
+          graphLens.internalGetNextMethodSignature(resolvedMethod.getReference());
+      if (rewrittenMethodReference != resolvedMethod.getReference()) {
+        affected = true;
+        // TODO(b/150583533): break/abort!
+      }
+    }
+
+    @Override
+    public void registerInitClass(DexType type) {}
+
+    @Override
+    public void registerInstanceFieldRead(DexField field) {}
+
+    @Override
+    public void registerInstanceFieldWrite(DexField field) {}
+
+    @Override
+    public void registerStaticFieldRead(DexField field) {}
+
+    @Override
+    public void registerStaticFieldWrite(DexField field) {}
+
+    @Override
+    public void registerTypeReference(DexType type) {}
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index 859830b..b10ff8d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -4,24 +4,61 @@
 
 package com.android.tools.r8.optimize.argumentpropagation;
 
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodSignature;
 import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
+import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.collections.DexMethodSignatureSet;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.collect.Iterables;
+import it.unimi.dsi.fastutil.ints.IntArraySet;
+import it.unimi.dsi.fastutil.ints.IntSet;
+import it.unimi.dsi.fastutil.ints.IntSets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
+import java.util.function.IntPredicate;
 
 public class ArgumentPropagatorProgramOptimizer {
 
   private final AppView<AppInfoWithLiveness> appView;
-  private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
 
-  public ArgumentPropagatorProgramOptimizer(
-      AppView<AppInfoWithLiveness> appView, ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
+  private final DexItemFactory dexItemFactory;
+
+  private final InternalOptions options;
+
+  private final Map<DexMethodSignature, IntSet> removableVirtualMethodParameters = new HashMap<>();
+
+  // Reserved names, i.e., mappings from pairs (old method signature, number of removed arguments)
+  // to the new method signature for that method.
+  private final Map<DexMethodSignature, Map<IntSet, DexMethodSignature>> newMethodSignatures =
+      new HashMap<>();
+
+  // Occupied method signatures (inverse of reserved names). Used to effectively check if a given
+  // method signature is already reserved.
+  private final Map<DexMethodSignature, Pair<IntSet, DexMethodSignature>> occupiedMethodSignatures =
+      new HashMap<>();
+
+  public ArgumentPropagatorProgramOptimizer(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
-    this.immediateSubtypingInfo = immediateSubtypingInfo;
+    this.dexItemFactory = appView.dexItemFactory();
+    this.options = appView.options();
   }
 
-  // TODO(b/190154391): Remove parameters with constant values.
   // TODO(b/190154391): Remove unused parameters by simulating they are constant.
   // TODO(b/190154391): Strengthen the static type of parameters.
   // TODO(b/190154391): If we learn that a method returns a constant, then consider changing its
@@ -32,6 +69,279 @@
   //  information to all call sites.
   public ArgumentPropagatorGraphLens.Builder optimize(
       Set<DexProgramClass> stronglyConnectedProgramClasses) {
-    return ArgumentPropagatorGraphLens.builder(appView);
+    // First reserve pinned method signatures.
+    reservePinnedMethodSignatures(stronglyConnectedProgramClasses);
+
+    // To ensure that we preserve the overriding relationships between methods, we only remove a
+    // constant or unused parameter from a virtual method when it can be removed from all other
+    // virtual methods in the component with the same method signature.
+    computeRemovableVirtualMethodParameters(stronglyConnectedProgramClasses);
+
+    // Build a graph lens while visiting the classes in the component.
+    // TODO(b/190154391): Consider visiting the interfaces first, and then processing the
+    //  (non-interface) classes in top-down order to reduce the amount of reserved names.
+    ArgumentPropagatorGraphLens.Builder partialGraphLensBuilder =
+        ArgumentPropagatorGraphLens.builder(appView);
+    for (DexProgramClass clazz : stronglyConnectedProgramClasses) {
+      visitClass(clazz, partialGraphLensBuilder);
+    }
+    return partialGraphLensBuilder;
+  }
+
+  private void reservePinnedMethodSignatures(Set<DexProgramClass> stronglyConnectedProgramClasses) {
+    DexMethodSignatureSet pinnedMethodSignatures = DexMethodSignatureSet.create();
+    for (DexProgramClass clazz : stronglyConnectedProgramClasses) {
+      clazz.forEachProgramMethod(
+          method -> {
+            if (!appView.getKeepInfo(method).isShrinkingAllowed(options)) {
+              pinnedMethodSignatures.add(method.getMethodSignature());
+            }
+          });
+    }
+    pinnedMethodSignatures.forEach(
+        signature -> reserveMethodSignature(signature, signature, IntSets.EMPTY_SET));
+  }
+
+  private void reserveMethodSignature(
+      DexMethodSignature newMethodSignature,
+      DexMethodSignature originalMethodSignature,
+      IntSet removedParameterIndices) {
+    // Record that methods with the given signature and removed parameters should be mapped to the
+    // new signature.
+    newMethodSignatures
+        .computeIfAbsent(originalMethodSignature, ignoreKey(HashMap::new))
+        .put(removedParameterIndices, newMethodSignature);
+
+    // Record that the new method signature is used, by a method with the old signature that had the
+    // given removed parameters.
+    occupiedMethodSignatures.put(
+        newMethodSignature, new Pair<>(removedParameterIndices, originalMethodSignature));
+  }
+
+  private void computeRemovableVirtualMethodParameters(
+      Set<DexProgramClass> stronglyConnectedProgramClasses) {
+    // Group the virtual methods in the component by their signatures.
+    Map<DexMethodSignature, ProgramMethodSet> virtualMethodsBySignature =
+        computeVirtualMethodsBySignature(stronglyConnectedProgramClasses);
+    virtualMethodsBySignature.forEach(
+        (signature, methods) -> {
+          // Check that there are no keep rules that prohibit parameter removal from any of the
+          // methods.
+          if (Iterables.any(methods, method -> !isParameterRemovalAllowed(method))) {
+            return;
+          }
+
+          // Find the parameters that are constant or unused in all methods.
+          IntSet removableVirtualMethodParametersInAllMethods = new IntArraySet();
+          for (int parameterIndex = 1;
+              parameterIndex < signature.getProto().getArity() + 1;
+              parameterIndex++) {
+            if (canRemoveParameterFromVirtualMethods(parameterIndex, methods)) {
+              removableVirtualMethodParametersInAllMethods.add(parameterIndex);
+            }
+          }
+
+          // If any parameters could be removed, record it.
+          if (!removableVirtualMethodParametersInAllMethods.isEmpty()) {
+            removableVirtualMethodParameters.put(
+                signature, removableVirtualMethodParametersInAllMethods);
+          }
+        });
+  }
+
+  private Map<DexMethodSignature, ProgramMethodSet> computeVirtualMethodsBySignature(
+      Set<DexProgramClass> stronglyConnectedProgramClasses) {
+    Map<DexMethodSignature, ProgramMethodSet> virtualMethodsBySignature = new HashMap<>();
+    for (DexProgramClass clazz : stronglyConnectedProgramClasses) {
+      clazz.forEachProgramVirtualMethod(
+          method ->
+              virtualMethodsBySignature
+                  .computeIfAbsent(method.getMethodSignature(), ignoreKey(ProgramMethodSet::create))
+                  .add(method));
+    }
+    return virtualMethodsBySignature;
+  }
+
+  private boolean isParameterRemovalAllowed(ProgramMethod method) {
+    return appView.getKeepInfo(method).isParameterRemovalAllowed(options)
+        && !method.getDefinition().isLibraryMethodOverride().isPossiblyTrue();
+  }
+
+  private boolean canRemoveParameterFromVirtualMethods(
+      int parameterIndex, ProgramMethodSet methods) {
+    for (ProgramMethod method : methods) {
+      if (method.getDefinition().isAbstract()) {
+        DexProgramClass holder = method.getHolder();
+        if (holder.isInterface()) {
+          ObjectAllocationInfoCollection objectAllocationInfoCollection =
+              appView.appInfo().getObjectAllocationInfoCollection();
+          if (objectAllocationInfoCollection.isImmediateInterfaceOfInstantiatedLambda(holder)) {
+            return false;
+          }
+        }
+        // OK, this parameter can be removed.
+        continue;
+      }
+      CallSiteOptimizationInfo optimizationInfo =
+          method.getDefinition().getCallSiteOptimizationInfo();
+      if (optimizationInfo.isConcreteCallSiteOptimizationInfo()) {
+        ConcreteCallSiteOptimizationInfo concreteOptimizationInfo =
+            optimizationInfo.asConcreteCallSiteOptimizationInfo();
+        AbstractValue abstractValue =
+            concreteOptimizationInfo.getAbstractArgumentValue(parameterIndex);
+        if (abstractValue.isSingleValue()
+            && abstractValue.asSingleValue().isMaterializableInContext(appView, method)) {
+          // OK, this parameter has a constant value and can be removed.
+          continue;
+        }
+      }
+      return false;
+    }
+    return true;
+  }
+
+  private void visitClass(
+      DexProgramClass clazz, ArgumentPropagatorGraphLens.Builder partialGraphLensBuilder) {
+    clazz.forEachProgramMethod(
+        method -> {
+          ArgumentInfoCollection removableParameters =
+              method.getDefinition().belongsToDirectPool()
+                  ? computeRemovableParametersFromDirectMethod(method)
+                  : computeRemovableParametersFromVirtualMethod(method);
+          DexMethod newMethodSignature = getNewMethodSignature(method, removableParameters);
+          partialGraphLensBuilder.recordMove(
+              method.getReference(), newMethodSignature, removableParameters);
+        });
+  }
+
+  private DexMethod getNewMethodSignature(
+      ProgramMethod method, ArgumentInfoCollection removableParameters) {
+    DexMethodSignature methodSignatureWithoutParametersRemoved = method.getMethodSignature();
+    IntSet removableParameterIndices = removableParameters.getKeys();
+
+    // Check if there is a reserved signature for this already.
+    DexMethodSignature reservedSignature =
+        newMethodSignatures
+            .getOrDefault(methodSignatureWithoutParametersRemoved, Collections.emptyMap())
+            .get(removableParameterIndices);
+    if (reservedSignature != null) {
+      return reservedSignature.withHolder(method.getHolderType(), dexItemFactory);
+    }
+
+    DexMethod methodReferenceWithParametersRemoved =
+        removableParameters.rewriteMethod(method, dexItemFactory);
+    DexMethodSignature methodSignatureWithParametersRemoved =
+        methodReferenceWithParametersRemoved.getSignature();
+
+    // Find a method signature. First check if the current signature is available.
+    if (!occupiedMethodSignatures.containsKey(methodSignatureWithParametersRemoved)) {
+      reserveMethodSignature(
+          methodSignatureWithParametersRemoved,
+          methodSignatureWithoutParametersRemoved,
+          removableParameterIndices);
+      return methodReferenceWithParametersRemoved;
+    }
+
+    Pair<IntSet, DexMethodSignature> occupant =
+        occupiedMethodSignatures.get(methodSignatureWithParametersRemoved);
+    // In this case we should have found a reserved method signature above.
+    assert !(occupant.getFirst().equals(removableParameterIndices)
+        && occupant.getSecond().equals(methodSignatureWithoutParametersRemoved));
+
+    // We need to find a new name for this method, since the signature is already occupied.
+    // TODO(b/190154391): Instead of generating a new name, we could also try permuting the order of
+    //  parameters.
+    DexMethod newMethod =
+        dexItemFactory.createFreshMethodNameWithoutHolder(
+            method.getName().toString(),
+            methodReferenceWithParametersRemoved.getProto(),
+            method.getHolderType(),
+            candidate -> {
+              Pair<IntSet, DexMethodSignature> candidateOccupant =
+                  occupiedMethodSignatures.get(candidate.getSignature());
+              if (candidateOccupant == null) {
+                return true;
+              }
+              return candidateOccupant.getFirst().equals(removableParameterIndices)
+                  && candidateOccupant.getSecond().equals(methodSignatureWithoutParametersRemoved);
+            });
+
+    // Reserve the newly generated method signature.
+    reserveMethodSignature(
+        newMethod.getSignature(),
+        methodSignatureWithoutParametersRemoved,
+        removableParameterIndices);
+
+    return newMethod;
+  }
+
+  private ArgumentInfoCollection computeRemovableParametersFromDirectMethod(ProgramMethod method) {
+    assert method.getDefinition().belongsToDirectPool();
+    if (method.getDefinition().isInstanceInitializer()) {
+      // TODO(b/190154391): Allow parameter removal from initializers. We need to guarantee absence
+      //  of collisions since initializers can't be renamed.
+      return ArgumentInfoCollection.empty();
+    }
+    return computeRemovableParametersFromMethod(method);
+  }
+
+  private ArgumentInfoCollection computeRemovableParametersFromVirtualMethod(ProgramMethod method) {
+    IntSet removableParameterIndices =
+        removableVirtualMethodParameters.getOrDefault(
+            method.getMethodSignature(), IntSets.EMPTY_SET);
+    if (removableParameterIndices.isEmpty()) {
+      return ArgumentInfoCollection.empty();
+    }
+
+    if (method.getAccessFlags().isAbstract()) {
+      // Treat the parameters as unused.
+      ArgumentInfoCollection.Builder removableParametersBuilder = ArgumentInfoCollection.builder();
+      for (int removableParameterIndex : removableParameterIndices) {
+        removableParametersBuilder.addArgumentInfo(
+            removableParameterIndex,
+            RemovedArgumentInfo.builder()
+                .setType(method.getArgumentType(removableParameterIndex))
+                .build());
+      }
+      return removableParametersBuilder.build();
+    }
+
+    ArgumentInfoCollection removableParameters =
+        computeRemovableParametersFromMethod(method, removableParameterIndices::contains);
+    assert removableParameters.size() == removableParameterIndices.size();
+    return removableParameters;
+  }
+
+  private ArgumentInfoCollection computeRemovableParametersFromMethod(ProgramMethod method) {
+    return computeRemovableParametersFromMethod(method, parameterIndex -> true);
+  }
+
+  private ArgumentInfoCollection computeRemovableParametersFromMethod(
+      ProgramMethod method, IntPredicate removableParameterIndices) {
+    ConcreteCallSiteOptimizationInfo optimizationInfo =
+        method.getDefinition().getCallSiteOptimizationInfo().asConcreteCallSiteOptimizationInfo();
+    if (optimizationInfo == null) {
+      return ArgumentInfoCollection.empty();
+    }
+
+    ArgumentInfoCollection.Builder removableParametersBuilder = ArgumentInfoCollection.builder();
+    for (int argumentIndex = method.getDefinition().getFirstNonReceiverArgumentIndex();
+        argumentIndex < method.getDefinition().getNumberOfArguments();
+        argumentIndex++) {
+      if (!removableParameterIndices.test(argumentIndex)) {
+        continue;
+      }
+      AbstractValue abstractValue = optimizationInfo.getAbstractArgumentValue(argumentIndex);
+      if (abstractValue.isSingleValue()
+          && abstractValue.asSingleValue().isMaterializableInContext(appView, method)) {
+        removableParametersBuilder.addArgumentInfo(
+            argumentIndex,
+            RemovedArgumentInfo.builder()
+                .setSingleValue(abstractValue.asSingleValue())
+                .setType(method.getArgumentType(argumentIndex))
+                .build());
+      }
+    }
+    return removableParametersBuilder.build();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java
index 3244db4..02db24d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java
@@ -51,12 +51,12 @@
     return this;
   }
 
-  public AbstractValue getAbstractValue() {
-    return abstractValue;
-  }
-
   @Override
   public AbstractValue getAbstractValue(AppView<AppInfoWithLiveness> appView) {
+    if (getDynamicType().getNullability().isDefinitelyNull()) {
+      assert abstractValue == null || abstractValue.isNull();
+      return appView.abstractValueFactory().createNullValue();
+    }
     return abstractValue;
   }
 
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 acc8410..55278ec 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -933,7 +933,8 @@
     if (!options().enableValuePropagation || neverPropagateValue.contains(method)) {
       return false;
     }
-    if (isPinned(method) && !method.getReturnType().isAlwaysNull(this)) {
+    if (!method.getReturnType().isAlwaysNull(this)
+        && !getKeepInfo().getMethodInfo(method, this).isInliningAllowed(options())) {
       return false;
     }
     return true;
@@ -1315,7 +1316,11 @@
       if (refinedReceiverClass.isProgramClass()) {
         DexClassAndMethod clazzAndMethod =
             resolution.lookupVirtualDispatchTarget(refinedReceiverClass.asProgramClass(), this);
-        if (clazzAndMethod == null || isPinned(clazzAndMethod.getDefinition().getReference())) {
+        if (clazzAndMethod == null
+            || (clazzAndMethod.isProgramMethod()
+                && !getKeepInfo()
+                    .getMethodInfo(clazzAndMethod.asProgramMethod())
+                    .isOptimizationAllowed(options()))) {
           // TODO(b/150640456): We should maybe only consider program methods.
           return DexEncodedMethod.SENTINEL;
         }
diff --git a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
index bfc3ae7..129acae 100644
--- a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
@@ -10,13 +10,11 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldAccessFlags;
-import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Visibility;
@@ -82,17 +80,15 @@
                   | Constants.ACC_FINAL
                   | Constants.ACC_PUBLIC
                   | Constants.ACC_STATIC);
-      boolean deprecated = false;
       encodedClinitField =
-          new DexEncodedField(
-              appView.dexItemFactory().createField(clazz.type, clinitField.type, clinitField.name),
-              accessFlags,
-              FieldTypeSignature.noSignature(),
-              DexAnnotationSet.empty(),
-              null,
-              deprecated,
-              DexEncodedField.D8_R8_SYNTHESIZED,
-              minApiLevelIfEnabledOrUnknown(appView));
+          DexEncodedField.syntheticBuilder()
+              .setField(
+                  appView
+                      .dexItemFactory()
+                      .createField(clazz.type, clinitField.type, clinitField.name))
+              .setAccessFlags(accessFlags)
+              .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
+              .build();
       clazz.appendStaticField(encodedClinitField);
     }
     lensBuilder.map(type, encodedClinitField.getReference());
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 66e9136..895e610 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
 import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -108,6 +109,7 @@
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
 import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringTypeLookupResult;
+import com.android.tools.r8.position.Position;
 import com.android.tools.r8.shaking.AnnotationMatchResult.MatchedAnnotation;
 import com.android.tools.r8.shaking.DelayedRootSetActionItem.InterfaceMethodSyntheticBridgeAction;
 import com.android.tools.r8.shaking.EnqueuerEvent.ClassEnqueuerEvent;
@@ -3933,7 +3935,19 @@
     assert workList.isEmpty();
 
     R8PostProcessingDesugaringEventConsumer eventConsumer =
-        CfPostProcessingDesugaringEventConsumer.createForR8(syntheticAdditions, desugaring);
+        CfPostProcessingDesugaringEventConsumer.createForR8(
+            syntheticAdditions,
+            desugaring,
+            (context, missing) ->
+                missingClassesBuilder.addNewMissingClassWithDesugarDiagnostic(
+                    missing,
+                    context,
+                    new InterfaceDesugarMissingTypeDiagnostic(
+                        context.getOrigin(),
+                        Position.UNKNOWN,
+                        missing.asClassReference(),
+                        context.getType().asClassReference(),
+                        null)));
     InterfaceMethodProcessorFacade interfaceDesugaring =
         desugaring.getInterfaceMethodPostProcessingDesugaringR8(
             ExcludeDexResources, liveMethods::contains, interfaceProcessor);
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
index c3165ad..f846462 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -70,6 +70,10 @@
     return allowAnnotationRemoval;
   }
 
+  public boolean isParameterRemovalAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration) && isShrinkingAllowed(configuration);
+  }
+
   /**
    * True if an item must be present in the output.
    *
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
index dd4226c..79fac0a 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -54,6 +54,10 @@
     return this.equals(bottom());
   }
 
+  public boolean isInliningAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration);
+  }
+
   public static class Builder extends KeepInfo.Builder<Builder, KeepMethodInfo> {
 
     private Builder() {
diff --git a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
index 1dd5360..ac8f1fb 100644
--- a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
+++ b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.diagnostic.internal.DefinitionContextUtils;
 import com.android.tools.r8.diagnostic.internal.MissingClassInfoImpl;
 import com.android.tools.r8.diagnostic.internal.MissingDefinitionsDiagnosticImpl;
+import com.android.tools.r8.errors.DesugarDiagnostic;
 import com.android.tools.r8.errors.dontwarn.DontWarnConfiguration;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -89,6 +90,17 @@
       }
     }
 
+    public void addNewMissingClassWithDesugarDiagnostic(
+        DexType type, ProgramDerivedContext context, DesugarDiagnostic diagnostic) {
+      // TODO(b/175659048): At this point we just throw out the diagnostic and report the
+      //  missing classes only. We should instead report the most specific diagnostic as the
+      //  information about missing for desugar is strictly more valuable than just missing.
+      //  Note that we should have deterministic reporting and so we should collect all of the
+      //  contexts for which desugaring needed the type and report all of those in the same
+      //  way as we do for missing classes.
+      addNewMissingClass(type, context);
+    }
+
     public void legacyAddNewMissingClass(DexType type) {
       if (!alreadyMissingClasses.contains(type)) {
         // The legacy reporting is context insensitive, so we just use an empty set of contexts.
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 27d25bb..6a89e9b 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -9,12 +9,12 @@
 import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
 
 import com.android.tools.r8.androidapi.AndroidApiReferenceLevelCache;
+import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.errors.Unreachable;
 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.DexAnnotationSet;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -46,7 +46,6 @@
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription;
 import com.android.tools.r8.graph.SubtypingInfo;
@@ -1440,18 +1439,17 @@
       // be updated by the end of vertical class merging.
       synthesizedBridges.add(code);
 
+      CfVersion classFileVersion =
+          method.hasClassFileVersion() ? method.getClassFileVersion() : null;
       DexEncodedMethod bridge =
-          new DexEncodedMethod(
-              newMethod,
-              accessFlags,
-              MethodTypeSignature.noSignature(),
-              DexAnnotationSet.empty(),
-              ParameterAnnotationsList.empty(),
-              code,
-              true,
-              method.hasClassFileVersion() ? method.getClassFileVersion() : null,
-              method.getApiLevelForDefinition(),
-              method.getApiLevelForDefinition());
+          DexEncodedMethod.syntheticBuilder()
+              .setMethod(newMethod)
+              .setAccessFlags(accessFlags)
+              .setCode(code)
+              .setClassFileVersion(classFileVersion)
+              .setApiLevelForDefinition(method.getApiLevelForDefinition())
+              .setApiLevelForCode(method.getApiLevelForDefinition())
+              .build();
       bridge.setLibraryMethodOverride(method.isLibraryMethodOverride());
       if (method.accessFlags.isPromotedToPublic()) {
         // The bridge is now the public method serving the role of the original method, and should
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
index 196af3b..0e45bd8 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -104,21 +104,21 @@
 
   DexEncodedMethod build() {
     assert name != null;
-    boolean isCompilerSynthesized = true;
     DexMethod methodSignature = getMethodSignature();
     MethodAccessFlags accessFlags = getAccessFlags();
+    Code code = accessFlags.isAbstract() ? null : getCodeObject(methodSignature);
     DexEncodedMethod method =
-        new DexEncodedMethod(
-            methodSignature,
-            accessFlags,
-            genericSignature,
-            annotations,
-            parameterAnnotationsList,
-            accessFlags.isAbstract() ? null : getCodeObject(methodSignature),
-            isCompilerSynthesized,
-            classFileVersion,
-            AndroidApiLevel.UNKNOWN,
-            AndroidApiLevel.UNKNOWN);
+        DexEncodedMethod.syntheticBuilder()
+            .setMethod(methodSignature)
+            .setAccessFlags(accessFlags)
+            .setGenericSignature(genericSignature)
+            .setAnnotations(annotations)
+            .setParameterAnnotations(parameterAnnotationsList)
+            .setCode(code)
+            .setClassFileVersion(classFileVersion)
+            .setApiLevelForDefinition(AndroidApiLevel.UNKNOWN)
+            .setApiLevelForCode(AndroidApiLevel.UNKNOWN)
+            .build();
     assert isValidSyntheticMethod(method, syntheticKind);
     if (onBuildConsumer != null) {
       onBuildConsumer.accept(method);
diff --git a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
index 7259af1..4f652cf 100644
--- a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
@@ -19,6 +19,12 @@
     return curry(function).apply(arg);
   }
 
+  public static <T> void acceptIfNotNull(T element, Consumer<T> consumer) {
+    if (element != null) {
+      consumer.accept(element);
+    }
+  }
+
   public static <T> Consumer<T> acceptIfNotSeen(Consumer<T> consumer, Set<T> seen) {
     return element -> {
       if (seen.add(element)) {
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index e0a5e76..7837852 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -307,7 +307,13 @@
   public int classInliningInstructionLimit = 10;
   public int classInliningInstructionAllowance = 50;
   // This defines the limit of instructions in the inlinee
-  public int inliningInstructionLimit = 3;
+  public int inliningInstructionLimit =
+      !Version.isDevelopmentVersion()
+          ? 3
+          : System.getProperty("com.android.tools.r8.inliningInstructionLimit") != null
+              ? Integer.parseInt(
+                  System.getProperty("com.android.tools.r8.inliningInstructionLimit"))
+              : 3;
   // This defines how many instructions of inlinees we can inlinee overall.
   public int inliningInstructionAllowance = 1500;
   // Maximum number of distinct values in a method that may be used in a monitor-enter instruction.
@@ -1212,12 +1218,12 @@
     private final int maxNumberOfDispatchTargetsBeforeAbandoning = 10;
 
     // TODO(b/69963623): enable if everything is ready, including signature rewriting at call sites.
-    private boolean enableConstantPropagation = false;
+    private boolean enableLegacyConstantPropagation = false;
     private boolean enableExperimentalArgumentPropagation = false;
     private boolean enableDynamicTypePropagation = true;
 
     public void disableOptimization() {
-      enableConstantPropagation = false;
+      enableLegacyConstantPropagation = false;
       enableDynamicTypePropagation = false;
     }
 
@@ -1233,7 +1239,7 @@
       if (!isOptimizing()) {
         return false;
       }
-      return enableConstantPropagation || enableDynamicTypePropagation;
+      return enableLegacyConstantPropagation || enableDynamicTypePropagation;
     }
 
     public boolean isExperimentalArgumentPropagationEnabled() {
@@ -1241,16 +1247,17 @@
     }
 
     public boolean isConstantPropagationEnabled() {
-      return enableConstantPropagation;
+      return enableLegacyConstantPropagation || isExperimentalArgumentPropagationEnabled();
     }
 
     public boolean isDynamicTypePropagationEnabled() {
       return enableDynamicTypePropagation;
     }
 
-    public void setEnableConstantPropagation() {
+    public CallSiteOptimizationOptions setEnableLegacyConstantPropagation() {
       assert !isConstantPropagationEnabled();
-      enableConstantPropagation = true;
+      enableLegacyConstantPropagation = true;
+      return this;
     }
 
     public CallSiteOptimizationOptions setEnableExperimentalArgumentPropagation(
@@ -1260,13 +1267,6 @@
     }
   }
 
-  // TODO(b/69963623): Remove this once enabled.
-  @VisibleForTesting
-  public static void enableConstantArgumentPropagationForTesting(InternalOptions options) {
-    assert !options.callSiteOptimizationOptions().isConstantPropagationEnabled();
-    options.callSiteOptimizationOptions().enableConstantPropagation = true;
-  }
-
   public class HorizontalClassMergerOptions {
 
     // TODO(b/138781768): Set enable to true when this bug is resolved.
diff --git a/src/main/java/com/android/tools/r8/utils/SetUtils.java b/src/main/java/com/android/tools/r8/utils/SetUtils.java
index bd97bd7..04b399d 100644
--- a/src/main/java/com/android/tools/r8/utils/SetUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/SetUtils.java
@@ -10,6 +10,7 @@
 import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 
 public class SetUtils {
@@ -23,6 +24,10 @@
     return false;
   }
 
+  public static <T> Set<T> newConcurrentHashSet(int capacity) {
+    return Collections.newSetFromMap(new ConcurrentHashMap<>(capacity));
+  }
+
   public static <T> HashSet<T> newHashSet(T element) {
     HashSet<T> result = new HashSet<>(1);
     result.add(element);
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java
index 5122253..b6f7c52 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java
@@ -94,6 +94,10 @@
     return backing.contains(signature);
   }
 
+  public boolean contains(DexEncodedMethod method) {
+    return contains(method.getSignature());
+  }
+
   public boolean contains(DexClassAndMethod method) {
     return contains(method.getMethodSignature());
   }
diff --git a/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
index 5152dde..7687a88 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
@@ -10,70 +10,133 @@
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.google.common.collect.Sets;
+import com.android.tools.r8.utils.SetUtils;
 import java.util.Set;
 import java.util.function.IntFunction;
 
 public class LongLivedProgramMethodSetBuilder<T extends ProgramMethodSet> {
 
+  // Factory for creating the final ProgramMethodSet.
   private final IntFunction<T> factory;
-  private final Set<DexMethod> methods;
 
-  private LongLivedProgramMethodSetBuilder(IntFunction<T> factory, Set<DexMethod> methods) {
+  // Factory for creating a Set<DexMethod>.
+  private final IntFunction<Set<DexMethod>> factoryForBuilder;
+
+  // The graph lens that this collection has been rewritten up until.
+  private GraphLens appliedGraphLens;
+
+  // The methods in this collection.
+  private Set<DexMethod> methods;
+
+  private LongLivedProgramMethodSetBuilder(
+      GraphLens currentGraphLens,
+      IntFunction<T> factory,
+      IntFunction<Set<DexMethod>> factoryBuilder) {
+    this.appliedGraphLens = currentGraphLens;
     this.factory = factory;
-    this.methods = methods;
+    this.factoryForBuilder = factoryBuilder;
+    this.methods = factoryBuilder.apply(2);
   }
 
-  public static LongLivedProgramMethodSetBuilder<?> createForIdentitySet() {
+  public static LongLivedProgramMethodSetBuilder<ProgramMethodSet> createForIdentitySet(
+      GraphLens currentGraphLens) {
     return new LongLivedProgramMethodSetBuilder<>(
-        ProgramMethodSet::create, Sets.newIdentityHashSet());
+        currentGraphLens, ProgramMethodSet::create, SetUtils::newIdentityHashSet);
   }
 
-  public static LongLivedProgramMethodSetBuilder<SortedProgramMethodSet> createForSortedSet() {
+  public static LongLivedProgramMethodSetBuilder<ProgramMethodSet> createConcurrentForIdentitySet(
+      GraphLens currentGraphLens) {
     return new LongLivedProgramMethodSetBuilder<>(
-        ignore -> SortedProgramMethodSet.create(), Sets.newIdentityHashSet());
+        currentGraphLens, ProgramMethodSet::create, SetUtils::newConcurrentHashSet);
   }
 
-  public static LongLivedProgramMethodSetBuilder<?> createConcurrentForIdentitySet() {
-    return new LongLivedProgramMethodSetBuilder<>(
-        ignore -> ProgramMethodSet.create(), Sets.newConcurrentHashSet());
-  }
-
+  @Deprecated
   public void add(ProgramMethod method) {
     methods.add(method.getReference());
   }
 
-  public void addAll(Iterable<ProgramMethod> methods) {
-    methods.forEach(this::add);
+  public void add(ProgramMethod method, GraphLens currentGraphLens) {
+    // All methods in a long lived program method set should be rewritten up until the same graph
+    // lens.
+    assert verifyIsRewrittenWithLens(currentGraphLens);
+    methods.add(method.getReference());
+  }
+
+  public void addAll(Iterable<ProgramMethod> methodsToAdd, GraphLens currentGraphLens) {
+    assert verifyIsRewrittenWithLens(currentGraphLens);
+    methodsToAdd.forEach(method -> methods.add(method.getReference()));
+  }
+
+  public void clear() {
+    methods.clear();
+  }
+
+  public boolean contains(ProgramMethod method, GraphLens currentGraphLens) {
+    // We can only query a long lived program method set that is fully lens rewritten.
+    assert verifyIsRewrittenWithLens(currentGraphLens);
+    return methods.contains(method.getReference());
+  }
+
+  public boolean isRewrittenWithLens(GraphLens graphLens) {
+    return appliedGraphLens == graphLens;
+  }
+
+  public LongLivedProgramMethodSetBuilder<T> merge(LongLivedProgramMethodSetBuilder<T> builder) {
+    // Check that the two builders are rewritten up until the same lens (if not we could rewrite the
+    // methods in the given builder up until the applied graph lens of this builder, but it must be
+    // such that this builder has the same or a newer graph lens than the given builder).
+    if (isRewrittenWithLens(builder.appliedGraphLens)) {
+      methods.addAll(builder.methods);
+    } else {
+      // Rewrite the methods in the given builder up until the applied graph lens of this builder.
+      // Note that this builder must have a newer graph lens than the given builder.
+      assert verifyIsRewrittenWithNewerLens(builder.appliedGraphLens);
+      for (DexMethod method : builder.methods) {
+        methods.add(appliedGraphLens.getRenamedMethodSignature(method, builder.appliedGraphLens));
+      }
+    }
+    return this;
   }
 
   public void remove(DexMethod method) {
     methods.remove(method);
   }
 
-  public void removeAll(Iterable<DexMethod> methods) {
+  public LongLivedProgramMethodSetBuilder<T> removeAll(Iterable<DexMethod> methods) {
     methods.forEach(this::remove);
+    return this;
   }
 
-  public void rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens applied) {
-    Set<DexMethod> newMethods = Sets.newIdentityHashSet();
-    for (DexMethod method : methods) {
-      newMethods.add(appView.graphLens().getRenamedMethodSignature(method, applied));
+  public LongLivedProgramMethodSetBuilder<T> rewrittenWithLens(
+      AppView<AppInfoWithLiveness> appView) {
+    return rewrittenWithLens(appView.graphLens());
+  }
+
+  public LongLivedProgramMethodSetBuilder<T> rewrittenWithLens(GraphLens newGraphLens) {
+    // Check if the graph lens has changed (otherwise lens rewriting is not needed).
+    if (newGraphLens == appliedGraphLens) {
+      return this;
     }
-    methods.clear();
-    methods.addAll(newMethods);
+
+    // Rewrite the backing.
+    Set<DexMethod> newMethods = factoryForBuilder.apply(methods.size());
+    for (DexMethod method : methods) {
+      newMethods.add(newGraphLens.getRenamedMethodSignature(method, appliedGraphLens));
+    }
+    methods = newMethods;
+
+    // Record that this collection is now rewritten up until the given graph lens.
+    appliedGraphLens = newGraphLens;
+    return this;
   }
 
   public T build(AppView<AppInfoWithLiveness> appView) {
-    return build(appView, null);
-  }
-
-  public T build(AppView<AppInfoWithLiveness> appView, GraphLens appliedGraphLens) {
     T result = factory.apply(methods.size());
-    for (DexMethod oldMethod : methods) {
-      DexMethod method = appView.graphLens().getRenamedMethodSignature(oldMethod, appliedGraphLens);
-      DexProgramClass holder = appView.definitionForHolder(method).asProgramClass();
-      result.createAndAdd(holder, holder.lookupMethod(method));
+    for (DexMethod method : methods) {
+      DexMethod rewrittenMethod =
+          appView.graphLens().getRenamedMethodSignature(method, appliedGraphLens);
+      DexProgramClass holder = appView.definitionForHolder(rewrittenMethod).asProgramClass();
+      result.createAndAdd(holder, holder.lookupMethod(rewrittenMethod));
     }
     return result;
   }
@@ -81,4 +144,18 @@
   public boolean isEmpty() {
     return methods.isEmpty();
   }
+
+  public boolean verifyIsRewrittenWithLens(GraphLens graphLens) {
+    assert isRewrittenWithLens(graphLens);
+    return true;
+  }
+
+  public boolean verifyIsRewrittenWithNewerLens(GraphLens graphLens) {
+    assert appliedGraphLens != graphLens;
+    assert appliedGraphLens.isNonIdentityLens();
+    assert graphLens.isIdentityLens()
+        || appliedGraphLens.asNonIdentityLens().findPrevious(previous -> previous == graphLens)
+            != null;
+    return true;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
index 0637a0b..2a8f1bb 100644
--- a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
+++ b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
@@ -1,6 +1,7 @@
 package com.android.tools.r8;
 
 import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
 import static org.hamcrest.CoreMatchers.anyOf;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.fail;
@@ -8,6 +9,7 @@
 
 import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.List;
 import org.junit.Test;
@@ -92,15 +94,10 @@
             diagnostics -> {
               diagnostics.assertErrorsMatch(
                   diagnosticMessage(containsString("java.util.concurrent.Flow$Subscriber")));
-              if (parameters.isCfRuntime()) {
-                diagnostics.assertOnlyErrors();
-              } else {
-                // TODO(b/198368663): R8 will double report missing classes in itf desugaring.
-                diagnostics.assertWarningsMatch(
-                    diagnosticMessage(containsString("java.util.concurrent.Flow$Subscriber")));
-                diagnostics.assertErrorsCount(1);
-                diagnostics.assertWarningsCount(1);
-                diagnostics.assertInfosCount(0);
+              diagnostics.assertOnlyErrors();
+              if (parameters.isDexRuntime()) {
+                // TODO(b/175659048): This should likely be a desugar diagnostic.
+                diagnostics.assertErrorsMatch(diagnosticType(MissingDefinitionsDiagnostic.class));
               }
             });
       } catch (CompilationFailedException e) {
diff --git a/src/test/java/com/android/tools/r8/JvmTestBuilder.java b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
index f92a490..718cf4d 100644
--- a/src/test/java/com/android/tools/r8/JvmTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
@@ -158,7 +158,8 @@
     return self();
   }
 
-  public JvmTestBuilder enableJaCoCoAgentForOfflineInstrumentedCode(Path jacocoAgent, Path output) {
+  public JvmTestBuilder configureJaCoCoAgentForOfflineInstrumentedCode(
+      Path jacocoAgent, Path output) {
     addProgramFiles(jacocoAgent);
     addVmArguments(
         "-Djacoco-agent.destfile=" + output.toString(),
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaDontPublicize.java b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaDontPublicize.java
new file mode 100644
index 0000000..cc9d06b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaDontPublicize.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DesugarTestConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassOrMemberSubject;
+import java.util.ArrayList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DesugarLambdaDontPublicize extends TestBase {
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  private final TestParameters parameters;
+
+  public DesugarLambdaDontPublicize(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testDesugar() throws Exception {
+    testForDesugaring(parameters)
+        .addInnerClasses(DesugarLambdaDontPublicize.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("foobar")
+        .inspectIf(
+            DesugarTestConfiguration::isDesugared,
+            inspector -> {
+              // Ensure that we don't add an extra public method for the lambda call.
+              // Mockito will potentially mock this, see: b/195402351
+              long count =
+                  inspector.clazz(TestClass.class).allMethods().stream()
+                      .filter(ClassOrMemberSubject::isPublic)
+                      .count();
+              assertEquals(count, 3);
+            });
+  }
+
+  public static class StringList extends ArrayList<String> {
+    public void forEachString(MyConsumer<String> consumer) {
+      for (String s : this) {
+        consumer.accept(s);
+      }
+    }
+  }
+
+  public interface MyConsumer<T> {
+    void accept(T s);
+  }
+
+  public static class TestClass {
+
+    public void test() {
+      StringList list = new StringList();
+
+      list.add("foobar");
+      list.forEachString(
+          s -> {
+            class MyConsumerImpl implements MyConsumer<String> {
+              public void accept(String s) {
+                System.out.println(s);
+              }
+            }
+            new MyConsumerImpl().accept(s);
+          });
+    }
+
+    public static void main(String[] args) {
+      new TestClass().test();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java
index 86fad6d..266745e 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java
@@ -81,13 +81,6 @@
         .run(parameters.getRuntime(), MAIN_CLASS)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
 
-    // Run instrumented code without an agent.
-    testForRuntime(parameters)
-        .addProgramFiles(testClasses.getInstrumented())
-        .addProgramFiles(ToolHelper.JACOCO_AGENT)
-        .run(parameters.getRuntime(), MAIN_CLASS)
-        .assertSuccessWithOutput(EXPECTED_OUTPUT);
-
     // Run non-instrumented code with an agent causing on the fly instrumentation on the JVM.
     Path output = temp.newFolder().toPath();
     Path agentOutputOnTheFly = output.resolve("on-the-fly");
@@ -99,11 +92,11 @@
     List<String> onTheFlyReport = testClasses.generateReport(agentOutputOnTheFly);
     assertEquals(2, onTheFlyReport.size());
 
-    // Run the instrumented code with offline instrumentation turned on.
+    // Run the instrumented code.
     Path agentOutputOffline = output.resolve("offline");
     testForJvm()
         .addProgramFiles(testClasses.getInstrumented())
-        .enableJaCoCoAgentForOfflineInstrumentedCode(ToolHelper.JACOCO_AGENT, agentOutputOffline)
+        .configureJaCoCoAgentForOfflineInstrumentedCode(ToolHelper.JACOCO_AGENT, agentOutputOffline)
         .run(parameters.getRuntime(), MAIN_CLASS)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
     List<String> offlineReport = testClasses.generateReport(agentOutputOffline);
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java
index 640796d..31f87d9 100644
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java
@@ -102,6 +102,7 @@
             .addKeepAttributeSourceFile()
             .addKeepRules("-renamesourcefileattribute SourceFile")
             .noTreeShaking()
+            .addDontOptimize()
             .run(parameters.getRuntime(), TestRunner.class, Boolean.toString(isDalvik))
             .assertSuccess()
             .getStdOut();
diff --git a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
index e5b4c08..afc7a62 100644
--- a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
+++ b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
@@ -17,16 +17,13 @@
 import com.android.tools.r8.code.IfNez;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexCode.Try;
 import com.android.tools.r8.graph.DexCode.TryHandler;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
@@ -155,13 +152,11 @@
     DexCode code = new DexCode(1, 0, 0, instructions, new Try[0], new TryHandler[0], null);
     MethodAccessFlags flags = MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC, false);
     DexEncodedMethod method =
-        new DexEncodedMethod(
-            null,
-            flags,
-            MethodTypeSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            ParameterAnnotationsList.empty(),
-            code);
+        DexEncodedMethod.builder()
+            .setAccessFlags(flags)
+            .setCode(code)
+            .disableMethodNotNullCheck()
+            .build();
     return new JumboStringRewriter(method, string, factory).rewrite();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java
index ddef0b4..b0be3f7 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
+import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
 import org.junit.Test;
 
 public class ArgumentInfoCollectionTest extends TestBase {
@@ -49,13 +50,22 @@
   @Test
   public void testCombineRemoved() {
     DexItemFactory factory = new DexItemFactory();
+    AbstractValueFactory abstractValueFactory = new AbstractValueFactory();
 
     // Arguments removed: 0 1 2 3 4 -> 0 2 4.
     ArgumentInfoCollection.Builder builder1 = ArgumentInfoCollection.builder();
     builder1.addArgumentInfo(
-        1, RemovedArgumentInfo.builder().setType(factory.intType).setIsAlwaysNull().build());
+        1,
+        RemovedArgumentInfo.builder()
+            .setType(factory.intType)
+            .setSingleValue(abstractValueFactory.createNullValue())
+            .build());
     builder1.addArgumentInfo(
-        3, RemovedArgumentInfo.builder().setType(factory.intType).setIsAlwaysNull().build());
+        3,
+        RemovedArgumentInfo.builder()
+            .setType(factory.intType)
+            .setSingleValue(abstractValueFactory.createNullValue())
+            .build());
     ArgumentInfoCollection arguments1 = builder1.build();
 
     // Arguments removed: 0 2 4 -> 0. Arguments 2 and 4 are at position 1 and 2 after first removal.
@@ -69,27 +79,36 @@
 
     RemovedArgumentInfo arg1 = combine.getArgumentInfo(1).asRemovedArgumentInfo();
     assertEquals(arg1.getType(), factory.intType);
-    assertTrue(arg1.isAlwaysNull());
+    assertTrue(arg1.hasSingleValue());
     RemovedArgumentInfo arg2 = combine.getArgumentInfo(2).asRemovedArgumentInfo();
     assertEquals(arg2.getType(), factory.doubleType);
-    assertFalse(arg2.isAlwaysNull());
+    assertFalse(arg2.hasSingleValue());
     RemovedArgumentInfo arg3 = combine.getArgumentInfo(3).asRemovedArgumentInfo();
     assertEquals(arg3.getType(), factory.intType);
-    assertTrue(arg3.isAlwaysNull());
+    assertTrue(arg3.hasSingleValue());
     RemovedArgumentInfo arg4 = combine.getArgumentInfo(4).asRemovedArgumentInfo();
     assertEquals(arg4.getType(), factory.doubleType);
-    assertFalse(arg4.isAlwaysNull());
+    assertFalse(arg4.hasSingleValue());
   }
 
   @Test
   public void testCombineRemoveRewritten() {
     DexItemFactory factory = new DexItemFactory();
+    AbstractValueFactory abstractValueFactory = new AbstractValueFactory();
 
     ArgumentInfoCollection.Builder builder1 = ArgumentInfoCollection.builder();
     builder1.addArgumentInfo(
-        1, RemovedArgumentInfo.builder().setType(factory.intType).setIsAlwaysNull().build());
+        1,
+        RemovedArgumentInfo.builder()
+            .setType(factory.intType)
+            .setSingleValue(abstractValueFactory.createNullValue())
+            .build());
     builder1.addArgumentInfo(
-        3, RemovedArgumentInfo.builder().setType(factory.intType).setIsAlwaysNull().build());
+        3,
+        RemovedArgumentInfo.builder()
+            .setType(factory.intType)
+            .setSingleValue(abstractValueFactory.createNullValue())
+            .build());
     ArgumentInfoCollection arguments1 = builder1.build();
 
     ArgumentInfoCollection.Builder builder2 = ArgumentInfoCollection.builder();
@@ -101,13 +120,13 @@
 
     RemovedArgumentInfo arg1 = combine.getArgumentInfo(1).asRemovedArgumentInfo();
     assertEquals(arg1.getType(), factory.intType);
-    assertTrue(arg1.isAlwaysNull());
+    assertTrue(arg1.hasSingleValue());
     RewrittenTypeInfo arg2 = combine.getArgumentInfo(2).asRewrittenTypeInfo();
     assertEquals(arg2.getOldType(), factory.floatType);
     assertEquals(arg2.getNewType(), factory.doubleType);
     RemovedArgumentInfo arg3 = combine.getArgumentInfo(3).asRemovedArgumentInfo();
     assertEquals(arg3.getType(), factory.intType);
-    assertTrue(arg3.isAlwaysNull());
+    assertTrue(arg3.hasSingleValue());
     RewrittenTypeInfo arg4 = combine.getArgumentInfo(4).asRewrittenTypeInfo();
     assertEquals(arg4.getOldType(), factory.floatType);
     assertEquals(arg4.getNewType(), factory.doubleType);
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java b/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
index 4e41e64..d8e2db4 100644
--- a/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
@@ -13,9 +13,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.GenericSignature.ClassSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.conversion.CallGraph.Node;
 import com.android.tools.r8.origin.SynthesizedOrigin;
@@ -53,13 +51,11 @@
     ProgramMethod method =
         new ProgramMethod(
             clazz,
-            new DexEncodedMethod(
-                signature,
-                MethodAccessFlags.fromDexAccessFlags(0),
-                MethodTypeSignature.noSignature(),
-                DexAnnotationSet.empty(),
-                ParameterAnnotationsList.empty(),
-                null));
+            DexEncodedMethod.builder()
+                .setMethod(signature)
+                .setAccessFlags(MethodAccessFlags.fromDexAccessFlags(0))
+                .setCode(null)
+                .build());
     return new Node(method);
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java
index 4b220b6..70d3f3b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java
@@ -5,6 +5,7 @@
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.NeverClassInline;
@@ -15,7 +16,6 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
 import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -51,16 +51,20 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(InvokeDirectPositiveTest.class)
         .addKeepMainRule(MAIN)
-        .enableNeverClassInliningAnnotations()
-        .enableInliningAnnotations()
         .addOptionsModification(
             o -> {
-              o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect;
+              if (!enableExperimentalArgumentPropagation) {
+                o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect;
+              }
               o.callSiteOptimizationOptions()
+                  .setEnableLegacyConstantPropagation()
                   .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation);
             })
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
+        .minification(!enableExperimentalArgumentPropagation)
         .setMinApi(parameters.getApiLevel())
-        .addOptionsModification(InternalOptions::enableConstantArgumentPropagationForTesting)
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutputLines("non-null")
         .inspect(this::inspect);
@@ -79,8 +83,20 @@
   private void inspect(CodeInspector inspector) {
     ClassSubject main = inspector.clazz(MAIN);
     assertThat(main, isPresent());
+
+    if (enableExperimentalArgumentPropagation) {
+      // Verify that the "nul" argument has been propagated to the test() method.
+      MethodSubject mainMethodSubject = main.mainMethod();
+      assertThat(mainMethodSubject, isPresent());
+      assertTrue(
+          mainMethodSubject.streamInstructions().noneMatch(InstructionSubject::isConstString));
+    }
+
     MethodSubject test = main.uniqueMethodWithName("test");
     assertThat(test, isPresent());
+    assertEquals(
+        1 - BooleanUtils.intValue(enableExperimentalArgumentPropagation),
+        test.getProgramMethod().getReference().getArity());
     // Can optimize branches since `arg` is definitely "nul", i.e., not containing "null".
     assertTrue(test.streamInstructions().noneMatch(InstructionSubject::isIf));
   }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
index 553ee4b..5612674 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
@@ -5,6 +5,7 @@
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.NeverClassInline;
@@ -16,7 +17,6 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
 import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -52,19 +52,23 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(InvokeInterfacePositiveTest.class)
         .addKeepMainRule(MAIN)
-        .enableNeverClassInliningAnnotations()
-        .enableInliningAnnotations()
-        .enableNoHorizontalClassMergingAnnotations()
-        .addOptionsModification(InternalOptions::enableConstantArgumentPropagationForTesting)
         .addOptionsModification(
             o -> {
               // To prevent invoke-interface from being rewritten to invoke-virtual w/ a single
               // target.
               o.enableDevirtualization = false;
-              o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect;
+              if (!enableExperimentalArgumentPropagation) {
+                o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect;
+              }
               o.callSiteOptimizationOptions()
+                  .setEnableLegacyConstantPropagation()
                   .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation);
             })
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .enableNoHorizontalClassMergingAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
+        .minification(!enableExperimentalArgumentPropagation)
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutputLines("non-null")
@@ -83,18 +87,45 @@
   }
 
   private void inspect(CodeInspector inspector) {
+    ClassSubject main = inspector.clazz(MAIN);
+    assertThat(main, isPresent());
+
+    if (enableExperimentalArgumentPropagation) {
+      // Verify that the "nul" argument has been propagated to the m() methods.
+      MethodSubject mainMethodSubject = main.mainMethod();
+      assertThat(mainMethodSubject, isPresent());
+      assertTrue(
+          mainMethodSubject.streamInstructions().noneMatch(InstructionSubject::isConstString));
+    }
+
     ClassSubject i = inspector.clazz(I.class);
     assertThat(i, isPresent());
+
+    MethodSubject i_m = i.uniqueMethodWithName("m");
+    assertThat(i_m, isPresent());
+    assertEquals(
+        1 - BooleanUtils.intValue(enableExperimentalArgumentPropagation),
+        i_m.getProgramMethod().getReference().getArity());
+
     ClassSubject a = inspector.clazz(A.class);
     assertThat(a, isPresent());
+
     MethodSubject a_m = a.uniqueMethodWithName("m");
     assertThat(a_m, isPresent());
+    assertEquals(
+        1 - BooleanUtils.intValue(enableExperimentalArgumentPropagation),
+        a_m.getProgramMethod().getReference().getArity());
     // Can optimize branches since `arg` is definitely "nul", i.e., not containing "null".
     assertTrue(a_m.streamInstructions().noneMatch(InstructionSubject::isIf));
+
     ClassSubject b = inspector.clazz(B.class);
     assertThat(b, isPresent());
+
     MethodSubject b_m = b.uniqueMethodWithName("m");
     assertThat(b_m, isPresent());
+    assertEquals(
+        1 - BooleanUtils.intValue(enableExperimentalArgumentPropagation),
+        b_m.getProgramMethod().getReference().getArity());
     // Can optimize branches since `arg` is definitely "nul", i.e., not containing "null".
     assertTrue(b_m.streamInstructions().noneMatch(InstructionSubject::isIf));
   }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java
index fe38d71..3f9baaa 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java
@@ -5,6 +5,7 @@
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.NeverInline;
@@ -14,7 +15,6 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
 import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -50,15 +50,19 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(InvokeStaticPositiveTest.class)
         .addKeepMainRule(MAIN)
-        .enableInliningAnnotations()
         .addOptionsModification(
             o -> {
-              o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect;
+              if (!enableExperimentalArgumentPropagation) {
+                o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect;
+              }
               o.callSiteOptimizationOptions()
+                  .setEnableLegacyConstantPropagation()
                   .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation);
             })
+        .enableInliningAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
+        .minification(!enableExperimentalArgumentPropagation)
         .setMinApi(parameters.getApiLevel())
-        .addOptionsModification(InternalOptions::enableConstantArgumentPropagationForTesting)
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutputLines("non-null")
         .inspect(this::inspect);
@@ -78,8 +82,20 @@
   private void inspect(CodeInspector inspector) {
     ClassSubject main = inspector.clazz(MAIN);
     assertThat(main, isPresent());
+
+    if (enableExperimentalArgumentPropagation) {
+      // Verify that the "nul" argument has been propagated to the test() method.
+      MethodSubject mainMethodSubject = main.mainMethod();
+      assertThat(mainMethodSubject, isPresent());
+      assertTrue(
+          mainMethodSubject.streamInstructions().noneMatch(InstructionSubject::isConstString));
+    }
+
     MethodSubject test = main.uniqueMethodWithName("test");
     assertThat(test, isPresent());
+    assertEquals(
+        1 - BooleanUtils.intValue(enableExperimentalArgumentPropagation),
+        test.getProgramMethod().getReference().getArity());
     // Can optimize branches since `arg` is definitely "nul", i.e., not containing "null".
     assertTrue(test.streamInstructions().noneMatch(InstructionSubject::isIf));
   }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
index 9853a4c..f3b3324 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
@@ -16,7 +16,6 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
 import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -51,17 +50,17 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(InvokeVirtualPositiveTest.class)
         .addKeepMainRule(MAIN)
-        .enableNoVerticalClassMergingAnnotations()
-        .enableNeverClassInliningAnnotations()
-        .enableInliningAnnotations()
         .addOptionsModification(
             o -> {
               o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect;
               o.callSiteOptimizationOptions()
+                  .setEnableLegacyConstantPropagation()
                   .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation);
             })
+        .enableNoVerticalClassMergingAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .enableInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
-        .addOptionsModification(InternalOptions::enableConstantArgumentPropagationForTesting)
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutputLines("non-null", "null")
         .inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningOptimize.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningOptimize.java
new file mode 100644
index 0000000..0c0bac6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningOptimize.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.inliner;
+
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import org.junit.Test;
+
+class Foobar {
+  public static void main(String[] args) {
+    System.out.println("Value: " + (new Bar()).returnPlusConstant(42));
+  }
+}
+
+class Bar {
+  public int returnPlusConstant(int value) {
+    return value + 42;
+  }
+}
+
+public class InliningOptimize extends TestBase {
+
+  @Test
+  public void test() throws Exception {
+    testForR8(Backend.DEX)
+        .addProgramClasses(Bar.class, Foobar.class)
+        .addKeepRules("-keep,allowoptimization class ** {\n" + "*;\n" + "}")
+        .compile()
+        .inspect(
+            inspector -> {
+              inspector
+                  .clazz(Foobar.class)
+                  .mainMethod()
+                  .iterateInstructions(InstructionSubject::isInvoke)
+                  .forEachRemaining(
+                      invoke -> {
+                        assertFalse(
+                            invoke.getMethod().name.toString().contains("returnPlusConstant"));
+                      });
+            })
+        .run(Foobar.class)
+        .assertSuccessWithOutputLines("Value: 84");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 0005549..c6182b5 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -47,11 +47,9 @@
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
 import com.android.tools.r8.graph.GenericSignature.ClassSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.CatchHandlers;
 import com.android.tools.r8.ir.code.IRCode;
@@ -856,13 +854,11 @@
             new SynthesizedCode(
                 (ignored, callerPosition) -> new ReturnVoidCode(voidReturnMethod, callerPosition));
         DexEncodedMethod method =
-            new DexEncodedMethod(
-                voidReturnMethod,
-                access,
-                MethodTypeSignature.noSignature(),
-                DexAnnotationSet.empty(),
-                ParameterAnnotationsList.empty(),
-                code);
+            DexEncodedMethod.builder()
+                .setMethod(voidReturnMethod)
+                .setAccessFlags(access)
+                .setCode(code)
+                .build();
         ProgramMethod programMethod = new ProgramMethod(programClass, method);
         IRCode ir = code.buildIR(programMethod, appView, Origin.unknown());
         RegisterAllocator allocator = new LinearScanRegisterAllocator(appView, ir);
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index bcea2fe..42cd647 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -391,6 +391,7 @@
         .setMinApi(minSdk)
         .noMinification()
         .noTreeShaking()
+        .addDontOptimize()
         .setMainDexListConsumer(ToolHelper.consumeString(r8MainDexListOutput::set))
         .allowDiagnosticMessages()
         .compileWithExpectedDiagnostics(
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ConstantUnboxedEnumArgumentTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ConstantUnboxedEnumArgumentTest.java
new file mode 100644
index 0000000..7b7589b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ConstantUnboxedEnumArgumentTest.java
@@ -0,0 +1,94 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ConstantUnboxedEnumArgumentTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection parameters() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options ->
+                options
+                    .callSiteOptimizationOptions()
+                    .setEnableExperimentalArgumentPropagation(true))
+        .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+        .enableInliningAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
+        .noMinification()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject mainClassSubject = inspector.clazz(Main.class);
+              assertThat(mainClassSubject, isPresent());
+
+              MethodSubject mainMethodSubject = mainClassSubject.mainMethod();
+              assertThat(mainMethodSubject, isPresent());
+              assertTrue(
+                  mainMethodSubject
+                      .streamInstructions()
+                      .noneMatch(InstructionSubject::isStaticGet));
+
+              MethodSubject testMethodSubject = mainClassSubject.uniqueMethodWithName("test");
+              assertThat(testMethodSubject, isPresent());
+              assertEquals(0, testMethodSubject.getProgramMethod().getReference().getArity());
+              assertTrue(
+                  testMethodSubject
+                      .streamInstructions()
+                      .anyMatch(instruction -> instruction.isConstString("A")));
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("A");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      // Argument should be removed by argument propagation.
+      test(MyEnum.A);
+    }
+
+    @NeverInline
+    static void test(MyEnum myEnum) {
+      // Argument propagation should remove the parameter `myEnum` and materialize an access to
+      // `MyEnum.A`, which should subsequently be rewritten by enum unboxing.
+      System.out.println(myEnum.name());
+    }
+  }
+
+  enum MyEnum {
+    A
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentTest.java
index 9b246e1..e1c2d9b 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentTest.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.InternalOptions.CallSiteOptimizationOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -41,13 +40,13 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> {
-              CallSiteOptimizationOptions callSiteOptimizationOptions =
-                  options.callSiteOptimizationOptions();
-              callSiteOptimizationOptions.setEnableExperimentalArgumentPropagation(true);
-              callSiteOptimizationOptions.setEnableConstantPropagation();
-            })
+            options ->
+                options
+                    .callSiteOptimizationOptions()
+                    .setEnableExperimentalArgumentPropagation(true))
         .enableInliningAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
+        .noMinification()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
@@ -58,8 +57,7 @@
               // The test() method has been optimized.
               MethodSubject testMethodSubject = mainClassSubject.uniqueMethodWithName("test");
               assertThat(testMethodSubject, isPresent());
-              // TODO(b/190154391): The parameter x should be removed.
-              assertEquals(1, testMethodSubject.getProgramMethod().getParameters().size());
+              assertEquals(0, testMethodSubject.getProgramMethod().getParameters().size());
               assertTrue(
                   testMethodSubject.streamInstructions().noneMatch(InstructionSubject::isIf));
 
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentThroughCallChainTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentThroughCallChainTest.java
index 45cd552..ae1d888 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentThroughCallChainTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentThroughCallChainTest.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.InternalOptions.CallSiteOptimizationOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -41,13 +40,13 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> {
-              CallSiteOptimizationOptions callSiteOptimizationOptions =
-                  options.callSiteOptimizationOptions();
-              callSiteOptimizationOptions.setEnableExperimentalArgumentPropagation(true);
-              callSiteOptimizationOptions.setEnableConstantPropagation();
-            })
+            options ->
+                options
+                    .callSiteOptimizationOptions()
+                    .setEnableExperimentalArgumentPropagation(true))
         .enableInliningAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
+        .noMinification()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
@@ -59,8 +58,7 @@
               for (int i = 1; i <= 3; i++) {
                 MethodSubject testMethodSubject = mainClassSubject.uniqueMethodWithName("test" + i);
                 assertThat(testMethodSubject, isPresent());
-                // TODO(b/190154391): The parameter x should be removed.
-                assertEquals(1, testMethodSubject.getProgramMethod().getParameters().size());
+                assertEquals(0, testMethodSubject.getProgramMethod().getParameters().size());
                 assertTrue(
                     testMethodSubject.streamInstructions().noneMatch(InstructionSubject::isIf));
               }
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/UnboxedEnumUserInMethodWithConstantArgumentTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/UnboxedEnumUserInMethodWithConstantArgumentTest.java
new file mode 100644
index 0000000..e5f64cf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/UnboxedEnumUserInMethodWithConstantArgumentTest.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class UnboxedEnumUserInMethodWithConstantArgumentTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection parameters() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options ->
+                options
+                    .callSiteOptimizationOptions()
+                    .setEnableExperimentalArgumentPropagation(true))
+        .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+        .enableInliningAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
+        .noMinification()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject mainClassSubject = inspector.clazz(Main.class);
+              assertThat(mainClassSubject, isPresent());
+
+              MethodSubject mainMethodSubject = mainClassSubject.mainMethod();
+              assertThat(mainMethodSubject, isPresent());
+              assertTrue(
+                  mainMethodSubject
+                      .streamInstructions()
+                      .noneMatch(InstructionSubject::isConstNumber));
+
+              MethodSubject testMethodSubject = mainClassSubject.uniqueMethodWithName("test");
+              assertThat(testMethodSubject, isPresent());
+              assertEquals(0, testMethodSubject.getProgramMethod().getReference().getArity());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("A");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      test(true);
+    }
+
+    @NeverInline
+    static void test(boolean alwaysTrue) {
+      if (alwaysTrue) {
+        // Failure to reprocess this method after the unboxing of MyEnum will lead to runtime
+        // errors.
+        MyEnum myEnum = System.currentTimeMillis() > 0 ? MyEnum.A : MyEnum.B;
+        System.out.println(myEnum.name());
+      }
+    }
+  }
+
+  enum MyEnum {
+    A,
+    B
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java b/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
index 252dcc5..d68447d 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
@@ -105,6 +105,7 @@
         .addKeepMainRule(Main.class)
         .addKeepPackageNamesRule(getClass().getPackage())
         .noTreeShaking()
+        .addDontOptimize()
         .addKeepAttributeSourceFile()
         .addKeepAttributeLineNumberTable()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java
index df7c599..6d9c328 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java
@@ -63,8 +63,10 @@
       assertNotEquals(0, existing.size());
       for (Path classFile : generated) {
         Path otherClassFile = binaryContents.resolve(generatedContents.relativize(classFile));
-        assertTrue(Files.exists(otherClassFile));
-        assertTrue(TestBase.filesAreEqual(classFile, otherClassFile));
+        assertTrue("Could not find file: " + otherClassFile, Files.exists(otherClassFile));
+        assertTrue(
+            "Non-equal files: " + otherClassFile,
+            TestBase.filesAreEqual(classFile, otherClassFile));
       }
     }
   }
@@ -75,10 +77,12 @@
 
   @Test
   public void runCheckedInBinaryJar() throws Exception {
+    // The retrace jar is only built when building r8lib.
+    Path jar = ToolHelper.isTestingR8Lib() ? ToolHelper.R8_RETRACE_JAR : ToolHelper.R8_JAR;
     for (CfRuntime cfRuntime : CfRuntime.getCheckedInCfRuntimes()) {
       RetraceApiTestHelper.runJunitOnTests(
           cfRuntime,
-          ToolHelper.R8_RETRACE_JAR,
+          jar,
           BINARY_COMPATIBILITY_JAR,
           RetraceApiTestHelper.getBinaryCompatibilityTests());
     }
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java
index 9b2f67d..7f9834b 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java
@@ -63,13 +63,13 @@
             classPaths,
             "org.junit.runner.JUnitCore",
             StringUtils.join(" ", tests, Class::getTypeName));
-    assertEquals(0, processResult.exitCode);
+    assertEquals(processResult.toString(), 0, processResult.exitCode);
     assertThat(processResult.stdout, containsString("OK (" + tests.size() + " test"));
   }
 
   private static Path getJunitDependency() {
     String junitPath =
-        Arrays.stream(System.getProperty("java.class.path").split(":"))
+        Arrays.stream(System.getProperty("java.class.path").split(File.pathSeparator))
             .filter(cp -> cp.endsWith(JUNIT_JAR))
             .collect(Collectors.toSingle());
     return Paths.get(junitPath);
@@ -77,7 +77,7 @@
 
   private static Path getHamcrest() {
     String junitPath =
-        Arrays.stream(System.getProperty("java.class.path").split(":"))
+        Arrays.stream(System.getProperty("java.class.path").split(File.pathSeparator))
             .filter(cp -> cp.endsWith(HAMCREST))
             .collect(Collectors.toSingle());
     return Paths.get(junitPath);
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index 21501d0..c9d30fd 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -85,6 +85,8 @@
     return options -> {
       // Disable inlining to make sure that code looks as expected.
       options.enableInlining = false;
+      // Disable the devirtulizer to not remove the super calls
+      options.enableDevirtualization = false;
       // Disable string concatenation optimization to not bother outlining of StringBuilder usage.
       options.enableStringConcatenationOptimization = false;
       // Also apply outline options.
diff --git a/tools/r8_release.py b/tools/r8_release.py
index bf98dd3..0446d64 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -15,7 +15,7 @@
 
 import utils
 
-R8_DEV_BRANCH = '3.1'
+R8_DEV_BRANCH = '3.2'
 R8_VERSION_FILE = os.path.join(
     'src', 'main', 'java', 'com', 'android', 'tools', 'r8', 'Version.java')
 THIS_FILE_RELATIVE = os.path.join('tools', 'r8_release.py')