Implement nest based access desugaring as cf-to-cf

This CL also changes the interface method rewriter to throw a NoSuchMethodError for static invokes to interface methods that do not resolve (see also MissingMethodTest).

Change-Id: Ia8862049d4e48dbc7645d4ef2c32f22a901911a9
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 5774e10..bf85148 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -97,7 +97,7 @@
 
     Enqueuer enqueuer =
         EnqueuerFactory.createForFinalMainDexTracing(
-            appView, subtypingInfo, graphConsumer, MainDexTracingResult.NONE);
+            appView, executor, subtypingInfo, graphConsumer, MainDexTracingResult.NONE);
     Set<DexProgramClass> liveTypes = enqueuer.traceMainDex(mainDexRootSet, executor, timing);
     // LiveTypes is the result.
     MainDexTracingResult mainDexTracingResult = new MainDexListBuilder(liveTypes, appView).run();
diff --git a/src/main/java/com/android/tools/r8/PrintSeeds.java b/src/main/java/com/android/tools/r8/PrintSeeds.java
index 212eb32..7f11da5 100644
--- a/src/main/java/com/android/tools/r8/PrintSeeds.java
+++ b/src/main/java/com/android/tools/r8/PrintSeeds.java
@@ -92,7 +92,8 @@
       RootSet rootSet =
           new RootSetBuilder(appView, subtypingInfo, options.getProguardConfiguration().getRules())
               .run(executor);
-      Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo);
+      Enqueuer enqueuer =
+          EnqueuerFactory.createForInitialTreeShaking(appView, executor, subtypingInfo);
       AppInfoWithLiveness appInfo =
           enqueuer.traceApplication(
               rootSet, options.getProguardConfiguration().getDontWarnPatterns(), executor, timing);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index a75f48f..0a5ef6e 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -31,7 +31,6 @@
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
-import com.android.tools.r8.graph.DirectMappedDexApplication.Builder;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
 import com.android.tools.r8.graph.InitClassLens;
@@ -49,8 +48,6 @@
 import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
 import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
-import com.android.tools.r8.ir.desugar.NestedPrivateMethodLens;
-import com.android.tools.r8.ir.desugar.R8NestBasedAccessDesugaring;
 import com.android.tools.r8.ir.optimize.AssertionsRewriter;
 import com.android.tools.r8.ir.optimize.MethodPoolCollection;
 import com.android.tools.r8.ir.optimize.NestReducer;
@@ -420,6 +417,8 @@
       }
 
       assert appView.appInfo().hasLiveness();
+      AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+
       assert verifyNoJarApplicationReaders(appView.appInfo().classes());
       // Build conservative main dex content after first round of tree shaking. This is used
       // by certain optimizations to avoid introducing additional class references into main dex
@@ -435,7 +434,7 @@
                 .run(executorService);
         // Live types is the tracing result.
         Set<DexProgramClass> mainDexBaseClasses =
-            EnqueuerFactory.createForInitialMainDexTracing(appView, subtypingInfo)
+            EnqueuerFactory.createForInitialMainDexTracing(appView, executorService, subtypingInfo)
                 .traceMainDex(mainDexRootSet, executorService, timing);
         // Calculate the automatic main dex list according to legacy multidex constraints.
         mainDexTracingResult = new MainDexListBuilder(mainDexBaseClasses, appView).run();
@@ -448,7 +447,6 @@
       appView.dexItemFactory().clearTypeElementsCache();
 
       if (options.getProguardConfiguration().isAccessModificationAllowed()) {
-        AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
         SubtypingInfo subtypingInfo = appViewWithLiveness.appInfo().computeSubtypingInfo();
         GraphLens publicizedLens =
             ClassAndMemberPublicizer.run(
@@ -466,29 +464,13 @@
         }
       }
 
-      AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+      // This pass attempts to reduce the number of nests and nest size to allow further passes, and
+      // should therefore be run after the publicizer.
+      new NestReducer(appViewWithLiveness).run(executorService, timing);
+
       appView.setGraphLens(new MemberRebindingAnalysis(appViewWithLiveness).run(executorService));
       appView.appInfo().withLiveness().getFieldAccessInfoCollection().restrictToProgram(appView);
 
-      if (options.shouldDesugarNests()) {
-        timing.begin("NestBasedAccessDesugaring");
-        R8NestBasedAccessDesugaring analyzer = new R8NestBasedAccessDesugaring(appViewWithLiveness);
-        Builder appBuilder = getDirectApp(appView).builder();
-        NestedPrivateMethodLens lens = analyzer.run(executorService, appBuilder);
-        if (lens != null) {
-          appView.rewriteWithLensAndApplication(lens, appBuilder.build());
-        }
-        timing.end();
-      } else {
-        timing.begin("NestReduction");
-        // This pass attempts to reduce the number of nests and nest size
-        // to allow further passes, specifically the class mergers, to do
-        // a better job. This pass is better run before the class merger
-        // but after the publicizer (cannot be run as part of the Enqueuer).
-        new NestReducer(appViewWithLiveness).run(executorService);
-        timing.end();
-      }
-
       boolean isKotlinLibraryCompilationWithInlinePassThrough =
           options.enableCfByteCodePassThrough && appView.hasCfByteCodePassThroughMethods();
 
@@ -646,6 +628,7 @@
         Enqueuer enqueuer =
             EnqueuerFactory.createForFinalMainDexTracing(
                 appView,
+                executorService,
                 new SubtypingInfo(appView),
                 mainDexKeptGraphConsumer,
                 mainDexTracingResult);
@@ -697,6 +680,7 @@
           Enqueuer enqueuer =
               EnqueuerFactory.createForFinalTreeShaking(
                   appView,
+                  executorService,
                   new SubtypingInfo(appView),
                   keptGraphConsumer,
                   prunedTypes);
@@ -1014,7 +998,8 @@
       SubtypingInfo subtypingInfo,
       RuntimeTypeCheckInfo.Builder classMergingEnqueuerExtensionBuilder)
       throws ExecutionException {
-    Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo);
+    Enqueuer enqueuer =
+        EnqueuerFactory.createForInitialTreeShaking(appView, executorService, subtypingInfo);
     enqueuer.setAnnotationRemoverBuilder(annotationRemoverBuilder);
     if (appView.options().enableInitializedClassesInInstanceMethodsAnalysis) {
       enqueuer.registerAnalysis(new InitializedClassesInInstanceMethodsAnalysis(appView));
@@ -1084,12 +1069,16 @@
       if (forMainDex) {
         enqueuer =
             EnqueuerFactory.createForFinalMainDexTracing(
-                appView, subtypingInfo, whyAreYouKeepingConsumer, MainDexTracingResult.NONE);
+                appView,
+                executorService,
+                subtypingInfo,
+                whyAreYouKeepingConsumer,
+                MainDexTracingResult.NONE);
         enqueuer.traceMainDex(rootSet, executorService, timing);
       } else {
         enqueuer =
             EnqueuerFactory.createForWhyAreYouKeeping(
-                appView, subtypingInfo, whyAreYouKeepingConsumer);
+                appView, executorService, subtypingInfo, whyAreYouKeepingConsumer);
         enqueuer.traceApplication(
             rootSet,
             options.getProguardConfiguration().getDontWarnPatterns(),
diff --git a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
index b4b0d5a..d56b873 100644
--- a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
@@ -452,9 +452,9 @@
 
   @Override
   public void print(CfFrame frame) {
-    String keys = join(frame.getLocals().keySet(), ",", BraceType.NONE);
-    String values = join(frame.getLocals().values(), ",", BraceType.NONE, this::frameTypeType);
-    String stack = join(frame.getStack(), ",", BraceType.NONE, this::frameTypeType);
+    String keys = join(frame.getLocals().keySet(), ",");
+    String values = join(",", frame.getLocals().values(), this::frameTypeType);
+    String stack = join(",", frame.getStack(), this::frameTypeType);
     printNewInstruction(
         "CfFrame",
         "new "
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index 6176b49..be6176b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -65,6 +65,10 @@
     return visitor.visit(this, other.asFieldInstruction(), CfFieldInstruction::specify);
   }
 
+  public boolean isFieldGet() {
+    return opcode == Opcodes.GETFIELD || opcode == Opcodes.GETSTATIC;
+  }
+
   @Override
   public CfFieldInstruction asFieldInstruction() {
     return this;
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 6c7f76f..00d4c93 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -170,6 +170,10 @@
         !method.name.toString().equals(Constants.INSTANCE_INITIALIZER_NAME);
   }
 
+  public boolean isInvokeStatic() {
+    return opcode == Opcodes.INVOKESTATIC;
+  }
+
   public boolean isInvokeVirtual() {
     return opcode == Opcodes.INVOKEVIRTUAL;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/ClasspathField.java b/src/main/java/com/android/tools/r8/graph/ClasspathField.java
new file mode 100644
index 0000000..b40e51c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ClasspathField.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2020, 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.graph;
+
+public class ClasspathField extends DexClassAndField {
+
+  public ClasspathField(DexClasspathClass holder, DexEncodedField field) {
+    super(holder, field);
+  }
+
+  @Override
+  public boolean isClasspathField() {
+    return true;
+  }
+
+  @Override
+  public ClasspathField asClasspathField() {
+    return this;
+  }
+
+  @Override
+  public boolean isClasspathMember() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ClasspathMethod.java b/src/main/java/com/android/tools/r8/graph/ClasspathMethod.java
index 9d2a941..0c58053 100644
--- a/src/main/java/com/android/tools/r8/graph/ClasspathMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ClasspathMethod.java
@@ -23,6 +23,11 @@
   }
 
   @Override
+  public boolean isClasspathMember() {
+    return true;
+  }
+
+  @Override
   public boolean isClasspathMethod() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index 6e38ed4..219b723 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -18,6 +18,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 public abstract class DexApplication {
@@ -109,7 +110,7 @@
     // that.
     if (options.testing.deterministicSortingBasedOnDexType) {
       // To keep the order deterministic, we sort the classes by their type, which is a unique key.
-      classes.sort((a, b) -> a.type.compareTo(b.type));
+      classes.sort(Comparator.comparing(DexClass::getType));
     }
     return classes;
   }
@@ -133,7 +134,7 @@
     // new or removing existing classes), classpath and library
     // collections will be considered monolithic collections.
 
-    final List<DexProgramClass> programClasses = new ArrayList<>();
+    List<DexProgramClass> programClasses = new ArrayList<>();
 
     final List<DataResourceProvider> dataResourceProviders = new ArrayList<>();
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 22a830e..f1c0a2f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -122,6 +122,11 @@
     }
   }
 
+  public abstract void accept(
+      Consumer<DexProgramClass> programClassConsumer,
+      Consumer<DexClasspathClass> classpathClassConsumer,
+      Consumer<DexLibraryClass> libraryClassConsumer);
+
   public void forEachClassMethod(Consumer<? super DexClassAndMethod> consumer) {
     forEachClassMethodMatching(alwaysTrue(), consumer);
   }
@@ -163,6 +168,10 @@
     return Iterables.concat(fields(), methods());
   }
 
+  public Iterable<DexEncodedMember<?, ?>> members(Predicate<DexEncodedMember<?, ?>> predicate) {
+    return Iterables.concat(fields(predicate), methods(predicate));
+  }
+
   public MethodCollection getMethodCollection() {
     return methodCollection;
   }
@@ -171,7 +180,7 @@
     return methodCollection.methods();
   }
 
-  public Iterable<DexEncodedMethod> methods(Predicate<DexEncodedMethod> predicate) {
+  public Iterable<DexEncodedMethod> methods(Predicate<? super DexEncodedMethod> predicate) {
     return methodCollection.methods(predicate);
   }
 
@@ -445,6 +454,15 @@
     return field;
   }
 
+  /** Find method in this class matching {@param method}. */
+  public DexClassAndField lookupClassField(DexField field) {
+    return toClassFieldOrNull(lookupField(field));
+  }
+
+  private DexClassAndField toClassFieldOrNull(DexEncodedField field) {
+    return field != null ? DexClassAndField.create(this, field) : null;
+  }
+
   /** Find field in this class matching {@param field}. */
   public DexEncodedField lookupField(DexField field) {
     DexEncodedField result = lookupInstanceField(field);
@@ -848,6 +866,10 @@
     nestHost = null;
   }
 
+  public void clearNestMembers() {
+    nestMembers.clear();
+  }
+
   public void setNestHost(DexType type) {
     assert type != null;
     this.nestHost = new NestHostClassAttribute(type);
@@ -887,6 +909,10 @@
   /** Returns kotlin class info if the class is synthesized by kotlin compiler. */
   public abstract KotlinClassLevelInfo getKotlinInfo();
 
+  public final String getTypeName() {
+    return getType().getTypeName();
+  }
+
   public boolean hasStaticFields() {
     return staticFields.length > 0;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
index bba0f82..cef86d0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
@@ -8,15 +8,20 @@
 
   DexClassAndField(DexClass holder, DexEncodedField field) {
     super(holder, field);
+    assert holder.isClasspathClass() == (this instanceof ClasspathField);
+    assert holder.isLibraryClass() == (this instanceof LibraryField);
     assert holder.isProgramClass() == (this instanceof ProgramField);
   }
 
   public static DexClassAndField create(DexClass holder, DexEncodedField field) {
     if (holder.isProgramClass()) {
       return new ProgramField(holder.asProgramClass(), field);
-    } else {
-      return new DexClassAndField(holder, field);
     }
+    if (holder.isLibraryClass()) {
+      return new LibraryField(holder.asLibraryClass(), field);
+    }
+    assert holder.isClasspathClass();
+    return new ClasspathField(holder.asClasspathClass(), field);
   }
 
   @Override
@@ -28,6 +33,22 @@
     return getReference().getType();
   }
 
+  public boolean isClasspathField() {
+    return false;
+  }
+
+  public ClasspathField asClasspathField() {
+    return null;
+  }
+
+  public boolean isLibraryField() {
+    return false;
+  }
+
+  public LibraryField asLibraryField() {
+    return null;
+  }
+
   public boolean isProgramField() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
index 719e2d6..83c4334 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
@@ -51,6 +51,18 @@
     return holder.origin;
   }
 
+  public boolean isClasspathMember() {
+    return false;
+  }
+
+  public boolean isLibraryMember() {
+    return false;
+  }
+
+  public boolean isProgramMember() {
+    return false;
+  }
+
   public String toSourceString() {
     return getReference().toSourceString();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
index 0446e09..075c1ba 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -9,6 +9,8 @@
 
   DexClassAndMethod(DexClass holder, DexEncodedMethod method) {
     super(holder, method);
+    assert holder.isClasspathClass() == (this instanceof ClasspathMethod);
+    assert holder.isLibraryClass() == (this instanceof LibraryMethod);
     assert holder.isProgramClass() == (this instanceof ProgramMethod);
   }
 
@@ -19,12 +21,12 @@
   public static DexClassAndMethod create(DexClass holder, DexEncodedMethod method) {
     if (holder.isProgramClass()) {
       return new ProgramMethod(holder.asProgramClass(), method);
-    } else if (holder.isLibraryClass()) {
-      return new LibraryMethod(holder.asLibraryClass(), method);
-    } else {
-      assert holder.isClasspathClass();
-      return new ClasspathMethod(holder.asClasspathClass(), method);
     }
+    if (holder.isLibraryClass()) {
+      return new LibraryMethod(holder.asLibraryClass(), method);
+    }
+    assert holder.isClasspathClass();
+    return new ClasspathMethod(holder.asClasspathClass(), method);
   }
 
   public boolean isDefaultMethod() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
index c97a1a4..dc3ff8d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import static com.google.common.base.Predicates.alwaysTrue;
+
 import com.android.tools.r8.ProgramResource;
 import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.dex.MixedSectionCollection;
@@ -12,6 +14,7 @@
 import com.android.tools.r8.origin.Origin;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 
@@ -58,6 +61,24 @@
   }
 
   @Override
+  public void accept(
+      Consumer<DexProgramClass> programClassConsumer,
+      Consumer<DexClasspathClass> classpathClassConsumer,
+      Consumer<DexLibraryClass> libraryClassConsumer) {
+    classpathClassConsumer.accept(this);
+  }
+
+  public void forEachClasspathMethod(Consumer<? super ClasspathMethod> consumer) {
+    forEachClasspathMethodMatching(alwaysTrue(), consumer);
+  }
+
+  public void forEachClasspathMethodMatching(
+      Predicate<DexEncodedMethod> predicate, Consumer<? super ClasspathMethod> consumer) {
+    methodCollection.forEachMethodMatching(
+        predicate, method -> consumer.accept(new ClasspathMethod(this, method)));
+  }
+
+  @Override
   public String toString() {
     return type.toString() + "(classpath class)";
   }
@@ -78,6 +99,10 @@
     return this;
   }
 
+  public static DexClasspathClass asClasspathClassOrNull(DexClass clazz) {
+    return clazz != null ? clazz.asClasspathClass() : null;
+  }
+
   @Override
   public boolean isNotProgramClass() {
     return true;
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 fc0eeb0..30d5cfd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -203,10 +203,6 @@
     return accessFlags.isPackagePrivate();
   }
 
-  public boolean isPrivate() {
-    return accessFlags.isPrivate();
-  }
-
   public boolean isProtected() {
     return accessFlags.isProtected();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
index bca8a6b..d5c5135 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
@@ -35,6 +35,10 @@
     return this;
   }
 
+  public final boolean isPrivate() {
+    return getAccessFlags().isPrivate();
+  }
+
   public abstract ProgramMember<D, R> asProgramMember(DexDefinitionSupplier definitions);
 
   @Override
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 2236b6c..1df9806 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -49,7 +49,6 @@
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.DexBuilder;
-import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring.DexFieldWithAccess;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
 import com.android.tools.r8.ir.optimize.NestUtils;
@@ -60,7 +59,8 @@
 import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
 import com.android.tools.r8.ir.synthetic.EmulateInterfaceSyntheticCfCodeProvider;
-import com.android.tools.r8.ir.synthetic.FieldAccessorSourceCode;
+import com.android.tools.r8.ir.synthetic.FieldAccessorBuilder;
+import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
 import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
 import com.android.tools.r8.kotlin.KotlinMethodLevelInfo;
@@ -497,10 +497,6 @@
     return accessFlags.isNative();
   }
 
-  public boolean isPrivate() {
-    return accessFlags.isPrivate();
-  }
-
   public boolean isPublic() {
     return accessFlags.isPublic();
   }
@@ -1192,59 +1188,47 @@
     return builder.build();
   }
 
-  public ProgramMethod toInitializerForwardingBridge(DexProgramClass holder, DexMethod newMethod) {
-    assert accessFlags.isPrivate()
+  public ProgramMethod toInitializerForwardingBridge(
+      DexProgramClass holder, DexMethod newMethod, DexItemFactory dexItemFactory) {
+    assert isPrivate()
         : "Expected to create bridge for private constructor as part of nest-based access"
             + " desugaring";
-    Builder builder = syntheticBuilder(this);
-    builder.setMethod(newMethod);
-    ForwardMethodSourceCode.Builder forwardSourceCodeBuilder =
-        ForwardMethodSourceCode.builder(newMethod);
-    forwardSourceCodeBuilder
-        .setReceiver(holder.type)
-        .setTargetReceiver(holder.type)
-        .setTarget(method)
-        .setInvokeType(Invoke.Type.DIRECT)
-        .setExtraNullParameter();
-    builder.setCode(
-        new SynthesizedCode(
-            forwardSourceCodeBuilder::build, registry -> registry.registerInvokeDirect(method)));
-    assert !builder.accessFlags.isStatic();
     assert !holder.isInterface();
-    builder.accessFlags.unsetPrivate();
-    builder.accessFlags.setSynthetic();
-    builder.accessFlags.setConstructor();
-    return new ProgramMethod(holder, builder.build());
+    return new ProgramMethod(
+        holder,
+        syntheticBuilder(this)
+            .setMethod(newMethod)
+            .setCode(
+                ForwardMethodBuilder.builder(dexItemFactory)
+                    .setNonStaticSourceWithExtraUnusedParameter(newMethod)
+                    .setConstructorTarget(getReference())
+                    .build())
+            .modifyAccessFlags(
+                accessFlags -> {
+                  assert !accessFlags.isStatic();
+                  accessFlags.unsetPrivate();
+                  accessFlags.setSynthetic();
+                  accessFlags.setConstructor();
+                })
+            .build());
   }
 
   public static ProgramMethod createFieldAccessorBridge(
-      DexFieldWithAccess fieldWithAccess, DexProgramClass holder, DexMethod newMethod) {
-    assert holder.type == fieldWithAccess.getHolder();
+      ProgramField field, boolean isGet, DexMethod newMethod) {
     MethodAccessFlags accessFlags =
         MethodAccessFlags.fromSharedAccessFlags(
             Constants.ACC_SYNTHETIC
                 | Constants.ACC_STATIC
-                | (holder.isInterface() ? Constants.ACC_PUBLIC : 0),
+                | (field.getHolder().isInterface() ? Constants.ACC_PUBLIC : 0),
             false);
-    Code code =
-        new SynthesizedCode(
-            callerPosition ->
-                new FieldAccessorSourceCode(
-                    null, newMethod, callerPosition, newMethod, fieldWithAccess),
-            registry -> {
-              if (fieldWithAccess.isInstanceGet()) {
-                registry.registerInstanceFieldRead(fieldWithAccess.getField());
-              } else if (fieldWithAccess.isStaticGet()) {
-                registry.registerStaticFieldRead(fieldWithAccess.getField());
-              } else if (fieldWithAccess.isInstancePut()) {
-                registry.registerInstanceFieldWrite(fieldWithAccess.getField());
-              } else {
-                assert fieldWithAccess.isStaticPut();
-                registry.registerStaticFieldWrite(fieldWithAccess.getField());
-              }
-            });
+    CfCode code =
+        FieldAccessorBuilder.builder()
+            .apply(isGet ? FieldAccessorBuilder::setGetter : FieldAccessorBuilder::setSetter)
+            .setField(field)
+            .setSourceMethod(newMethod)
+            .build();
     return new ProgramMethod(
-        holder,
+        field.getHolder(),
         new DexEncodedMethod(
             newMethod,
             accessFlags,
@@ -1285,35 +1269,36 @@
         true);
   }
 
-  public ProgramMethod toStaticForwardingBridge(DexProgramClass holder, DexMethod newMethod) {
-    assert accessFlags.isPrivate()
+  public ProgramMethod toStaticForwardingBridge(
+      DexProgramClass holder, DexMethod newMethod, DexItemFactory dexItemFactory) {
+    assert isPrivate()
         : "Expected to create bridge for private method as part of nest-based access desugaring";
-    Builder builder = syntheticBuilder(this);
-    builder.setMethod(newMethod);
-    ForwardMethodSourceCode.Builder forwardSourceCodeBuilder =
-        ForwardMethodSourceCode.builder(newMethod);
-    forwardSourceCodeBuilder
-        .setTargetReceiver(accessFlags.isStatic() ? null : method.holder)
-        .setTarget(method)
-        .setInvokeType(accessFlags.isStatic() ? Invoke.Type.STATIC : Invoke.Type.DIRECT)
-        .setIsInterface(holder.isInterface());
-    builder.setCode(
-        new SynthesizedCode(
-            forwardSourceCodeBuilder::build,
-            registry -> {
-              if (accessFlags.isStatic()) {
-                registry.registerInvokeStatic(method);
-              } else {
-                registry.registerInvokeDirect(method);
-              }
-            }));
-    builder.accessFlags.setSynthetic();
-    builder.accessFlags.setStatic();
-    builder.accessFlags.unsetPrivate();
-    if (holder.isInterface()) {
-      builder.accessFlags.setPublic();
-    }
-    return new ProgramMethod(holder, builder.build());
+    return new ProgramMethod(
+        holder,
+        syntheticBuilder(this)
+            .setMethod(newMethod)
+            .setCode(
+                ForwardMethodBuilder.builder(dexItemFactory)
+                    .setStaticSource(newMethod)
+                    .apply(
+                        builder -> {
+                          if (isStatic()) {
+                            builder.setStaticTarget(getReference(), holder.isInterface());
+                          } else {
+                            builder.setDirectTarget(getReference(), holder.isInterface());
+                          }
+                        })
+                    .build())
+            .modifyAccessFlags(
+                accessFlags -> {
+                  accessFlags.setSynthetic();
+                  accessFlags.setStatic();
+                  accessFlags.unsetPrivate();
+                  if (holder.isInterface()) {
+                    accessFlags.setPublic();
+                  }
+                })
+            .build());
   }
 
   public DexEncodedMethod toPrivateSyntheticMethod(DexMethod method) {
@@ -1587,12 +1572,18 @@
       return this;
     }
 
+    public Builder modifyAccessFlags(Consumer<MethodAccessFlags> consumer) {
+      consumer.accept(accessFlags);
+      return this;
+    }
+
     public void setAccessFlags(MethodAccessFlags accessFlags) {
       this.accessFlags = accessFlags.copy();
     }
 
-    public void setMethod(DexMethod method) {
+    public Builder setMethod(DexMethod method) {
       this.method = method;
+      return this;
     }
 
     public Builder setCompilationState(CompilationState compilationState) {
@@ -1677,8 +1668,9 @@
       return this;
     }
 
-    public void setCode(Code code) {
+    public Builder setCode(Code code) {
       this.code = code;
+      return this;
     }
 
     public DexEncodedMethod build() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
index e9c2fc5..a925fcc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -52,6 +52,11 @@
   }
 
   @Override
+  public DexClassAndField lookupMemberOnClass(DexClass clazz) {
+    return clazz != null ? clazz.lookupClassField(this) : null;
+  }
+
+  @Override
   public ProgramField lookupOnProgramClass(DexProgramClass clazz) {
     return clazz != null ? clazz.lookupProgramField(this) : null;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index bc6db0a..fd33483 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -25,7 +25,7 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
+import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
 import com.android.tools.r8.kotlin.Kotlin;
 import com.android.tools.r8.utils.ArrayUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -417,6 +417,8 @@
   public final DexType noClassDefFoundErrorType =
       createStaticallyKnownType(noClassDefFoundErrorDescriptor);
   public final DexType noSuchFieldErrorType = createStaticallyKnownType(noSuchFieldErrorDescriptor);
+  public final DexType noSuchMethodErrorType =
+      createStaticallyKnownType("Ljava/lang/NoSuchMethodError;");
   public final DexType npeType = createStaticallyKnownType(npeDescriptor);
   public final DexType reflectiveOperationExceptionType =
       createStaticallyKnownType(reflectiveOperationExceptionDescriptor);
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
index e5a8097..df36fb7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -13,6 +13,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 
@@ -85,6 +86,14 @@
   }
 
   @Override
+  public void accept(
+      Consumer<DexProgramClass> programClassConsumer,
+      Consumer<DexClasspathClass> classpathClassConsumer,
+      Consumer<DexLibraryClass> libraryClassConsumer) {
+    libraryClassConsumer.accept(this);
+  }
+
+  @Override
   public String toString() {
     return type.toString() + "(library class)";
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexMember.java b/src/main/java/com/android/tools/r8/graph/DexMember.java
index 1c02f3b..9b62855 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMember.java
@@ -24,6 +24,8 @@
 
   public abstract DexEncodedMember<?, ?> lookupOnClass(DexClass clazz);
 
+  public abstract DexClassAndMember<?, ?> lookupMemberOnClass(DexClass clazz);
+
   public abstract ProgramMember<?, ?> lookupOnProgramClass(DexProgramClass clazz);
 
   public abstract boolean match(R entry);
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index d35b1a0..29e9792 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -102,6 +102,11 @@
   }
 
   @Override
+  public DexClassAndMethod lookupMemberOnClass(DexClass clazz) {
+    return clazz != null ? clazz.lookupClassMethod(this) : null;
+  }
+
+  @Override
   public ProgramMethod lookupOnProgramClass(DexProgramClass clazz) {
     return clazz != null ? clazz.lookupProgramMethod(this) : null;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 7d69899..1d66acd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -148,6 +148,14 @@
   }
 
   @Override
+  public void accept(
+      Consumer<DexProgramClass> programClassConsumer,
+      Consumer<DexClasspathClass> classpathClassConsumer,
+      Consumer<DexLibraryClass> libraryClassConsumer) {
+    programClassConsumer.accept(this);
+  }
+
+  @Override
   public DexProgramClass self() {
     return this;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index d2fb517..56f9a92 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -15,7 +15,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.horizontalclassmerging.SyntheticArgumentClass;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
-import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
+import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.synthesis.SyntheticNaming;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -175,6 +175,10 @@
     classConsumer.accept(this, arg);
   }
 
+  public String getTypeName() {
+    return toSourceString();
+  }
+
   @Override
   public String toSourceString() {
     if (toStringCache == null) {
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index 5a7d18f..c6cf5f8 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -213,6 +213,12 @@
       return self();
     }
 
+    public Builder addProgramClasses(Collection<DexProgramClass> classes) {
+      programClasses =
+          ImmutableList.<DexProgramClass>builder().addAll(programClasses).addAll(classes).build();
+      return self();
+    }
+
     public Builder addClasspathClasses(Collection<DexClasspathClass> classes) {
       classpathClasses =
           ImmutableList.<DexClasspathClass>builder()
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 88596f9..40b6445 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -817,8 +817,7 @@
 
     private boolean classRequiresCode() {
       return parent.classKind == ClassKind.PROGRAM
-          || (parent.application.options.enableNestBasedAccessDesugaring
-              && !parent.application.options.canUseNestBasedAccess()
+          || (!parent.application.options.canUseNestBasedAccess()
               && parent.classKind == ClassKind.CLASSPATH
               && parent.isInANest());
     }
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 6a70cdd..ba22b16 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -105,7 +105,7 @@
   private final Origin origin;
   private JarApplicationReader application;
   private CfCode code;
-  protected ReparseContext context;
+  private ReparseContext context;
   private boolean reachabilitySensitive = false;
 
   public void markReachabilitySensitive() {
diff --git a/src/main/java/com/android/tools/r8/graph/LibraryField.java b/src/main/java/com/android/tools/r8/graph/LibraryField.java
new file mode 100644
index 0000000..49ed565
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/LibraryField.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2020, 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.graph;
+
+public class LibraryField extends DexClassAndField
+    implements LibraryMember<DexEncodedField, DexField> {
+
+  public LibraryField(DexLibraryClass holder, DexEncodedField field) {
+    super(holder, field);
+  }
+
+  @Override
+  public DexLibraryClass getHolder() {
+    DexClass holder = super.getHolder();
+    assert holder.isLibraryClass();
+    return holder.asLibraryClass();
+  }
+
+  @Override
+  public boolean isLibraryField() {
+    return true;
+  }
+
+  @Override
+  public LibraryField asLibraryField() {
+    return this;
+  }
+
+  @Override
+  public boolean isLibraryMember() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/LibraryMember.java b/src/main/java/com/android/tools/r8/graph/LibraryMember.java
new file mode 100644
index 0000000..880da78
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/LibraryMember.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2020, 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.graph;
+
+public interface LibraryMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> {
+
+  D getDefinition();
+
+  DexLibraryClass getHolder();
+
+  DexType getHolderType();
+}
diff --git a/src/main/java/com/android/tools/r8/graph/LibraryMethod.java b/src/main/java/com/android/tools/r8/graph/LibraryMethod.java
index c06ebab..29cdc41 100644
--- a/src/main/java/com/android/tools/r8/graph/LibraryMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/LibraryMethod.java
@@ -4,13 +4,26 @@
 package com.android.tools.r8.graph;
 
 /** Type representing a method definition from the library and its holder. */
-public final class LibraryMethod extends DexClassAndMethod {
+public final class LibraryMethod extends DexClassAndMethod
+    implements LibraryMember<DexEncodedMethod, DexMethod> {
 
   public LibraryMethod(DexLibraryClass holder, DexEncodedMethod method) {
     super(holder, method);
   }
 
   @Override
+  public DexLibraryClass getHolder() {
+    DexClass holder = super.getHolder();
+    assert holder.isLibraryClass();
+    return holder.asLibraryClass();
+  }
+
+  @Override
+  public boolean isLibraryMember() {
+    return true;
+  }
+
+  @Override
   public boolean isLibraryMethod() {
     return true;
   }
@@ -19,11 +32,4 @@
   public LibraryMethod asLibraryMethod() {
     return this;
   }
-
-  @Override
-  public DexLibraryClass getHolder() {
-    DexClass holder = super.getHolder();
-    assert holder.isLibraryClass();
-    return holder.asLibraryClass();
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollection.java b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
index 9339630..ccf33aa 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -130,7 +130,7 @@
     return backing.methods();
   }
 
-  public Iterable<DexEncodedMethod> methods(Predicate<DexEncodedMethod> predicate) {
+  public Iterable<DexEncodedMethod> methods(Predicate<? super DexEncodedMethod> predicate) {
     return IterableUtils.filter(methods(), predicate);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
index 015b182..2b5e70f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -62,7 +62,7 @@
 
   public void recordFieldAccesses(
       IRCode code, OptimizationFeedback feedback, MethodProcessor methodProcessor) {
-    if (!methodProcessor.isPrimary()) {
+    if (!methodProcessor.isPrimaryMethodProcessor()) {
       return;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index a1c4e7b..d0e0027 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -227,9 +227,9 @@
     for (Value value : current.inValues()) {
       value.removeUser(current);
     }
-    if (current.outValue() != null && current.outValue().isUsed()) {
+    if (current.hasUsedOutValue()) {
       assert newInstruction.outValue() != null;
-      if (affectedValues != null) {
+      if (affectedValues != null && newInstruction.getOutType() != current.getOutType()) {
         current.outValue().addAffectedValuesTo(affectedValues);
       }
       current.outValue().replaceUsers(newInstruction.outValue());
@@ -243,6 +243,7 @@
     listIterator.add(newInstruction);
     current.clearBlock();
     metadata.record(newInstruction);
+    current = newInstruction;
   }
 
   @Override
@@ -402,6 +403,69 @@
   }
 
   @Override
+  public void replaceCurrentInstructionWithThrow(
+      AppView<?> appView,
+      IRCode code,
+      ListIterator<BasicBlock> blockIterator,
+      Value exceptionValue,
+      Set<BasicBlock> blocksToRemove,
+      Set<Value> affectedValues) {
+    if (current == null) {
+      throw new IllegalStateException();
+    }
+
+    Instruction toBeReplaced = current;
+    InternalOptions options = appView.options();
+
+    BasicBlock block = toBeReplaced.getBlock();
+    assert !blocksToRemove.contains(block);
+    assert affectedValues != null;
+
+    // Split the block before the instruction that should be replaced by `throw exceptionValue`.
+    previous();
+
+    BasicBlock throwBlock;
+    if (block.hasCatchHandlers() && !toBeReplaced.instructionTypeCanThrow()) {
+      // We need to insert the throw instruction in a block of its own, so split the current block
+      // into three blocks, where the intermediate block only contains a goto instruction.
+      throwBlock = splitCopyCatchHandlers(code, blockIterator, options);
+      throwBlock.listIterator(code).split(code, blockIterator, true);
+    } else {
+      splitCopyCatchHandlers(code, blockIterator, options);
+      throwBlock = block;
+    }
+
+    // Unlink all blocks that are dominated by the unique normal successor of the throw block.
+    blocksToRemove.addAll(
+        throwBlock.unlink(
+            throwBlock.getUniqueNormalSuccessor(),
+            new DominatorTree(code, MAY_HAVE_UNREACHABLE_BLOCKS),
+            affectedValues));
+
+    InstructionListIterator throwBlockInstructionIterator;
+    if (throwBlock == block) {
+      throwBlockInstructionIterator = this;
+      previous();
+      next();
+    } else {
+      throwBlockInstructionIterator = throwBlock.listIterator(code, 1);
+    }
+    assert !throwBlockInstructionIterator.hasNext();
+
+    // Replace the instruction by throw.
+    Throw throwInstruction = new Throw(exceptionValue);
+    if (hasInsertionPosition()) {
+      throwInstruction.setPosition(position);
+    } else if (toBeReplaced.getPosition().isSome()) {
+      throwInstruction.setPosition(toBeReplaced.getPosition());
+    } else {
+      assert !toBeReplaced.instructionTypeCanThrow();
+      throwInstruction.setPosition(Position.syntheticNone());
+    }
+    throwBlockInstructionIterator.replaceCurrentInstruction(throwInstruction);
+  }
+
+  @Override
   public void replaceCurrentInstructionWithThrowNull(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       IRCode code,
@@ -563,8 +627,9 @@
       IRCode code, ListIterator<BasicBlock> blockIterator, InternalOptions options) {
     BasicBlock splitBlock = split(code, blockIterator, false);
     assert !block.hasCatchHandlers();
-    assert splitBlock.hasCatchHandlers();
-    block.copyCatchHandlers(code, blockIterator, splitBlock, options);
+    if (splitBlock.hasCatchHandlers()) {
+      block.copyCatchHandlers(code, blockIterator, splitBlock, options);
+    }
     return splitBlock;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
index 757a581..4e223924 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
@@ -85,6 +85,17 @@
   }
 
   @Override
+  public void replaceCurrentInstructionWithThrow(
+      AppView<?> appView,
+      IRCode code,
+      ListIterator<BasicBlock> blockIterator,
+      Value exceptionValue,
+      Set<BasicBlock> blocksToRemove,
+      Set<Value> affectedValues) {
+    throw new Unimplemented();
+  }
+
+  @Override
   public void replaceCurrentInstructionWithThrowNull(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       IRCode code,
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index 6b17a04..32b1f29 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -120,6 +120,14 @@
   void replaceCurrentInstructionWithStaticGet(
       AppView<?> appView, IRCode code, DexField field, Set<Value> affectedValues);
 
+  void replaceCurrentInstructionWithThrow(
+      AppView<?> appView,
+      IRCode code,
+      ListIterator<BasicBlock> blockIterator,
+      Value exceptionValue,
+      Set<BasicBlock> blocksToRemove,
+      Set<Value> affectedValues);
+
   /**
    * Replace the current instruction with null throwing instructions.
    *
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index 9d7d69b..0d9d21a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.ir.code;
 
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
 
 import com.android.tools.r8.cf.LoadStoreHelper;
 import com.android.tools.r8.cf.TypeVerificationHelper;
@@ -21,6 +22,7 @@
 import com.android.tools.r8.ir.analysis.modeling.LibraryMethodReadSetModeling;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 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.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
 import com.android.tools.r8.ir.optimize.DefaultInliningOracle;
@@ -253,6 +255,11 @@
       return self();
     }
 
+    public B setFreshOutValue(AppView<?> appView, ValueFactory factory) {
+      return super.setFreshOutValue(
+          factory, TypeElement.fromDexType(method.getReturnType(), maybeNull(), appView));
+    }
+
     public B setSingleArgument(Value argument) {
       return setArguments(ImmutableList.of(argument));
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
index bbb6d27..3e61866 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -100,6 +100,18 @@
   }
 
   @Override
+  public void replaceCurrentInstructionWithThrow(
+      AppView<?> appView,
+      IRCode code,
+      ListIterator<BasicBlock> blockIterator,
+      Value exceptionValue,
+      Set<BasicBlock> blocksToRemove,
+      Set<Value> affectedValues) {
+    currentBlockIterator.replaceCurrentInstructionWithThrow(
+        appView, code, blockIterator, exceptionValue, blocksToRemove, affectedValues);
+  }
+
+  @Override
   public void replaceCurrentInstructionWithThrowNull(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       IRCode code,
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
new file mode 100644
index 0000000..e747891
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -0,0 +1,115 @@
+// 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.conversion;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public abstract class ClassConverter {
+
+  private final IRConverter converter;
+  private final D8MethodProcessor methodProcessor;
+
+  ClassConverter(IRConverter converter, D8MethodProcessor methodProcessor) {
+    this.converter = converter;
+    this.methodProcessor = methodProcessor;
+  }
+
+  public static ClassConverter create(
+      AppView<?> appView, IRConverter converter, D8MethodProcessor methodProcessor) {
+    return appView.options().desugarSpecificOptions().allowAllDesugaredInput
+        ? new LibraryDesugaredClassConverter(appView, converter, methodProcessor)
+        : new DefaultClassConverter(converter, methodProcessor);
+  }
+
+  public void convertClasses(DexApplication application, ExecutorService executorService)
+      throws ExecutionException {
+    internalConvertClasses(application, executorService);
+    notifyAllClassesConverted();
+  }
+
+  private void internalConvertClasses(DexApplication application, ExecutorService executorService)
+      throws ExecutionException {
+    List<DexProgramClass> classes = application.classes();
+    while (!classes.isEmpty()) {
+      Set<DexType> seenNestHosts = Sets.newIdentityHashSet();
+      List<DexProgramClass> deferred = new ArrayList<>(classes.size() / 2);
+      List<DexProgramClass> wave = new ArrayList<>(classes.size());
+      for (DexProgramClass clazz : classes) {
+        if (clazz.isInANest() && !seenNestHosts.add(clazz.getNestHost())) {
+          deferred.add(clazz);
+        } else {
+          wave.add(clazz);
+        }
+      }
+      ThreadUtils.processItems(wave, this::convertClass, executorService);
+      classes = deferred;
+    }
+    methodProcessor.awaitMethodProcessing();
+  }
+
+  abstract void convertClass(DexProgramClass clazz);
+
+  void convertMethods(DexProgramClass clazz) {
+    converter.convertMethods(clazz, methodProcessor);
+  }
+
+  abstract void notifyAllClassesConverted();
+
+  static class DefaultClassConverter extends ClassConverter {
+
+    DefaultClassConverter(IRConverter converter, D8MethodProcessor methodProcessor) {
+      super(converter, methodProcessor);
+    }
+
+    @Override
+    void convertClass(DexProgramClass clazz) {
+      convertMethods(clazz);
+    }
+
+    @Override
+    void notifyAllClassesConverted() {
+      // Intentionally empty.
+    }
+  }
+
+  static class LibraryDesugaredClassConverter extends ClassConverter {
+
+    private final AppView<?> appView;
+    private final Set<DexType> alreadyLibraryDesugared = Sets.newConcurrentHashSet();
+
+    LibraryDesugaredClassConverter(
+        AppView<?> appView, IRConverter converter, D8MethodProcessor methodProcessor) {
+      super(converter, methodProcessor);
+      this.appView = appView;
+    }
+
+    @Override
+    void convertClass(DexProgramClass clazz) {
+      // Classes which has already been through library desugaring will not go through IR
+      // processing again.
+      LibraryDesugaredChecker libraryDesugaredChecker = new LibraryDesugaredChecker(appView);
+      if (libraryDesugaredChecker.isClassLibraryDesugared(clazz)) {
+        alreadyLibraryDesugared.add(clazz.getType());
+      } else {
+        convertMethods(clazz);
+      }
+    }
+
+    @Override
+    void notifyAllClassesConverted() {
+      appView.setAlreadyLibraryDesugared(alreadyLibraryDesugared);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
new file mode 100644
index 0000000..61ea47c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
+import com.android.tools.r8.utils.ThreadUtils;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+public class D8MethodProcessor extends MethodProcessor {
+
+  private final IRConverter converter;
+  private final ExecutorService executorService;
+  private final List<Future<?>> futures = Collections.synchronizedList(new ArrayList<>());
+
+  public D8MethodProcessor(IRConverter converter, ExecutorService executorService) {
+    this.converter = converter;
+    this.executorService = executorService;
+  }
+
+  public void awaitMethodProcessing() throws ExecutionException {
+    ThreadUtils.awaitFutures(futures);
+    futures.clear();
+  }
+
+  @Override
+  public boolean shouldApplyCodeRewritings(ProgramMethod method) {
+    return true;
+  }
+
+  @Override
+  public void scheduleMethodForProcessingAfterCurrentWave(ProgramMethod method) {
+    futures.add(
+        ThreadUtils.processAsynchronously(
+            () ->
+                converter.rewriteCode(method, OptimizationFeedbackIgnore.getInstance(), this, null),
+            executorService));
+  }
+
+  public boolean verifyAllMethodsProcessed() {
+    assert futures.isEmpty();
+    return true;
+  }
+}
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 da599dc..60e2477 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
@@ -45,7 +45,6 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
 import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
-import com.android.tools.r8.ir.desugar.D8NestBasedAccessDesugaring;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter.Mode;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
@@ -54,6 +53,8 @@
 import com.android.tools.r8.ir.desugar.LambdaRewriter;
 import com.android.tools.r8.ir.desugar.StringConcatRewriter;
 import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
+import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
+import com.android.tools.r8.ir.desugar.nest.NestBridgeConsumer;
 import com.android.tools.r8.ir.optimize.AssertionsRewriter;
 import com.android.tools.r8.ir.optimize.AssumeInserter;
 import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization;
@@ -108,12 +109,10 @@
 import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
 import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -393,10 +392,21 @@
     }
   }
 
-  private void desugarNestBasedAccess(Builder<?> builder, ExecutorService executorService)
-      throws ExecutionException {
+  private void synthesizeBridgesForNestBasedAccessesOnClasspath(
+      MethodProcessor methodProcessor, ExecutorService executorService) throws ExecutionException {
     if (d8NestBasedAccessDesugaring != null) {
-      d8NestBasedAccessDesugaring.desugarNestBasedAccess(builder, executorService, this);
+      d8NestBasedAccessDesugaring.synthesizeBridgesForNestBasedAccessesOnClasspath(
+          methodProcessor, executorService);
+    }
+  }
+
+  private void finalizeNestBasedAccessDesugaring(Builder<?> builder) {
+    if (d8NestBasedAccessDesugaring != null) {
+      DexProgramClass nestConstructor = d8NestBasedAccessDesugaring.synthesizeNestConstructor();
+      if (nestConstructor != null) {
+        builder.addProgramClass(nestConstructor);
+      }
+      d8NestBasedAccessDesugaring.reportDesugarDependencies();
     }
   }
 
@@ -479,38 +489,12 @@
     DexApplication application = appView.appInfo().app();
     timing.begin("IR conversion");
 
-    if (appView.options().desugarSpecificOptions().allowAllDesugaredInput) {
-      // Classes which has already been through library desugaring will not go through IR
-      // processing again.
-      LibraryDesugaredChecker libraryDesugaredChecker = new LibraryDesugaredChecker(appView);
-      Set<DexType> alreadyLibraryDesugared = Sets.newConcurrentHashSet();
-      ThreadUtils.processItems(
-          application.classes(),
-          clazz -> {
-            if (libraryDesugaredChecker.isClassLibraryDesugared(clazz)) {
-              if (appView.options().desugarSpecificOptions().allowAllDesugaredInput) {
-                alreadyLibraryDesugared.add(clazz.getType());
-              } else {
-                throw new CompilationError(
-                    "Code for "
-                        + clazz.getType().getDescriptor()
-                        + "has already been library desugared.");
-              }
-            } else {
-              convertMethods(clazz);
-            }
-          },
-          executor);
-      appView.setAlreadyLibraryDesugared(alreadyLibraryDesugared);
-    } else {
-      ThreadUtils.processItems(application.classes(), this::convertMethods, executor);
-    }
+    convertClasses(application, executor);
 
     // Build a new application with jumbo string info,
-    Builder<?> builder = application.builder();
-    builder.setHighestSortingString(highestSortingString);
+    Builder<?> builder = application.builder().setHighestSortingString(highestSortingString);
 
-    desugarNestBasedAccess(builder, executor);
+    finalizeNestBasedAccessDesugaring(builder);
 
     // Synthesize lambda classes and commit to the app in full.
     synthesizeLambdaClasses(executor);
@@ -541,7 +525,17 @@
             appView.appInfo().getMainDexClasses()));
   }
 
-  private void convertMethods(DexProgramClass clazz) {
+  private void convertClasses(DexApplication application, ExecutorService executorService)
+      throws ExecutionException {
+    D8MethodProcessor methodProcessor = new D8MethodProcessor(this, executorService);
+    ClassConverter classConverter = ClassConverter.create(appView, this, methodProcessor);
+    classConverter.convertClasses(application, executorService);
+
+    synthesizeBridgesForNestBasedAccessesOnClasspath(methodProcessor, executorService);
+    methodProcessor.awaitMethodProcessing();
+  }
+
+  void convertMethods(DexProgramClass clazz, MethodProcessor methodProcessor) {
     boolean isReachabilitySensitive = clazz.hasReachabilitySensitiveAnnotation(options.itemFactory);
     // When converting all methods on a class always convert <clinit> first.
     DexEncodedMethod classInitializer = clazz.getClassInitializer();
@@ -549,14 +543,14 @@
       classInitializer
           .getMutableOptimizationInfo()
           .setReachabilitySensitive(isReachabilitySensitive);
-      convertMethod(new ProgramMethod(clazz, classInitializer));
+      convertMethod(new ProgramMethod(clazz, classInitializer), methodProcessor);
     }
     clazz.forEachProgramMethodMatching(
         definition -> !definition.isClassInitializer(),
         method -> {
           DexEncodedMethod definition = method.getDefinition();
           definition.getMutableOptimizationInfo().setReachabilitySensitive(isReachabilitySensitive);
-          convertMethod(method);
+          convertMethod(method, methodProcessor);
         });
     // The class file version is downgraded after compilation. Some of the desugaring might need
     // the initial class file version to determine how far a method can be downgraded.
@@ -566,7 +560,7 @@
     }
   }
 
-  private void convertMethod(ProgramMethod method) {
+  private void convertMethod(ProgramMethod method, MethodProcessor methodProcessor) {
     DexEncodedMethod definition = method.getDefinition();
     if (definition.hasClassFileVersion()) {
       definition.downgradeClassFileVersion(
@@ -585,8 +579,7 @@
     if (options.isGeneratingClassFiles()
         || !(options.passthroughDexCode && definition.getCode().isDexCode())) {
       // We do not process in call graph order, so anything could be a leaf.
-      rewriteCode(
-          method, simpleOptimizationFeedback, OneTimeMethodProcessor.create(method, appView), null);
+      rewriteCode(method, simpleOptimizationFeedback, methodProcessor, null);
     } else {
       assert definition.getCode().isDexCode();
     }
@@ -1142,7 +1135,7 @@
     }
   }
 
-  private Timing rewriteCode(
+  Timing rewriteCode(
       ProgramMethod method,
       OptimizationFeedback feedback,
       MethodProcessor methodProcessor,
@@ -1169,7 +1162,7 @@
           method.toSourceString(),
           logCode(options, method.getDefinition()));
     }
-    boolean didDesugar = desugar(method);
+    boolean didDesugar = desugar(method, methodProcessor);
     if (Log.ENABLED && didDesugar) {
       Log.debug(
           getClass(),
@@ -1192,7 +1185,7 @@
     return optimize(code, feedback, methodProcessor, methodProcessingId);
   }
 
-  private boolean desugar(ProgramMethod method) {
+  private boolean desugar(ProgramMethod method, MethodProcessor methodProcessor) {
     if (options.desugarState != DesugarState.ON) {
       return false;
     }
@@ -1201,13 +1194,17 @@
     }
     boolean didDesugar = false;
     Supplier<AppInfoWithClassHierarchy> lazyAppInfo =
-        Suppliers.memoize(() -> appView.appInfoForDesugaring());
+        Suppliers.memoize(appView::appInfoForDesugaring);
     if (lambdaRewriter != null) {
       didDesugar |= lambdaRewriter.desugarLambdas(method, lazyAppInfo.get()) > 0;
     }
     if (backportedMethodRewriter != null) {
       didDesugar |= backportedMethodRewriter.desugar(method, lazyAppInfo.get());
     }
+    if (d8NestBasedAccessDesugaring != null) {
+      NestBridgeConsumer bridgeConsumer = NestBridgeConsumer.createForD8(methodProcessor);
+      didDesugar |= d8NestBasedAccessDesugaring.desugar(method, bridgeConsumer);
+    }
     return didDesugar;
   }
 
@@ -1537,18 +1534,9 @@
 
     previous = printMethod(code, "IR after class inlining (SSA)", previous);
 
-    if (d8NestBasedAccessDesugaring != null) {
-      timing.begin("Desugar nest access");
-      d8NestBasedAccessDesugaring.rewriteNestBasedAccesses(context, code, appView);
-      timing.end();
-      assert code.isConsistentSSA();
-    }
-
-    previous = printMethod(code, "IR after nest based access desugaring (SSA)", previous);
-
     if (interfaceMethodRewriter != null) {
       timing.begin("Rewrite interface methods");
-      interfaceMethodRewriter.rewriteMethodReferences(code);
+      interfaceMethodRewriter.rewriteMethodReferences(code, methodProcessor, methodProcessingId);
       timing.end();
       assert code.isConsistentSSA();
     }
@@ -1557,7 +1545,8 @@
 
     // This pass has to be after interfaceMethodRewriter and BackportedMethodRewriter.
     if (desugaredLibraryAPIConverter != null
-        && (!appView.enableWholeProgramOptimizations() || methodProcessor.isPrimary())) {
+        && (!appView.enableWholeProgramOptimizations()
+            || methodProcessor.isPrimaryMethodProcessor())) {
       timing.begin("Desugar library API");
       desugaredLibraryAPIConverter.desugar(code);
       timing.end();
@@ -1587,7 +1576,7 @@
 
     // TODO(b/140766440): an ideal solution would be puttting CodeOptimization for this into
     //  the list for primary processing only.
-    if (options.outline.enabled && outliner != null && methodProcessor.isPrimary()) {
+    if (options.outline.enabled && outliner != null && methodProcessor.isPrimaryMethodProcessor()) {
       timing.begin("Identify outlines");
       outliner.getOutlineMethodIdentifierGenerator().accept(code);
       timing.end();
@@ -1640,7 +1629,7 @@
       timing.end();
     }
 
-    if (enumUnboxer != null && methodProcessor.isPrimary()) {
+    if (enumUnboxer != null && methodProcessor.isPrimaryMethodProcessor()) {
       enumUnboxer.analyzeEnums(code);
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
index 6706aa5..ef9a31c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
@@ -8,25 +8,13 @@
 
 public abstract class MethodProcessor {
 
-  public enum Phase {
-    ONE_TIME,
-    PRIMARY,
-    POST
-  }
-
   protected SortedProgramMethodSet wave;
   protected SortedProgramMethodSet waveExtension = SortedProgramMethodSet.createConcurrent();
 
-  public abstract Phase getPhase();
-
   public abstract boolean shouldApplyCodeRewritings(ProgramMethod method);
 
-  public boolean isPrimary() {
-    return getPhase() == Phase.PRIMARY;
-  }
-
-  public boolean isPost() {
-    return getPhase() == Phase.POST;
+  public boolean isPrimaryMethodProcessor() {
+    return false;
   }
 
   public CallSiteInformation getCallSiteInformation() {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java b/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
index a89e3f9..c44ce41 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
@@ -18,7 +18,7 @@
 
 class NeedsIRDesugarUseRegistry extends UseRegistry {
 
-  private boolean needsDesugarging = false;
+  private boolean needsDesugaring = false;
   private final AppView appView;
   private final BackportedMethodRewriter backportedMethodRewriter;
   private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
@@ -40,15 +40,15 @@
   }
 
   public boolean needsDesugaring() {
-    return needsDesugarging;
+    return needsDesugaring;
   }
 
   @Override
   public void registerInitClass(DexType type) {
-    if (!needsDesugarging
+    if (!needsDesugaring
         && desugaredLibraryAPIConverter != null
         && desugaredLibraryAPIConverter.canConvert(type)) {
-      needsDesugarging = true;
+      needsDesugaring = true;
     }
   }
 
@@ -68,37 +68,37 @@
   }
 
   private void registerTwrCloseResourceRewriting(DexMethod method) {
-    if (!needsDesugarging) {
-      needsDesugarging =
+    if (!needsDesugaring) {
+      needsDesugaring =
           TwrCloseResourceRewriter.isTwrCloseResourceMethod(method, appView.dexItemFactory());
     }
   }
 
   private void registerBackportedMethodRewriting(DexMethod method) {
-    if (!needsDesugarging) {
-      needsDesugarging = backportedMethodRewriter.needsDesugaring(method);
+    if (!needsDesugaring) {
+      needsDesugaring = backportedMethodRewriter.needsDesugaring(method);
     }
   }
 
   private void registerInterfaceMethodRewriting(DexMethod method, boolean isInvokeSuper) {
-    if (!needsDesugarging) {
-      needsDesugarging =
+    if (!needsDesugaring) {
+      needsDesugaring =
           interfaceMethodRewriter != null
               && interfaceMethodRewriter.needsRewriting(method, isInvokeSuper, appView);
     }
   }
 
   private void registerDesugaredLibraryAPIConverter(DexMethod method) {
-    if (!needsDesugarging) {
-      needsDesugarging =
+    if (!needsDesugaring) {
+      needsDesugaring =
           desugaredLibraryAPIConverter != null
               && desugaredLibraryAPIConverter.shouldRewriteInvoke(method);
     }
   }
 
   private void registerLibraryRetargeting(DexMethod method, boolean b) {
-    if (!needsDesugarging) {
-      needsDesugarging =
+    if (!needsDesugaring) {
+      needsDesugaring =
           desugaredLibraryRetargeter != null
               && desugaredLibraryRetargeter.getRetargetedMethod(method, b) != null;
     }
@@ -123,7 +123,7 @@
   @Override
   public void registerInvokeStatic(DexMethod method, boolean itf) {
     if (itf) {
-      needsDesugarging = true;
+      needsDesugaring = true;
     }
     registerInvokeStatic(method);
   }
@@ -131,7 +131,7 @@
   @Override
   public void registerCallSite(DexCallSite callSite) {
     super.registerCallSite(callSite);
-    needsDesugarging = true;
+    needsDesugaring = true;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
index bb3f07c..a8c2e57 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
@@ -52,11 +52,6 @@
     return true;
   }
 
-  @Override
-  public Phase getPhase() {
-    return Phase.ONE_TIME;
-  }
-
   public <E extends Exception> void forEachWaveWithExtension(
       ThrowingBiConsumer<ProgramMethod, MethodProcessingId, E> consumer) throws E {
     while (!wave.isEmpty()) {
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 a3cccb4..649edfd 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
@@ -53,11 +53,6 @@
   }
 
   @Override
-  public Phase getPhase() {
-    return Phase.POST;
-  }
-
-  @Override
   public boolean shouldApplyCodeRewritings(ProgramMethod method) {
     assert !wave.contains(method);
     return !processed.contains(method);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
index dd4c0a0..cb288b0 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
@@ -62,8 +62,8 @@
   }
 
   @Override
-  public Phase getPhase() {
-    return Phase.PRIMARY;
+  public boolean isPrimaryMethodProcessor() {
+    return true;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
deleted file mode 100644
index 787915f..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.desugar;
-
-import 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.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-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.ProgramMethod;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.FieldInstruction;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-
-// Summary:
-// - Process all methods compiled rewriting nest based access (Methods processed concurrently).
-// - Process classes on class path in reachable nests to find bridges to add
-//    in Program classes (Nests processed concurrently).
-// - Add bridges and nest constructor class (Sequential).
-// - Optimize bridges (Bridges processed concurrently).
-public class D8NestBasedAccessDesugaring extends NestBasedAccessDesugaring {
-
-  // Maps a nest host to a class met which has that nest host.
-  // The value is used because the nest host might be missing.
-  private final Map<DexType, DexProgramClass> metNestHosts = new ConcurrentHashMap<>();
-
-  public D8NestBasedAccessDesugaring(AppView<?> appView) {
-    super(appView);
-  }
-
-  public void rewriteNestBasedAccesses(ProgramMethod method, IRCode code, AppView<?> appView) {
-    if (!method.getHolder().isInANest()) {
-      return;
-    }
-    metNestHosts.put(method.getHolder().getNestHost(), method.getHolder());
-
-    ListIterator<BasicBlock> blocks = code.listIterator();
-    while (blocks.hasNext()) {
-      BasicBlock block = blocks.next();
-      InstructionListIterator instructions = block.listIterator(code);
-      while (instructions.hasNext()) {
-        Instruction instruction = instructions.next();
-        if (instruction.isInvokeMethod()) {
-          InvokeMethod invokeMethod = instruction.asInvokeMethod();
-          DexMethod invokedMethod = invokeMethod.getInvokedMethod();
-          if (!invokedMethod.holder.isClassType()) {
-            continue;
-          }
-          // Since we only need to desugar accesses to private methods, and all accesses to private
-          // methods must be accessing the private method directly on its holder, we can lookup the
-          // method on the holder instead of resolving the method.
-          DexClass holder = appView.definitionForHolder(invokedMethod);
-          DexEncodedMethod definition = invokedMethod.lookupOnClass(holder);
-          if (definition != null && invokeRequiresRewriting(definition, method)) {
-            DexMethod bridge = ensureInvokeBridge(definition);
-            if (definition.isInstanceInitializer()) {
-              instructions.previous();
-              Value extraNullValue =
-                  instructions.insertConstNullInstruction(code, appView.options());
-              instructions.next();
-              List<Value> parameters = new ArrayList<>(invokeMethod.arguments());
-              parameters.add(extraNullValue);
-              instructions.replaceCurrentInstruction(
-                  new InvokeDirect(bridge, invokeMethod.outValue(), parameters));
-            } else {
-              instructions.replaceCurrentInstruction(
-                  new InvokeStatic(bridge, invokeMethod.outValue(), invokeMethod.arguments()));
-            }
-          }
-        } else if (instruction.isFieldInstruction()) {
-          // Since we only need to desugar accesses to private fields, and all accesses to private
-          // fields must be accessing the private field directly on its holder, we can lookup the
-          // field on the holder instead of resolving the field.
-          FieldInstruction fieldInstruction = instruction.asFieldInstruction();
-          DexClass holder = appView.definitionForHolder(fieldInstruction.getField());
-          DexEncodedField field = fieldInstruction.getField().lookupOnClass(holder);
-          if (field != null && fieldAccessRequiresRewriting(field, method)) {
-            if (instruction.isInstanceGet() || instruction.isStaticGet()) {
-              DexMethod bridge = ensureFieldAccessBridge(field, true);
-              instructions.replaceCurrentInstruction(
-                  new InvokeStatic(bridge, instruction.outValue(), instruction.inValues()));
-            } else {
-              assert instruction.isInstancePut() || instruction.isStaticPut();
-              DexMethod bridge = ensureFieldAccessBridge(field, false);
-              instructions.replaceCurrentInstruction(
-                  new InvokeStatic(bridge, instruction.outValue(), instruction.inValues()));
-            }
-          }
-        }
-      }
-    }
-  }
-
-  private void processNestsConcurrently(ExecutorService executorService) throws ExecutionException {
-    List<Future<?>> futures = new ArrayList<>();
-    for (DexProgramClass clazz : metNestHosts.values()) {
-      futures.add(asyncProcessNest(clazz, executorService));
-    }
-    ThreadUtils.awaitFutures(futures);
-  }
-
-  private void addDeferredBridges() {
-    addDeferredBridges(bridges.values());
-    addDeferredBridges(getFieldBridges.values());
-    addDeferredBridges(putFieldBridges.values());
-  }
-
-  private void addDeferredBridges(Collection<ProgramMethod> bridges) {
-    for (ProgramMethod bridge : bridges) {
-      bridge.getHolder().addMethod(bridge.getDefinition());
-    }
-  }
-
-  private void optimizeDeferredBridgesConcurrently(
-      ExecutorService executorService, IRConverter converter) throws ExecutionException {
-    SortedProgramMethodSet methods = SortedProgramMethodSet.create();
-    methods.addAll(bridges.values());
-    methods.addAll(getFieldBridges.values());
-    methods.addAll(putFieldBridges.values());
-    converter.processMethodsConcurrently(methods, executorService);
-  }
-
-  public void desugarNestBasedAccess(
-      DexApplication.Builder<?> builder, ExecutorService executorService, IRConverter converter)
-      throws ExecutionException {
-    if (metNestHosts.isEmpty()) {
-      return;
-    }
-    processNestsConcurrently(executorService);
-    addDeferredBridges();
-    synthesizeNestConstructor(builder);
-    optimizeDeferredBridgesConcurrently(executorService, converter);
-  }
-
-  // In D8, programClass are processed on the fly so they do not need to be processed again here.
-  @Override
-  protected boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest) {
-    return clazz.isClasspathClass();
-  }
-
-  @Override
-  void reportMissingNestHost(DexClass clazz) {
-    appView.options().nestDesugaringWarningMissingNestHost(clazz);
-  }
-
-  @Override
-  void reportIncompleteNest(List<DexType> nest) {
-    appView.options().nestDesugaringWarningIncompleteNest(nest, appView);
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 9ea3aa3..8682669 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -38,6 +38,7 @@
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
@@ -48,9 +49,14 @@
 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.InvokeSuper;
+import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodProcessingId;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
 import com.android.tools.r8.ir.desugar.DefaultMethodsHelper.Collection;
 import com.android.tools.r8.ir.desugar.InterfaceProcessor.InterfaceProcessorNestedGraphLens;
+import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations;
+import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.UtilityMethodForCodeOptimizations;
 import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.SynthesizedOrigin;
@@ -251,15 +257,21 @@
 
   // Rewrites the references to static and default interface methods.
   // NOTE: can be called for different methods concurrently.
-  public void rewriteMethodReferences(IRCode code) {
+  public void rewriteMethodReferences(
+      IRCode code, MethodProcessor methodProcessor, MethodProcessingId methodProcessingId) {
     ProgramMethod context = code.context();
     if (synthesizedMethods.contains(context)) {
       return;
     }
 
+    Set<Value> affectedValues = Sets.newIdentityHashSet();
+    Set<BasicBlock> blocksToRemove = Sets.newIdentityHashSet();
     ListIterator<BasicBlock> blocks = code.listIterator();
     while (blocks.hasNext()) {
       BasicBlock block = blocks.next();
+      if (blocksToRemove.contains(block)) {
+        continue;
+      }
       InstructionListIterator instructions = block.listIterator(code);
       while (instructions.hasNext()) {
         Instruction instruction = instructions.next();
@@ -271,7 +283,15 @@
             rewriteInvokeDirect(instruction.asInvokeDirect(), instructions, context);
             break;
           case INVOKE_STATIC:
-            rewriteInvokeStatic(instruction.asInvokeStatic(), instructions, context);
+            rewriteInvokeStatic(
+                instruction.asInvokeStatic(),
+                code,
+                blocks,
+                instructions,
+                affectedValues,
+                blocksToRemove,
+                methodProcessor,
+                methodProcessingId);
             break;
           case INVOKE_SUPER:
             rewriteInvokeSuper(instruction.asInvokeSuper(), instructions, context);
@@ -287,6 +307,14 @@
         }
       }
     }
+
+    code.removeBlocks(blocksToRemove);
+
+    if (!affectedValues.isEmpty()) {
+      new TypeAnalysis(appView).narrowing(affectedValues);
+    }
+
+    assert code.isConsistentSSA();
   }
 
   private void rewriteInvokeCustom(InvokeCustom invoke, ProgramMethod context) {
@@ -359,13 +387,21 @@
   }
 
   private void rewriteInvokeStatic(
-      InvokeStatic invoke, InstructionListIterator instructions, ProgramMethod context) {
+      InvokeStatic invoke,
+      IRCode code,
+      ListIterator<BasicBlock> blockIterator,
+      InstructionListIterator instructions,
+      Set<Value> affectedValues,
+      Set<BasicBlock> blocksToRemove,
+      MethodProcessor methodProcessor,
+      MethodProcessingId methodProcessingId) {
     DexMethod invokedMethod = invoke.getInvokedMethod();
     if (appView.getSyntheticItems().isPendingSynthetic(invokedMethod.holder)) {
       // We did not create this code yet, but it will not require rewriting.
       return;
     }
 
+    ProgramMethod context = code.context();
     DexClass clazz = appView.definitionFor(invokedMethod.holder, context);
     if (clazz == null) {
       // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
@@ -432,13 +468,44 @@
         // When leaving static interface method invokes upgrade the class file version.
         context.getDefinition().upgradeClassFileVersion(CfVersion.V1_8);
       }
-    } else {
+      return;
+    }
+
+    SingleResolutionResult resolutionResult =
+        appView
+            .appInfoForDesugaring()
+            .resolveMethodOnInterface(clazz, invokedMethod)
+            .asSingleResolution();
+    if (resolutionResult != null) {
       instructions.replaceCurrentInstruction(
           new InvokeStatic(
-              staticAsMethodOfCompanionClass(invokedMethod),
+              staticAsMethodOfCompanionClass(resolutionResult.getResolutionPair()),
               invoke.outValue(),
               invoke.arguments()));
+      return;
     }
+
+    // Replace by throw new NoSuchMethodError.
+    UtilityMethodForCodeOptimizations throwNoSuchMethodErrorMethod =
+        UtilityMethodsForCodeOptimizations.synthesizeThrowNoSuchMethodErrorMethod(
+            appView, context, methodProcessingId);
+    throwNoSuchMethodErrorMethod.optimize(methodProcessor);
+    InvokeStatic throwNoSuchMethodErrorInvoke =
+        InvokeStatic.builder()
+            .setMethod(throwNoSuchMethodErrorMethod.getMethod())
+            .setFreshOutValue(appView, code)
+            .setPosition(invoke)
+            .build();
+    instructions.previous();
+    instructions.add(throwNoSuchMethodErrorInvoke);
+    instructions.next();
+    instructions.replaceCurrentInstructionWithThrow(
+        appView,
+        code,
+        blockIterator,
+        throwNoSuchMethodErrorInvoke.outValue(),
+        blocksToRemove,
+        affectedValues);
   }
 
   private void rewriteInvokeSuper(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
deleted file mode 100644
index eb42231..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ /dev/null
@@ -1,529 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.desugar;
-
-
-import 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.ClassAccessFlags;
-import com.android.tools.r8.graph.ClasspathMethod;
-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.DexClassAndMethod;
-import com.android.tools.r8.graph.DexEncodedField;
-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.DexProto;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.GenericSignature.ClassSignature;
-import com.android.tools.r8.graph.NestMemberClassAttribute;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.Pair;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-
-// NestBasedAccessDesugaring contains common code between the two subclasses
-// which are specialized for d8 and r8
-public abstract class NestBasedAccessDesugaring {
-
-  // Short names to avoid creating long strings
-  public static final String NEST_ACCESS_NAME_PREFIX = "-$$Nest$";
-  private static final String NEST_ACCESS_METHOD_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "m";
-  private static final String NEST_ACCESS_STATIC_METHOD_NAME_PREFIX =
-      NEST_ACCESS_NAME_PREFIX + "sm";
-  private static final String NEST_ACCESS_FIELD_GET_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "fget";
-  private static final String NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX =
-      NEST_ACCESS_NAME_PREFIX + "sfget";
-  private static final String NEST_ACCESS_FIELD_PUT_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "fput";
-  private static final String NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX =
-      NEST_ACCESS_NAME_PREFIX + "sfput";
-  public static final String NEST_CONSTRUCTOR_NAME = NEST_ACCESS_NAME_PREFIX + "Constructor";
-
-  protected final AppView<?> appView;
-  // Following maps are there to avoid creating the bridges multiple times
-  // and remember the bridges to add once the nests are processed.
-  final Map<DexMethod, ProgramMethod> bridges = new ConcurrentHashMap<>();
-  final Map<DexField, ProgramMethod> getFieldBridges = new ConcurrentHashMap<>();
-  final Map<DexField, ProgramMethod> putFieldBridges = new ConcurrentHashMap<>();
-  // Common single empty class for nest based private constructors
-  private final DexProgramClass nestConstructor;
-  private boolean nestConstructorUsed = false;
-
-  NestBasedAccessDesugaring(AppView<?> appView) {
-    this.appView = appView;
-    this.nestConstructor = createNestAccessConstructor();
-  }
-
-  DexType getNestConstructorType() {
-    assert nestConstructor != null;
-    return nestConstructor.type;
-  }
-
-  abstract void reportMissingNestHost(DexClass clazz);
-
-  abstract void reportIncompleteNest(List<DexType> nest);
-
-  DexClass definitionFor(DexType type) {
-    return appView.definitionFor(appView.graphLens().lookupType(type));
-  }
-
-  private DexEncodedMethod lookupOnHolder(
-      DexMethod method, DexClassAndMethod context, Invoke.Type invokeType) {
-    DexMethod rewritten =
-        appView.graphLens().lookupMethod(method, context.getReference(), invokeType).getReference();
-    return rewritten.lookupOnClass(appView.definitionForHolder(rewritten));
-  }
-
-  private DexEncodedField lookupOnHolder(DexField field) {
-    DexField rewritten = appView.graphLens().lookupField(field);
-    return rewritten.lookupOnClass(appView.definitionForHolder(rewritten));
-  }
-
-  // Extract the list of types in the programClass' nest, of host hostClass
-  private Pair<DexClass, List<DexType>> extractNest(DexClass clazz) {
-    assert clazz != null;
-    DexClass hostClass = clazz.isNestHost() ? clazz : definitionFor(clazz.getNestHost());
-    if (hostClass == null) {
-      reportMissingNestHost(clazz);
-      // Missing nest host means the class is considered as not being part of a nest.
-      clazz.clearNestHost();
-      return null;
-    }
-    List<DexType> classesInNest =
-        new ArrayList<>(hostClass.getNestMembersClassAttributes().size() + 1);
-    for (NestMemberClassAttribute nestmate : hostClass.getNestMembersClassAttributes()) {
-      classesInNest.add(nestmate.getNestMember());
-    }
-    classesInNest.add(hostClass.type);
-    return new Pair<>(hostClass, classesInNest);
-  }
-
-  Future<?> asyncProcessNest(DexProgramClass clazz, ExecutorService executorService) {
-    return executorService.submit(
-        () -> {
-          Pair<DexClass, List<DexType>> nest = extractNest(clazz);
-          // Nest is null when nest host is missing, we do nothing in this case.
-          if (nest != null) {
-            processNest(nest.getFirst(), nest.getSecond());
-          }
-          return null; // we want a Callable not a Runnable to be able to throw
-        });
-  }
-
-  private void processNest(DexClass host, List<DexType> nest) {
-    boolean reported = false;
-    for (DexType type : nest) {
-      DexClass clazz = definitionFor(type);
-      if (clazz == null) {
-        if (!reported) {
-          reportIncompleteNest(nest);
-          reported = true;
-        }
-      } else {
-        reportDesugarDependencies(host, clazz);
-        if (shouldProcessClassInNest(clazz, nest)) {
-          for (DexEncodedMethod definition : clazz.methods()) {
-            if (clazz.isProgramClass()) {
-              ProgramMethod method = new ProgramMethod(clazz.asProgramClass(), definition);
-              method.registerCodeReferences(new NestBasedAccessDesugaringUseRegistry(method));
-            } else if (clazz.isClasspathClass()) {
-              ClasspathMethod method = new ClasspathMethod(clazz.asClasspathClass(), definition);
-              method.registerCodeReferencesForDesugaring(
-                  new NestBasedAccessDesugaringUseRegistry(method));
-            } else {
-              assert false;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  private void reportDesugarDependencies(DexClass host, DexClass clazz) {
-    if (host == clazz) {
-      return;
-    }
-    if (host.isProgramClass()) {
-      InterfaceMethodRewriter.reportDependencyEdge(host.asProgramClass(), clazz, appView.options());
-    }
-    if (clazz.isProgramClass()) {
-      InterfaceMethodRewriter.reportDependencyEdge(clazz.asProgramClass(), host, appView.options());
-    }
-  }
-
-  protected abstract boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest);
-
-  private DexProgramClass createNestAccessConstructor() {
-    // TODO(b/176900254): The class generated should be in a package that has lower change of
-    //   collisions (and perhaps be unique by some hash from the nest).
-    return new DexProgramClass(
-        appView.dexItemFactory().nestConstructorType,
-        null,
-        new SynthesizedOrigin("Nest based access desugaring", getClass()),
-        // Make the synthesized class public since shared in the whole program.
-        ClassAccessFlags.fromDexAccessFlags(
-            Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
-        appView.dexItemFactory().objectType,
-        DexTypeList.empty(),
-        appView.dexItemFactory().createString("nest"),
-        null,
-        Collections.emptyList(),
-        null,
-        Collections.emptyList(),
-        ClassSignature.noSignature(),
-        DexAnnotationSet.empty(),
-        DexEncodedField.EMPTY_ARRAY,
-        DexEncodedField.EMPTY_ARRAY,
-        DexEncodedMethod.EMPTY_ARRAY,
-        DexEncodedMethod.EMPTY_ARRAY,
-        appView.dexItemFactory().getSkipNameValidationForTesting(),
-        DexProgramClass::checksumFromType);
-  }
-
-  void synthesizeNestConstructor(DexApplication.Builder<?> builder) {
-    if (nestConstructorUsed) {
-      appView.appInfo().addSynthesizedClass(nestConstructor, true);
-      builder.addSynthesizedClass(nestConstructor);
-    }
-  }
-
-  private DexString computeMethodBridgeName(DexEncodedMethod method) {
-    String methodName = method.method.name.toString();
-    String fullName;
-    if (method.isStatic()) {
-      fullName = NEST_ACCESS_STATIC_METHOD_NAME_PREFIX + methodName;
-    } else {
-      fullName = NEST_ACCESS_METHOD_NAME_PREFIX + methodName;
-    }
-    return appView.dexItemFactory().createString(fullName);
-  }
-
-  private DexString computeFieldBridgeName(DexEncodedField field, boolean isGet) {
-    String fieldName = field.field.name.toString();
-    String fullName;
-    if (isGet && !field.isStatic()) {
-      fullName = NEST_ACCESS_FIELD_GET_NAME_PREFIX + fieldName;
-    } else if (isGet) {
-      fullName = NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX + fieldName;
-    } else if (!field.isStatic()) {
-      fullName = NEST_ACCESS_FIELD_PUT_NAME_PREFIX + fieldName;
-    } else {
-      fullName = NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX + fieldName;
-    }
-    return appView.dexItemFactory().createString(fullName);
-  }
-
-  private DexMethod computeMethodBridge(DexEncodedMethod encodedMethod) {
-    DexMethod method = encodedMethod.method;
-    DexProto proto =
-        encodedMethod.accessFlags.isStatic()
-            ? method.proto
-            : appView.dexItemFactory().prependHolderToProto(method);
-    return appView
-        .dexItemFactory()
-        .createMethod(method.holder, proto, computeMethodBridgeName(encodedMethod));
-  }
-
-  private DexMethod computeInitializerBridge(DexMethod method) {
-    DexProto newProto =
-        appView.dexItemFactory().appendTypeToProto(method.proto, nestConstructor.type);
-    return appView.dexItemFactory().createMethod(method.holder, newProto, method.name);
-  }
-
-  private DexMethod computeFieldBridge(DexEncodedField field, boolean isGet) {
-    DexType holderType = field.getHolderType();
-    DexType fieldType = field.field.type;
-    int bridgeParameterCount =
-        BooleanUtils.intValue(!field.isStatic()) + BooleanUtils.intValue(!isGet);
-    DexType[] parameters = new DexType[bridgeParameterCount];
-    if (!isGet) {
-      parameters[parameters.length - 1] = fieldType;
-    }
-    if (!field.isStatic()) {
-      parameters[0] = holderType;
-    }
-    DexType returnType = isGet ? fieldType : appView.dexItemFactory().voidType;
-    DexProto proto = appView.dexItemFactory().createProto(returnType, parameters);
-    return appView
-        .dexItemFactory()
-        .createMethod(holderType, proto, computeFieldBridgeName(field, isGet));
-  }
-
-  boolean invokeRequiresRewriting(DexEncodedMethod method, DexClassAndMethod context) {
-    assert method != null;
-    // Rewrite only when targeting other nest members private fields.
-    if (!method.accessFlags.isPrivate() || method.getHolderType() == context.getHolderType()) {
-      return false;
-    }
-    DexClass methodHolder = definitionFor(method.getHolderType());
-    assert methodHolder != null; // from encodedMethod
-    return methodHolder.getNestHost() == context.getHolder().getNestHost();
-  }
-
-  boolean fieldAccessRequiresRewriting(DexEncodedField field, DexClassAndMethod context) {
-    assert field != null;
-    // Rewrite only when targeting other nest members private fields.
-    if (!field.accessFlags.isPrivate() || field.getHolderType() == context.getHolderType()) {
-      return false;
-    }
-    DexClass fieldHolder = definitionFor(field.getHolderType());
-    assert fieldHolder != null; // from encodedField
-    return fieldHolder.getNestHost() == context.getHolder().getNestHost();
-  }
-
-  private boolean holderRequiresBridge(DexClass holder) {
-    // Bridges are added on program classes only.
-    // Bridges on class paths are added in different compilation units.
-    if (holder.isProgramClass()) {
-      return false;
-    } else if (holder.isClasspathClass()) {
-      return true;
-    }
-    assert holder.isLibraryClass();
-    Pair<DexClass, List<DexType>> nest = extractNest(holder);
-    assert nest != null : "Should be a compilation error if missing nest host on library class.";
-    reportIncompleteNest(nest.getSecond());
-    throw new Unreachable(
-        "Incomplete nest due to missing library class should raise a compilation error.");
-  }
-
-  DexMethod ensureFieldAccessBridge(DexEncodedField field, boolean isGet) {
-    DexClass holder = definitionFor(field.getHolderType());
-    assert holder != null;
-    DexMethod bridgeMethod = computeFieldBridge(field, isGet);
-    if (holderRequiresBridge(holder)) {
-      return bridgeMethod;
-    }
-    assert holder.isProgramClass();
-    // The map is used to avoid creating multiple times the bridge
-    // and remembers the bridges to add.
-    Map<DexField, ProgramMethod> fieldMap = isGet ? getFieldBridges : putFieldBridges;
-    assert holder.isProgramClass();
-    fieldMap.computeIfAbsent(
-        field.field,
-        k ->
-            DexEncodedMethod.createFieldAccessorBridge(
-                new DexFieldWithAccess(field, isGet), holder.asProgramClass(), bridgeMethod));
-    return bridgeMethod;
-  }
-
-  DexMethod ensureInvokeBridge(DexEncodedMethod method) {
-    // We add bridges only when targeting other nest members.
-    DexClass holder = definitionFor(method.getHolderType());
-    assert holder != null;
-    DexMethod bridgeMethod;
-    if (method.isInstanceInitializer()) {
-      nestConstructorUsed = true;
-      bridgeMethod = computeInitializerBridge(method.method);
-    } else {
-      bridgeMethod = computeMethodBridge(method);
-    }
-    if (holderRequiresBridge(holder)) {
-      return bridgeMethod;
-    }
-    // The map is used to avoid creating multiple times the bridge
-    // and remembers the bridges to add.
-    assert holder.isProgramClass();
-    bridges.computeIfAbsent(
-        method.method,
-        k ->
-            method.isInstanceInitializer()
-                ? method.toInitializerForwardingBridge(holder.asProgramClass(), bridgeMethod)
-                : method.toStaticForwardingBridge(
-                    holder.asProgramClass(), computeMethodBridge(method)));
-    return bridgeMethod;
-  }
-
-  protected class NestBasedAccessDesugaringUseRegistry extends UseRegistry {
-
-    private final DexClassAndMethod context;
-
-    NestBasedAccessDesugaringUseRegistry(DexClassAndMethod context) {
-      super(appView.options().itemFactory);
-      this.context = context;
-    }
-
-    private void registerInvoke(DexMethod method, Invoke.Type invokeType) {
-      // Calls to non class type are not done through nest based access control.
-      // Work-around for calls to enum.clone().
-      if (!method.holder.isClassType()) {
-        return;
-      }
-      DexEncodedMethod encodedMethod = lookupOnHolder(method, context, invokeType);
-      if (encodedMethod != null && invokeRequiresRewriting(encodedMethod, context)) {
-        ensureInvokeBridge(encodedMethod);
-      }
-    }
-
-    private void registerFieldAccess(DexField field, boolean isGet) {
-      // Since we only need to desugar accesses to private fields, and all accesses to private
-      // fields must be accessing the private field directly on its holder, we can lookup the field
-      // on the holder instead of resolving the field.
-      DexEncodedField encodedField = lookupOnHolder(field);
-      if (encodedField != null && fieldAccessRequiresRewriting(encodedField, context)) {
-        ensureFieldAccessBridge(encodedField, isGet);
-      }
-    }
-
-    @Override
-    public void registerInitClass(DexType clazz) {
-      // Nothing to do since we always use a public field for initializing the class.
-    }
-
-    @Override
-    public void registerInvokeVirtual(DexMethod method) {
-      // Calls to class nest mate private methods are targeted by invokeVirtual in jdk11.
-      // The spec recommends to do so, but do not enforce it, hence invokeDirect is also registered.
-      registerInvoke(method, Invoke.Type.VIRTUAL);
-    }
-
-    @Override
-    public void registerInvokeDirect(DexMethod method) {
-      registerInvoke(method, Invoke.Type.DIRECT);
-    }
-
-    @Override
-    public void registerInvokeStatic(DexMethod method) {
-      registerInvoke(method, Invoke.Type.STATIC);
-    }
-
-    @Override
-    public void registerInvokeInterface(DexMethod method) {
-      // Calls to interface nest mate private methods are targeted by invokeInterface in jdk11.
-      // The spec recommends to do so, but do not enforce it, hence invokeDirect is also registered.
-      registerInvoke(method, Invoke.Type.INTERFACE);
-    }
-
-    @Override
-    public void registerInvokeSuper(DexMethod method) {
-      registerInvoke(method, Invoke.Type.SUPER);
-    }
-
-    @Override
-    public void registerInstanceFieldWrite(DexField field) {
-      registerFieldAccess(field, false);
-    }
-
-    @Override
-    public void registerInstanceFieldRead(DexField field) {
-      registerFieldAccess(field, true);
-    }
-
-    @Override
-    public void registerNewInstance(DexType type) {
-      // Unrelated to access based control.
-      // The <init> method has to be rewritten instead
-      // and <init> is called through registerInvoke.
-    }
-
-    @Override
-    public void registerStaticFieldRead(DexField field) {
-      registerFieldAccess(field, true);
-    }
-
-    @Override
-    public void registerStaticFieldWrite(DexField field) {
-      registerFieldAccess(field, false);
-    }
-
-    @Override
-    public void registerTypeReference(DexType type) {
-      // Unrelated to access based control.
-    }
-
-    @Override
-    public void registerInstanceOf(DexType type) {
-      // Unrelated to access based control.
-    }
-  }
-
-  public static final class DexFieldWithAccess {
-
-    private final DexEncodedField field;
-    private final boolean isGet;
-
-    DexFieldWithAccess(DexEncodedField field, boolean isGet) {
-      this.field = field;
-      this.isGet = isGet;
-    }
-
-    @Override
-    public int hashCode() {
-      return Objects.hash(field, isGet);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-      if (o == null) {
-        return false;
-      }
-      if (getClass() != o.getClass()) {
-        return false;
-      }
-      DexFieldWithAccess other = (DexFieldWithAccess) o;
-      return isGet == other.isGet && field == other.field;
-    }
-
-    public boolean isGet() {
-      return isGet;
-    }
-
-    public boolean isStatic() {
-      return field.accessFlags.isStatic();
-    }
-
-    public boolean isPut() {
-      return !isGet();
-    }
-
-    public boolean isInstance() {
-      return !isStatic();
-    }
-
-    public boolean isStaticGet() {
-      return isStatic() && isGet();
-    }
-
-    public boolean isStaticPut() {
-      return isStatic() && isPut();
-    }
-
-    public boolean isInstanceGet() {
-      return isInstance() && isGet();
-    }
-
-    public boolean isInstancePut() {
-      return isInstance() && isPut();
-    }
-
-    public DexType getType() {
-      return field.field.type;
-    }
-
-    public DexType getHolder() {
-      return field.getHolderType();
-    }
-
-    public DexField getField() {
-      return field.field;
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
deleted file mode 100644
index 12418b3..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.desugar;
-
-import 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.NestedGraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap;
-import com.google.common.collect.ImmutableMap;
-import java.util.IdentityHashMap;
-import java.util.Map;
-
-public class NestedPrivateMethodLens extends NestedGraphLens {
-
-  // Map from nestHost to nest members including nest hosts
-  private final DexType nestConstructorType;
-  private final Map<DexField, DexMethod> getFieldMap;
-  private final Map<DexField, DexMethod> putFieldMap;
-
-  NestedPrivateMethodLens(
-      AppView<?> appView,
-      DexType nestConstructorType,
-      Map<DexMethod, DexMethod> methodMap,
-      Map<DexField, DexMethod> getFieldMap,
-      Map<DexField, DexMethod> putFieldMap,
-      GraphLens previousLens) {
-    super(
-        ImmutableMap.of(),
-        methodMap,
-        new EmptyBidirectionalOneToOneMap<>(),
-        new EmptyBidirectionalOneToOneMap<>(),
-        previousLens,
-        appView.dexItemFactory());
-    // No concurrent maps here, we do not want synchronization overhead.
-    assert methodMap instanceof IdentityHashMap;
-    assert getFieldMap instanceof IdentityHashMap;
-    assert putFieldMap instanceof IdentityHashMap;
-    this.nestConstructorType = nestConstructorType;
-    this.getFieldMap = getFieldMap;
-    this.putFieldMap = putFieldMap;
-  }
-
-  private DexMethod lookupFieldForMethod(
-      DexField field, DexMethod context, Map<DexField, DexMethod> map) {
-    assert context != null;
-    DexMethod bridge = map.get(field);
-    if (bridge != null && bridge.holder != context.holder) {
-      return bridge;
-    }
-    return null;
-  }
-
-  @Override
-  public DexMethod lookupGetFieldForMethod(DexField field, DexMethod context) {
-    assert getPrevious().lookupGetFieldForMethod(field, context) == null;
-    return lookupFieldForMethod(field, context, getFieldMap);
-  }
-
-  @Override
-  public DexMethod lookupPutFieldForMethod(DexField field, DexMethod context) {
-    assert getPrevious().lookupPutFieldForMethod(field, context) == null;
-    return lookupFieldForMethod(field, context, putFieldMap);
-  }
-
-  @Override
-  public boolean isContextFreeForMethods() {
-    return methodMap.isEmpty();
-  }
-
-  @Override
-  public boolean verifyIsContextFreeForMethod(DexMethod method) {
-    assert !methodMap.containsKey(getPrevious().lookupMethod(method));
-    return true;
-  }
-
-  // This is true because mappings specific to this class can be filled.
-  @Override
-  protected boolean isLegitimateToHaveEmptyMappings() {
-    return true;
-  }
-
-  private boolean isConstructorBridge(DexMethod method) {
-    DexType[] parameters = method.proto.parameters.values;
-    if (parameters.length == 0) {
-      return false;
-    }
-    DexType lastParameterType = parameters[parameters.length - 1];
-    return lastParameterType == nestConstructorType;
-  }
-
-  @Override
-  protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
-      RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
-    if (isConstructorBridge(method)) {
-      // TODO (b/132767654): Try to write a test which breaks that assertion.
-      assert prototypeChanges.isEmpty();
-      // TODO(b/164901008): Fix when the number of arguments overflows.
-      return RewrittenPrototypeDescription.none().withExtraUnusedNullParameter();
-    }
-    return prototypeChanges;
-  }
-
-  @Override
-  public MethodLookupResult internalDescribeLookupMethod(
-      MethodLookupResult previous, DexMethod context) {
-    DexMethod bridge = methodMap.get(previous.getReference());
-    if (bridge == null) {
-      return previous;
-    }
-    assert context != null : "Guaranteed by isContextFreeForMethod";
-    if (bridge.holder == context.holder) {
-      return previous;
-    }
-    MethodLookupResult.Builder resultBuilder =
-        MethodLookupResult.builder(this).setReference(bridge);
-    if (isConstructorBridge(bridge)) {
-      resultBuilder
-          .setPrototypeChanges(
-              internalDescribePrototypeChanges(previous.getPrototypeChanges(), bridge))
-          .setType(Type.DIRECT);
-    } else {
-      resultBuilder.setPrototypeChanges(previous.getPrototypeChanges()).setType(Type.STATIC);
-    }
-    return resultBuilder.build();
-  }
-
-  public static Builder builder() {
-    return new Builder();
-  }
-
-  public static class Builder extends NestedGraphLens.Builder {
-
-    private Map<DexField, DexMethod> getFieldMap = new IdentityHashMap<>();
-    private Map<DexField, DexMethod> putFieldMap = new IdentityHashMap<>();
-
-    public void mapGetField(DexField from, DexMethod to) {
-      getFieldMap.put(from, to);
-    }
-
-    public void mapPutField(DexField from, DexMethod to) {
-      putFieldMap.put(from, to);
-    }
-
-    public NestedPrivateMethodLens build(AppView<?> appView, DexType nestConstructorType) {
-      assert typeMap.isEmpty();
-      assert fieldMap.isEmpty();
-      if (getFieldMap.isEmpty() && methodMap.isEmpty() && putFieldMap.isEmpty()) {
-        return null;
-      }
-      return new NestedPrivateMethodLens(
-          appView, nestConstructorType, methodMap, getFieldMap, putFieldMap, appView.graphLens());
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
deleted file mode 100644
index e9ac116..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.desugar;
-
-import 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.DexMethod;
-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.utils.ThreadUtils;
-import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.function.BiConsumer;
-
-// Summary:
-// - Computes all the live nests reachable from Program Classes (Sequential), each time a
-//  nest is computed, its processing starts concurrently.
-// - Add bridges to be processed by further passes and create the maps
-// for the lens (Sequential)
-public class R8NestBasedAccessDesugaring extends NestBasedAccessDesugaring {
-
-  public R8NestBasedAccessDesugaring(AppView<?> appView) {
-    super(appView);
-  }
-
-  public NestedPrivateMethodLens run(
-      ExecutorService executorService, DexApplication.Builder<?> appBuilder)
-      throws ExecutionException {
-    assert !appView.options().canUseNestBasedAccess()
-        || appView.options().testing.enableForceNestBasedAccessDesugaringForTest;
-    computeAndProcessNestsConcurrently(executorService);
-    NestedPrivateMethodLens.Builder lensBuilder = NestedPrivateMethodLens.builder();
-    addDeferredBridgesAndMapMethods(lensBuilder);
-    clearNestAttributes();
-    synthesizeNestConstructor(appBuilder);
-    return lensBuilder.build(appView, getNestConstructorType());
-  }
-
-  private void addDeferredBridgesAndMapMethods(NestedPrivateMethodLens.Builder lensBuilder) {
-    // Here we add the bridges and we fill the lens map.
-    addDeferredBridgesAndMapMethods(bridges, lensBuilder::map);
-    addDeferredBridgesAndMapMethods(getFieldBridges, lensBuilder::mapGetField);
-    addDeferredBridgesAndMapMethods(putFieldBridges, lensBuilder::mapPutField);
-  }
-
-  private <E> void addDeferredBridgesAndMapMethods(
-      Map<E, ProgramMethod> bridges, BiConsumer<E, DexMethod> lensInserter) {
-    for (Map.Entry<E, ProgramMethod> entry : bridges.entrySet()) {
-      ProgramMethod method = entry.getValue();
-      method.getHolder().addMethod(method.getDefinition());
-      lensInserter.accept(entry.getKey(), method.getReference());
-    }
-    bridges.clear();
-  }
-
-  private void clearNestAttributes() {
-    // Clearing nest attributes is required to allow class merging to be performed across nests
-    // and to forbid other optimizations to introduce nest based accesses.
-    for (DexClass clazz : appView.appInfo().classes()) {
-      clazz.clearNestHost();
-      clazz.getNestMembersClassAttributes().clear();
-    }
-  }
-
-  private void computeAndProcessNestsConcurrently(ExecutorService executorService)
-      throws ExecutionException {
-    Set<DexType> nestHosts = Sets.newIdentityHashSet();
-    ;
-    List<Future<?>> futures = new ArrayList<>();
-    // It is possible that a nest member is on the program path but its nest host
-    // is only in the class path (or missing, raising an error).
-    // Nests are therefore computed the first time a nest member is met, host or not.
-    // The computedNestHosts list is there to avoid processing multiple times the same nest.
-    for (DexProgramClass clazz : appView.appInfo().classes()) {
-      if (clazz.isInANest()) {
-        DexType hostType = clazz.getNestHost();
-        if (!nestHosts.contains(hostType)) {
-          nestHosts.add(hostType);
-          futures.add(asyncProcessNest(clazz, executorService));
-        }
-      }
-    }
-    ThreadUtils.awaitFutures(futures);
-  }
-
-  // In R8, all classes are processed ahead of time.
-  @Override
-  protected boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest) {
-    return true;
-  }
-
-  @Override
-  void reportMissingNestHost(DexClass clazz) {
-    if (appView.options().ignoreMissingClasses) {
-      appView.options().nestDesugaringWarningMissingNestHost(clazz);
-    } else {
-      appView.options().errorMissingClassMissingNestHost(clazz);
-    }
-  }
-
-  @Override
-  void reportIncompleteNest(List<DexType> nest) {
-    if (appView.options().ignoreMissingClasses) {
-      appView.options().nestDesugaringWarningIncompleteNest(nest, appView);
-    } else {
-      appView.options().errorMissingClassIncompleteNest(nest, appView);
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
new file mode 100644
index 0000000..de211e8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
@@ -0,0 +1,172 @@
+// 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.desugar.nest;
+
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.reportDependencyEdge;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClasspathMethod;
+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.DexClasspathClass;
+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.UseRegistry;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * Responsible for reporting desugar dependencies and for synthesizing bridges in the program for
+ * accesses from the classpath into the program.
+ */
+public class D8NestBasedAccessDesugaring extends NestBasedAccessDesugaring {
+
+  public D8NestBasedAccessDesugaring(AppView<?> appView) {
+    super(appView);
+  }
+
+  public void reportDesugarDependencies() {
+    forEachNest(
+        nest -> {
+          DexClass hostClass = nest.getHostClass();
+          for (DexClass memberClass : nest.getMembers()) {
+            if (hostClass.isProgramClass() || memberClass.isProgramClass()) {
+              reportDependencyEdge(hostClass, memberClass, appView.options());
+              reportDependencyEdge(memberClass, hostClass, appView.options());
+            }
+          }
+        });
+  }
+
+  public void synthesizeBridgesForNestBasedAccessesOnClasspath(
+      MethodProcessor methodProcessor, ExecutorService executorService) throws ExecutionException {
+    List<DexClasspathClass> classpathClassesInNests = new ArrayList<>();
+    forEachNest(
+        nest -> {
+          if (nest.getHostClass().isClasspathClass()) {
+            classpathClassesInNests.add(nest.getHostClass().asClasspathClass());
+          }
+          Iterables.addAll(classpathClassesInNests, nest.getClasspathMembers());
+        });
+
+    NestBridgeConsumer bridgeConsumer = NestBridgeConsumer.createForD8(methodProcessor);
+    ThreadUtils.processItems(
+        classpathClassesInNests,
+        clazz -> synthesizeBridgesForNestBasedAccessesOnClasspath(clazz, bridgeConsumer),
+        executorService);
+  }
+
+  public void synthesizeBridgesForNestBasedAccessesOnClasspath(
+      DexClasspathClass clazz, NestBridgeConsumer bridgeConsumer) {
+    clazz.forEachClasspathMethod(
+        method ->
+            method.registerCodeReferencesForDesugaring(
+                new NestBasedAccessDesugaringUseRegistry(method, bridgeConsumer)));
+  }
+
+  private class NestBasedAccessDesugaringUseRegistry extends UseRegistry {
+
+    private final NestBridgeConsumer bridgeConsumer;
+    private final ClasspathMethod context;
+
+    NestBasedAccessDesugaringUseRegistry(
+        ClasspathMethod context, NestBridgeConsumer bridgeConsumer) {
+      super(appView.dexItemFactory());
+      this.bridgeConsumer = bridgeConsumer;
+      this.context = context;
+    }
+
+    private void registerFieldAccess(DexField reference, boolean isGet) {
+      DexClassAndField field =
+          reference.lookupMemberOnClass(appView.definitionForHolder(reference));
+      if (field != null && needsDesugaring(field, context)) {
+        ensureFieldAccessBridge(field, isGet, bridgeConsumer);
+      }
+    }
+
+    private void registerInvoke(DexMethod reference) {
+      if (!reference.getHolderType().isClassType()) {
+        return;
+      }
+      DexClassAndMethod method =
+          reference.lookupMemberOnClass(appView.definitionForHolder(reference));
+      if (method != null && needsDesugaring(method, context)) {
+        ensureMethodBridge(method, bridgeConsumer);
+      }
+    }
+
+    @Override
+    public void registerInvokeDirect(DexMethod method) {
+      registerInvoke(method);
+    }
+
+    @Override
+    public void registerInvokeInterface(DexMethod method) {
+      registerInvoke(method);
+    }
+
+    @Override
+    public void registerInvokeStatic(DexMethod method) {
+      registerInvoke(method);
+    }
+
+    @Override
+    public void registerInvokeSuper(DexMethod method) {
+      registerInvoke(method);
+    }
+
+    @Override
+    public void registerInvokeVirtual(DexMethod method) {
+      registerInvoke(method);
+    }
+
+    @Override
+    public void registerInstanceFieldWrite(DexField field) {
+      registerFieldAccess(field, false);
+    }
+
+    @Override
+    public void registerInstanceFieldRead(DexField field) {
+      registerFieldAccess(field, true);
+    }
+
+    @Override
+    public void registerStaticFieldRead(DexField field) {
+      registerFieldAccess(field, true);
+    }
+
+    @Override
+    public void registerStaticFieldWrite(DexField field) {
+      registerFieldAccess(field, false);
+    }
+
+    @Override
+    public void registerInitClass(DexType clazz) {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void registerInstanceOf(DexType type) {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void registerNewInstance(DexType type) {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void registerTypeReference(DexType type) {
+      // Intentionally empty.
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBridgeConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBridgeConsumer.java
new file mode 100644
index 0000000..7468cba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBridgeConsumer.java
@@ -0,0 +1,33 @@
+// 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.desugar.nest;
+
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+
+public class D8NestBridgeConsumer extends NestBridgeConsumer {
+
+  private final MethodProcessor methodProcessor;
+
+  public D8NestBridgeConsumer(MethodProcessor methodProcessor) {
+    this.methodProcessor = methodProcessor;
+  }
+
+  @Override
+  public void acceptFieldGetBridge(ProgramField target, ProgramMethod bridge) {
+    methodProcessor.scheduleMethodForProcessingAfterCurrentWave(bridge);
+  }
+
+  @Override
+  public void acceptFieldPutBridge(ProgramField target, ProgramMethod bridge) {
+    methodProcessor.scheduleMethodForProcessingAfterCurrentWave(bridge);
+  }
+
+  @Override
+  public void acceptMethodBridge(ProgramMethod target, ProgramMethod bridge) {
+    methodProcessor.scheduleMethodForProcessingAfterCurrentWave(bridge);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/Nest.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/Nest.java
new file mode 100644
index 0000000..1e66edc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/Nest.java
@@ -0,0 +1,76 @@
+// 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.desugar.nest;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Nest {
+
+  private final DexClass hostClass;
+  private final List<DexClass> members;
+  private final List<DexType> missingMembers;
+
+  private Nest(DexClass hostClass, List<DexClass> members, List<DexType> missingMembers) {
+    this.hostClass = hostClass;
+    this.members = members;
+    this.missingMembers = missingMembers;
+  }
+
+  public static Nest create(AppView<?> appView, DexClass clazz) {
+    assert clazz.isInANest();
+
+    DexClass hostClass = clazz.isNestHost() ? clazz : appView.definitionFor(clazz.getNestHost());
+    if (hostClass == null) {
+      // Missing nest host means the class is considered as not being part of a nest.
+      clazz.clearNestHost();
+      appView.options().nestDesugaringWarningMissingNestHost(clazz);
+      return null;
+    }
+
+    List<DexClass> members = new ArrayList<>(hostClass.getNestMembersClassAttributes().size());
+    List<DexType> missingMembers = new ArrayList<>();
+    hostClass.forEachNestMember(
+        memberType -> {
+          DexClass memberClass = appView.definitionFor(memberType);
+          if (memberClass != null) {
+            members.add(memberClass);
+          } else {
+            missingMembers.add(memberType);
+          }
+        });
+    return new Nest(hostClass, members, missingMembers);
+  }
+
+  public Iterable<DexClasspathClass> getClasspathMembers() {
+    return Iterables.transform(
+        Iterables.filter(members, DexClass::isClasspathClass), DexClass::asClasspathClass);
+  }
+
+  public DexClass getHostClass() {
+    return hostClass;
+  }
+
+  public List<DexClass> getMembers() {
+    return members;
+  }
+
+  public List<DexType> getMissingMembers() {
+    return missingMembers;
+  }
+
+  public boolean hasLibraryMember() {
+    return Iterables.any(members, DexClass::isLibraryClass);
+  }
+
+  public boolean hasMissingMembers() {
+    return !missingMembers.isEmpty();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
new file mode 100644
index 0000000..bab308e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
@@ -0,0 +1,376 @@
+// 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.desugar.nest;
+
+import com.android.tools.r8.cf.code.CfConstNull;
+import com.android.tools.r8.cf.code.CfFieldInstruction;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+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.CfCode;
+import com.android.tools.r8.graph.ClassAccessFlags;
+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.DexClassAndField;
+import com.android.tools.r8.graph.DexClassAndMember;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMember;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.LibraryMember;
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+import org.objectweb.asm.Opcodes;
+
+// NestBasedAccessDesugaring contains common code between the two subclasses
+// which are specialized for d8 and r8
+public class NestBasedAccessDesugaring {
+
+  // Short names to avoid creating long strings
+  public static final String NEST_ACCESS_NAME_PREFIX = "-$$Nest$";
+  private static final String NEST_ACCESS_METHOD_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "m";
+  private static final String NEST_ACCESS_STATIC_METHOD_NAME_PREFIX =
+      NEST_ACCESS_NAME_PREFIX + "sm";
+  private static final String NEST_ACCESS_FIELD_GET_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "fget";
+  private static final String NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX =
+      NEST_ACCESS_NAME_PREFIX + "sfget";
+  private static final String NEST_ACCESS_FIELD_PUT_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "fput";
+  private static final String NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX =
+      NEST_ACCESS_NAME_PREFIX + "sfput";
+  public static final String NEST_CONSTRUCTOR_NAME = NEST_ACCESS_NAME_PREFIX + "Constructor";
+
+  protected final AppView<?> appView;
+  private final DexItemFactory dexItemFactory;
+
+  // Common single empty class for nest based private constructors
+  private DexProgramClass nestConstructor;
+  private boolean nestConstructorUsed;
+
+  public NestBasedAccessDesugaring(AppView<?> appView) {
+    this.appView = appView;
+    this.dexItemFactory = appView.dexItemFactory();
+  }
+
+  void forEachNest(Consumer<Nest> consumer) {
+    Set<DexType> seenNestHosts = Sets.newIdentityHashSet();
+    for (DexProgramClass clazz : appView.appInfo().classes()) {
+      if (!clazz.isInANest() || !seenNestHosts.add(clazz.getNestHost())) {
+        continue;
+      }
+
+      Nest nest = Nest.create(appView, clazz);
+      if (nest != null) {
+        consumer.accept(nest);
+      }
+    }
+  }
+
+  public boolean needsDesugaring(ProgramMethod method) {
+    if (!method.getHolder().isInANest() || !method.getDefinition().hasCode()) {
+      return false;
+    }
+
+    Code code = method.getDefinition().getCode();
+    if (code.isDexCode()) {
+      assert appView.testing().allowDexInputForTesting;
+      return false;
+    }
+
+    if (!code.isCfCode()) {
+      throw new Unreachable("Unexpected attempt to determine if non-CF code needs desugaring");
+    }
+
+    return Iterables.any(
+        code.asCfCode().getInstructions(), instruction -> needsDesugaring(instruction, method));
+  }
+
+  private boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+    if (instruction.isFieldInstruction()) {
+      return needsDesugaring(instruction.asFieldInstruction().getField(), context);
+    }
+    if (instruction.isInvoke()) {
+      return needsDesugaring(instruction.asInvoke().getMethod(), context);
+    }
+    return false;
+  }
+
+  public boolean needsDesugaring(DexMember<?, ?> memberReference, ProgramMethod context) {
+    if (!context.getHolder().isInANest() || !memberReference.getHolderType().isClassType()) {
+      return false;
+    }
+    DexClass holder = appView.definitionForHolder(memberReference, context);
+    DexClassAndMember<?, ?> member = memberReference.lookupMemberOnClass(holder);
+    return member != null && needsDesugaring(member, context);
+  }
+
+  public boolean needsDesugaring(DexClassAndMember<?, ?> member, DexClassAndMethod context) {
+    return member.getAccessFlags().isPrivate()
+        && member.getHolderType() != context.getHolderType()
+        && member.getHolder().isInANest()
+        && member.getHolder().getNestHost() == context.getHolder().getNestHost();
+  }
+
+  public boolean desugar(ProgramMethod method) {
+    return desugar(method, null);
+  }
+
+  public boolean desugar(ProgramMethod method, NestBridgeConsumer bridgeConsumer) {
+    if (!method.getHolder().isInANest()) {
+      return false;
+    }
+
+    Code code = method.getDefinition().getCode();
+    if (!code.isCfCode()) {
+      appView
+          .options()
+          .reporter
+          .error(
+              new StringDiagnostic(
+                  "Unsupported attempt to desugar non-CF code",
+                  method.getOrigin(),
+                  method.getPosition()));
+      return false;
+    }
+
+    CfCode cfCode = code.asCfCode();
+    List<CfInstruction> desugaredInstructions =
+        ListUtils.flatMap(
+            cfCode.getInstructions(),
+            instruction -> desugarInstruction(instruction, method, bridgeConsumer),
+            null);
+    if (desugaredInstructions != null) {
+      cfCode.setInstructions(desugaredInstructions);
+      return true;
+    }
+    return false;
+  }
+
+  public List<CfInstruction> desugarInstruction(
+      CfInstruction instruction, ProgramMethod context, NestBridgeConsumer bridgeConsumer) {
+    if (instruction.isFieldInstruction()) {
+      return desugarFieldInstruction(instruction.asFieldInstruction(), context, bridgeConsumer);
+    }
+    if (instruction.isInvoke()) {
+      return desugarInvokeInstruction(instruction.asInvoke(), context, bridgeConsumer);
+    }
+    return null;
+  }
+
+  private List<CfInstruction> desugarFieldInstruction(
+      CfFieldInstruction instruction, ProgramMethod context, NestBridgeConsumer bridgeConsumer) {
+    // Since we only need to desugar accesses to private fields, and all accesses to private
+    // fields must be accessing the private field directly on its holder, we can lookup the
+    // field on the holder instead of resolving the field.
+    DexClass holder = appView.definitionForHolder(instruction.getField(), context);
+    DexClassAndField field = instruction.getField().lookupMemberOnClass(holder);
+    if (field == null || !needsDesugaring(field, context)) {
+      return null;
+    }
+
+    DexMethod bridge = ensureFieldAccessBridge(field, instruction.isFieldGet(), bridgeConsumer);
+    return ImmutableList.of(
+        new CfInvoke(Opcodes.INVOKESTATIC, bridge, field.getHolder().isInterface()));
+  }
+
+  private List<CfInstruction> desugarInvokeInstruction(
+      CfInvoke invoke, ProgramMethod context, NestBridgeConsumer bridgeConsumer) {
+    DexMethod invokedMethod = invoke.getMethod();
+    if (!invokedMethod.getHolderType().isClassType()) {
+      return null;
+    }
+
+    // Since we only need to desugar accesses to private methods, and all accesses to private
+    // methods must be accessing the private method directly on its holder, we can lookup the
+    // method on the holder instead of resolving the method.
+    DexClass holder = appView.definitionForHolder(invokedMethod, context);
+    DexClassAndMethod target = invokedMethod.lookupMemberOnClass(holder);
+    if (target == null || !needsDesugaring(target, context)) {
+      return null;
+    }
+
+    DexMethod bridge = ensureMethodBridge(target, bridgeConsumer);
+    if (target.getDefinition().isInstanceInitializer()) {
+      assert !invoke.isInterface();
+      return ImmutableList.of(
+          new CfConstNull(), new CfInvoke(Opcodes.INVOKESPECIAL, bridge, false));
+    }
+
+    return ImmutableList.of(new CfInvoke(Opcodes.INVOKESTATIC, bridge, invoke.isInterface()));
+  }
+
+  private RuntimeException reportIncompleteNest(LibraryMember<?, ?> member) {
+    Nest nest = Nest.create(appView, member.getHolder());
+    assert nest != null : "Should be a compilation error if missing nest host on library class.";
+    throw appView.options().errorMissingClassIncompleteNest(nest);
+  }
+
+  private DexProgramClass createNestAccessConstructor() {
+    // TODO(b/176900254): ensure hygienic synthetic class.
+    return new DexProgramClass(
+        dexItemFactory.nestConstructorType,
+        null,
+        new SynthesizedOrigin("Nest based access desugaring", getClass()),
+        // Make the synthesized class public since shared in the whole program.
+        ClassAccessFlags.fromDexAccessFlags(
+            Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
+        dexItemFactory.objectType,
+        DexTypeList.empty(),
+        dexItemFactory.createString("nest"),
+        null,
+        Collections.emptyList(),
+        null,
+        Collections.emptyList(),
+        ClassSignature.noSignature(),
+        DexAnnotationSet.empty(),
+        DexEncodedField.EMPTY_ARRAY,
+        DexEncodedField.EMPTY_ARRAY,
+        DexEncodedMethod.EMPTY_ARRAY,
+        DexEncodedMethod.EMPTY_ARRAY,
+        dexItemFactory.getSkipNameValidationForTesting(),
+        DexProgramClass::checksumFromType);
+  }
+
+  public DexProgramClass synthesizeNestConstructor() {
+    if (nestConstructorUsed && nestConstructor == null) {
+      nestConstructor = createNestAccessConstructor();
+      return nestConstructor;
+    }
+    return null;
+  }
+
+  DexMethod ensureFieldAccessBridge(
+      DexClassAndField field, boolean isGet, NestBridgeConsumer bridgeConsumer) {
+    if (field.isProgramField()) {
+      return ensureFieldAccessBridge(field.asProgramField(), isGet, bridgeConsumer);
+    }
+    if (field.isClasspathField()) {
+      return getFieldAccessBridgeReference(field, isGet);
+    }
+    assert field.isLibraryField();
+    throw reportIncompleteNest(field.asLibraryField());
+  }
+
+  private DexMethod ensureFieldAccessBridge(
+      ProgramField field, boolean isGet, NestBridgeConsumer bridgeConsumer) {
+    DexMethod bridgeReference = getFieldAccessBridgeReference(field, isGet);
+    synchronized (field.getHolder().getMethodCollection()) {
+      ProgramMethod bridge = field.getHolder().lookupProgramMethod(bridgeReference);
+      if (bridge == null) {
+        bridge = DexEncodedMethod.createFieldAccessorBridge(field, isGet, bridgeReference);
+        bridge.getHolder().addDirectMethod(bridge.getDefinition());
+        if (bridgeConsumer != null) {
+          bridgeConsumer.acceptFieldBridge(field, bridge, isGet);
+        }
+      }
+      return bridge.getReference();
+    }
+  }
+
+  private DexMethod getFieldAccessBridgeReference(DexClassAndField field, boolean isGet) {
+    int bridgeParameterCount =
+        BooleanUtils.intValue(!field.getAccessFlags().isStatic()) + BooleanUtils.intValue(!isGet);
+    DexType[] parameters = new DexType[bridgeParameterCount];
+    if (!isGet) {
+      parameters[parameters.length - 1] = field.getType();
+    }
+    if (!field.getAccessFlags().isStatic()) {
+      parameters[0] = field.getHolderType();
+    }
+    DexType returnType = isGet ? field.getType() : dexItemFactory.voidType;
+    DexProto proto = dexItemFactory.createProto(returnType, parameters);
+    return dexItemFactory.createMethod(
+        field.getHolderType(), proto, getFieldAccessBridgeName(field, isGet));
+  }
+
+  private DexString getFieldAccessBridgeName(DexClassAndField field, boolean isGet) {
+    String prefix;
+    if (isGet && !field.getAccessFlags().isStatic()) {
+      prefix = NEST_ACCESS_FIELD_GET_NAME_PREFIX;
+    } else if (isGet) {
+      prefix = NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX;
+    } else if (!field.getAccessFlags().isStatic()) {
+      prefix = NEST_ACCESS_FIELD_PUT_NAME_PREFIX;
+    } else {
+      prefix = NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX;
+    }
+    return dexItemFactory.createString(prefix + field.getName().toString());
+  }
+
+  DexMethod ensureMethodBridge(DexClassAndMethod method, NestBridgeConsumer bridgeConsumer) {
+    if (method.isProgramMethod()) {
+      return ensureMethodBridge(method.asProgramMethod(), bridgeConsumer);
+    }
+    if (method.isClasspathMethod()) {
+      return getMethodBridgeReference(method);
+    }
+    assert method.isLibraryMethod();
+    throw reportIncompleteNest(method.asLibraryMethod());
+  }
+
+  private DexMethod ensureMethodBridge(ProgramMethod method, NestBridgeConsumer bridgeConsumer) {
+    DexMethod bridgeReference = getMethodBridgeReference(method);
+    synchronized (method.getHolder().getMethodCollection()) {
+      ProgramMethod bridge = method.getHolder().lookupProgramMethod(bridgeReference);
+      if (bridge == null) {
+        DexEncodedMethod definition = method.getDefinition();
+        bridge =
+            definition.isInstanceInitializer()
+                ? definition.toInitializerForwardingBridge(
+                    method.getHolder(), bridgeReference, dexItemFactory)
+                : definition.toStaticForwardingBridge(
+                    method.getHolder(), bridgeReference, dexItemFactory);
+        bridge.getHolder().addDirectMethod(bridge.getDefinition());
+        if (bridgeConsumer != null) {
+          bridgeConsumer.acceptMethodBridge(method, bridge);
+        }
+      }
+    }
+    return bridgeReference;
+  }
+
+  private DexMethod getMethodBridgeReference(DexClassAndMethod method) {
+    if (method.getDefinition().isInstanceInitializer()) {
+      DexProto newProto =
+          dexItemFactory.appendTypeToProto(method.getProto(), dexItemFactory.nestConstructorType);
+      nestConstructorUsed = true;
+      return method.getReference().withProto(newProto, dexItemFactory);
+    }
+    DexProto proto =
+        method.getAccessFlags().isStatic()
+            ? method.getProto()
+            : dexItemFactory.prependHolderToProto(method.getReference());
+    return dexItemFactory.createMethod(method.getHolderType(), proto, getMethodBridgeName(method));
+  }
+
+  private DexString getMethodBridgeName(DexClassAndMethod method) {
+    String prefix =
+        method.getAccessFlags().isStatic()
+            ? NEST_ACCESS_STATIC_METHOD_NAME_PREFIX
+            : NEST_ACCESS_METHOD_NAME_PREFIX;
+    return dexItemFactory.createString(prefix + method.getName().toString());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBridgeConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBridgeConsumer.java
new file mode 100644
index 0000000..e6a8a03
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBridgeConsumer.java
@@ -0,0 +1,30 @@
+// 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.desugar.nest;
+
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+
+public abstract class NestBridgeConsumer {
+
+  public static D8NestBridgeConsumer createForD8(MethodProcessor methodProcessor) {
+    return new D8NestBridgeConsumer(methodProcessor);
+  }
+
+  public abstract void acceptFieldGetBridge(ProgramField target, ProgramMethod bridge);
+
+  public abstract void acceptFieldPutBridge(ProgramField target, ProgramMethod bridge);
+
+  public abstract void acceptMethodBridge(ProgramMethod target, ProgramMethod bridge);
+
+  public final void acceptFieldBridge(ProgramField target, ProgramMethod bridge, boolean isGet) {
+    if (isGet) {
+      acceptFieldGetBridge(target, bridge);
+    } else {
+      acceptFieldPutBridge(target, bridge);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index ef0e5ad..5112116 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1476,19 +1476,16 @@
       UtilityMethodForCodeOptimizations throwClassCastExceptionIfNotNullMethod =
           UtilityMethodsForCodeOptimizations.synthesizeThrowClassCastExceptionIfNotNullMethod(
               appView, context, methodProcessingId);
-      // TODO(b/172194277): Allow synthetics when generating CF.
-      if (throwClassCastExceptionIfNotNullMethod != null) {
-        throwClassCastExceptionIfNotNullMethod.optimize(methodProcessor);
-        InvokeStatic replacement =
-            InvokeStatic.builder()
-                .setMethod(throwClassCastExceptionIfNotNullMethod.getMethod())
-                .setSingleArgument(checkCast.object())
-                .setPosition(checkCast)
-                .build();
-        it.replaceCurrentInstruction(replacement);
-        assert replacement.lookupSingleTarget(appView, context) != null;
-        return RemoveCheckCastInstructionIfTrivialResult.REMOVED_CAST_DO_NARROW;
-      }
+      throwClassCastExceptionIfNotNullMethod.optimize(methodProcessor);
+      InvokeStatic replacement =
+          InvokeStatic.builder()
+              .setMethod(throwClassCastExceptionIfNotNullMethod.getMethod())
+              .setSingleArgument(checkCast.object())
+              .setPosition(checkCast)
+              .build();
+      it.replaceCurrentInstruction(replacement);
+      assert replacement.lookupSingleTarget(appView, context) != null;
+      return RemoveCheckCastInstructionIfTrivialResult.REMOVED_CAST_DO_NARROW;
     }
 
     // Otherwise, keep the checkcast to preserve verification errors. E.g., down-cast:
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NestReducer.java b/src/main/java/com/android/tools/r8/ir/optimize/NestReducer.java
index 6fa08c8..e20e28c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NestReducer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NestReducer.java
@@ -4,131 +4,102 @@
 
 package com.android.tools.r8.ir.optimize;
 
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.NestMemberClassAttribute;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.BooleanBox;
+import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
 
-// This pass
-// - cleans the nests: it removes missing nest host/members from the input and
-// report them (warning or error).
-// - clears nests which do not use nest based access control to allow other
-// optimizations such as class merging to perform better.
+/**
+ * This pass:
+ *
+ * <ul>
+ *   <li>cleans the nests: it removes missing nest host/members from the input,
+ *   <li>clears nests which do not use nest based access control to allow other optimizations such
+ *       as class merging to perform better.
+ * </ul>
+ */
 public class NestReducer {
 
-  private AppView<?> appView;
+  private AppView<AppInfoWithLiveness> appView;
 
-  public NestReducer(AppView<?> appView) {
+  public NestReducer(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
   }
 
-  private DexClass definitionFor(DexType type) {
-    assert appView.graphLens().lookupType(type) == type;
-    return appView.definitionFor(appView.graphLens().lookupType(type));
+  public void run(ExecutorService executorService, Timing timing) throws ExecutionException {
+    timing.begin("NestReduction");
+    if (appView.options().shouldDesugarNests()) {
+      removeNests();
+    } else {
+      reduceNests(executorService);
+    }
+    timing.end();
   }
 
-  public void run(ExecutorService executorService) throws ExecutionException {
-    Set<DexType> nestHosts = Sets.newIdentityHashSet();
-    List<Future<?>> futures = new ArrayList<>();
-    // It is possible that a nest member is on the program path but its nest host
-    // is only in the class path.
-    // Nests are therefore computed the first time a nest member is met, host or not.
-    // The computedNestHosts list is there to avoid processing multiple times the same nest.
+  private void removeNests() {
     for (DexProgramClass clazz : appView.appInfo().classes()) {
-      DexType hostType = clazz.getNestHost();
-      if (hostType != null && !nestHosts.contains(hostType)) {
-        nestHosts.add(hostType);
-        futures.add(
-            executorService.submit(
-                () -> {
-                  processNestFrom(clazz);
-                  return null; // we want a Callable not a Runnable to be able to throw
-                }));
-      }
-    }
-    ThreadUtils.awaitFutures(futures);
-  }
-
-  private void processNestFrom(DexClass clazz) {
-    DexClass nestHost = definitionFor(clazz.getNestHost());
-    if (nestHost == null) {
-      reportMissingNestHost(clazz);
-      clazz.clearNestHost();
-      return;
-    }
-    boolean hasPrivateMembers = hasPrivateMembers(nestHost);
-    Iterator<NestMemberClassAttribute> iterator =
-        nestHost.getNestMembersClassAttributes().iterator();
-    boolean reported = false;
-    while (iterator.hasNext()) {
-      DexClass member = definitionFor(iterator.next().getNestMember());
-      if (member == null) {
-        if (!reported) {
-          reported = true;
-          reportIncompleteNest(nestHost);
+      if (clazz.isInANest()) {
+        if (clazz.isNestHost()) {
+          clazz.clearNestMembers();
+        } else {
+          clazz.clearNestHost();
         }
-        iterator.remove();
-      } else {
-        hasPrivateMembers = hasPrivateMembers || hasPrivateMembers(member);
       }
     }
-    if (!hasPrivateMembers && appView.options().enableNestReduction) {
-      clearNestAttributes(nestHost);
-    }
   }
 
-  private void reportMissingNestHost(DexClass clazz) {
-    if (appView.options().ignoreMissingClasses) {
-      appView.options().warningMissingClassMissingNestHost(clazz);
-    } else {
-      appView.options().errorMissingClassMissingNestHost(clazz);
-    }
-  }
-
-  private void reportIncompleteNest(DexClass nestHost) {
-    List<DexType> nest = new ArrayList<>(nestHost.getNestMembersClassAttributes().size() + 1);
-    for (NestMemberClassAttribute attr : nestHost.getNestMembersClassAttributes()) {
-      nest.add(attr.getNestMember());
-    }
-    nest.add(nestHost.type);
-    if (appView.options().ignoreMissingClasses) {
-      appView.options().warningMissingClassIncompleteNest(nest, appView);
-    } else {
-      appView.options().errorMissingClassIncompleteNest(nest, appView);
-    }
-  }
-
-  private void clearNestAttributes(DexClass nestHost) {
-    nestHost.getNestMembersClassAttributes().clear();
-    for (NestMemberClassAttribute attr : nestHost.getNestMembersClassAttributes()) {
-      DexClass member = appView.definitionFor(appView.graphLens().lookupType(attr.getNestMember()));
-      member.clearNestHost();
-    }
-  }
-
-  private boolean hasPrivateMembers(DexClass clazz) {
-    for (DexEncodedMethod method : clazz.methods()) {
-      if (method.accessFlags.isPrivate()) {
-        return true;
+  private void reduceNests(ExecutorService executorService) throws ExecutionException {
+    Set<DexProgramClass> nestHosts = Sets.newIdentityHashSet();
+    Set<DexProgramClass> nestMembers = Sets.newIdentityHashSet();
+    for (DexProgramClass clazz : appView.appInfo().classes()) {
+      if (clazz.isInANest()) {
+        if (clazz.isNestHost()) {
+          nestHosts.add(clazz);
+        } else {
+          nestMembers.add(clazz);
+        }
       }
     }
-    for (DexEncodedField field : clazz.fields()) {
-      if (field.accessFlags.isPrivate()) {
-        return true;
-      }
+    ThreadUtils.processItems(nestHosts, this::processNestHost, executorService);
+    ThreadUtils.processItems(nestMembers, this::processNestMember, executorService);
+  }
+
+  private void processNestHost(DexProgramClass clazz) {
+    BooleanBox nestHasPrivateMembers =
+        new BooleanBox(IterableUtils.hasNext(clazz.members(DexEncodedMember::isPrivate)));
+    clazz
+        .getNestMembersClassAttributes()
+        .removeIf(
+            attribute -> {
+              DexProgramClass member =
+                  asProgramClassOrNull(appView.definitionFor(attribute.getNestMember(), clazz));
+              if (member == null) {
+                return true;
+              }
+              nestHasPrivateMembers.computeIfNotSet(
+                  () -> IterableUtils.hasNext(member.members(DexEncodedMember::isPrivate)));
+              return false;
+            });
+    if (nestHasPrivateMembers.isFalse() && appView.options().enableNestReduction) {
+      clazz.getNestMembersClassAttributes().clear();
     }
-    return false;
+  }
+
+  private void processNestMember(DexProgramClass clazz) {
+    DexProgramClass hostClass =
+        asProgramClassOrNull(appView.definitionFor(clazz.getNestHost(), clazz));
+    if (hostClass == null || !hostClass.isNestHost()) {
+      clazz.clearNestHost();
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
index a972b39..35467e9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.optimize;
 
+import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -23,10 +24,6 @@
   public static UtilityMethodForCodeOptimizations synthesizeToStringIfNotNullMethod(
       AppView<?> appView, ProgramMethod context, MethodProcessingId methodProcessingId) {
     InternalOptions options = appView.options();
-    if (options.isGeneratingClassFiles()) {
-      // TODO(b/172194277): Allow synthetics when generating CF.
-      return null;
-    }
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     DexProto proto = dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.objectType);
     SyntheticItems syntheticItems = appView.getSyntheticItems();
@@ -37,9 +34,10 @@
             dexItemFactory,
             builder ->
                 builder
-                    .setProto(proto)
                     .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
-                    .setCode(method -> getToStringIfNotNullCodeTemplate(method, options)),
+                    .setClassFileVersion(CfVersion.V1_8)
+                    .setCode(method -> getToStringIfNotNullCodeTemplate(method, options))
+                    .setProto(proto),
             methodProcessingId);
     return new UtilityMethodForCodeOptimizations(syntheticMethod);
   }
@@ -53,10 +51,6 @@
   public static UtilityMethodForCodeOptimizations synthesizeThrowClassCastExceptionIfNotNullMethod(
       AppView<?> appView, ProgramMethod context, MethodProcessingId methodProcessingId) {
     InternalOptions options = appView.options();
-    if (options.isGeneratingClassFiles()) {
-      // TODO(b/172194277): Allow synthetics when generating CF.
-      return null;
-    }
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     DexProto proto = dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.objectType);
     SyntheticItems syntheticItems = appView.getSyntheticItems();
@@ -67,10 +61,11 @@
             dexItemFactory,
             builder ->
                 builder
-                    .setProto(proto)
                     .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+                    .setClassFileVersion(CfVersion.V1_8)
                     .setCode(
-                        method -> getThrowClassCastExceptionIfNotNullCodeTemplate(method, options)),
+                        method -> getThrowClassCastExceptionIfNotNullCodeTemplate(method, options))
+                    .setProto(proto),
             methodProcessingId);
     return new UtilityMethodForCodeOptimizations(syntheticMethod);
   }
@@ -82,6 +77,33 @@
             options, method);
   }
 
+  public static UtilityMethodForCodeOptimizations synthesizeThrowNoSuchMethodErrorMethod(
+      AppView<?> appView, ProgramMethod context, MethodProcessingId methodProcessingId) {
+    InternalOptions options = appView.options();
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+    DexProto proto = dexItemFactory.createProto(dexItemFactory.noSuchMethodErrorType);
+    SyntheticItems syntheticItems = appView.getSyntheticItems();
+    ProgramMethod syntheticMethod =
+        syntheticItems.createMethod(
+            SyntheticNaming.SyntheticKind.THROW_NSME,
+            context,
+            dexItemFactory,
+            builder ->
+                builder
+                    .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+                    .setClassFileVersion(CfVersion.V1_8)
+                    .setCode(method -> getThrowNoSuchMethodErrorCodeTemplate(method, options))
+                    .setProto(proto),
+            methodProcessingId);
+    return new UtilityMethodForCodeOptimizations(syntheticMethod);
+  }
+
+  private static CfCode getThrowNoSuchMethodErrorCodeTemplate(
+      DexMethod method, InternalOptions options) {
+    return CfUtilityMethodsForCodeOptimizations
+        .CfUtilityMethodsForCodeOptimizationsTemplates_throwNoSuchMethodError(options, method);
+  }
+
   public static class UtilityMethodForCodeOptimizations {
 
     private final ProgramMethod method;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
index 99b1bda..d75e060 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
@@ -122,16 +122,13 @@
         UtilityMethodForCodeOptimizations toStringIfNotNullMethod =
             UtilityMethodsForCodeOptimizations.synthesizeToStringIfNotNullMethod(
                 appView, code.context(), state.methodProcessingId);
-        // TODO(b/172194277): Allow synthetics when generating CF.
-        if (toStringIfNotNullMethod != null) {
-          toStringIfNotNullMethod.optimize(state.methodProcessor);
-          InvokeStatic replacement =
-              InvokeStatic.builder()
-                  .setMethod(toStringIfNotNullMethod.getMethod())
-                  .setSingleArgument(object)
-                  .build();
-          instructionIterator.replaceCurrentInstruction(replacement);
-        }
+        toStringIfNotNullMethod.optimize(state.methodProcessor);
+        InvokeStatic replacement =
+            InvokeStatic.builder()
+                .setMethod(toStringIfNotNullMethod.getMethod())
+                .setSingleArgument(object)
+                .build();
+        instructionIterator.replaceCurrentInstruction(replacement);
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java b/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java
index 9625b93..a1731e7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java
@@ -33,6 +33,7 @@
 
   public static void registerSynthesizedCodeReferences(DexItemFactory factory) {
     factory.createSynthesizedType("Ljava/lang/ClassCastException;");
+    factory.createSynthesizedType("Ljava/lang/NoSuchMethodError;");
   }
 
   public static CfCode
@@ -73,6 +74,29 @@
         ImmutableList.of());
   }
 
+  public static CfCode CfUtilityMethodsForCodeOptimizationsTemplates_throwNoSuchMethodError(
+      InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        2,
+        0,
+        ImmutableList.of(
+            label0,
+            new CfNew(options.itemFactory.createType("Ljava/lang/NoSuchMethodError;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfInvoke(
+                183,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/NoSuchMethodError;"),
+                    options.itemFactory.createProto(options.itemFactory.voidType),
+                    options.itemFactory.createString("<init>")),
+                false),
+            new CfThrow()),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
   public static CfCode CfUtilityMethodsForCodeOptimizationsTemplates_toStringIfNotNull(
       InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/FieldAccessorBuilder.java b/src/main/java/com/android/tools/r8/ir/synthetic/FieldAccessorBuilder.java
new file mode 100644
index 0000000..b02544d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/FieldAccessorBuilder.java
@@ -0,0 +1,134 @@
+// 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.synthetic;
+
+import com.android.tools.r8.cf.code.CfFieldInstruction;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfTryCatch;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexClassAndField;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.OptionalBool;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import java.util.function.Consumer;
+import org.objectweb.asm.Opcodes;
+
+public class FieldAccessorBuilder {
+
+  private DexField field;
+  private OptionalBool isInstanceField = OptionalBool.unknown();
+  private OptionalBool isSetter = OptionalBool.unknown();
+  private DexMethod sourceMethod;
+
+  private FieldAccessorBuilder() {}
+
+  public static FieldAccessorBuilder builder() {
+    return new FieldAccessorBuilder();
+  }
+
+  public FieldAccessorBuilder apply(Consumer<FieldAccessorBuilder> consumer) {
+    consumer.accept(this);
+    return this;
+  }
+
+  public FieldAccessorBuilder setField(DexClassAndField field) {
+    return field.getAccessFlags().isStatic()
+        ? setStaticField(field.getReference())
+        : setInstanceField(field.getReference());
+  }
+
+  public FieldAccessorBuilder setGetter() {
+    isSetter = OptionalBool.FALSE;
+    return this;
+  }
+
+  public FieldAccessorBuilder setInstanceField(DexField field) {
+    this.field = field;
+    this.isInstanceField = OptionalBool.TRUE;
+    return this;
+  }
+
+  public FieldAccessorBuilder setSetter() {
+    isSetter = OptionalBool.TRUE;
+    return this;
+  }
+
+  public FieldAccessorBuilder setSourceMethod(DexMethod sourceMethod) {
+    this.sourceMethod = sourceMethod;
+    return this;
+  }
+
+  public FieldAccessorBuilder setStaticField(DexField field) {
+    this.field = field;
+    this.isInstanceField = OptionalBool.FALSE;
+    return this;
+  }
+
+  public CfCode build() {
+    assert validate();
+    int maxStack = 0;
+    int maxLocals = 0;
+    Builder<CfInstruction> instructions = ImmutableList.builder();
+    if (isInstanceField()) {
+      // Load the receiver.
+      instructions.add(new CfLoad(ValueType.OBJECT, maxLocals));
+      maxStack += 1;
+      maxLocals += 1;
+    }
+
+    if (isSetter()) {
+      // Load the argument.
+      ValueType fieldType = ValueType.fromDexType(field.getType());
+      instructions.add(new CfLoad(fieldType, maxLocals));
+      maxLocals += fieldType.requiredRegisters();
+    }
+
+    // Get or set the field.
+    int opcode =
+        Opcodes.GETSTATIC + BooleanUtils.intValue(isSetter()) + (isInstanceField.ordinal() << 1);
+    instructions.add(new CfFieldInstruction(opcode, field, field));
+
+    // Return.
+    if (isSetter()) {
+      instructions.add(new CfReturnVoid());
+    } else {
+      ValueType fieldType = ValueType.fromDexType(field.getType());
+      instructions.add(new CfReturn(fieldType));
+    }
+
+    ImmutableList<CfTryCatch> tryCatchRanges = ImmutableList.of();
+    ImmutableList<CfCode.LocalVariableInfo> localVariables = ImmutableList.of();
+    return new CfCode(
+        sourceMethod.getHolderType(),
+        maxStack,
+        maxLocals,
+        instructions.build(),
+        tryCatchRanges,
+        localVariables);
+  }
+
+  private boolean isSetter() {
+    return isSetter.isTrue();
+  }
+
+  private boolean isInstanceField() {
+    return isInstanceField.isTrue();
+  }
+
+  private boolean validate() {
+    assert field != null;
+    assert !isInstanceField.isUnknown();
+    assert !isSetter.isUnknown();
+    assert sourceMethod != null;
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/FieldAccessorSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/FieldAccessorSourceCode.java
deleted file mode 100644
index 8936c73..0000000
--- a/src/main/java/com/android/tools/r8/ir/synthetic/FieldAccessorSourceCode.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 2017, 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.synthetic;
-
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring.DexFieldWithAccess;
-
-// Source code representing simple forwarding method.
-public final class FieldAccessorSourceCode extends SyntheticSourceCode {
-
-  private final DexFieldWithAccess fieldWithAccess;
-
-  public FieldAccessorSourceCode(
-      DexType receiver,
-      DexMethod method,
-      Position callerPosition,
-      DexMethod originalMethod,
-      DexFieldWithAccess field) {
-    super(receiver, method, callerPosition, originalMethod);
-    this.fieldWithAccess = field;
-    assert method.proto.returnType == fieldWithAccess.getType() || fieldWithAccess.isPut();
-  }
-
-  @Override
-  protected void prepareInstructions() {
-    if (fieldWithAccess.isInstanceGet()) {
-      ValueType valueType = ValueType.fromDexType(proto.returnType);
-      int objReg = getParamRegister(0);
-      int returnReg = nextRegister(valueType);
-      add(builder -> builder.addInstanceGet(returnReg, objReg, fieldWithAccess.getField()));
-      add(builder -> builder.addReturn(returnReg));
-    } else if (fieldWithAccess.isStaticGet()) {
-      ValueType valueType = ValueType.fromDexType(proto.returnType);
-      int returnReg = nextRegister(valueType);
-      add(builder -> builder.addStaticGet(returnReg, fieldWithAccess.getField()));
-      add(builder -> builder.addReturn(returnReg));
-    } else if (fieldWithAccess.isInstancePut()) {
-      int objReg = getParamRegister(0);
-      int putValueReg = getParamRegister(1);
-      add(builder -> builder.addInstancePut(putValueReg, objReg, fieldWithAccess.getField()));
-      add(IRBuilder::addReturn);
-    } else {
-      assert fieldWithAccess.isStaticPut();
-      int putValueReg = getParamRegister(0);
-      add(builder -> builder.addStaticPut(putValueReg, fieldWithAccess.getField()));
-      add(IRBuilder::addReturn);
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
index 8d8ca9f..794fd3e 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
@@ -20,8 +20,10 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
+import java.util.function.Consumer;
 import org.objectweb.asm.Opcodes;
 
 public class ForwardMethodBuilder {
@@ -41,6 +43,7 @@
   private DexMethod sourceMethod = null;
   private DexMethod targetMethod = null;
 
+  private boolean sourceMethodHasExtraUnusedParameter = false;
   private boolean staticSource = false;
 
   private InvokeType invokeType = null;
@@ -53,12 +56,24 @@
     this.factory = factory;
   }
 
+  public ForwardMethodBuilder apply(Consumer<ForwardMethodBuilder> fn) {
+    fn.accept(this);
+    return this;
+  }
+
   public ForwardMethodBuilder setNonStaticSource(DexMethod method) {
     sourceMethod = method;
     staticSource = false;
     return this;
   }
 
+  public ForwardMethodBuilder setNonStaticSourceWithExtraUnusedParameter(DexMethod method) {
+    sourceMethod = method;
+    staticSource = false;
+    sourceMethodHasExtraUnusedParameter = true;
+    return this;
+  }
+
   public ForwardMethodBuilder setStaticSource(DexMethod method) {
     sourceMethod = method;
     staticSource = true;
@@ -79,6 +94,10 @@
     return this;
   }
 
+  public ForwardMethodBuilder setConstructorTarget(DexMethod method) {
+    return setDirectTarget(method, false);
+  }
+
   public ForwardMethodBuilder setDirectTarget(DexMethod method, boolean isInterface) {
     targetMethod = method;
     invokeType = InvokeType.SPECIAL;
@@ -126,7 +145,9 @@
       maxLocals += 1;
     }
     DexType[] sourceParameters = getSourceParameters();
-    for (int i = 0; i < sourceParameters.length; i++) {
+    for (int i = 0;
+        i < sourceParameters.length - BooleanUtils.intValue(sourceMethodHasExtraUnusedParameter);
+        i++) {
       DexType parameter = sourceParameters[i];
       ValueType parameterType = ValueType.fromDexType(parameter);
       instructions.add(new CfLoad(parameterType, maxLocals));
@@ -212,7 +233,9 @@
   }
 
   private int sourceArguments() {
-    return sourceMethod.getParameters().size() + (isStaticSource() ? 0 : 1);
+    return sourceMethod.getParameters().size()
+        + (isStaticSource() ? 0 : 1)
+        - BooleanUtils.intValue(sourceMethodHasExtraUnusedParameter);
   }
 
   private int targetArguments() {
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
index f0ba88d..6decd5e 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
@@ -15,7 +15,6 @@
 import com.android.tools.r8.retrace.RetracedType;
 import com.android.tools.r8.retrace.StackTraceElementProxy;
 import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.StringUtils.BraceType;
 import com.android.tools.r8.utils.TriFunction;
 import java.util.ArrayList;
 import java.util.List;
@@ -287,7 +286,7 @@
                   return original.getMethodArguments();
                 }
                 return StringUtils.join(
-                    retraced.getMethodArguments(), ",", BraceType.NONE, RetracedType::getTypeName);
+                    ",", retraced.getMethodArguments(), RetracedType::getTypeName);
               });
       orderedIndices.add(methodArguments);
       return this;
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 95cdf4a..1905d90 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.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.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexApplication;
@@ -56,6 +57,7 @@
 import com.android.tools.r8.graph.LookupLambdaTarget;
 import com.android.tools.r8.graph.LookupTarget;
 import com.android.tools.r8.graph.MethodAccessInfoCollection;
+import com.android.tools.r8.graph.NestMemberClassAttribute;
 import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
 import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.graph.ProgramField;
@@ -88,6 +90,7 @@
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
 import com.android.tools.r8.ir.desugar.LambdaRewriter;
 import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
+import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
 import com.android.tools.r8.kotlin.KotlinMetadataEnqueuerExtension;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
@@ -107,11 +110,13 @@
 import com.android.tools.r8.utils.InternalOptions.DesugarState;
 import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
 import com.android.tools.r8.utils.IteratorUtils;
+import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
 import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.Visibility;
 import com.android.tools.r8.utils.WorkList;
@@ -121,6 +126,7 @@
 import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
 import com.google.common.base.Equivalence.Wrapper;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import com.google.common.collect.Sets.SetView;
@@ -137,6 +143,7 @@
 import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
@@ -210,6 +217,7 @@
   // Don't hold a direct pointer to app info (use appView).
   private AppInfoWithClassHierarchy appInfo;
   private final AppView<AppInfoWithClassHierarchy> appView;
+  private final ExecutorService executorService;
   private SubtypingInfo subtypingInfo;
   private final InternalOptions options;
   private RootSet rootSet;
@@ -371,7 +379,9 @@
 
   private final LambdaRewriter lambdaRewriter;
   private final BackportedMethodRewriter backportRewriter;
+  private final NestBasedAccessDesugaring nestBasedAccessRewriter;
   private final TwrCloseResourceRewriter twrCloseResourceRewriter;
+
   private final DesugaredLibraryConversionWrapperAnalysis desugaredLibraryWrapperAnalysis;
   private final Map<DexType, Pair<LambdaClass, ProgramMethod>> lambdaClasses =
       new IdentityHashMap<>();
@@ -380,10 +390,12 @@
   private final Map<DexMethod, ProgramMethod> methodsWithBackports = new IdentityHashMap<>();
   private final Map<DexMethod, ProgramMethod> methodsWithTwrCloseResource = new IdentityHashMap<>();
   private final Set<DexProgramClass> classesWithSerializableLambdas = Sets.newIdentityHashSet();
+  private final ProgramMethodSet pendingDesugaring = ProgramMethodSet.create();
   private final MainDexTracingResult previousMainDexTracingResult;
 
   Enqueuer(
       AppView<? extends AppInfoWithClassHierarchy> appView,
+      ExecutorService executorService,
       SubtypingInfo subtypingInfo,
       GraphConsumer keptGraphConsumer,
       Mode mode,
@@ -392,6 +404,7 @@
     InternalOptions options = appView.options();
     this.appInfo = appView.appInfo();
     this.appView = appView.withClassHierarchy();
+    this.executorService = executorService;
     this.subtypingInfo = subtypingInfo;
     this.forceProguardCompatibility = options.forceProguardCompatibility;
     this.graphReporter = new GraphReporter(appView, keptGraphConsumer);
@@ -418,6 +431,8 @@
     liveMethods = new LiveMethodsSet(graphReporter::registerMethod);
     liveFields = new LiveFieldsSet(graphReporter::registerField);
     lambdaRewriter = options.desugarState == DesugarState.ON ? new LambdaRewriter(appView) : null;
+    nestBasedAccessRewriter =
+        options.shouldDesugarNests() ? new NestBasedAccessDesugaring(appView) : null;
     backportRewriter =
         options.desugarState == DesugarState.ON ? new BackportedMethodRewriter(appView) : null;
     twrCloseResourceRewriter =
@@ -1705,6 +1720,17 @@
       recordTypeReference(innerClassAttribute.getInner(), clazz, this::ignoreMissingClass);
       recordTypeReference(innerClassAttribute.getOuter(), clazz, this::ignoreMissingClass);
     }
+
+    // Mark types in nest attributes referenced.
+    if (clazz.isNestHost()) {
+      for (NestMemberClassAttribute nestMemberClassAttribute :
+          clazz.getNestMembersClassAttributes()) {
+        recordTypeReference(nestMemberClassAttribute.getNestMember(), clazz);
+      }
+    } else {
+      recordTypeReference(clazz.getNestHost(), clazz);
+    }
+
     EnclosingMethodAttribute enclosingMethodAttribute = clazz.getEnclosingMethodAttribute();
     if (enclosingMethodAttribute != null) {
       DexMethod enclosingMethod = enclosingMethodAttribute.getEnclosingMethod();
@@ -3053,6 +3079,8 @@
 
   private static class SyntheticAdditions {
 
+    List<ProgramMethod> desugaredMethods = new LinkedList<>();
+
     Map<DexType, Pair<DexProgramClass, ProgramMethod>> syntheticInstantiations =
         new IdentityHashMap<>();
 
@@ -3061,6 +3089,8 @@
 
     Map<DexMethod, ProgramMethod> liveMethods = new IdentityHashMap<>();
 
+    Map<DexType, DexProgramClass> syntheticProgramClasses = new IdentityHashMap<>();
+
     Map<DexType, DexClasspathClass> syntheticClasspathClasses = new IdentityHashMap<>();
 
     // Subset of live methods that need have keep requirements.
@@ -3072,8 +3102,10 @@
 
     boolean isEmpty() {
       boolean empty =
-          syntheticInstantiations.isEmpty()
+          desugaredMethods.isEmpty()
+              && syntheticInstantiations.isEmpty()
               && liveMethods.isEmpty()
+              && syntheticProgramClasses.isEmpty()
               && syntheticClasspathClasses.isEmpty();
       assert !empty || (liveMethodsWithKeepActions.isEmpty() && mainDexTypes.isEmpty());
       return empty;
@@ -3089,6 +3121,11 @@
       assert old == null;
     }
 
+    void addProgramClass(DexProgramClass clazz) {
+      DexProgramClass old = syntheticProgramClasses.put(clazz.type, clazz);
+      assert old == null;
+    }
+
     void addLiveMethod(ProgramMethod method) {
       DexMethod signature = method.getDefinition().method;
       assert !liveMethods.containsKey(signature);
@@ -3103,6 +3140,7 @@
 
     void amendApplication(Builder appBuilder) {
       assert !isEmpty();
+      appBuilder.addProgramClasses(syntheticProgramClasses.values());
       appBuilder.addClasspathClasses(syntheticClasspathClasses.values());
     }
 
@@ -3114,9 +3152,14 @@
     void enqueueWorkItems(Enqueuer enqueuer) {
       assert !isEmpty();
       assert enqueuer.mode.isInitialTreeShaking();
+
       // All synthetic additions are initial tree shaking only. No need to track keep reasons.
       KeepReasonWitness fakeReason = enqueuer.graphReporter.fakeReportShouldNotBeUsed();
 
+      for (ProgramMethod desugaredMethod : desugaredMethods) {
+        enqueuer.workList.enqueueTraceCodeAction(desugaredMethod);
+      }
+
       liveMethodsWithKeepActions.forEach(
           item -> enqueuer.keepInfo.joinMethod(item.getFirst(), item.getSecond()));
       for (Pair<DexProgramClass, ProgramMethod> clazzAndContext :
@@ -3160,7 +3203,7 @@
     }
   }
 
-  private void synthesize() {
+  private void synthesize() throws ExecutionException {
     if (!mode.isInitialTreeShaking()) {
       return;
     }
@@ -3168,11 +3211,13 @@
     // In particular these additions are order independent, i.e., it does not matter which are
     // registered first and no dependencies may exist among them.
     SyntheticAdditions additions = new SyntheticAdditions();
+    desugar(additions);
     synthesizeInvokeSpecialBridges(additions);
     synthesizeInterfaceMethodBridges(additions);
     synthesizeLambdas(additions);
     synthesizeLibraryConversionWrappers(additions);
     synthesizeBackports(additions);
+    synthesizeNestConstructor(additions);
     synthesizeTwrCloseResource(additions);
     if (additions.isEmpty()) {
       return;
@@ -3195,6 +3240,50 @@
     additions.enqueueWorkItems(this);
   }
 
+  private void desugar(SyntheticAdditions additions) throws ExecutionException {
+    ThreadUtils.processItems(pendingDesugaring, this::desugar, executorService);
+    Iterables.addAll(additions.desugaredMethods, pendingDesugaring);
+    pendingDesugaring.clear();
+  }
+
+  private void desugar(ProgramMethod method) {
+    Code code = method.getDefinition().getCode();
+    if (!code.isCfCode()) {
+      appView
+          .options()
+          .reporter
+          .error(
+              new StringDiagnostic(
+                  "Unsupported attempt to desugar non-CF code",
+                  method.getOrigin(),
+                  method.getPosition()));
+      return;
+    }
+
+    CfCode cfCode = code.asCfCode();
+    List<CfInstruction> desugaredInstructions =
+        ListUtils.flatMap(
+            cfCode.getInstructions(),
+            instruction -> {
+              // TODO(b/177810578): Migrate other cf-to-cf based desugaring here, and assert that
+              //  that at most one instruction desugarer applies to each instruction.
+              if (nestBasedAccessRewriter != null) {
+                List<CfInstruction> replacement =
+                    nestBasedAccessRewriter.desugarInstruction(instruction, method, null);
+                if (replacement != null) {
+                  return replacement;
+                }
+              }
+              return null;
+            },
+            null);
+    if (desugaredInstructions != null) {
+      cfCode.setInstructions(desugaredInstructions);
+    } else {
+      assert false : "Expected code to be desugared";
+    }
+  }
+
   private void synthesizeInvokeSpecialBridges(SyntheticAdditions additions) {
     SortedProgramMethodSet bridges =
         appView.getInvokeSpecialBridgeSynthesizer().insertBridgesForR8();
@@ -3217,6 +3306,16 @@
     }
   }
 
+  private void synthesizeNestConstructor(SyntheticAdditions additions) {
+    if (nestBasedAccessRewriter != null) {
+      DexProgramClass nestConstructor = nestBasedAccessRewriter.synthesizeNestConstructor();
+      if (nestConstructor != null) {
+        // TODO(b/177638147): use getSyntheticItems().createClass().
+        additions.addProgramClass(nestConstructor);
+      }
+    }
+  }
+
   private void synthesizeTwrCloseResource(SyntheticAdditions additions) {
     for (ProgramMethod method : methodsWithTwrCloseResource.values()) {
       twrCloseResourceRewriter.rewriteCf(method, additions::addLiveMethod);
@@ -3874,7 +3973,8 @@
     processAnnotations(holder, method);
     definition.parameterAnnotationsList.forEachAnnotation(
         annotation -> processAnnotation(holder, method, annotation));
-    method.registerCodeReferences(useRegistryFactory.create(appView, method, this));
+
+    traceNonDesugaredCode(method);
 
     // Add all dependent members to the workqueue.
     enqueueRootItems(rootSet.getDependentItems(definition));
@@ -3885,6 +3985,21 @@
     analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method, context));
   }
 
+  private void traceNonDesugaredCode(ProgramMethod method) {
+    if (getMode().isInitialTreeShaking()) {
+      if (nestBasedAccessRewriter != null && nestBasedAccessRewriter.needsDesugaring(method)) {
+        pendingDesugaring.add(method);
+        return;
+      }
+    }
+
+    traceCode(method);
+  }
+
+  void traceCode(ProgramMethod method) {
+    method.registerCodeReferences(useRegistryFactory.create(appView, method, this));
+  }
+
   private void checkMemberForSoftPinning(ProgramMember<?, ?> member) {
     DexMember<?, ?> reference = member.getDefinition().getReference();
     Set<ProguardKeepRuleBase> softPinRules = rootSet.softPinned.getRulesForReference(reference);
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
index 3c9101a..04fb840 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
@@ -11,24 +11,33 @@
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.shaking.Enqueuer.Mode;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
 
 public class EnqueuerFactory {
 
   public static Enqueuer createForInitialTreeShaking(
       AppView<? extends AppInfoWithClassHierarchy> appView,
+      ExecutorService executorService,
       SubtypingInfo subtypingInfo) {
     return new Enqueuer(
-        appView, subtypingInfo, null, Mode.INITIAL_TREE_SHAKING, MainDexTracingResult.NONE);
+        appView,
+        executorService,
+        subtypingInfo,
+        null,
+        Mode.INITIAL_TREE_SHAKING,
+        MainDexTracingResult.NONE);
   }
 
   public static Enqueuer createForFinalTreeShaking(
       AppView<? extends AppInfoWithClassHierarchy> appView,
+      ExecutorService executorService,
       SubtypingInfo subtypingInfo,
       GraphConsumer keptGraphConsumer,
       Set<DexType> initialPrunedTypes) {
     Enqueuer enqueuer =
         new Enqueuer(
             appView,
+            executorService,
             subtypingInfo,
             keptGraphConsumer,
             Mode.FINAL_TREE_SHAKING,
@@ -40,18 +49,27 @@
   }
 
   public static Enqueuer createForInitialMainDexTracing(
-      AppView<? extends AppInfoWithClassHierarchy> appView, SubtypingInfo subtypingInfo) {
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      ExecutorService executorService,
+      SubtypingInfo subtypingInfo) {
     return new Enqueuer(
-        appView, subtypingInfo, null, Mode.INITIAL_MAIN_DEX_TRACING, MainDexTracingResult.NONE);
+        appView,
+        executorService,
+        subtypingInfo,
+        null,
+        Mode.INITIAL_MAIN_DEX_TRACING,
+        MainDexTracingResult.NONE);
   }
 
   public static Enqueuer createForFinalMainDexTracing(
       AppView<? extends AppInfoWithClassHierarchy> appView,
+      ExecutorService executorService,
       SubtypingInfo subtypingInfo,
       GraphConsumer keptGraphConsumer,
       MainDexTracingResult previousMainDexTracingResult) {
     return new Enqueuer(
         appView,
+        executorService,
         subtypingInfo,
         keptGraphConsumer,
         Mode.FINAL_MAIN_DEX_TRACING,
@@ -60,10 +78,12 @@
 
   public static Enqueuer createForWhyAreYouKeeping(
       AppView<? extends AppInfoWithClassHierarchy> appView,
+      ExecutorService executorService,
       SubtypingInfo subtypingInfo,
       GraphConsumer keptGraphConsumer) {
     return new Enqueuer(
         appView,
+        executorService,
         subtypingInfo,
         keptGraphConsumer,
         Mode.WHY_ARE_YOU_KEEPING,
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
index 0be900c..b403b54 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -174,6 +174,19 @@
     }
   }
 
+  static class TraceCodeAction extends EnqueuerAction {
+    private final ProgramMethod method;
+
+    TraceCodeAction(ProgramMethod method) {
+      this.method = method;
+    }
+
+    @Override
+    public void run(Enqueuer enqueuer) {
+      enqueuer.traceCode(method);
+    }
+  }
+
   static class TraceConstClassAction extends EnqueuerAction {
     private final DexType type;
     // TODO(b/175854431): Avoid pushing context on worklist.
@@ -304,6 +317,10 @@
     queue.add(new MarkFieldKeptAction(field, witness));
   }
 
+  public void enqueueTraceCodeAction(ProgramMethod method) {
+    queue.add(new TraceCodeAction(method));
+  }
+
   public void enqueueTraceConstClassAction(DexType type, ProgramMethod context) {
     queue.add(new TraceConstClassAction(type, context));
   }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 11dd893..9c03710 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -300,7 +300,13 @@
       Consumer<SyntheticMethodBuilder> fn,
       MethodProcessingId methodProcessingId) {
     return createMethod(
-        kind, context, factory, fn, methodProcessingId::getFullyQualifiedIdAndIncrement);
+        kind,
+        context,
+        factory,
+        fn,
+        methodProcessingId != null
+            ? methodProcessingId::getFullyQualifiedIdAndIncrement
+            : this::getNextSyntheticId);
   }
 
   // TODO(b/172194101): Remove/private this once the uniqueness is a property of the context.
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index c501eb4..93c3f64 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -28,6 +28,7 @@
     STATIC_INTERFACE_CALL("StaticInterfaceCall", true),
     TO_STRING_IF_NOT_NULL("ToStringIfNotNull", true),
     THROW_CCE_IF_NOT_NULL("ThrowCCEIfNotNull", true),
+    THROW_NSME("ThrowNSME", true),
     TWR_CLOSE_RESOURCE("TwrCloseResource", true),
     SERVICE_LOADER("ServiceLoad", true);
 
diff --git a/src/main/java/com/android/tools/r8/utils/BooleanBox.java b/src/main/java/com/android/tools/r8/utils/BooleanBox.java
index f46ac1c..f6b790d 100644
--- a/src/main/java/com/android/tools/r8/utils/BooleanBox.java
+++ b/src/main/java/com/android/tools/r8/utils/BooleanBox.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.utils;
 
+import java.util.function.BooleanSupplier;
+
 public class BooleanBox {
 
   private boolean value;
@@ -14,10 +16,24 @@
     set(initialValue);
   }
 
+  public void computeIfNotSet(BooleanSupplier supplier) {
+    if (isFalse()) {
+      set(supplier.getAsBoolean());
+    }
+  }
+
   public boolean get() {
     return value;
   }
 
+  public boolean isFalse() {
+    return !get();
+  }
+
+  public boolean isTrue() {
+    return get();
+  }
+
   public void set() {
     set(true);
   }
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 2e2a75c..decafef 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
-import static com.google.common.base.Predicates.not;
 
 import com.android.tools.r8.ClassFileConsumer;
 import com.android.tools.r8.CompilationMode;
@@ -22,7 +21,6 @@
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.ExperimentalClassFileVersionDiagnostic;
-import com.android.tools.r8.errors.IncompleteNestNestDesugarDiagnosic;
 import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
 import com.android.tools.r8.errors.InvalidDebugInfoException;
 import com.android.tools.r8.errors.InvalidLibrarySuperclassDiagnostic;
@@ -32,10 +30,11 @@
 import com.android.tools.r8.features.FeatureSplitConfiguration;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexDefinition;
+import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexLibraryClass;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
@@ -48,6 +47,7 @@
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.conversion.MethodProcessingId;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.nest.Nest;
 import com.android.tools.r8.ir.optimize.Inliner;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
 import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory;
@@ -73,13 +73,7 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
-import java.io.IOException;
 import java.io.PrintStream;
-import java.lang.reflect.GenericSignatureFormatError;
-import java.nio.file.Files;
-import java.nio.file.InvalidPathException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -493,7 +487,7 @@
     if (testing.enableForceNestBasedAccessDesugaringForTest) {
       return true;
     }
-    return enableNestBasedAccessDesugaring && !canUseNestBasedAccess();
+    return !canUseNestBasedAccess();
   }
 
   public Set<String> extensiveLoggingFilter = getExtensiveLoggingFilter();
@@ -514,10 +508,6 @@
   public boolean enableLambdaMerging = false;
   // Flag to turn on/off desugaring in D8/R8.
   public DesugarState desugarState = DesugarState.ON;
-  // Flag to turn on/off desugaring of invoke-special to a virtual method on the current class.
-  public boolean enableInvokeSpecialToVirtualMethodDesugaring = true;
-  // Flag to turn on/off JDK11+ nest-access control
-  public boolean enableNestBasedAccessDesugaring = true;
   // Flag to turn on/off reduction of nest to improve class merging optimizations.
   public boolean enableNestReduction = true;
   // Defines interface method rewriter behavior.
@@ -681,19 +671,6 @@
     return ImmutableSet.of();
   }
 
-  private static Set<String> getExtensiveFieldMinifierLoggingFilter() {
-    String property =
-        System.getProperty("com.android.tools.r8.extensiveFieldMinifierLoggingFilter");
-    if (property != null) {
-      ImmutableSet.Builder<String> builder = ImmutableSet.builder();
-      for (String method : property.split(";")) {
-        builder.add(method);
-      }
-      return builder.build();
-    }
-    return ImmutableSet.of();
-  }
-
   private static Set<String> getExtensiveInterfaceMethodMinifierLoggingFilter() {
     String property =
         System.getProperty("com.android.tools.r8.extensiveInterfaceMethodMinifierLoggingFilter");
@@ -707,40 +684,6 @@
     return ImmutableSet.of();
   }
 
-  private static Set<String> getNullableReceiverInliningFilter() {
-    String property = System.getProperty("com.android.tools.r8.nullableReceiverInliningFilter");
-    if (property != null) {
-      // The property is allowed to be either (1) a path to a file where each line is a method
-      // signature, or (2) a semicolon separated list of method signatures.
-      Path path = null;
-      try {
-        Path tmp = Paths.get(property);
-        if (Files.exists(tmp)) {
-          path = tmp;
-        }
-      } catch (InvalidPathException | NullPointerException e) {
-        // Ignore, treat as a semicolon separated list of method signatures.
-      }
-      ImmutableSet.Builder<String> builder = ImmutableSet.builder();
-      if (path != null) {
-        try {
-          Files.readAllLines(path).stream()
-              .map(String::trim)
-              .filter(not(String::isEmpty))
-              .forEach(builder::add);
-        } catch (IOException e) {
-          throw new RuntimeException(e);
-        }
-      } else {
-        for (String method : property.split(";")) {
-          builder.add(method);
-        }
-      }
-      return builder.build();
-    }
-    return ImmutableSet.of();
-  }
-
   public static class InvalidParameterAnnotationInfo {
 
     final DexMethod method;
@@ -846,20 +789,13 @@
 
   private final Set<DexItem> invalidLibraryClasses = Sets.newConcurrentHashSet();
 
-  public void errorMissingClassMissingNestHost(DexClass compiledClass) {
+  public RuntimeException errorMissingClassMissingNestHost(DexClass compiledClass) {
     throw reporter.fatalError(messageErrorMissingNestHost(compiledClass));
   }
 
-  public void warningMissingClassMissingNestHost(DexClass compiledClass) {
-    if (compiledClass.isLibraryClass()) {
-      errorMissingClassMissingNestHost(compiledClass);
-    }
-    reporter.warning(new StringDiagnostic(messageWarningMissingNestHost(compiledClass)));
-  }
-
   public void nestDesugaringWarningMissingNestHost(DexClass compiledClass) {
     if (compiledClass.isLibraryClass()) {
-      errorMissingClassMissingNestHost(compiledClass);
+      throw errorMissingClassMissingNestHost(compiledClass);
     }
     reporter.warning(
         new MissingNestHostNestDesugarDiagnostic(
@@ -868,38 +804,8 @@
             messageWarningMissingNestHost(compiledClass)));
   }
 
-  public void errorMissingClassIncompleteNest(List<DexType> nest, AppView<?> appView) {
-    throw reporter.fatalError(messageErrorIncompleteNest(nest, appView));
-  }
-
-  public void warningMissingClassIncompleteNest(List<DexType> nest, AppView<?> appView) {
-    for (DexType type : nest) {
-      DexClass clazz = appView.definitionFor(type);
-      if (clazz != null && clazz.isLibraryClass()) {
-        errorMissingClassIncompleteNest(nest, appView);
-        return;
-      }
-    }
-    reporter.warning(new StringDiagnostic(messageWarningIncompleteNest(nest, appView)));
-  }
-
-  public void nestDesugaringWarningIncompleteNest(List<DexType> nest, AppView<?> appView) {
-    DexClass availableClass = null;
-    for (DexType type : nest) {
-      DexClass clazz = appView.definitionFor(type);
-      if (clazz != null && clazz.isProgramClass()) {
-        availableClass = clazz;
-      } else if (clazz != null && clazz.isLibraryClass()) {
-        errorMissingClassIncompleteNest(nest, appView);
-        return;
-      }
-    }
-    assert availableClass != null;
-    reporter.warning(
-        new IncompleteNestNestDesugarDiagnosic(
-            availableClass.getOrigin(),
-            Position.UNKNOWN,
-            messageWarningIncompleteNest(nest, appView)));
+  public RuntimeException errorMissingClassIncompleteNest(Nest nest) {
+    throw reporter.fatalError(messageErrorIncompleteNest(nest));
   }
 
   private String messageErrorMissingNestHost(DexClass compiledClass) {
@@ -918,49 +824,43 @@
         + " is considered as not being part of any nest.";
   }
 
-  private String messageErrorIncompleteNest(List<DexType> nest, AppView<?> appView) {
-    List<String> programClassesFromNest = new ArrayList<>();
-    List<String> unavailableClasses = new ArrayList<>();
-    List<String> classPathClasses = new ArrayList<>();
-    List<String> libraryClasses = new ArrayList<>();
-    for (DexType type : nest) {
-      DexClass clazz = appView.definitionFor(appView.graphLens().lookupType(type));
-      if (clazz == null) {
-        unavailableClasses.add(type.getName());
-      } else if (clazz.isLibraryClass()) {
-        libraryClasses.add(type.getName());
-      } else if (clazz.isProgramClass()) {
-        programClassesFromNest.add(type.getName());
-      } else {
-        assert clazz.isClasspathClass();
-        classPathClasses.add(type.getName());
-      }
+  private String messageErrorIncompleteNest(Nest nest) {
+    List<DexProgramClass> programClassesFromNest = new ArrayList<>();
+    List<DexClasspathClass> classpathClassesFromNest = new ArrayList<>();
+    List<DexLibraryClass> libraryClassesFromNest = new ArrayList<>();
+    nest.getHostClass()
+        .accept(
+            programClassesFromNest::add,
+            classpathClassesFromNest::add,
+            libraryClassesFromNest::add);
+    for (DexClass memberClass : nest.getMembers()) {
+      memberClass.accept(
+          programClassesFromNest::add, classpathClassesFromNest::add, libraryClassesFromNest::add);
     }
     StringBuilder stringBuilder =
         new StringBuilder("Compilation of classes ")
-            .append(String.join(", ", programClassesFromNest))
+            .append(StringUtils.join(", ", programClassesFromNest, DexClass::getTypeName))
             .append(" requires its nest mates ");
-    if (!unavailableClasses.isEmpty()) {
-      stringBuilder.append(String.join(", ", unavailableClasses)).append(" (unavailable) ");
+    if (nest.hasMissingMembers()) {
+      stringBuilder
+          .append(StringUtils.join(", ", nest.getMissingMembers(), DexType::getTypeName))
+          .append(" (unavailable) ");
     }
-    if (!libraryClasses.isEmpty()) {
-      stringBuilder.append(String.join(", ", unavailableClasses)).append(" (on library path) ");
+    if (!libraryClassesFromNest.isEmpty()) {
+      stringBuilder
+          .append(StringUtils.join(", ", libraryClassesFromNest, DexClass::getTypeName))
+          .append(" (on library path) ");
     }
     stringBuilder.append("to be on program or class path.");
-    if (!classPathClasses.isEmpty()) {
+    if (!classpathClassesFromNest.isEmpty()) {
       stringBuilder
           .append("(Classes ")
-          .append(String.join(", ", classPathClasses))
+          .append(StringUtils.join(", ", classpathClassesFromNest, DexClass::getTypeName))
           .append(" from the same nest are on class path).");
     }
     return stringBuilder.toString();
   }
 
-  private String messageWarningIncompleteNest(List<DexType> nest, AppView<?> appView) {
-    return messageErrorIncompleteNest(nest, appView)
-        + " Unavailable classes are considered as not being part of the nest.";
-  }
-
   public void warningMissingTypeForDesugar(
       Origin origin, Position position, DexType missingType, DexMethod context) {
     if (reportedMissingForDesugaring.add(missingType)) {
@@ -1035,31 +935,6 @@
     }
   }
 
-  public void warningInvalidSignature(
-      DexDefinition item, Origin origin, String signature, GenericSignatureFormatError e) {
-    StringBuilder message = new StringBuilder("Invalid signature '");
-    message.append(signature);
-    message.append("' for ");
-    if (item.isDexClass()) {
-      message.append("class ");
-      message.append((item.asDexClass()).getType().toSourceString());
-    } else if (item.isDexEncodedField()) {
-      message.append("field ");
-      message.append(item.toSourceString());
-    } else {
-      assert item.isDexEncodedMethod();
-      message.append("method ");
-      message.append(item.toSourceString());
-    }
-    message.append(".");
-    message.append(System.lineSeparator());
-    message.append("Signature is ignored and will not be present in the output.");
-    message.append(System.lineSeparator());
-    message.append("Parser error: ");
-    message.append(e.getMessage());
-    reporter.warning(new StringDiagnostic(message.toString(), origin));
-  }
-
   private final Box<Boolean> reportedExperimentClassFileVersion = new Box<>(false);
 
   public void warningExperimentalClassFileVersion(Origin origin) {
@@ -1431,6 +1306,7 @@
     public boolean addCallEdgesForLibraryInvokes = false;
 
     public boolean allowCheckDiscardedErrors = false;
+    public boolean allowDexInputForTesting = false;
     public boolean allowInjectedAnnotationMethods = false;
     public boolean allowTypeErrors =
         !Version.isDevelopmentVersion()
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
index cd1c0a0..914307f 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -59,10 +59,14 @@
     return -1;
   }
 
-  public static <T> Iterable<T> filter(Iterable<T> iterable, Predicate<T> predicate) {
+  public static <T> Iterable<T> filter(Iterable<T> iterable, Predicate<? super T> predicate) {
     return () -> IteratorUtils.filter(iterable.iterator(), predicate);
   }
 
+  public static <T> boolean hasNext(Iterable<T> iterable) {
+    return iterable.iterator().hasNext();
+  }
+
   public static <T> int size(Iterable<T> iterable) {
     int result = 0;
     for (T element : iterable) {
@@ -77,6 +81,10 @@
     return result;
   }
 
+  public static <S, T> Iterable<T> transform(Iterable<S> iterable, Function<S, T> fn) {
+    return Iterables.transform(iterable, fn::apply);
+  }
+
   public static <T> boolean isEmpty(Iterable<T> iterable) {
     return !iterable.iterator().hasNext();
   }
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index fe6feab..e6b0085 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -14,6 +14,36 @@
 
 public class ListUtils {
 
+  /**
+   * Maps each element in the list using on the given function. Returns the mapped list if any
+   * elements were rewritten, otherwise returns the original list.
+   *
+   * <p>If the given function {@param fn} returns null for an element {@code v}, this is interpreted
+   * as the singleton list containing {@code v} (i.e., no changes should be made to the given
+   * element).
+   */
+  public static <T> List<T> flatMap(List<T> list, Function<T, List<T>> fn, List<T> defaultValue) {
+    List<T> result = null;
+    for (int i = 0; i < list.size(); i++) {
+      T element = list.get(i);
+      List<T> replacement = fn.apply(element);
+      if (replacement == null) {
+        if (result != null) {
+          result.add(element);
+        }
+      } else {
+        if (result == null) {
+          result = new ArrayList<>(list.size() + replacement.size() - 1);
+          for (int j = 0; j < i; j++) {
+            result.add(list.get(j));
+          }
+        }
+        result.addAll(replacement);
+      }
+    }
+    return result != null ? result : defaultValue;
+  }
+
   public static <T> T first(List<T> list) {
     return list.get(0);
   }
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index ac79586..abae424 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -45,6 +45,6 @@
 
   public static String toString(Map<?, ?> map) {
     return StringUtils.join(
-        map.entrySet(), ",", BraceType.TUBORG, entry -> entry.getKey() + ":" + entry.getValue());
+        ",", map.entrySet(), entry -> entry.getKey() + ":" + entry.getValue(), BraceType.TUBORG);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index a90d2bc..8f398ac 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -108,8 +108,8 @@
     return append(builder, collection, ", ", BraceType.PARENS);
   }
 
-  public static <T> StringBuilder append(StringBuilder builder, Collection<T> collection,
-      String seperator, BraceType brace) {
+  public static <T> StringBuilder append(
+      StringBuilder builder, Iterable<T> collection, String seperator, BraceType brace) {
     builder.append(brace.left());
     boolean first = true;
     for (T element : collection) {
@@ -128,18 +128,22 @@
     return join(collection, separator, BraceType.NONE);
   }
 
+  public static <T> String join(String separator, Iterable<T> iterable, Function<T, String> fn) {
+    return join(separator, iterable, fn, BraceType.NONE);
+  }
+
   public static String join(String separator, String... strings) {
     return join(Arrays.asList(strings), separator, BraceType.NONE);
   }
 
   public static <T> String join(Collection<T> collection, String separator, BraceType brace) {
-    return join(collection, separator, brace, Object::toString);
+    return join(separator, collection, Object::toString, brace);
   }
 
-  public static <T> String join(Collection<T> collection, String separator, BraceType brace,
-      Function<T, String> fn) {
+  public static <T> String join(
+      String separator, Iterable<T> iterable, Function<T, String> fn, BraceType brace) {
     StringBuilder builder = new StringBuilder();
-    append(builder, ListUtils.map(collection, fn), separator, brace);
+    append(builder, IterableUtils.transform(iterable, fn), separator, brace);
     return builder.toString();
   }
 
diff --git a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
index 2a16dbf..a8fa794 100644
--- a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -8,6 +8,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -18,6 +19,11 @@
 
   public static final int NOT_SPECIFIED = -1;
 
+  public static <T> Future<T> processAsynchronously(
+      Callable<T> callable, ExecutorService executorService) {
+    return executorService.submit(callable);
+  }
+
   public static <T, R, E extends Exception> Collection<R> processItemsWithResults(
       Iterable<T> items, ThrowingFunction<T, R, E> consumer, ExecutorService executorService)
       throws ExecutionException {
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
index 23ec0c9..8a004a9 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
@@ -169,6 +169,7 @@
 
   protected void configure(InternalOptions options) {
     options.lineNumberOptimization = LineNumberOptimization.OFF;
+    options.testing.allowDexInputForTesting = input == Input.DX;
   }
 
   private boolean shouldCompileFail() {
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index bb69054..533cbe4 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -720,9 +720,9 @@
         MainDexClasses.createEmptyMainDexClasses());
   }
 
-  protected static AppView<AppInfoWithClassHierarchy> computeAppViewWithSubtyping(AndroidApp app)
-      throws Exception {
-    return computeAppViewWithSubtyping(
+  protected static AppView<AppInfoWithClassHierarchy> computeAppViewWithClassHierachy(
+      AndroidApp app) throws Exception {
+    return computeAppViewWithClassHierachy(
         app,
         factory ->
             buildConfigForRules(
@@ -730,7 +730,7 @@
                 Collections.singletonList(ProguardKeepRule.defaultKeepAllRule(unused -> {}))));
   }
 
-  private static AppView<AppInfoWithClassHierarchy> computeAppViewWithSubtyping(
+  private static AppView<AppInfoWithClassHierarchy> computeAppViewWithClassHierachy(
       AndroidApp app, Function<DexItemFactory, ProguardConfiguration> keepConfig) throws Exception {
     DexItemFactory dexItemFactory = new DexItemFactory();
     InternalOptions options = new InternalOptions(keepConfig.apply(dexItemFactory), new Reporter());
@@ -759,7 +759,7 @@
 
   protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness(
       AndroidApp app, Function<DexItemFactory, ProguardConfiguration> keepConfig) throws Exception {
-    AppView<AppInfoWithClassHierarchy> appView = computeAppViewWithSubtyping(app, keepConfig);
+    AppView<AppInfoWithClassHierarchy> appView = computeAppViewWithClassHierachy(app, keepConfig);
     // Run the tree shaker to compute an instance of AppInfoWithLiveness.
     ExecutorService executor = Executors.newSingleThreadExecutor();
     SubtypingInfo subtypingInfo = new SubtypingInfo(appView);
@@ -769,7 +769,7 @@
             .run(executor);
     appView.setRootSet(rootSet);
     AppInfoWithLiveness appInfoWithLiveness =
-        EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo)
+        EnqueuerFactory.createForInitialTreeShaking(appView, executor, subtypingInfo)
             .traceApplication(rootSet, ProguardClassFilter.empty(), executor, Timing.empty());
     // We do not run the tree pruner to ensure that the hierarchy is as designed and not modified
     // due to liveness.
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
index 72a22e6..4f8713f 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
 import static org.hamcrest.CoreMatchers.not;
 
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -64,6 +65,11 @@
     return assertWarningsMatch(Collections.singletonList(matcher));
   }
 
+  @SuppressWarnings("unchecked")
+  default TestDiagnosticMessages assertWarningsMatch(Matcher<Diagnostic>... matchers) {
+    return assertWarningsMatch(Arrays.asList(matchers));
+  }
+
   TestDiagnosticMessages assertWarningsMatch(Collection<Matcher<Diagnostic>> matchers);
 
   default TestDiagnosticMessages assertErrorsMatch(Matcher<Diagnostic> matcher) {
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 3da831f..12b42cb 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -101,7 +101,11 @@
   }
 
   public T addDontWarn(Class<?> clazz) {
-    return addKeepRules("-dontwarn " + clazz.getTypeName());
+    return addDontWarn(clazz.getTypeName());
+  }
+
+  public T addDontWarn(String className) {
+    return addKeepRules("-dontwarn " + className);
   }
 
   public T addKeepKotlinMetadata() {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index 5b87397..8a9fc12 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -24,7 +24,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
-import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
+import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.tracereferences.TraceReferences;
 import com.android.tools.r8.utils.AndroidApiLevel;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
index 394f36c..7a1d422 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
@@ -287,14 +287,17 @@
       D8TestRunResult result =
           compileResult.run(
               parameters.getRuntime(), "TestNGMainRunner", verbosity, runnableTests.get(path));
-      if (result.getStdOut().contains("java.lang.NoSuchMethodError")
-          && Arrays.stream(missingDesugaredMethods())
-              .anyMatch(method -> result.getStdOut().contains(method))) {
+      String stdout = result.getStdOut();
+      if (stdout.contains("java.lang.NoSuchMethodError")
+          && Arrays.stream(missingDesugaredMethods()).anyMatch(method -> stdout.contains(method))) {
         // TODO(b/134732760): support Java 9 APIs.
-      } else if (result.getStdOut().contains("in class Ljava/util/Random")
-          && result.getStdOut().contains("java.lang.NoSuchMethodError")) {
+      } else if (stdout.contains("java.lang.NoSuchMethodError")
+          && stdout.contains("org.openjdk.tests.java.util.stream.IterateTest.testIterate")) {
+        // TODO(b/134732760): support Java 9 APIs.
+      } else if (stdout.contains("in class Ljava/util/Random")
+          && stdout.contains("java.lang.NoSuchMethodError")) {
         // TODO(b/134732760): Random Java 9 Apis, support or do not use them.
-      } else if (result.getStdOut().contains("java.lang.AssertionError")) {
+      } else if (stdout.contains("java.lang.AssertionError")) {
         // TODO(b/134732760): Investigate and fix these issues.
       } else {
         String errorMessage = "STDOUT:\n" + result.getStdOut() + "STDERR:\n" + result.getStdErr();
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java
index e4447b53..1fa2bdc 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java
@@ -107,11 +107,7 @@
           .addKeepAllAttributes()
           .setMinApi(parameters.getApiLevel())
           .addProgramFiles(classesOfNest(nestID))
-          .addOptionsModification(
-              options -> {
-                options.enableNestBasedAccessDesugaring = true;
-                options.enableNestReduction = false;
-              })
+          .addOptionsModification(options -> options.enableNestReduction = false)
           .compile()
           .run(parameters.getRuntime(), getMainClass(nestID))
           .assertSuccessWithOutput(getExpectedResult(nestID));
@@ -125,7 +121,6 @@
       testForD8()
           .setMinApi(parameters.getApiLevel())
           .addProgramFiles(classesOfNest(nestID))
-          .addOptionsModification(options -> options.enableNestBasedAccessDesugaring = true)
           .compile()
           .run(parameters.getRuntime(), getMainClass(nestID))
           .assertSuccessWithOutput(getExpectedResult(nestID));
@@ -136,10 +131,6 @@
       throws CompilationFailedException {
     return testForD8(getStaticTemp())
         .addProgramFiles(JAR)
-        .addOptionsModification(
-            options -> {
-              options.enableNestBasedAccessDesugaring = true;
-            })
         .setMinApi(minApi)
         .compile();
   }
@@ -150,11 +141,7 @@
         .noTreeShaking()
         .noMinification()
         .addKeepAllAttributes()
-        .addOptionsModification(
-            options -> {
-              options.enableNestBasedAccessDesugaring = true;
-              options.enableNestReduction = false;
-            })
+        .addOptionsModification(options -> options.enableNestReduction = false)
         .addProgramFiles(JAR)
         .setMinApi(minApi)
         .compile();
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/MinimumNumberOfBridgesGenerated.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/MinimumNumberOfBridgesGenerated.java
index 9d01c91..56b025c 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/MinimumNumberOfBridgesGenerated.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/MinimumNumberOfBridgesGenerated.java
@@ -13,7 +13,7 @@
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
+import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
index 7f2fede..e9cdbe0 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
@@ -4,12 +4,15 @@
 
 package com.android.tools.r8.desugar.nestaccesscontrol;
 
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
 import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.CLASSES_PATH;
 import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.CLASS_NAMES;
 import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
 import static java.util.stream.Collectors.toList;
 import static org.hamcrest.core.StringContains.containsString;
 import static org.hamcrest.core.StringEndsWith.endsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -20,9 +23,12 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.errors.IncompleteNestNestDesugarDiagnosic;
+import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
 import com.android.tools.r8.errors.MissingNestHostNestDesugarDiagnostic;
+import com.android.tools.r8.shaking.MissingClassesDiagnostic;
 import java.nio.file.Path;
 import java.util.List;
 import org.hamcrest.Matcher;
@@ -81,10 +87,7 @@
             .map(name -> CLASSES_PATH.resolve(name + CLASS_EXTENSION))
             .collect(toList());
     if (d8) {
-      return testForD8()
-          .setMinApi(parameters.getApiLevel())
-          .addProgramFiles(matchingClasses)
-          .addOptionsModification(options -> options.enableNestBasedAccessDesugaring = true);
+      return testForD8().setMinApi(parameters.getApiLevel()).addProgramFiles(matchingClasses);
     } else {
       return testForR8(parameters.getBackend())
           .noTreeShaking()
@@ -92,10 +95,12 @@
           .addKeepAllAttributes()
           .setMinApi(parameters.getApiLevel())
           .addProgramFiles(matchingClasses)
+          .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+          .addDontWarn("java.lang.invoke.StringConcatFactory")
           .addOptionsModification(
               options -> {
-                options.enableNestBasedAccessDesugaring = true;
                 options.ignoreMissingClasses = ignoreMissingClasses;
+                options.testing.enableExperimentalMissingClassesReporting = true;
               })
           .allowDiagnosticWarningMessages(allowDiagnosticWarningMessages);
     }
@@ -108,7 +113,16 @@
       compileOnlyClassesMatching(innerClassMatcher, false, false, false)
           .compileWithExpectedDiagnostics(
               diagnostics -> {
-                diagnostics.assertErrorMessageThatMatches(containsString("requires its nest host"));
+                diagnostics
+                    .assertOnlyErrors()
+                    .assertErrorsMatch(diagnosticType(MissingClassesDiagnostic.class));
+
+                MissingClassesDiagnostic diagnostic =
+                    (MissingClassesDiagnostic) diagnostics.getErrors().get(0);
+                assertEquals(1, diagnostic.getMissingClasses().size());
+                assertEquals(
+                    "nesthostexample.BasicNestHostWithInnerClassMethods",
+                    diagnostic.getMissingClasses().iterator().next().getTypeName());
               });
     } catch (CompilationFailedException e) {
       // Expected failure.
@@ -123,8 +137,16 @@
       compileOnlyClassesMatching(innerClassMatcher, false, false, false)
           .compileWithExpectedDiagnostics(
               diagnostics -> {
-                diagnostics.assertErrorMessageThatMatches(
-                    containsString("requires its nest mates"));
+                diagnostics
+                    .assertOnlyErrors()
+                    .assertErrorsMatch(diagnosticType(MissingClassesDiagnostic.class));
+
+                MissingClassesDiagnostic diagnostic =
+                    (MissingClassesDiagnostic) diagnostics.getErrors().get(0);
+                assertEquals(1, diagnostic.getMissingClasses().size());
+                assertEquals(
+                    "nesthostexample.BasicNestHostWithInnerClassMethods$BasicNestedClass",
+                    diagnostic.getMissingClasses().iterator().next().getTypeName());
               });
     } catch (Exception e) {
       // Expected failure.
@@ -139,15 +161,31 @@
     TestCompileResult<?, ?> compileResult =
         compileOnlyClassesMatching(innerClassMatcher, d8, !d8, true).compile();
     assertTrue(compileResult.getDiagnosticMessages().getWarnings().size() >= 1);
-    if (desugarWarning) {
+    if (d8 && desugarWarning) {
       assertTrue(
           compileResult.getDiagnosticMessages().getWarnings().stream()
               .anyMatch(warn -> warn instanceof MissingNestHostNestDesugarDiagnostic));
-    } else if (!d8) {
+    }
+    if (!d8) {
       // R8 should raise extra warning when cleaning the nest.
-      assertTrue(
-          compileResult.getDiagnosticMessages().getWarnings().stream()
-              .anyMatch(warn -> warn.getDiagnosticMessage().contains("requires its nest host")));
+      compileResult.inspectDiagnosticMessages(
+          diagnostics -> {
+            diagnostics.assertOnlyWarnings();
+            if (parameters.isCfRuntime() || parameters.canUseDefaultAndStaticInterfaceMethods()) {
+              diagnostics.assertWarningsMatch(diagnosticType(MissingClassesDiagnostic.class));
+            } else {
+              diagnostics.assertWarningsMatch(
+                  diagnosticType(MissingClassesDiagnostic.class),
+                  diagnosticType(InterfaceDesugarMissingTypeDiagnostic.class));
+            }
+
+            MissingClassesDiagnostic diagnostic =
+                (MissingClassesDiagnostic) diagnostics.getWarnings().get(0);
+            assertEquals(1, diagnostic.getMissingClasses().size());
+            assertEquals(
+                "nesthostexample.BasicNestHostWithInnerClassMethods",
+                diagnostic.getMissingClasses().iterator().next().getTypeName());
+          });
     }
   }
 
@@ -156,13 +194,32 @@
     TestCompileResult<?, ?> compileResult =
         compileOnlyClassesMatching(innerClassMatcher, d8, !d8, true).compile();
     assertTrue(compileResult.getDiagnosticMessages().getWarnings().size() >= 1);
-    if (desugarWarning) {
+    if (d8 && desugarWarning) {
       assertTrue(
           compileResult.getDiagnosticMessages().getWarnings().stream()
               .anyMatch(warn -> warn instanceof IncompleteNestNestDesugarDiagnosic));
-    } else if (!d8) {
+    }
+    if (!d8) {
       // R8 should raise extra warning when cleaning the nest.
-      compileResult.assertWarningMessageThatMatches(containsString("requires its nest mates"));
+      compileResult.inspectDiagnosticMessages(
+          diagnostics -> {
+            diagnostics.assertOnlyWarnings();
+            if (parameters.isCfRuntime() || parameters.canUseDefaultAndStaticInterfaceMethods()) {
+              diagnostics.assertWarningsMatch(diagnosticType(MissingClassesDiagnostic.class));
+            } else {
+              diagnostics.assertWarningsMatch(
+                  diagnosticType(MissingClassesDiagnostic.class),
+                  diagnosticType(InterfaceDesugarMissingTypeDiagnostic.class));
+            }
+
+            MissingClassesDiagnostic diagnostic =
+                (MissingClassesDiagnostic) diagnostics.getWarnings().get(0);
+            assertNotNull(diagnostic);
+            assertEquals(1, diagnostic.getMissingClasses().size());
+            assertEquals(
+                "nesthostexample.BasicNestHostWithInnerClassMethods$BasicNestedClass",
+                diagnostic.getMissingClasses().iterator().next().getTypeName());
+          });
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestOnProgramAndClassPathTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestOnProgramAndClassPathTest.java
index bb59533..4029469 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestOnProgramAndClassPathTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestOnProgramAndClassPathTest.java
@@ -99,9 +99,7 @@
     testForD8()
         .addProgramFiles(inner.writeToZip(), host.writeToZip())
         .setMinApi(parameters.getApiLevel())
-        .addOptionsModification(options -> options.enableNestBasedAccessDesugaring = true)
         .compile()
-        .inspect(inspector -> assertEquals(3, inspector.allClasses().size()))
         .run(parameters.getRuntime(), getMainClass("constructors"))
         .assertSuccessWithOutput(getExpectedResult("constructors"));
   }
@@ -117,7 +115,6 @@
         .setMinApi(parameters.getApiLevel())
         .addProgramFiles(matchingClasses)
         .addClasspathFiles(JAR)
-        .addOptionsModification(options -> options.enableNestBasedAccessDesugaring = true)
         .compile();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MissingMethodTest.java b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MissingMethodTest.java
index f147d3b..d36953c 100644
--- a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MissingMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MissingMethodTest.java
@@ -6,7 +6,6 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRunResult;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -28,33 +27,19 @@
 
   @Test
   public void testReference() throws Exception {
-    TestRunResult<?> result =
-        testForRuntime(parameters)
-            .addProgramClassFileData(InterfaceDump.dump(), MainDump.dump())
-            .run(parameters.getRuntime(), "Main");
-    if (parameters.isDexRuntime()
-        && parameters.getApiLevel().isLessThan(apiLevelWithStaticInterfaceMethodsSupport())) {
-      // TODO(b/69835274): Desugaring should preserve exception.
-      result.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
-    } else {
-      result.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
-    }
+    testForRuntime(parameters)
+        .addProgramClassFileData(InterfaceDump.dump(), MainDump.dump())
+        .run(parameters.getRuntime(), "Main")
+        .assertFailureWithErrorThatThrows(NoSuchMethodError.class);
   }
 
   @Test
   public void testR8() throws Exception {
-    TestRunResult<?> result =
-        testForR8(parameters.getBackend())
-            .addProgramClassFileData(InterfaceDump.dump(), MainDump.dump())
-            .addKeepMainRule("Main")
-            .setMinApi(parameters.getApiLevel())
-            .run(parameters.getRuntime(), "Main");
-    if (parameters.isDexRuntime()
-        && parameters.getApiLevel().isLessThan(apiLevelWithStaticInterfaceMethodsSupport())) {
-      // TODO(b/69835274): Desugaring should preserve exception.
-      result.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
-    } else {
-      result.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
-    }
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(InterfaceDump.dump(), MainDump.dump())
+        .addKeepMainRule("Main")
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), "Main")
+        .assertFailureWithErrorThatThrows(NoSuchMethodError.class);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java
index cb48232..4d8926c 100644
--- a/src/test/java/com/android/tools/r8/ir/InlineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -75,7 +75,8 @@
                 ImmutableList.of(ProguardKeepRule.defaultKeepAllRule(unused -> {})))
             .run(executorService));
     Timing timing = Timing.empty();
-    Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo);
+    Enqueuer enqueuer =
+        EnqueuerFactory.createForInitialTreeShaking(appView, executorService, subtypingInfo);
     appView.setAppInfo(
         enqueuer.traceApplication(
             appView.rootSet(), ProguardClassFilter.empty(), executorService, timing));
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index a2fe521..553a2f4 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -36,7 +36,6 @@
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
 import java.io.IOException;
-import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -49,7 +48,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   public FieldBitAccessInfoTest(TestParameters parameters) {
@@ -61,7 +60,7 @@
     testForR8(parameters.getBackend())
         .addProgramClasses(TestClass.class)
         .addKeepMainRule(TestClass.class)
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .compile()
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutputLines(
@@ -93,7 +92,7 @@
     clazz.forEachProgramMethod(
         method -> {
           IRCode code = method.buildIR(appView);
-          fieldAccessAnalysis.recordFieldAccesses(code, feedback, new MethodProcessorMock());
+          fieldAccessAnalysis.recordFieldAccesses(code, feedback, new PrimaryMethodProcessorMock());
         });
 
     int bitsReadInBitField = feedback.bitsReadPerField.getInt(uniqueFieldByName(clazz, "bitField"));
@@ -116,7 +115,7 @@
     }
   }
 
-  private AppView<AppInfoWithClassHierarchy> buildApp() throws IOException, ExecutionException {
+  private AppView<AppInfoWithClassHierarchy> buildApp() throws IOException {
     DexItemFactory dexItemFactory = new DexItemFactory();
     InternalOptions options = new InternalOptions(dexItemFactory, new Reporter());
     options.programConsumer =
@@ -210,12 +209,7 @@
     }
   }
 
-  static class MethodProcessorMock extends MethodProcessor {
-
-    @Override
-    public Phase getPhase() {
-      return Phase.PRIMARY;
-    }
+  static class PrimaryMethodProcessorMock extends MethodProcessor {
 
     @Override
     public boolean shouldApplyCodeRewritings(ProgramMethod method) {
@@ -223,6 +217,11 @@
     }
 
     @Override
+    public boolean isPrimaryMethodProcessor() {
+      return true;
+    }
+
+    @Override
     public boolean isProcessedConcurrently(ProgramMethod method) {
       return false;
     }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CastToUninstantiatedClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CastToUninstantiatedClassTest.java
index aa385fd..895e6ab 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CastToUninstantiatedClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CastToUninstantiatedClassTest.java
@@ -25,8 +25,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    // TODO(b/172194277): Add support for synthetics when generating CF.
-    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   public CastToUninstantiatedClassTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java b/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java
index 8f7c579..9dd23f8 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java
@@ -17,4 +17,8 @@
       throw new ClassCastException();
     }
   }
+
+  public static NoSuchMethodError throwNoSuchMethodError() {
+    throw new NoSuchMethodError();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index 8d01a11..105008c 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -99,6 +99,17 @@
     }
 
     @Override
+    public void replaceCurrentInstructionWithThrow(
+        AppView<?> appView,
+        IRCode code,
+        ListIterator<BasicBlock> blockIterator,
+        Value exceptionValue,
+        Set<BasicBlock> blocksToRemove,
+        Set<Value> affectedValues) {
+      throw new Unimplemented();
+    }
+
+    @Override
     public void replaceCurrentInstructionWithThrowNull(
         AppView<? extends AppInfoWithClassHierarchy> appView,
         IRCode code,
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
index 3f7568a..425f3b0 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
@@ -11,14 +11,15 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMethod;
+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.ResolutionResult;
 import com.android.tools.r8.graph.ResolutionResult.NoSuchMethodResult;
 import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.transformers.ClassFileTransformer;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
@@ -108,15 +109,15 @@
     Class<?> declaredClass = symbolicReferenceIsDefiningType ? definingClass : A.class;
     Class<?> callerClass = A.class;
 
-    AppView<AppInfoWithLiveness> appView = getAppView();
-    AppInfoWithLiveness appInfo = appView.appInfo();
+    AppView<AppInfoWithClassHierarchy> appView = getAppView();
+    AppInfoWithClassHierarchy appInfo = appView.appInfo();
 
-    DexProgramClass definingClassDefinition = getDexProgramClass(definingClass, appInfo);
-    DexProgramClass declaredClassDefinition = getDexProgramClass(declaredClass, appInfo);
-    DexProgramClass callerClassDefinition = getDexProgramClass(callerClass, appInfo);
+    DexProgramClass definingClassDefinition = getDexProgramClass(definingClass, appView);
+    DexProgramClass declaredClassDefinition = getDexProgramClass(declaredClass, appView);
+    DexProgramClass callerClassDefinition = getDexProgramClass(callerClass, appView);
 
-    DexMethod method = getTargetMethodSignature(declaredClass, appInfo);
-    assertCallingClassCallsTarget(callerClass, appInfo, method);
+    DexMethod method = getTargetMethodSignature(declaredClass, appView.dexItemFactory());
+    assertCallingClassCallsTarget(callerClass, appView, method);
 
     // Resolve the method from the point of the declared holder.
     assertEquals(method.holder, declaredClassDefinition.type);
@@ -136,7 +137,7 @@
     DexClassAndMethod targetSuper =
         resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
     if (inSameNest) {
-      assertEquals(definingClassDefinition.type, targetSpecial.getHolderType());
+      assertEquals(definingClassDefinition.getType(), targetSpecial.getHolderType());
       assertEquals(targetSpecial.getReference(), targetSuper.getReference());
     } else {
       assertNull(targetSpecial);
@@ -145,30 +146,30 @@
   }
 
   private void assertCallingClassCallsTarget(
-      Class<?> callerClass, AppInfoWithLiveness appInfo, DexMethod target) {
-    CodeInspector inspector = new CodeInspector(appInfo.app());
+      Class<?> callerClass, AppView<?> appView, DexMethod target) {
+    CodeInspector inspector = new CodeInspector(appView.appInfo().app());
     MethodSubject foo = inspector.clazz(callerClass).uniqueMethodWithName("foo");
     assertTrue(
         foo.streamInstructions().anyMatch(i -> i.isInvokeSpecial() && i.getMethod() == target));
   }
 
-  private DexMethod getTargetMethodSignature(Class<?> declaredClass, AppInfoWithLiveness appInfo) {
+  private DexMethod getTargetMethodSignature(
+      Class<?> declaredClass, DexItemFactory dexItemFactory) {
     return buildMethod(
         Reference.method(Reference.classFromClass(declaredClass), "bar", ImmutableList.of(), null),
-        appInfo.dexItemFactory());
+        dexItemFactory);
   }
 
-  private DexProgramClass getDexProgramClass(Class<?> clazz, AppInfoWithLiveness appInfo) {
-    return appInfo.definitionFor(buildType(clazz, appInfo.dexItemFactory())).asProgramClass();
+  private DexProgramClass getDexProgramClass(Class<?> clazz, AppView<?> appView) {
+    return appView.definitionFor(buildType(clazz, appView.dexItemFactory())).asProgramClass();
   }
 
-  private AppView<AppInfoWithLiveness> getAppView() throws Exception {
-    return computeAppViewWithLiveness(
+  private AppView<AppInfoWithClassHierarchy> getAppView() throws Exception {
+    return computeAppViewWithClassHierachy(
         buildClasses(getClasses())
             .addClassProgramData(getTransformedClasses())
             .addLibraryFile(TestBase.runtimeJar(parameters.getBackend()))
-            .build(),
-        Main.class);
+            .build());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
index 5a3d01e..c6a2dbf 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMethod;
@@ -18,7 +19,6 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.transformers.ClassFileTransformer;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.OptionalBool;
@@ -82,16 +82,16 @@
     Class<?> declaredClass = A.class;
     Class<?> callerClass = B.class;
 
-    AppView<AppInfoWithLiveness> appView = getAppView();
-    AppInfoWithLiveness appInfo = appView.appInfo();
+    AppView<AppInfoWithClassHierarchy> appView = getAppView();
+    AppInfoWithClassHierarchy appInfo = appView.appInfo();
 
-    DexClass definingClassDefinition = getDexProgramClass(definingClass, appInfo);
-    DexClass declaredClassDefinition = getDexProgramClass(declaredClass, appInfo);
-    DexProgramClass callerClassDefinition = getDexProgramClass(callerClass, appInfo);
+    DexClass definingClassDefinition = getDexProgramClass(definingClass, appView);
+    DexClass declaredClassDefinition = getDexProgramClass(declaredClass, appView);
+    DexProgramClass callerClassDefinition = getDexProgramClass(callerClass, appView);
 
-    DexMethod method = getTargetMethodSignature(declaredClass, appInfo);
+    DexMethod method = getTargetMethodSignature(declaredClass, appView);
 
-    assertCallingClassCallsTarget(callerClass, appInfo, method);
+    assertCallingClassCallsTarget(callerClass, appView, method);
 
     // Resolve the method from the point of the declared holder.
     assertEquals(method.holder, declaredClassDefinition.type);
@@ -121,29 +121,28 @@
   }
 
   private void assertCallingClassCallsTarget(
-      Class<?> callerClass, AppInfoWithLiveness appInfo, DexMethod target) {
-    CodeInspector inspector = new CodeInspector(appInfo.app());
+      Class<?> callerClass, AppView<?> appView, DexMethod target) {
+    CodeInspector inspector = new CodeInspector(appView.appInfo().app());
     MethodSubject foo = inspector.clazz(callerClass).uniqueMethodWithName("foo");
     assertTrue(
         foo.streamInstructions().anyMatch(i -> i.isInvokeSpecial() && i.getMethod() == target));
   }
 
-  private DexMethod getTargetMethodSignature(Class<?> declaredClass, AppInfoWithLiveness appInfo) {
+  private DexMethod getTargetMethodSignature(Class<?> declaredClass, AppView<?> appView) {
     return buildMethod(
         Reference.method(Reference.classFromClass(declaredClass), "bar", ImmutableList.of(), null),
-        appInfo.dexItemFactory());
+        appView.dexItemFactory());
   }
 
-  private DexProgramClass getDexProgramClass(Class<?> definingClass, AppInfoWithLiveness appInfo) {
-    return appInfo
-        .definitionFor(buildType(definingClass, appInfo.dexItemFactory()))
+  private DexProgramClass getDexProgramClass(Class<?> definingClass, AppView<?> appView) {
+    return appView
+        .definitionFor(buildType(definingClass, appView.dexItemFactory()))
         .asProgramClass();
   }
 
-  private AppView<AppInfoWithLiveness> getAppView() throws Exception {
-    return computeAppViewWithLiveness(
-        buildClasses(getClasses()).addClassProgramData(getTransformedClasses()).build(),
-        Main.class);
+  private AppView<AppInfoWithClassHierarchy> getAppView() throws Exception {
+    return computeAppViewWithClassHierachy(
+        buildClasses(getClasses()).addClassProgramData(getTransformedClasses()).build());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
index b291f91..ce6f4d7 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMethod;
@@ -19,7 +20,6 @@
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.graph.ResolutionResult.IllegalAccessOrNoSuchMethodResult;
 import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.transformers.ClassFileTransformer;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -107,16 +107,16 @@
     Class<?> declaredClass = symbolicReferenceIsDefiningType ? definingClass : B.class;
     Class<?> callerClass = C.class;
 
-    AppView<AppInfoWithLiveness> appView = getAppView();
-    AppInfoWithLiveness appInfo = appView.appInfo();
+    AppView<AppInfoWithClassHierarchy> appView = getAppView();
+    AppInfoWithClassHierarchy appInfo = appView.appInfo();
 
-    DexClass definingClassDefinition = getDexProgramClass(definingClass, appInfo);
-    DexClass declaredClassDefinition = getDexProgramClass(declaredClass, appInfo);
-    DexProgramClass callerClassDefinition = getDexProgramClass(callerClass, appInfo);
+    DexClass definingClassDefinition = getDexProgramClass(definingClass, appView);
+    DexClass declaredClassDefinition = getDexProgramClass(declaredClass, appView);
+    DexProgramClass callerClassDefinition = getDexProgramClass(callerClass, appView);
 
-    DexMethod method = getTargetMethodSignature(declaredClass, appInfo);
+    DexMethod method = getTargetMethodSignature(declaredClass, appView);
 
-    assertCallingClassCallsTarget(callerClass, appInfo, method);
+    assertCallingClassCallsTarget(callerClass, appView, method);
 
     // Resolve the method from the point of the declared holder.
     assertEquals(method.holder, declaredClassDefinition.type);
@@ -153,27 +153,26 @@
   }
 
   private void assertCallingClassCallsTarget(
-      Class<?> callerClass, AppInfoWithLiveness appInfo, DexMethod target) {
-    CodeInspector inspector = new CodeInspector(appInfo.app());
+      Class<?> callerClass, AppView<?> appView, DexMethod target) {
+    CodeInspector inspector = new CodeInspector(appView.appInfo().app());
     MethodSubject foo = inspector.clazz(callerClass).uniqueMethodWithName("foo");
     assertTrue(
         foo.streamInstructions().anyMatch(i -> i.isInvokeSpecial() && i.getMethod() == target));
   }
 
-  private DexMethod getTargetMethodSignature(Class<?> declaredClass, AppInfoWithLiveness appInfo) {
+  private DexMethod getTargetMethodSignature(Class<?> declaredClass, AppView<?> appView) {
     return buildMethod(
         Reference.method(Reference.classFromClass(declaredClass), "bar", ImmutableList.of(), null),
-        appInfo.dexItemFactory());
+        appView.dexItemFactory());
   }
 
-  private DexProgramClass getDexProgramClass(Class<?> clazz, AppInfoWithLiveness appInfo) {
-    return appInfo.definitionFor(buildType(clazz, appInfo.dexItemFactory())).asProgramClass();
+  private DexProgramClass getDexProgramClass(Class<?> clazz, AppView<?> appView) {
+    return appView.definitionFor(buildType(clazz, appView.dexItemFactory())).asProgramClass();
   }
 
-  private AppView<AppInfoWithLiveness> getAppView() throws Exception {
-    return computeAppViewWithLiveness(
-        buildClasses(getClasses()).addClassProgramData(getTransformedClasses()).build(),
-        Main.class);
+  private AppView<AppInfoWithClassHierarchy> getAppView() throws Exception {
+    return computeAppViewWithClassHierachy(
+        buildClasses(getClasses()).addClassProgramData(getTransformedClasses()).build());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
index 3566d3a..bc35d2b 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
@@ -232,7 +232,7 @@
     // ----- Program -----
     // B extends A { } <-- initial
     AppView<AppInfoWithClassHierarchy> appView =
-        computeAppViewWithSubtyping(
+        computeAppViewWithClassHierachy(
             buildClasses(Collections.singletonList(B.class), Arrays.asList(A.class, I.class))
                 .build());
     AppInfoWithClassHierarchy appInfo = appView.appInfo();
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index 96c6fb3..3a58245 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -179,6 +179,9 @@
                   if (optionsConsumer != null) {
                     optionsConsumer.accept(options);
                   }
+                  if (frontend == Frontend.DEX) {
+                    options.testing.allowDexInputForTesting = true;
+                  }
                 })
             .allowStdoutMessages()
             .applyIf(testBuilderConsumer != null, testBuilderConsumer);
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/ClassNotInitializedByCheckCastTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/ClassNotInitializedByCheckCastTest.java
index 5a3efa6..484526f 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/ClassNotInitializedByCheckCastTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/ClassNotInitializedByCheckCastTest.java
@@ -5,13 +5,11 @@
 package com.android.tools.r8.shaking.clinit;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 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.CodeInspector;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -45,15 +43,8 @@
   }
 
   private void inspect(CodeInspector inspector) {
-    if (parameters.isCfRuntime()) {
-      // Check that A.<clinit>() is removed.
-      ClassSubject aClassSubject = inspector.clazz(A.class);
-      assertThat(aClassSubject, isPresent());
-      assertThat(aClassSubject.clinit(), isAbsent());
-    } else {
-      // Check that A is removed.
-      assertThat(inspector.clazz(A.class), isAbsent());
-    }
+    // Check that A is removed.
+    assertThat(inspector.clazz(A.class), isAbsent());
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index 04e2c9f..dd1b4bc 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -215,7 +215,7 @@
     testCheckCast(
         TestMainWithCheckCast.class,
         TestClassWithDefaultConstructor.class,
-        forceProguardCompatibility || parameters.isCfRuntime());
+        forceProguardCompatibility);
     testCheckCast(TestMainWithoutCheckCast.class, TestClassWithDefaultConstructor.class, false);
   }
 
diff --git a/src/test/java/com/android/tools/r8/utils/StringUtilsTest.java b/src/test/java/com/android/tools/r8/utils/StringUtilsTest.java
index cac07ed..8279af3 100644
--- a/src/test/java/com/android/tools/r8/utils/StringUtilsTest.java
+++ b/src/test/java/com/android/tools/r8/utils/StringUtilsTest.java
@@ -33,9 +33,9 @@
 
   private void assertListEquals(List<String> xs, List<String> ys) {
     assertEquals(
-        StringUtils.join(xs, ", ", BraceType.SQUARE, s -> '"' + StringUtils.toASCIIString(s) + '"'),
-        StringUtils.join(ys, ", ", BraceType.SQUARE, s -> '"' + StringUtils.toASCIIString(s) + '"')
-    );
+        StringUtils.join(", ", xs, s -> '"' + StringUtils.toASCIIString(s) + '"', BraceType.SQUARE),
+        StringUtils.join(
+            ", ", ys, s -> '"' + StringUtils.toASCIIString(s) + '"', BraceType.SQUARE));
   }
 
   @Test