Merge commit '8a8c4b56794f822e0dd053f6e510b8b6635234ab' into dev-release
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 2e73927..99ff6a7 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -341,7 +341,7 @@
     new KotlinMetadataRewriter(appView, prefixRewritingNamingLens).runForD8(executor);
     new ApplicationWriter(
             cfApp,
-            null,
+            appView,
             options,
             null,
             GraphLens.getIdentityLens(),
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 7203f8e..9935d84 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -100,7 +100,7 @@
         try {
           new ApplicationWriter(
                   appView.appInfo().app(),
-                  null,
+                  appView,
                   options,
                   markers,
                   GraphLens.getIdentityLens(),
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index aae1486..ef71e03 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -99,8 +99,12 @@
           });
       assert !options.cfToCfDesugar;
       if (shrink) {
-        R8.run(r8Command);
-      } else {
+        AndroidApp r8CommandInputApp = r8Command.getInputApp();
+        InternalOptions r8CommandInternalOptions = r8Command.getInternalOptions();
+        // TODO(b/167843161): Disable temporarily enum unboxing in L8 due to naming issues.
+        r8CommandInternalOptions.enableEnumUnboxing = false;
+        R8.runForTesting(r8CommandInputApp, r8CommandInternalOptions);
+      } else if (d8Command != null) {
         D8.run(d8Command, executorService);
       }
     } finally {
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index a2cf589..db24f23 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -260,9 +260,6 @@
       if (!hasDesugaredLibraryConfiguration()) {
         reporter.error("L8 requires a desugared library configuration");
       }
-      if (getProgramConsumer() instanceof ClassFileConsumer) {
-        reporter.error("L8 does not support compiling to class files");
-      }
       if (getProgramConsumer() instanceof DexFilePerClassFileConsumer) {
         reporter.error("L8 does not support compiling to dex per class");
       }
@@ -271,6 +268,9 @@
       } else if (getMainDexListConsumer() != null) {
         reporter.error("L8 does not support generating a main dex list");
       }
+      if (isShrinking() && getProgramConsumer() instanceof ClassFileConsumer) {
+        reporter.error("L8 does not support shrinking when generating class files");
+      }
       super.validate();
     }
 
@@ -292,12 +292,12 @@
       D8Command d8Command = null;
 
       AndroidApp inputs = getAppBuilder().build();
-      DesugaredLibrary desugaredLibrary = new DesugaredLibrary();
-
+      ProgramConsumer l8CfConsumer = null;
       if (isShrinking()) {
+        l8CfConsumer = new InMemoryJarContent();
         R8Command.Builder r8Builder =
             new CompatProguardCommandBuilder(true, getReporter())
-                .addProgramResourceProvider(desugaredLibrary)
+                .addProgramResourceProvider((ProgramResourceProvider) l8CfConsumer)
                 .setSynthesizedClassesPrefix(
                     libraryConfiguration.getSynthesizedLibraryClassesPackagePrefix())
                 .setMinApiLevel(getMinApiLevel())
@@ -316,10 +316,11 @@
             libraryConfiguration.getExtraKeepRules(), Origin.unknown());
         r8Builder.addProguardConfigurationFiles(proguardConfigFiles);
         r8Command = r8Builder.makeCommand();
-      } else {
+      } else if (!(getProgramConsumer() instanceof ClassFileConsumer)) {
+        l8CfConsumer = new InMemoryJarContent();
         D8Command.Builder d8Builder =
             D8Command.builder(getReporter())
-                .addProgramResourceProvider(desugaredLibrary)
+                .addProgramResourceProvider((ProgramResourceProvider) l8CfConsumer)
                 .setSynthesizedClassesPrefix(
                     libraryConfiguration.getSynthesizedLibraryClassesPackagePrefix())
                 .setMinApiLevel(getMinApiLevel())
@@ -332,13 +333,17 @@
           d8Builder.addLibraryResourceProvider(libraryResourceProvider);
         }
         d8Command = d8Builder.makeCommand();
+      } else {
+        assert getProgramConsumer() instanceof ClassFileConsumer;
+        l8CfConsumer = getProgramConsumer();
+        d8Command = null;
       }
       return new L8Command(
           r8Command,
           d8Command,
           inputs,
           getMode(),
-          desugaredLibrary,
+          l8CfConsumer,
           getMainDexListConsumer(),
           getMinApiLevel(),
           getReporter(),
@@ -352,7 +357,7 @@
     }
   }
 
-  static class DesugaredLibrary implements ClassFileConsumer, ProgramResourceProvider {
+  static class InMemoryJarContent implements ClassFileConsumer, ProgramResourceProvider {
 
     private final List<ProgramResource> resources = new ArrayList<>();
 
diff --git a/src/main/java/com/android/tools/r8/L8CommandParser.java b/src/main/java/com/android/tools/r8/L8CommandParser.java
index e083cec..5d9392c 100644
--- a/src/main/java/com/android/tools/r8/L8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/L8CommandParser.java
@@ -88,7 +88,7 @@
   private L8Command.Builder parse(String[] args, Origin origin, L8Command.Builder builder) {
     CompilationMode compilationMode = null;
     Path outputPath = null;
-    OutputMode outputMode = null;
+    OutputMode outputMode = OutputMode.DexIndexed;
     boolean hasDefinedApiLevel = false;
     OrderedClassFileResourceProvider.Builder classpathBuilder =
         OrderedClassFileResourceProvider.builder();
@@ -149,6 +149,8 @@
         builder.addProguardConfigurationFiles(Paths.get(nextArg));
       } else if (arg.equals("--desugared-lib")) {
         builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
+      } else if (arg.equals("--classfile")) {
+        outputMode = OutputMode.ClassFile;
       } else if (arg.equals(THREAD_COUNT_FLAG)) {
         parsePositiveIntArgument(
             builder::error, THREAD_COUNT_FLAG, nextArg, origin, builder::setThreadCount);
@@ -167,9 +169,6 @@
     if (compilationMode != null) {
       builder.setMode(compilationMode);
     }
-    if (outputMode == null) {
-      outputMode = OutputMode.DexIndexed;
-    }
     if (outputPath == null) {
       outputPath = Paths.get(".");
     }
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 9d6b199..3a9b0d9 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -78,6 +78,7 @@
 import com.android.tools.r8.optimize.VisibilityBridgeRemover;
 import com.android.tools.r8.origin.CommandLineOrigin;
 import com.android.tools.r8.repackaging.Repackaging;
+import com.android.tools.r8.repackaging.RepackagingLens;
 import com.android.tools.r8.shaking.AbstractMethodRemover;
 import com.android.tools.r8.shaking.AnnotationRemover;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -226,12 +227,7 @@
       Set<Marker> markers = new HashSet<>(options.itemFactory.extractMarkers());
       markers.remove(marker);
       if (options.isGeneratingClassFiles()) {
-        new CfApplicationWriter(
-                appView,
-                marker,
-                graphLens,
-                namingLens,
-                proguardMapSupplier)
+        new CfApplicationWriter(appView, marker, graphLens, namingLens, proguardMapSupplier)
             .write(options.getClassFileConsumer());
       } else {
         new ApplicationWriter(
@@ -390,7 +386,7 @@
         assert appView.rootSet().verifyKeptFieldsAreAccessedAndLive(appViewWithLiveness.appInfo());
         assert appView.rootSet().verifyKeptMethodsAreTargetedAndLive(appViewWithLiveness.appInfo());
         assert appView.rootSet().verifyKeptTypesAreLive(appViewWithLiveness.appInfo());
-        assert appView.rootSet().verifyKeptItemsAreKept(appView.appInfo().app(), appView.appInfo());
+        assert appView.rootSet().verifyKeptItemsAreKept(appView);
 
         missingClasses =
             Sets.union(missingClasses, appViewWithLiveness.appInfo().getMissingTypes());
@@ -551,7 +547,8 @@
         if (options.enableHorizontalClassMerging) {
           timing.begin("HorizontalClassMerger");
           HorizontalClassMerger merger =
-              new HorizontalClassMerger(appViewWithLiveness, mainDexClasses);
+              new HorizontalClassMerger(
+                  appViewWithLiveness, mainDexClasses, classMergingEnqueuerExtension);
           HorizontalClassMergerGraphLens lens = merger.run();
           if (lens != null) {
             appView.setHorizontallyMergedClasses(lens.getHorizontallyMergedClasses());
@@ -809,7 +806,15 @@
       // Perform repackaging.
       // TODO(b/165783399): Consider making repacking available without minification.
       if (options.isMinifying() && options.testing.enableExperimentalRepackaging) {
-        new Repackaging(appView.withLiveness()).run(executorService, timing);
+        DirectMappedDexApplication.Builder appBuilder =
+            appView.appInfo().app().asDirect().builder();
+        // TODO(b/165783399): We need to deal with non-rebound member references in the writer,
+        //  possibly by adding a member rebinding lens on top of the repackaging lens.
+        RepackagingLens lens =
+            new Repackaging(appView.withLiveness()).run(appBuilder, executorService, timing);
+        if (lens != null) {
+          appView.rewriteWithLensAndApplication(lens, appBuilder.build());
+        }
       }
 
       // Perform minification.
@@ -886,7 +891,7 @@
       if (options.isShrinking()
           || options.isMinifying()
           || options.getProguardConfiguration().hasApplyMappingFile()) {
-        assert appView.rootSet().verifyKeptItemsAreKept(appView.appInfo().app(), appView.appInfo());
+        assert appView.rootSet().verifyKeptItemsAreKept(appView);
       }
       assert appView
           .graphLens()
@@ -912,6 +917,7 @@
           .run(appView.appInfo().classes(), executorService);
 
       // Generate the resulting application resources.
+      // TODO(b/165783399): Apply the graph lens to all instructions in the CF and DEX backends.
       writeApplication(
           executorService,
           appView,
diff --git a/src/main/java/com/android/tools/r8/ResourceShrinker.java b/src/main/java/com/android/tools/r8/ResourceShrinker.java
index 0affffd..8174420 100644
--- a/src/main/java/com/android/tools/r8/ResourceShrinker.java
+++ b/src/main/java/com/android/tools/r8/ResourceShrinker.java
@@ -288,7 +288,8 @@
         // of integers. This can be improved later, but for now we make sure no ints are missed.
       }
 
-      methodIntArrayPayloadOffsets.add(fillArrayData.getPayloadOffset() + fillArrayData.offset);
+      methodIntArrayPayloadOffsets.add(
+          fillArrayData.getPayloadOffset() + fillArrayData.getOffset());
     }
 
     private void processAnnotationValue(DexValue value) {
diff --git a/src/main/java/com/android/tools/r8/bisect/Bisect.java b/src/main/java/com/android/tools/r8/bisect/Bisect.java
index 0e86dc7..80dd656 100644
--- a/src/main/java/com/android/tools/r8/bisect/Bisect.java
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -10,6 +10,8 @@
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfo;
+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.InitClassLens;
@@ -188,7 +190,7 @@
     ApplicationWriter writer =
         new ApplicationWriter(
             app,
-            null,
+            AppView.createForD8(AppInfo.createInitialAppInfo(app)),
             options,
             null,
             null,
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 21a5392..5fb9822 100644
--- a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.cf;
 
+import static com.android.tools.r8.utils.StringUtils.join;
+
 import com.android.tools.r8.cf.code.CfArithmeticBinop;
 import com.android.tools.r8.cf.code.CfArrayLength;
 import com.android.tools.r8.cf.code.CfArrayLoad;
@@ -18,6 +20,7 @@
 import com.android.tools.r8.cf.code.CfDexItemBasedConstString;
 import com.android.tools.r8.cf.code.CfFieldInstruction;
 import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
 import com.android.tools.r8.cf.code.CfGoto;
 import com.android.tools.r8.cf.code.CfIf;
 import com.android.tools.r8.cf.code.CfIfCmp;
@@ -46,6 +49,7 @@
 import com.android.tools.r8.cf.code.CfTryCatch;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
@@ -185,6 +189,23 @@
     return type("ImmutableList", ImmutableList.of("com", "google", "common", "collect"));
   }
 
+  private String int2ReferenceAVLTreeMapType() {
+    return type(
+        "Int2ReferenceAVLTreeMap", ImmutableList.of("it", "unimi", "dsi", "fastutil", "ints"));
+  }
+
+  private String frameTypeType() {
+    return r8Type("FrameType", ImmutableList.of("cf", "code", "CfFrame"));
+  }
+
+  private String dexItemFactoryType() {
+    return r8Type("DexItemFactory", "graph");
+  }
+
+  private String arraysType() {
+    return type("Arrays", ImmutableList.of("java", "util"));
+  }
+
   private String r8Type(String name, String pkg) {
     return r8Type(name, Collections.singletonList(pkg));
   }
@@ -427,7 +448,40 @@
 
   @Override
   public void print(CfFrame frame) {
-    throw new Unimplemented(frame.getClass().getSimpleName());
+    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);
+    printNewInstruction(
+        "CfFrame",
+        "new "
+            + int2ReferenceAVLTreeMapType()
+            + "<>("
+            + "new int[] {"
+            + keys
+            + "},"
+            + "new "
+            + frameTypeType()
+            + "[] { "
+            + values
+            + " })",
+        arraysType() + ".asList(" + stack + ")");
+  }
+
+  private String frameTypeType(FrameType frameType) {
+    if (frameType.isTop()) {
+      return frameTypeType() + ".top()";
+    } else if (frameType.isUninitializedThis()) {
+      return frameTypeType() + ".uninitializedThis()";
+    } else if (frameType.isUninitializedNew()) {
+      return frameTypeType() + ".uninitializedNew(new " + cfType("CfLabel") + "())";
+    } else {
+      assert frameType.isInitialized();
+      if (frameType.getInitializedType() == DexItemFactory.nullValueType) {
+        return frameTypeType() + ".initialized(" + dexItemFactoryType() + ".nullValueType)";
+      } else {
+        return frameTypeType() + ".initialized(" + dexType(frameType.getInitializedType()) + ")";
+      }
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
index 6f4e958..b679a54 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.ValueType;
@@ -129,7 +130,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitInsn(getAsmOpcode());
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
index 1290256..6510e23 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -19,7 +20,8 @@
 public class CfArrayLength extends CfInstruction {
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitInsn(Opcodes.ARRAYLENGTH);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
index 02e20ea..a9d754e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.ValueType;
@@ -56,7 +57,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitInsn(getLoadType());
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
index 1976942..5e8c152 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -54,7 +55,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitInsn(getStoreType());
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
index a46e68c..7f6a380 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -32,8 +33,9 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
-    visitor.visitTypeInsn(Opcodes.CHECKCAST, lens.lookupInternalName(type));
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
+    visitor.visitTypeInsn(Opcodes.CHECKCAST, lens.lookupInternalName(graphLens.lookupType(type)));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
index 8d128be..1163483 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.Cmp;
 import com.android.tools.r8.ir.code.Cmp.Bias;
@@ -79,7 +80,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitInsn(getAsmOpcode());
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
index 05d8568..37255ff 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -32,8 +33,12 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
-    visitor.visitLdcInsn(Type.getObjectType(getInternalName(lens)));
+  public void write(
+      MethodVisitor visitor,
+      GraphLens graphLens,
+      InitClassLens initClassLens,
+      NamingLens namingLens) {
+    visitor.visitLdcInsn(Type.getObjectType(getInternalName(graphLens, namingLens)));
   }
 
   @Override
@@ -46,11 +51,11 @@
     return true;
   }
 
-  private String getInternalName(NamingLens lens) {
+  private String getInternalName(GraphLens graphLens, NamingLens namingLens) {
     switch (type.toShorty()) {
       case '[':
       case 'L':
-        return lens.lookupInternalName(type);
+        return namingLens.lookupInternalName(graphLens.lookupType(type));
       case 'Z':
         return "java/lang/Boolean/TYPE";
       case 'B':
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
index 89fae02..c5d52f2 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
@@ -31,7 +32,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitLdcInsn(handle.toAsmHandle(lens));
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
index 8283278..17577b9 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -31,7 +32,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitLdcInsn(Type.getType(type.toDescriptorString(lens)));
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
index 889a63c..1c5e970 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -19,7 +20,8 @@
 public class CfConstNull extends CfInstruction {
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitInsn(Opcodes.ACONST_NULL);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
index 5451986..b4e63a9 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -56,7 +57,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     switch (type) {
       case INT:
         {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
index 3b113cb..649f756 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -42,7 +43,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitLdcInsn(string.toString());
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
index 94eadd1..79e59db 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -48,7 +49,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     throw new Unreachable(
         "CfDexItemBasedConstString instructions should always be rewritten into CfConstString");
   }
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 7e31f5b..32c8494 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
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -53,10 +54,13 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
-    String owner = lens.lookupInternalName(field.holder);
-    String name = lens.lookupName(declaringField).toString();
-    String desc = lens.lookupDescriptor(field.type).toString();
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
+    DexField newField = graphLens.lookupField(field);
+    DexField newDeclaringField = graphLens.lookupField(declaringField);
+    String owner = lens.lookupInternalName(newField.holder);
+    String name = lens.lookupName(newDeclaringField).toString();
+    String desc = lens.lookupDescriptor(newField.type).toString();
     visitor.visitFieldInsn(opcode, owner, name, desc);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
index f1058d6..152f9e9 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -223,7 +224,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     int stackCount = computeStackCount();
     Object[] stackTypes = computeStackTypes(stackCount, lens);
     int localsCount = computeLocalsCount();
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
index bbb8996..d38c0fe 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -44,7 +45,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitJumpInsn(Opcodes.GOTO, target.getLabel());
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIf.java b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
index a6d0fca..f64d79c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.If;
 import com.android.tools.r8.ir.code.If.Type;
@@ -69,7 +70,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitJumpInsn(getOpcode(), target.getLabel());
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
index 24c81a5..1ee17b1 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.If;
 import com.android.tools.r8.ir.code.If.Type;
@@ -69,7 +70,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitJumpInsn(getOpcode(), target.getLabel());
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIinc.java b/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
index 4ce64a4..edba7b4 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -26,7 +27,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitIincInsn(var, increment);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
index 115d27a..afcae7d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -37,7 +38,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     DexField field = initClassLens.getInitClassField(clazz);
     String owner = lens.lookupInternalName(field.holder);
     String name = lens.lookupName(field).toString();
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
index ca6b7ed..fc26a95 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -41,7 +42,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitTypeInsn(Opcodes.INSTANCEOF, lens.lookupInternalName(type));
   }
 
@@ -52,7 +54,7 @@
 
   @Override
   void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
-    registry.registerTypeReference(type);
+    registry.registerInstanceOf(type);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index 041409b..16ef22f 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.ClasspathMethod;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.UseRegistry;
@@ -20,7 +21,8 @@
 
 public abstract class CfInstruction {
 
-  public abstract void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens);
+  public abstract void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens);
 
   public abstract void print(CfPrinter printer);
 
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 510249b..3851031 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
@@ -71,10 +71,12 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
-    String owner = lens.lookupInternalName(method.holder);
-    String name = lens.lookupName(method).toString();
-    String desc = method.proto.toDescriptorString(lens);
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
+    DexMethod newMethod = graphLens.lookupMethod(method);
+    String owner = lens.lookupInternalName(newMethod.holder);
+    String name = lens.lookupName(newMethod).toString();
+    String desc = newMethod.proto.toDescriptorString(lens);
     visitor.visitMethodInsn(opcode, owner, name, desc, itf);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index fac04cd..f57046b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.code.ValueType;
@@ -36,7 +37,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     DexMethodHandle bootstrapMethod = callSite.bootstrapMethod;
     List<DexValue> bootstrapArgs = callSite.bootstrapArgs;
     Object[] bsmArgs = new Object[bootstrapArgs.size()];
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
index 2fc78ea..88659de 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -29,7 +30,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     throw error();
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
index 6cc49de..19ba74a 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -42,7 +43,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitLabel(getLabel());
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
index 0680d73..80bb1da 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -56,7 +57,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitVarInsn(getLoadType(), var);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
index f34a7d7..0f5bd4e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.ValueType;
@@ -104,7 +105,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitInsn(getAsmOpcode());
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
index 7fd0167..ebefa78 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.Monitor.Type;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -30,7 +31,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitInsn(type == Type.ENTER ? Opcodes.MONITORENTER : Opcodes.MONITOREXIT);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
index 1f787f8..69a69af 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -37,7 +38,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitMultiANewArrayInsn(lens.lookupInternalName(type), dimensions);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
index cd61c29..32ef906 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.ValueType;
@@ -31,7 +32,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitInsn(getAsmOpcode());
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNew.java b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
index 8bd1249..e0b8785 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNew.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -31,7 +32,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitTypeInsn(Opcodes.NEW, lens.lookupInternalName(type));
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
index 9ef5f1b..6c3869b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -66,7 +67,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     if (type.isPrimitiveArrayType()) {
       visitor.visitIntInsn(Opcodes.NEWARRAY, getPrimitiveTypeCode());
     } else {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNop.java b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
index 4578bff..18a2ae6 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -18,7 +19,8 @@
 public class CfNop extends CfInstruction {
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitInsn(Opcodes.NOP);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
index afbff92..405fb42 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.ValueType;
@@ -41,7 +42,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitInsn(this.getAsmOpcode());
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
index 93febbb..70a3f07 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -26,7 +27,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitLineNumber(position.line, label.getLabel());
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
index 5934617..d66d6cf 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -53,7 +54,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitInsn(getOpcode());
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
index c9d7f51..83b5e60 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -23,7 +24,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitInsn(Opcodes.RETURN);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
index ef291d0..a4ecce6 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -75,7 +76,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitInsn(opcode.opcode);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStore.java b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
index d8f13d1..c3ce5db 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -56,7 +57,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitVarInsn(getStoreType(), var);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
index 3169da0..3a3c72e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -69,7 +70,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     Label[] labels = new Label[targets.size()];
     for (int i = 0; i < targets.size(); i++) {
       labels[i] = targets.get(i).getLabel();
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
index 9b88cb5..3b49cb0 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -24,7 +25,8 @@
   }
 
   @Override
-  public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+  public void write(
+      MethodVisitor visitor, GraphLens graphLens, InitClassLens initClassLens, NamingLens lens) {
     visitor.visitInsn(Opcodes.ATHROW);
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/CheckCast.java b/src/main/java/com/android/tools/r8/code/CheckCast.java
index f472800..569b358 100644
--- a/src/main/java/com/android/tools/r8/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/code/CheckCast.java
@@ -3,12 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
+import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class CheckCast extends Format21c {
+public class CheckCast extends Format21c<DexType> {
 
   public static final int OPCODE = 0x1f;
   public static final String NAME = "CheckCast";
@@ -38,6 +39,11 @@
   }
 
   @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getType().collectIndexedItems(indexedItems);
+  }
+
+  @Override
   public CheckCast asCheckCast() {
     return this;
   }
@@ -53,7 +59,7 @@
   }
 
   public DexType getType() {
-    return (DexType) BBBB;
+    return BBBB;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/ConstClass.java b/src/main/java/com/android/tools/r8/code/ConstClass.java
index 33cef07..2f8f7db 100644
--- a/src/main/java/com/android/tools/r8/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/code/ConstClass.java
@@ -3,12 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
+import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class ConstClass extends Format21c {
+public class ConstClass extends Format21c<DexType> {
 
   public static final int OPCODE = 0x1c;
   public static final String NAME = "ConstClass";
@@ -38,12 +39,17 @@
   }
 
   @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getType().collectIndexedItems(indexedItems);
+  }
+
+  @Override
   public void registerUse(UseRegistry registry) {
     registry.registerConstClass(getType());
   }
 
   public DexType getType() {
-    return (DexType) BBBB;
+    return BBBB;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
index fd17fef..8518946 100644
--- a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
+import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.graph.DexMethodHandle;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
@@ -13,7 +14,7 @@
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
 
-public class ConstMethodHandle extends Format21c {
+public class ConstMethodHandle extends Format21c<DexMethodHandle> {
 
   public static final int OPCODE = 0xfe;
   public static final String NAME = "ConstMethodHandle";
@@ -28,7 +29,7 @@
   }
 
   public DexMethodHandle getMethodHandle() {
-    return (DexMethodHandle) BBBB;
+    return BBBB;
   }
 
   @Override
@@ -72,8 +73,13 @@
   }
 
   @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getMethodHandle().collectIndexedItems(indexedItems);
+  }
+
+  @Override
   public void buildIR(IRBuilder builder) {
-    builder.addConstMethodHandle(AA, (DexMethodHandle) BBBB);
+    builder.addConstMethodHandle(AA, BBBB);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/code/ConstMethodType.java
index 84c090a..8eb5443 100644
--- a/src/main/java/com/android/tools/r8/code/ConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/code/ConstMethodType.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
+import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
@@ -12,7 +13,7 @@
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
 
-public class ConstMethodType extends Format21c {
+public class ConstMethodType extends Format21c<DexProto> {
 
   public static final int OPCODE = 0xff;
   public static final String NAME = "ConstMethodType";
@@ -27,7 +28,7 @@
   }
 
   public DexProto getMethodType() {
-    return (DexProto) BBBB;
+    return BBBB;
   }
 
   @Override
@@ -70,8 +71,13 @@
   }
 
   @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getMethodType().collectIndexedItems(indexedItems);
+  }
+
+  @Override
   public void buildIR(IRBuilder builder) {
-    builder.addConstMethodType(AA, (DexProto) BBBB);
+    builder.addConstMethodType(AA, BBBB);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/ConstString.java b/src/main/java/com/android/tools/r8/code/ConstString.java
index a5733ad..261016b 100644
--- a/src/main/java/com/android/tools/r8/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/code/ConstString.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
+import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
@@ -11,7 +12,7 @@
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
 
-public class ConstString extends Format21c {
+public class ConstString extends Format21c<DexString> {
 
   public static final int OPCODE = 0x1a;
   public static final String NAME = "ConstString";
@@ -26,7 +27,12 @@
   }
 
   public DexString getString() {
-    return (DexString) BBBB;
+    return BBBB;
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getString().collectIndexedItems(indexedItems);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/DexInitClass.java b/src/main/java/com/android/tools/r8/code/DexInitClass.java
index 9df8285..20f2047 100644
--- a/src/main/java/com/android/tools/r8/code/DexInitClass.java
+++ b/src/main/java/com/android/tools/r8/code/DexInitClass.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.errors.Unreachable;
 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.ObjectToOffsetMapping;
 import com.android.tools.r8.graph.UseRegistry;
@@ -37,10 +36,9 @@
   }
 
   @Override
-  public void collectIndexedItems(
-      IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     DexField field = indexedItems.getInitClassLens().getInitClassField(clazz);
-    field.collectIndexedItems(indexedItems, method, instructionOffset);
+    field.collectIndexedItems(indexedItems);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/code/DexItemBasedConstString.java
index 5ae471a..1fb6e8c 100644
--- a/src/main/java/com/android/tools/r8/code/DexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/code/DexItemBasedConstString.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
+import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
@@ -12,7 +13,7 @@
 import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
 import java.nio.ShortBuffer;
 
-public class DexItemBasedConstString extends Format21c {
+public class DexItemBasedConstString extends Format21c<DexReference> {
 
   public static final String NAME = "DexItemBasedConstString";
   public static final String SMALI_NAME = "const-string*";
@@ -26,7 +27,7 @@
   }
 
   public DexReference getItem() {
-    return (DexReference) BBBB;
+    return BBBB;
   }
 
   public NameComputationInfo<?> getNameComputationInfo() {
@@ -34,6 +35,11 @@
   }
 
   @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getItem().collectIndexedItems(indexedItems);
+  }
+
+  @Override
   public String getName() {
     return NAME;
   }
diff --git a/src/main/java/com/android/tools/r8/code/FilledNewArray.java b/src/main/java/com/android/tools/r8/code/FilledNewArray.java
index 4f2ebb6..1932e16 100644
--- a/src/main/java/com/android/tools/r8/code/FilledNewArray.java
+++ b/src/main/java/com/android/tools/r8/code/FilledNewArray.java
@@ -3,11 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
+import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class FilledNewArray extends Format35c {
+public class FilledNewArray extends Format35c<DexType> {
 
   public static final int OPCODE = 0x24;
   public static final String NAME = "FilledNewArray";
@@ -36,8 +37,13 @@
     return OPCODE;
   }
 
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getType().collectIndexedItems(indexedItems);
+  }
+
   public DexType getType() {
-    return (DexType) BBBB;
+    return BBBB;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
index 0ee63b0..7fc2adc 100644
--- a/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
+++ b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
@@ -3,11 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
+import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class FilledNewArrayRange extends Format3rc {
+public class FilledNewArrayRange extends Format3rc<DexType> {
 
   public static final int OPCODE = 0x25;
   public static final String NAME = "FilledNewArrayRange";
@@ -36,8 +37,13 @@
     return OPCODE;
   }
 
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getType().collectIndexedItems(indexedItems);
+  }
+
   public DexType getType() {
-    return (DexType) BBBB;
+    return BBBB;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format10t.java b/src/main/java/com/android/tools/r8/code/Format10t.java
index 8365a28..4c61fa3 100644
--- a/src/main/java/com/android/tools/r8/code/Format10t.java
+++ b/src/main/java/com/android/tools/r8/code/Format10t.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
@@ -54,8 +53,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format10x.java b/src/main/java/com/android/tools/r8/code/Format10x.java
index 0b497fb..8eed571 100644
--- a/src/main/java/com/android/tools/r8/code/Format10x.java
+++ b/src/main/java/com/android/tools/r8/code/Format10x.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
@@ -46,8 +45,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format11n.java b/src/main/java/com/android/tools/r8/code/Format11n.java
index 4d9a01b..f2b8ac2 100644
--- a/src/main/java/com/android/tools/r8/code/Format11n.java
+++ b/src/main/java/com/android/tools/r8/code/Format11n.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
@@ -59,8 +58,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format11x.java b/src/main/java/com/android/tools/r8/code/Format11x.java
index dd5a76b..318bf04 100644
--- a/src/main/java/com/android/tools/r8/code/Format11x.java
+++ b/src/main/java/com/android/tools/r8/code/Format11x.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
@@ -54,8 +53,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format12x.java b/src/main/java/com/android/tools/r8/code/Format12x.java
index 07ec8e2..ac90623 100644
--- a/src/main/java/com/android/tools/r8/code/Format12x.java
+++ b/src/main/java/com/android/tools/r8/code/Format12x.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
@@ -58,8 +57,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format20t.java b/src/main/java/com/android/tools/r8/code/Format20t.java
index ee32757..f145476 100644
--- a/src/main/java/com/android/tools/r8/code/Format20t.java
+++ b/src/main/java/com/android/tools/r8/code/Format20t.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
@@ -54,8 +53,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format21c.java b/src/main/java/com/android/tools/r8/code/Format21c.java
index faa9d58..04848f0 100644
--- a/src/main/java/com/android/tools/r8/code/Format21c.java
+++ b/src/main/java/com/android/tools/r8/code/Format21c.java
@@ -4,27 +4,25 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.IndexedDexItem;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
 import java.util.function.BiPredicate;
 
-abstract class Format21c extends Base2Format {
+abstract class Format21c<T extends IndexedDexItem> extends Base2Format {
 
   public final short AA;
-  public IndexedDexItem BBBB;
+  public T BBBB;
 
   // AA | op | [type|field|string]@BBBB
-  Format21c(int high, BytecodeStream stream, IndexedDexItem[] map) {
+  Format21c(int high, BytecodeStream stream, T[] map) {
     super(stream);
     AA = (short) high;
     BBBB = map[read16BitValue(stream)];
   }
 
-  protected Format21c(int AA, IndexedDexItem BBBB) {
+  protected Format21c(int AA, T BBBB) {
     assert 0 <= AA && AA <= Constants.U8BIT_MAX;
     this.AA = (short) AA;
     this.BBBB = BBBB;
@@ -46,7 +44,7 @@
     if (other == null || this.getClass() != other.getClass()) {
       return false;
     }
-    Format21c o = (Format21c) other;
+    Format21c<?> o = (Format21c<?>) other;
     return o.AA == AA && o.BBBB.equals(BBBB);
   }
 
@@ -63,17 +61,11 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    BBBB.collectIndexedItems(indexedItems, method, instructionOffset);
-  }
-
-  @Override
   public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
     if (other == null || this.getClass() != other.getClass()) {
       return false;
     }
-    Format21c o = (Format21c) other;
+    Format21c<?> o = (Format21c<?>) other;
     return o.AA == AA && equality.test(BBBB, o.BBBB);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format21h.java b/src/main/java/com/android/tools/r8/code/Format21h.java
index 074a0a0..221dc9e 100644
--- a/src/main/java/com/android/tools/r8/code/Format21h.java
+++ b/src/main/java/com/android/tools/r8/code/Format21h.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import java.nio.ShortBuffer;
 
@@ -49,8 +48,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format21s.java b/src/main/java/com/android/tools/r8/code/Format21s.java
index 48f457c..a9d7dc1 100644
--- a/src/main/java/com/android/tools/r8/code/Format21s.java
+++ b/src/main/java/com/android/tools/r8/code/Format21s.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.utils.StringUtils;
@@ -61,8 +60,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format21t.java b/src/main/java/com/android/tools/r8/code/Format21t.java
index 7266a6a..d89b029 100644
--- a/src/main/java/com/android/tools/r8/code/Format21t.java
+++ b/src/main/java/com/android/tools/r8/code/Format21t.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.ir.code.If.Type;
 import com.android.tools.r8.ir.code.ValueTypeConstraint;
@@ -79,8 +78,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format22b.java b/src/main/java/com/android/tools/r8/code/Format22b.java
index 86a51b6..3952eb7 100644
--- a/src/main/java/com/android/tools/r8/code/Format22b.java
+++ b/src/main/java/com/android/tools/r8/code/Format22b.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.utils.StringUtils;
@@ -66,8 +65,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format22c.java b/src/main/java/com/android/tools/r8/code/Format22c.java
index 16082a4..751ef61 100644
--- a/src/main/java/com/android/tools/r8/code/Format22c.java
+++ b/src/main/java/com/android/tools/r8/code/Format22c.java
@@ -4,29 +4,27 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.IndexedDexItem;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
 import java.util.function.BiPredicate;
 
-public abstract class Format22c extends Base2Format {
+public abstract class Format22c<T extends IndexedDexItem> extends Base2Format {
 
   public final byte A;
   public final byte B;
-  public IndexedDexItem CCCC;
+  public T CCCC;
 
   // vB | vA | op | [type|field]@CCCC
-  /*package*/ Format22c(int high, BytecodeStream stream, IndexedDexItem[] map) {
+  /*package*/ Format22c(int high, BytecodeStream stream, T[] map) {
     super(stream);
     A = (byte) (high & 0xf);
     B = (byte) ((high >> 4) & 0xf);
     CCCC = map[read16BitValue(stream)];
   }
 
-  /*package*/ Format22c(int A, int B, IndexedDexItem CCCC) {
+  /*package*/ Format22c(int A, int B, T CCCC) {
     assert 0 <= A && A <= Constants.U4BIT_MAX;
     assert 0 <= B && B <= Constants.U4BIT_MAX;
     this.A = (byte) A;
@@ -50,7 +48,7 @@
     if (other == null || this.getClass() != other.getClass()) {
       return false;
     }
-    Format22c o = (Format22c) other;
+    Format22c<?> o = (Format22c<?>) other;
     return o.A == A && o.B == B && o.CCCC.equals(CCCC);
   }
 
@@ -67,17 +65,11 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    CCCC.collectIndexedItems(indexedItems, method, instructionOffset);
-  }
-
-  @Override
   public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
     if (other == null || this.getClass() != other.getClass()) {
       return false;
     }
-    Format22c o = (Format22c) other;
+    Format22c<?> o = (Format22c<?>) other;
     return o.A == A && o.B == B && equality.test(CCCC, o.CCCC);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format22s.java b/src/main/java/com/android/tools/r8/code/Format22s.java
index 9666b21..25c51d6 100644
--- a/src/main/java/com/android/tools/r8/code/Format22s.java
+++ b/src/main/java/com/android/tools/r8/code/Format22s.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.utils.StringUtils;
@@ -66,8 +65,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format22t.java b/src/main/java/com/android/tools/r8/code/Format22t.java
index 66e5df1..6505e5c 100644
--- a/src/main/java/com/android/tools/r8/code/Format22t.java
+++ b/src/main/java/com/android/tools/r8/code/Format22t.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.ir.code.If.Type;
 import com.android.tools.r8.ir.code.ValueTypeConstraint;
@@ -83,8 +82,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format22x.java b/src/main/java/com/android/tools/r8/code/Format22x.java
index d30f023..5c1b2c4 100644
--- a/src/main/java/com/android/tools/r8/code/Format22x.java
+++ b/src/main/java/com/android/tools/r8/code/Format22x.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
@@ -60,8 +59,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format23x.java b/src/main/java/com/android/tools/r8/code/Format23x.java
index 02722db..739b66f 100644
--- a/src/main/java/com/android/tools/r8/code/Format23x.java
+++ b/src/main/java/com/android/tools/r8/code/Format23x.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
@@ -64,8 +63,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format30t.java b/src/main/java/com/android/tools/r8/code/Format30t.java
index 5ef18eb..bfcf0d3 100644
--- a/src/main/java/com/android/tools/r8/code/Format30t.java
+++ b/src/main/java/com/android/tools/r8/code/Format30t.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
@@ -53,8 +52,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format31c.java b/src/main/java/com/android/tools/r8/code/Format31c.java
index 3019b9b..ead4920 100644
--- a/src/main/java/com/android/tools/r8/code/Format31c.java
+++ b/src/main/java/com/android/tools/r8/code/Format31c.java
@@ -6,7 +6,6 @@
 import static com.android.tools.r8.dex.Constants.U8BIT_MAX;
 
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.IndexedDexItem;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
@@ -59,9 +58,8 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    BBBBBBBB.collectIndexedItems(indexedItems, method, instructionOffset);
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    BBBBBBBB.collectIndexedItems(indexedItems);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format31i.java b/src/main/java/com/android/tools/r8/code/Format31i.java
index 192b28d..5e55ca4 100644
--- a/src/main/java/com/android/tools/r8/code/Format31i.java
+++ b/src/main/java/com/android/tools/r8/code/Format31i.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
@@ -54,8 +53,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format31t.java b/src/main/java/com/android/tools/r8/code/Format31t.java
index 19de81f..d650fed 100644
--- a/src/main/java/com/android/tools/r8/code/Format31t.java
+++ b/src/main/java/com/android/tools/r8/code/Format31t.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
@@ -69,8 +68,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format32x.java b/src/main/java/com/android/tools/r8/code/Format32x.java
index 4c70702..a31a494 100644
--- a/src/main/java/com/android/tools/r8/code/Format32x.java
+++ b/src/main/java/com/android/tools/r8/code/Format32x.java
@@ -6,7 +6,6 @@
 import static com.android.tools.r8.dex.Constants.U16BIT_MAX;
 
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
@@ -62,8 +61,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format35c.java b/src/main/java/com/android/tools/r8/code/Format35c.java
index f8c68c3..fba7d0d 100644
--- a/src/main/java/com/android/tools/r8/code/Format35c.java
+++ b/src/main/java/com/android/tools/r8/code/Format35c.java
@@ -4,15 +4,13 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.IndexedDexItem;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
 import java.util.function.BiPredicate;
 
-public abstract class Format35c extends Base3Format {
+public abstract class Format35c<T extends IndexedDexItem> extends Base3Format {
 
   public final byte A;
   public final byte C;
@@ -20,10 +18,10 @@
   public final byte E;
   public final byte F;
   public final byte G;
-  public IndexedDexItem BBBB;
+  public T BBBB;
 
   // A | G | op | BBBB | F | E | D | C
-  Format35c(int high, BytecodeStream stream, IndexedDexItem[] map) {
+  Format35c(int high, BytecodeStream stream, T[] map) {
     super(stream);
     G = (byte) (high & 0xf);
     A = (byte) ((high >> 4) & 0xf);
@@ -36,7 +34,7 @@
     D = (byte) ((next >> 4) & 0xf);
   }
 
-  protected Format35c(int A, IndexedDexItem BBBB, int C, int D, int E, int F, int G) {
+  Format35c(int A, T BBBB, int C, int D, int E, int F, int G) {
     assert 0 <= A && A <= Constants.U4BIT_MAX;
     assert 0 <= C && C <= Constants.U4BIT_MAX;
     assert 0 <= D && D <= Constants.U4BIT_MAX;
@@ -111,12 +109,6 @@
   }
 
   @Override
-  public void collectIndexedItems(
-      IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
-    BBBB.collectIndexedItems(indexedItems, method, instructionOffset);
-  }
-
-  @Override
   public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
     if (other == null || (this.getClass() != other.getClass())) {
       return false;
diff --git a/src/main/java/com/android/tools/r8/code/Format3rc.java b/src/main/java/com/android/tools/r8/code/Format3rc.java
index 77d3571..a50a9d9 100644
--- a/src/main/java/com/android/tools/r8/code/Format3rc.java
+++ b/src/main/java/com/android/tools/r8/code/Format3rc.java
@@ -4,29 +4,27 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.IndexedDexItem;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
 import java.util.function.BiPredicate;
 
-public abstract class Format3rc extends Base3Format {
+public abstract class Format3rc<T extends IndexedDexItem> extends Base3Format {
 
   public final short AA;
   public final char CCCC;
-  public IndexedDexItem BBBB;
+  public T BBBB;
 
   // AA | op | [meth|type]@BBBBB | CCCC
-  Format3rc(int high, BytecodeStream stream, IndexedDexItem[] map) {
+  Format3rc(int high, BytecodeStream stream, T[] map) {
     super(stream);
     this.AA = (short) high;
     this.BBBB = map[read16BitValue(stream)];
     this.CCCC = read16BitValue(stream);
   }
 
-  Format3rc(int firstArgumentRegister, int argumentCount, IndexedDexItem dexItem) {
+  Format3rc(int firstArgumentRegister, int argumentCount, T dexItem) {
     assert 0 <= firstArgumentRegister && firstArgumentRegister <= Constants.U16BIT_MAX;
     assert 0 <= argumentCount && argumentCount <= Constants.U8BIT_MAX;
     this.CCCC = (char) firstArgumentRegister;
@@ -34,6 +32,10 @@
     BBBB = dexItem;
   }
 
+  public T getItem() {
+    return BBBB;
+  }
+
   @Override
   public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
     writeFirst(AA, dest);
@@ -51,7 +53,7 @@
     if (other == null || (this.getClass() != other.getClass())) {
       return false;
     }
-    Format3rc o = (Format3rc) other;
+    Format3rc<?> o = (Format3rc<?>) other;
     return o.AA == AA && o.CCCC == CCCC && o.BBBB.equals(BBBB);
   }
 
@@ -89,17 +91,11 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    BBBB.collectIndexedItems(indexedItems, method, instructionOffset);
-  }
-
-  @Override
   public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
     if (other == null || (this.getClass() != other.getClass())) {
       return false;
     }
-    Format3rc o = (Format3rc) other;
+    Format3rc<?> o = (Format3rc<?>) other;
     return o.AA == AA && o.CCCC == CCCC && equality.test(BBBB, o.BBBB);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format45cc.java b/src/main/java/com/android/tools/r8/code/Format45cc.java
index fc88461..f5123cf 100644
--- a/src/main/java/com/android/tools/r8/code/Format45cc.java
+++ b/src/main/java/com/android/tools/r8/code/Format45cc.java
@@ -87,10 +87,9 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    BBBB.collectIndexedItems(indexedItems, method, instructionOffset);
-    HHHH.collectIndexedItems(indexedItems, method, instructionOffset);
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    BBBB.collectIndexedItems(indexedItems);
+    HHHH.collectIndexedItems(indexedItems);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format4rcc.java b/src/main/java/com/android/tools/r8/code/Format4rcc.java
index c139b03..0ec2780 100644
--- a/src/main/java/com/android/tools/r8/code/Format4rcc.java
+++ b/src/main/java/com/android/tools/r8/code/Format4rcc.java
@@ -93,10 +93,9 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    BBBB.collectIndexedItems(indexedItems, method, instructionOffset);
-    HHHH.collectIndexedItems(indexedItems, method, instructionOffset);
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    BBBB.collectIndexedItems(indexedItems);
+    HHHH.collectIndexedItems(indexedItems);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format51l.java b/src/main/java/com/android/tools/r8/code/Format51l.java
index 008e724..0e9ad93 100644
--- a/src/main/java/com/android/tools/r8/code/Format51l.java
+++ b/src/main/java/com/android/tools/r8/code/Format51l.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
@@ -54,8 +53,7 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     // No references.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Iget.java b/src/main/java/com/android/tools/r8/code/Iget.java
index 65ebb44..3625037 100644
--- a/src/main/java/com/android/tools/r8/code/Iget.java
+++ b/src/main/java/com/android/tools/r8/code/Iget.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class Iget extends Format22c {
+public class Iget extends IgetOrIput {
 
   public static final int OPCODE = 0x52;
   public static final String NAME = "Iget";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) CCCC;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInstanceGet(A, B, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/IgetBoolean.java b/src/main/java/com/android/tools/r8/code/IgetBoolean.java
index 756969c..0df2731 100644
--- a/src/main/java/com/android/tools/r8/code/IgetBoolean.java
+++ b/src/main/java/com/android/tools/r8/code/IgetBoolean.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class IgetBoolean extends Format22c {
+public class IgetBoolean extends IgetOrIput {
 
   public static final int OPCODE = 0x55;
   public static final String NAME = "IgetBoolean";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) CCCC;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInstanceGet(A, B, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/IgetByte.java b/src/main/java/com/android/tools/r8/code/IgetByte.java
index 56a239a..f3cb157 100644
--- a/src/main/java/com/android/tools/r8/code/IgetByte.java
+++ b/src/main/java/com/android/tools/r8/code/IgetByte.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class IgetByte extends Format22c {
+public class IgetByte extends IgetOrIput {
 
   public static final int OPCODE = 0x56;
   public static final String NAME = "IgetByte";
@@ -41,10 +41,6 @@
   public void registerUse(UseRegistry registry) {
     registry.registerInstanceFieldRead(getField());
   }
-  @Override
-  public DexField getField() {
-    return (DexField) CCCC;
-  }
 
   @Override
   public void buildIR(IRBuilder builder) {
diff --git a/src/main/java/com/android/tools/r8/code/IgetChar.java b/src/main/java/com/android/tools/r8/code/IgetChar.java
index ad9c44e..2c0793c 100644
--- a/src/main/java/com/android/tools/r8/code/IgetChar.java
+++ b/src/main/java/com/android/tools/r8/code/IgetChar.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class IgetChar extends Format22c {
+public class IgetChar extends IgetOrIput {
 
   public static final int OPCODE = 0x57;
   public static final String NAME = "IgetChar";
@@ -41,10 +41,6 @@
   public void registerUse(UseRegistry registry) {
     registry.registerInstanceFieldRead(getField());
   }
-  @Override
-  public DexField getField() {
-    return (DexField) CCCC;
-  }
 
   @Override
   public void buildIR(IRBuilder builder) {
diff --git a/src/main/java/com/android/tools/r8/code/IgetObject.java b/src/main/java/com/android/tools/r8/code/IgetObject.java
index d0c7c00..802409e 100644
--- a/src/main/java/com/android/tools/r8/code/IgetObject.java
+++ b/src/main/java/com/android/tools/r8/code/IgetObject.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class IgetObject extends Format22c {
+public class IgetObject extends IgetOrIput {
 
   public static final int OPCODE = 0x54;
   public static final String NAME = "IgetObject";
@@ -38,11 +38,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) CCCC;
-  }
-
-  @Override
   public void registerUse(UseRegistry registry) {
     registry.registerInstanceFieldRead(getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/IgetOrIput.java b/src/main/java/com/android/tools/r8/code/IgetOrIput.java
new file mode 100644
index 0000000..7800ace
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IgetOrIput.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2016, 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.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.DexField;
+
+public abstract class IgetOrIput extends Format22c<DexField> {
+
+  IgetOrIput(int high, BytecodeStream stream, DexField[] map) {
+    super(high, stream, map);
+  }
+
+  IgetOrIput(int A, int B, DexField CCCC) {
+    super(A, B, CCCC);
+  }
+
+  @Override
+  public final void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getField().collectIndexedItems(indexedItems);
+  }
+
+  @Override
+  public final DexField getField() {
+    return CCCC;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IgetShort.java b/src/main/java/com/android/tools/r8/code/IgetShort.java
index 75a9752..a8ca9a7 100644
--- a/src/main/java/com/android/tools/r8/code/IgetShort.java
+++ b/src/main/java/com/android/tools/r8/code/IgetShort.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class IgetShort extends Format22c {
+public class IgetShort extends IgetOrIput {
 
   public static final int OPCODE = 0x58;
   public static final String NAME = "IgetShort";
@@ -38,11 +38,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) CCCC;
-  }
-
-  @Override
   public void registerUse(UseRegistry registry) {
     registry.registerInstanceFieldRead(getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/IgetWide.java b/src/main/java/com/android/tools/r8/code/IgetWide.java
index 9893911..bd63727 100644
--- a/src/main/java/com/android/tools/r8/code/IgetWide.java
+++ b/src/main/java/com/android/tools/r8/code/IgetWide.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class IgetWide extends Format22c {
+public class IgetWide extends IgetOrIput {
 
   public static final int OPCODE = 0x53;
   public static final String NAME = "IgetWide";
@@ -38,11 +38,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) CCCC;
-  }
-
-  @Override
   public void registerUse(UseRegistry registry) {
     registry.registerInstanceFieldRead(getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/InstanceOf.java b/src/main/java/com/android/tools/r8/code/InstanceOf.java
index 36d7a0b..05a9558 100644
--- a/src/main/java/com/android/tools/r8/code/InstanceOf.java
+++ b/src/main/java/com/android/tools/r8/code/InstanceOf.java
@@ -3,12 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
+import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class InstanceOf extends Format22c {
+public class InstanceOf extends Format22c<DexType> {
 
   public static final int OPCODE = 0x20;
   public static final String NAME = "InstanceOf";
@@ -48,12 +49,17 @@
   }
 
   @Override
-  public void registerUse(UseRegistry registry) {
-    registry.registerInstanceOf(getType());
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getType().collectIndexedItems(indexedItems);
   }
 
   public DexType getType() {
-    return (DexType) CCCC;
+    return CCCC;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInstanceOf(getType());
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java
index 9d93b40..d8accd7 100644
--- a/src/main/java/com/android/tools/r8/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -24,7 +24,7 @@
   public final static int[] NO_TARGETS = null;
   public final static int[] EXIT_TARGET = {};
 
-  public int offset;
+  private int offset;
 
   Instruction(BytecodeStream stream) {
     // When this constructor is invoked, we have already read 1 ushort from the stream.
@@ -311,8 +311,7 @@
 
   public abstract void write(ShortBuffer buffer, ObjectToOffsetMapping mapping);
 
-  public abstract void collectIndexedItems(
-      IndexedItemCollection indexedItems, DexMethod method, int instructionOffset);
+  public abstract void collectIndexedItems(IndexedItemCollection indexedItems);
 
   public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
     // In the default case, there is nothing to substitute.
diff --git a/src/main/java/com/android/tools/r8/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/code/InvokeCustom.java
index 171aef2..72d0adb 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeCustom.java
@@ -3,13 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
+import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.graph.DexCallSite;
-import com.android.tools.r8.graph.IndexedDexItem;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class InvokeCustom extends Format35c {
+public class InvokeCustom extends Format35c<DexCallSite> {
 
   public static final int OPCODE = 0xfc;
   public static final String NAME = "InvokeCustom";
@@ -19,7 +19,7 @@
     super(high, stream, mapping.getCallSiteMap());
   }
 
-  public InvokeCustom(int A, IndexedDexItem BBBB, int C, int D, int E, int F, int G) {
+  public InvokeCustom(int A, DexCallSite BBBB, int C, int D, int E, int F, int G) {
     super(A, BBBB, C, D, E, F, G);
   }
 
@@ -39,13 +39,18 @@
   }
 
   @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getCallSite().collectIndexedItems(indexedItems);
+  }
+
+  @Override
   public void registerUse(UseRegistry registry) {
     registry.registerCallSite(getCallSite());
   }
 
   @Override
   public DexCallSite getCallSite() {
-    return (DexCallSite) BBBB;
+    return BBBB;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java b/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java
index 3aa60fc..ae63c4d 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java
@@ -3,12 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
+import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class InvokeCustomRange extends Format3rc {
+public class InvokeCustomRange extends Format3rc<DexCallSite> {
 
   public static final int OPCODE = 0xfd;
   public static final String NAME = "InvokeCustomRange";
@@ -38,8 +39,13 @@
   }
 
   @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getCallSite().collectIndexedItems(indexedItems);
+  }
+
+  @Override
   public DexCallSite getCallSite() {
-    return (DexCallSite) BBBB;
+    return BBBB;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/code/InvokeDirect.java
index 08d6826..cd8c1b7 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeDirect.java
@@ -4,13 +4,12 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.IndexedDexItem;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class InvokeDirect extends Format35c {
+public class InvokeDirect extends InvokeMethod {
 
   public static final int OPCODE = 0x70;
   public static final String NAME = "InvokeDirect";
@@ -20,7 +19,7 @@
     super(high, stream, mapping.getMethodMap());
   }
 
-  public InvokeDirect(int A, IndexedDexItem BBBB, int C, int D, int E, int F, int G) {
+  public InvokeDirect(int A, DexMethod BBBB, int C, int D, int E, int F, int G) {
     super(A, BBBB, C, D, E, F, G);
   }
 
@@ -45,11 +44,6 @@
   }
 
   @Override
-  public DexMethod getMethod() {
-    return (DexMethod) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInvokeRegisters(Type.DIRECT, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
   }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java b/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java
index 8f72a2b..41070dd 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class InvokeDirectRange extends Format3rc {
+public class InvokeDirectRange extends InvokeMethodRange {
 
   public static final int OPCODE = 0x76;
   public static final String NAME = "InvokeDirectRange";
@@ -39,11 +39,6 @@
   }
 
   @Override
-  public DexMethod getMethod() {
-    return (DexMethod) BBBB;
-  }
-
-  @Override
   public void registerUse(UseRegistry registry) {
     registry.registerInvokeDirect(getMethod());
   }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/code/InvokeInterface.java
index b98af41..3e172e2 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeInterface.java
@@ -4,13 +4,12 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.IndexedDexItem;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class InvokeInterface extends Format35c {
+public class InvokeInterface extends InvokeMethod {
 
   public static final int OPCODE = 0x72;
   public static final String NAME = "InvokeInterface";
@@ -20,7 +19,7 @@
     super(high, stream, mapping.getMethodMap());
   }
 
-  public InvokeInterface(int A, IndexedDexItem BBBB, int C, int D, int E, int F, int G) {
+  public InvokeInterface(int A, DexMethod BBBB, int C, int D, int E, int F, int G) {
     super(A, BBBB, C, D, E, F, G);
   }
 
@@ -45,11 +44,6 @@
   }
 
   @Override
-  public DexMethod getMethod() {
-    return (DexMethod) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInvokeRegisters(
         Type.INTERFACE, getMethod(), getProto(), A, new int[] {C, D, E, F, G});
diff --git a/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java b/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java
index e4d63b4..2d1986c 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class InvokeInterfaceRange extends Format3rc {
+public class InvokeInterfaceRange extends InvokeMethodRange {
 
   public static final int OPCODE = 0x78;
   public static final String NAME = "InvokeInterfaceRange";
@@ -44,11 +44,6 @@
   }
 
   @Override
-  public DexMethod getMethod() {
-    return (DexMethod) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInvokeRange(Type.INTERFACE, getMethod(), getProto(), AA, CCCC);
   }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/code/InvokeMethod.java
new file mode 100644
index 0000000..cab1eac
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeMethod.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2016, 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.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.DexMethod;
+
+public abstract class InvokeMethod extends Format35c<DexMethod> {
+
+  InvokeMethod(int high, BytecodeStream stream, DexMethod[] map) {
+    super(high, stream, map);
+  }
+
+  InvokeMethod(int A, DexMethod BBBB, int C, int D, int E, int F, int G) {
+    super(A, BBBB, C, D, E, F, G);
+  }
+
+  @Override
+  public final void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getMethod().collectIndexedItems(indexedItems);
+  }
+
+  @Override
+  public final DexMethod getMethod() {
+    return BBBB;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeMethodRange.java b/src/main/java/com/android/tools/r8/code/InvokeMethodRange.java
new file mode 100644
index 0000000..25fad42
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeMethodRange.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2016, 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.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.DexMethod;
+
+public abstract class InvokeMethodRange extends Format3rc<DexMethod> {
+
+  InvokeMethodRange(int high, BytecodeStream stream, DexMethod[] map) {
+    super(high, stream, map);
+  }
+
+  InvokeMethodRange(int firstArgumentRegister, int argumentCount, DexMethod dexItem) {
+    super(firstArgumentRegister, argumentCount, dexItem);
+  }
+
+  @Override
+  public final void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getMethod().collectIndexedItems(indexedItems);
+  }
+
+  @Override
+  public final DexMethod getMethod() {
+    return BBBB;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/code/InvokeStatic.java
index 73cb887..531105b 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeStatic.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class InvokeStatic extends Format35c {
+public class InvokeStatic extends InvokeMethod {
 
   public static final int OPCODE = 0x71;
   public static final String NAME = "InvokeStatic";
@@ -44,11 +44,6 @@
   }
 
   @Override
-  public DexMethod getMethod() {
-    return (DexMethod) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInvokeRegisters(Type.STATIC, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
   }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java b/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java
index 6fd3f42..7d3ff3d 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class InvokeStaticRange extends Format3rc {
+public class InvokeStaticRange extends InvokeMethodRange {
 
   public static final int OPCODE = 0x77;
   public static final String NAME = "InvokeStaticRange";
@@ -44,11 +44,6 @@
   }
 
   @Override
-  public DexMethod getMethod() {
-    return (DexMethod) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInvokeRange(Type.STATIC, getMethod(), getProto(), AA, CCCC);
   }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/code/InvokeSuper.java
index 9575ed9..1dc8ac6 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeSuper.java
@@ -4,13 +4,12 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.IndexedDexItem;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class InvokeSuper extends Format35c {
+public class InvokeSuper extends InvokeMethod {
 
   public static final int OPCODE = 0x6f;
   public static final String NAME = "InvokeSuper";
@@ -20,7 +19,7 @@
     super(high, stream, mapping.getMethodMap());
   }
 
-  public InvokeSuper(int A, IndexedDexItem BBBB, int C, int D, int E, int F, int G) {
+  public InvokeSuper(int A, DexMethod BBBB, int C, int D, int E, int F, int G) {
     super(A, BBBB, C, D, E, F, G);
   }
 
@@ -45,11 +44,6 @@
   }
 
   @Override
-  public DexMethod getMethod() {
-    return (DexMethod) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInvokeRegisters(Type.SUPER, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
   }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java b/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java
index 63c6318..af6ebe8 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class InvokeSuperRange extends Format3rc {
+public class InvokeSuperRange extends InvokeMethodRange {
 
   public static final int OPCODE = 0x75;
   public static final String NAME = "InvokeSuperRange";
@@ -44,11 +44,6 @@
   }
 
   @Override
-  public DexMethod getMethod() {
-    return (DexMethod) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInvokeRange(Type.SUPER, getMethod(), getProto(), AA, CCCC);
   }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/code/InvokeVirtual.java
index b35882b..17596a4 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeVirtual.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class InvokeVirtual extends Format35c {
+public class InvokeVirtual extends InvokeMethod {
 
   public static final int OPCODE = 0x6e;
   public static final String NAME = "InvokeVirtual";
@@ -54,11 +54,6 @@
   }
 
   @Override
-  public DexMethod getMethod() {
-    return (DexMethod) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInvokeRegisters(Type.VIRTUAL, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
   }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java b/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java
index cd78458..c2a0d6c 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class InvokeVirtualRange extends Format3rc {
+public class InvokeVirtualRange extends InvokeMethodRange {
 
   public static final int OPCODE = 0x74;
   public static final String NAME = "InvokeVirtualRange";
@@ -54,11 +54,6 @@
   }
 
   @Override
-  public DexMethod getMethod() {
-    return (DexMethod) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInvokeRange(Type.VIRTUAL, getMethod(), getProto(), AA, CCCC);
   }
diff --git a/src/main/java/com/android/tools/r8/code/Iput.java b/src/main/java/com/android/tools/r8/code/Iput.java
index eabfb2b..ea170c7 100644
--- a/src/main/java/com/android/tools/r8/code/Iput.java
+++ b/src/main/java/com/android/tools/r8/code/Iput.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class Iput extends Format22c {
+public class Iput extends IgetOrIput {
 
   public static final int OPCODE = 0x59;
   public static final String NAME = "Iput";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) CCCC;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInstancePut(A, B, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/IputBoolean.java b/src/main/java/com/android/tools/r8/code/IputBoolean.java
index 6276441..8a992c7 100644
--- a/src/main/java/com/android/tools/r8/code/IputBoolean.java
+++ b/src/main/java/com/android/tools/r8/code/IputBoolean.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class IputBoolean extends Format22c {
+public class IputBoolean extends IgetOrIput {
 
   public static final int OPCODE = 0x5c;
   public static final String NAME = "IputBoolean";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) CCCC;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInstancePut(A, B, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/IputByte.java b/src/main/java/com/android/tools/r8/code/IputByte.java
index e333a9f..4005875 100644
--- a/src/main/java/com/android/tools/r8/code/IputByte.java
+++ b/src/main/java/com/android/tools/r8/code/IputByte.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class IputByte extends Format22c {
+public class IputByte extends IgetOrIput {
 
   public static final int OPCODE = 0x5d;
   public static final String NAME = "IputByte";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) CCCC;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInstancePut(A, B, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/IputChar.java b/src/main/java/com/android/tools/r8/code/IputChar.java
index f4902ee..55f72fe 100644
--- a/src/main/java/com/android/tools/r8/code/IputChar.java
+++ b/src/main/java/com/android/tools/r8/code/IputChar.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class IputChar extends Format22c {
+public class IputChar extends IgetOrIput {
 
   public static final int OPCODE = 0x5e;
   public static final String NAME = "IputChar";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) CCCC;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInstancePut(A, B, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/IputObject.java b/src/main/java/com/android/tools/r8/code/IputObject.java
index 7532340..5ca2f90 100644
--- a/src/main/java/com/android/tools/r8/code/IputObject.java
+++ b/src/main/java/com/android/tools/r8/code/IputObject.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class IputObject extends Format22c {
+public class IputObject extends IgetOrIput {
 
   public static final int OPCODE = 0x5b;
   public static final String NAME = "IputObject";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) CCCC;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInstancePut(A, B, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/IputShort.java b/src/main/java/com/android/tools/r8/code/IputShort.java
index f0d4308..bf41801 100644
--- a/src/main/java/com/android/tools/r8/code/IputShort.java
+++ b/src/main/java/com/android/tools/r8/code/IputShort.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class IputShort extends Format22c {
+public class IputShort extends IgetOrIput {
 
   public static final int OPCODE = 0x5f;
   public static final String NAME = "IputShort";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) CCCC;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInstancePut(A, B, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/IputWide.java b/src/main/java/com/android/tools/r8/code/IputWide.java
index 68d09a6..4462875 100644
--- a/src/main/java/com/android/tools/r8/code/IputWide.java
+++ b/src/main/java/com/android/tools/r8/code/IputWide.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class IputWide extends Format22c {
+public class IputWide extends IgetOrIput {
 
   public static final int OPCODE = 0x5a;
   public static final String NAME = "IputWide";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) CCCC;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addInstancePut(A, B, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/NewArray.java b/src/main/java/com/android/tools/r8/code/NewArray.java
index 0cce39f..dfb4ccb 100644
--- a/src/main/java/com/android/tools/r8/code/NewArray.java
+++ b/src/main/java/com/android/tools/r8/code/NewArray.java
@@ -3,12 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
+import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class NewArray extends Format22c {
+public class NewArray extends Format22c<DexType> {
 
   public static final int OPCODE = 0x23;
   public static final String NAME = "NewArray";
@@ -38,12 +39,17 @@
   }
 
   @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getType().collectIndexedItems(indexedItems);
+  }
+
+  @Override
   public void registerUse(UseRegistry registry) {
     registry.registerTypeReference(getType());
   }
 
   public DexType getType() {
-    return (DexType) CCCC;
+    return CCCC;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/NewInstance.java b/src/main/java/com/android/tools/r8/code/NewInstance.java
index 31b8f68..695ae96 100644
--- a/src/main/java/com/android/tools/r8/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/code/NewInstance.java
@@ -3,12 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
+import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class NewInstance extends Format21c {
+public class NewInstance extends Format21c<DexType> {
 
   public static final int OPCODE = 0x22;
   public static final String NAME = "NewInstance";
@@ -38,6 +39,11 @@
   }
 
   @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getType().collectIndexedItems(indexedItems);
+  }
+
+  @Override
   public void registerUse(UseRegistry registry) {
     registry.registerNewInstance(getType());
   }
diff --git a/src/main/java/com/android/tools/r8/code/Sget.java b/src/main/java/com/android/tools/r8/code/Sget.java
index 3347fdd..0c727ac 100644
--- a/src/main/java/com/android/tools/r8/code/Sget.java
+++ b/src/main/java/com/android/tools/r8/code/Sget.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class Sget extends Format21c {
+public class Sget extends SgetOrSput {
 
   public static final int OPCODE = 0x60;
   public static final String NAME = "Sget";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addStaticGet(AA, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/SgetBoolean.java b/src/main/java/com/android/tools/r8/code/SgetBoolean.java
index 27f18f2..3efb6b1 100644
--- a/src/main/java/com/android/tools/r8/code/SgetBoolean.java
+++ b/src/main/java/com/android/tools/r8/code/SgetBoolean.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class SgetBoolean extends Format21c {
+public class SgetBoolean extends SgetOrSput {
 
   public static final int OPCODE = 0x63;
   public static final String NAME = "SgetBoolean";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addStaticGet(AA, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/SgetByte.java b/src/main/java/com/android/tools/r8/code/SgetByte.java
index 3c0219b..9a40297 100644
--- a/src/main/java/com/android/tools/r8/code/SgetByte.java
+++ b/src/main/java/com/android/tools/r8/code/SgetByte.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class SgetByte extends Format21c {
+public class SgetByte extends SgetOrSput {
 
   public static final int OPCODE = 0x64;
   public static final String NAME = "SgetByte";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addStaticGet(AA, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/SgetChar.java b/src/main/java/com/android/tools/r8/code/SgetChar.java
index aa69c7f..429eaa6 100644
--- a/src/main/java/com/android/tools/r8/code/SgetChar.java
+++ b/src/main/java/com/android/tools/r8/code/SgetChar.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class SgetChar extends Format21c {
+public class SgetChar extends SgetOrSput {
 
   public static final int OPCODE = 0x65;
   public static final String NAME = "SgetChar";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addStaticGet(AA, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/SgetObject.java b/src/main/java/com/android/tools/r8/code/SgetObject.java
index f70da49..6b1130d 100644
--- a/src/main/java/com/android/tools/r8/code/SgetObject.java
+++ b/src/main/java/com/android/tools/r8/code/SgetObject.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class SgetObject extends Format21c {
+public class SgetObject extends SgetOrSput {
 
   public static final int OPCODE = 0x62;
   public static final String NAME = "SgetObject";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addStaticGet(AA, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/SgetOrSput.java b/src/main/java/com/android/tools/r8/code/SgetOrSput.java
new file mode 100644
index 0000000..d04e098
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SgetOrSput.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2016, 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.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.DexField;
+
+abstract class SgetOrSput extends Format21c<DexField> {
+
+  SgetOrSput(int high, BytecodeStream stream, DexField[] map) {
+    super(high, stream, map);
+  }
+
+  protected SgetOrSput(int AA, DexField BBBB) {
+    super(AA, BBBB);
+  }
+
+  @Override
+  public final void collectIndexedItems(IndexedItemCollection indexedItems) {
+    getField().collectIndexedItems(indexedItems);
+  }
+
+  @Override
+  public final DexField getField() {
+    return BBBB;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SgetShort.java b/src/main/java/com/android/tools/r8/code/SgetShort.java
index 0149fb4..37f331e 100644
--- a/src/main/java/com/android/tools/r8/code/SgetShort.java
+++ b/src/main/java/com/android/tools/r8/code/SgetShort.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class SgetShort extends Format21c {
+public class SgetShort extends SgetOrSput {
 
   public static final int OPCODE = 0x66;
   public static final String NAME = "SgetShort";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addStaticGet(AA, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/SgetWide.java b/src/main/java/com/android/tools/r8/code/SgetWide.java
index ea008ad..638ca0c 100644
--- a/src/main/java/com/android/tools/r8/code/SgetWide.java
+++ b/src/main/java/com/android/tools/r8/code/SgetWide.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class SgetWide extends Format21c {
+public class SgetWide extends SgetOrSput {
 
   public static final int OPCODE = 0x61;
   public static final String NAME = "SgetWide";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addStaticGet(AA, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/Sput.java b/src/main/java/com/android/tools/r8/code/Sput.java
index 6885da6..e6f3b4e 100644
--- a/src/main/java/com/android/tools/r8/code/Sput.java
+++ b/src/main/java/com/android/tools/r8/code/Sput.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class Sput extends Format21c {
+public class Sput extends SgetOrSput {
 
   public static final int OPCODE = 0x67;
   public static final String NAME = "Sput";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addStaticPut(AA, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/SputBoolean.java b/src/main/java/com/android/tools/r8/code/SputBoolean.java
index 041ba02..6dae9b2 100644
--- a/src/main/java/com/android/tools/r8/code/SputBoolean.java
+++ b/src/main/java/com/android/tools/r8/code/SputBoolean.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class SputBoolean extends Format21c {
+public class SputBoolean extends SgetOrSput {
 
   public static final int OPCODE = 0x6a;
   public static final String NAME = "SputBoolean";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addStaticPut(AA, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/SputByte.java b/src/main/java/com/android/tools/r8/code/SputByte.java
index 87b79bd..c6cb521 100644
--- a/src/main/java/com/android/tools/r8/code/SputByte.java
+++ b/src/main/java/com/android/tools/r8/code/SputByte.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class SputByte extends Format21c {
+public class SputByte extends SgetOrSput {
 
   public static final int OPCODE = 0x6b;
   public static final String NAME = "SputByte";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addStaticPut(AA, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/SputChar.java b/src/main/java/com/android/tools/r8/code/SputChar.java
index 5fcd836..727ba25 100644
--- a/src/main/java/com/android/tools/r8/code/SputChar.java
+++ b/src/main/java/com/android/tools/r8/code/SputChar.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class SputChar extends Format21c {
+public class SputChar extends SgetOrSput {
 
   public static final int OPCODE = 0x6c;
   public static final String NAME = "SputChar";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addStaticPut(AA, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/SputObject.java b/src/main/java/com/android/tools/r8/code/SputObject.java
index fa661ea..4850664 100644
--- a/src/main/java/com/android/tools/r8/code/SputObject.java
+++ b/src/main/java/com/android/tools/r8/code/SputObject.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class SputObject extends Format21c {
+public class SputObject extends SgetOrSput {
 
   public static final int OPCODE = 0x69;
   public static final String NAME = "SputObject";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addStaticPut(AA, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/SputShort.java b/src/main/java/com/android/tools/r8/code/SputShort.java
index d2b5f85..eb33e33 100644
--- a/src/main/java/com/android/tools/r8/code/SputShort.java
+++ b/src/main/java/com/android/tools/r8/code/SputShort.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class SputShort extends Format21c {
+public class SputShort extends SgetOrSput {
 
   public static final int OPCODE = 0x6d;
   public static final String NAME = "SputShort";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addStaticPut(AA, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/code/SputWide.java b/src/main/java/com/android/tools/r8/code/SputWide.java
index 7e6aeaa..8104f75 100644
--- a/src/main/java/com/android/tools/r8/code/SputWide.java
+++ b/src/main/java/com/android/tools/r8/code/SputWide.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 
-public class SputWide extends Format21c {
+public class SputWide extends SgetOrSput {
 
   public static final int OPCODE = 0x68;
   public static final String NAME = "SputWide";
@@ -43,11 +43,6 @@
   }
 
   @Override
-  public DexField getField() {
-    return (DexField) BBBB;
-  }
-
-  @Override
   public void buildIR(IRBuilder builder) {
     builder.addStaticPut(AA, getField());
   }
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index f0e8da6..21b5cd4 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.ProgramConsumer;
 import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.dex.FileWriter.ByteBufferResult;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.features.FeatureSplitConfiguration.DataResourceProvidersAndConsumer;
@@ -23,6 +24,7 @@
 import com.android.tools.r8.graph.DexAnnotationDirectory;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexDebugInfo;
 import com.android.tools.r8.graph.DexEncodedArray;
@@ -251,6 +253,11 @@
       // TODO(b/151313715): Move this to the writer threads.
       insertAttributeAnnotations();
 
+      // Each DexCallSite must have its instruction offset set for sorting.
+      if (options.isGeneratingDex()) {
+        setCallSiteContexts(executorService);
+      }
+
       // Generate the dex file contents.
       List<Future<Boolean>> dexDataFutures = new ArrayList<>();
       List<VirtualFile> virtualFiles = distribute(executorService);
@@ -526,6 +533,26 @@
     }
   }
 
+  private void setCallSiteContexts(ExecutorService executorService) throws ExecutionException {
+    ThreadUtils.processItems(
+        appView.appInfo().classes(), this::setCallSiteContexts, executorService);
+  }
+
+  private void setCallSiteContexts(DexProgramClass clazz) {
+    for (DexEncodedMethod method : clazz.methods()) {
+      if (method.hasCode()) {
+        DexCode code = method.getCode().asDexCode();
+        assert code != null;
+        for (Instruction instruction : code.instructions) {
+          DexCallSite callSite = instruction.getCallSite();
+          if (callSite != null) {
+            callSite.setContext(method.getReference(), instruction.getOffset());
+          }
+        }
+      }
+    }
+  }
+
   /**
    * Rewrites the code for all methods in the given file so that they use JumboString for at least
    * the strings that require it in mapping.
diff --git a/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java b/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java
index 45b8bfe..273bf3b 100644
--- a/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java
+++ b/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java
@@ -12,6 +12,7 @@
 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.GraphLens;
 import com.android.tools.r8.graph.IndexedDexItem;
 import com.android.tools.r8.graph.InitClassLens;
 
@@ -100,6 +101,10 @@
    */
   boolean addMethodHandle(DexMethodHandle methodHandle);
 
+  default GraphLens getGraphLens() {
+    return GraphLens.getIdentityLens();
+  }
+
   default InitClassLens getInitClassLens() {
     return InitClassLens.getDefault();
   }
diff --git a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
index e623f5e..709eb9d 100644
--- a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
+++ b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -70,7 +71,7 @@
 
     public void updateNumbersOfIds() {
       // Use a temporary VirtualFile to evaluate the number of ids in the group.
-      VirtualFile virtualFile = new VirtualFile(0, initClassLens, namingLens);
+      VirtualFile virtualFile = new VirtualFile(0, graphLens, initClassLens, namingLens);
       // Note: sort not needed.
       for (DexProgramClass clazz : members) {
         virtualFile.addClass(clazz);
@@ -284,6 +285,7 @@
   private final Set<DexProgramClass> classes;
   private final DexApplication app;
   private int dexIndexOffset;
+  private final GraphLens graphLens;
   private final InitClassLens initClassLens;
   private final NamingLens namingLens;
   private final DirectSubClassesInfo directSubClasses;
@@ -293,6 +295,7 @@
       List<VirtualFile> dexes,
       Set<DexProgramClass> classes,
       int dexIndexOffset,
+      GraphLens graphLens,
       InitClassLens initClassLens,
       NamingLens namingLens,
       DexApplication app,
@@ -301,6 +304,7 @@
     this.dexes = dexes;
     this.classes = classes;
     this.dexIndexOffset = dexIndexOffset;
+    this.graphLens = graphLens;
     this.initClassLens = initClassLens;
     this.namingLens = namingLens;
     this.app = app;
@@ -374,7 +378,7 @@
 
   private Collection<VirtualFile> assignGroup(ClassGroup group, List<VirtualFile> dexBlackList) {
     VirtualFileCycler cycler =
-        new VirtualFileCycler(dexes, initClassLens, namingLens, dexIndexOffset);
+        new VirtualFileCycler(dexes, graphLens, initClassLens, namingLens, dexIndexOffset);
     if (group.members.isEmpty()) {
       return Collections.emptyList();
     } else if (group.canFitInOneDex()) {
@@ -423,7 +427,7 @@
 
     Collection<VirtualFile> usedDex = new ArrayList<>();
     VirtualFileCycler cycler =
-        new VirtualFileCycler(dexes, initClassLens, namingLens, dexIndexOffset);
+        new VirtualFileCycler(dexes, graphLens, initClassLens, namingLens, dexIndexOffset);
     // Don't modify input dexBlackList. Think about modifying the input collection considering this
     // is private API.
     Set<VirtualFile> currentBlackList = new HashSet<>(dexBlackList);
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index a419f4a..b036d96 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -18,6 +18,7 @@
 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.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.logging.Log;
@@ -76,29 +77,39 @@
 
   private final DexProgramClass primaryClass;
 
-  VirtualFile(int id, InitClassLens initClassLens, NamingLens namingLens) {
-    this(id, initClassLens, namingLens, null, null);
+  VirtualFile(int id, GraphLens graphLens, InitClassLens initClassLens, NamingLens namingLens) {
+    this(id, graphLens, initClassLens, namingLens, null, null);
   }
 
   VirtualFile(
-      int id, InitClassLens initClassLens, NamingLens namingLens, FeatureSplit featureSplit) {
-    this(id, initClassLens, namingLens, null, featureSplit);
-  }
-
-  private VirtualFile(
-      int id, InitClassLens initClassLens, NamingLens namingLens, DexProgramClass primaryClass) {
-    this(id, initClassLens, namingLens, primaryClass, null);
+      int id,
+      GraphLens graphLens,
+      InitClassLens initClassLens,
+      NamingLens namingLens,
+      FeatureSplit featureSplit) {
+    this(id, graphLens, initClassLens, namingLens, null, featureSplit);
   }
 
   private VirtualFile(
       int id,
+      GraphLens graphLens,
+      InitClassLens initClassLens,
+      NamingLens namingLens,
+      DexProgramClass primaryClass) {
+    this(id, graphLens, initClassLens, namingLens, primaryClass, null);
+  }
+
+  private VirtualFile(
+      int id,
+      GraphLens graphLens,
       InitClassLens initClassLens,
       NamingLens namingLens,
       DexProgramClass primaryClass,
       FeatureSplit featureSplit) {
     this.id = id;
-    this.indexedItems = new VirtualFileIndexedItemCollection(initClassLens, namingLens);
-    this.transaction = new IndexedItemTransaction(indexedItems, initClassLens, namingLens);
+    this.indexedItems = new VirtualFileIndexedItemCollection(graphLens, initClassLens, namingLens);
+    this.transaction =
+        new IndexedItemTransaction(indexedItems, graphLens, initClassLens, namingLens);
     this.primaryClass = primaryClass;
     this.featureSplit = featureSplit;
   }
@@ -296,7 +307,12 @@
       for (DexProgramClass clazz : application.classes()) {
         if (!combineSyntheticClassesWithPrimaryClass || clazz.getSynthesizedFrom().isEmpty()) {
           VirtualFile file =
-              new VirtualFile(virtualFiles.size(), writer.initClassLens, writer.namingLens, clazz);
+              new VirtualFile(
+                  virtualFiles.size(),
+                  writer.graphLens,
+                  writer.initClassLens,
+                  writer.namingLens,
+                  clazz);
           virtualFiles.add(file);
           file.addClass(clazz);
           files.put(clazz, file);
@@ -329,7 +345,7 @@
       this.options = options;
 
       // Create the primary dex file. The distribution will add more if needed.
-      mainDexFile = new VirtualFile(0, writer.initClassLens, writer.namingLens);
+      mainDexFile = new VirtualFile(0, writer.graphLens, writer.initClassLens, writer.namingLens);
       assert virtualFiles.isEmpty();
       virtualFiles.add(mainDexFile);
       addMarkers(mainDexFile);
@@ -447,7 +463,11 @@
         // Add a new virtual file, start from index 0 again
         VirtualFile featureFile =
             new VirtualFile(
-                0, writer.initClassLens, writer.namingLens, featureSplitSetEntry.getKey());
+                0,
+                writer.graphLens,
+                writer.initClassLens,
+                writer.namingLens,
+                featureSplitSetEntry.getKey());
         virtualFiles.add(featureFile);
         addMarkers(featureFile);
         Set<DexProgramClass> featureClasses =
@@ -461,6 +481,7 @@
                 application.dexItemFactory,
                 fillStrategy,
                 0,
+                writer.graphLens,
                 writer.initClassLens,
                 writer.namingLens,
                 options)
@@ -497,7 +518,8 @@
         assert !virtualFiles.get(0).isEmpty();
         assert virtualFiles.size() == 1;
         // The main dex file is filtered out, so ensure at least one file for the remaining classes.
-        virtualFiles.add(new VirtualFile(1, writer.initClassLens, writer.namingLens));
+        virtualFiles.add(
+            new VirtualFile(1, writer.graphLens, writer.initClassLens, writer.namingLens));
         filesForDistribution = virtualFiles.subList(1, virtualFiles.size());
         fileIndexOffset = 1;
       }
@@ -511,6 +533,7 @@
                 filesForDistribution,
                 classes,
                 fileIndexOffset,
+                writer.graphLens,
                 writer.initClassLens,
                 writer.namingLens,
                 writer.application,
@@ -527,6 +550,7 @@
                 application.dexItemFactory,
                 fillStrategy,
                 fileIndexOffset,
+                writer.graphLens,
                 writer.initClassLens,
                 writer.namingLens,
                 options)
@@ -566,6 +590,7 @@
 
   private static class VirtualFileIndexedItemCollection implements IndexedItemCollection {
 
+    private final GraphLens graphLens;
     private final InitClassLens initClassLens;
     private final NamingLens namingLens;
 
@@ -578,7 +603,9 @@
     private final Set<DexCallSite> callSites = Sets.newIdentityHashSet();
     private final Set<DexMethodHandle> methodHandles = Sets.newIdentityHashSet();
 
-    public VirtualFileIndexedItemCollection(InitClassLens initClassLens, NamingLens namingLens) {
+    public VirtualFileIndexedItemCollection(
+        GraphLens graphLens, InitClassLens initClassLens, NamingLens namingLens) {
+      this.graphLens = graphLens;
       this.initClassLens = initClassLens;
       this.namingLens = namingLens;
     }
@@ -636,6 +663,11 @@
     }
 
     @Override
+    public GraphLens getGraphLens() {
+      return graphLens;
+    }
+
+    @Override
     public InitClassLens getInitClassLens() {
       return initClassLens;
     }
@@ -660,6 +692,7 @@
   public static class IndexedItemTransaction implements IndexedItemCollection {
 
     private final VirtualFileIndexedItemCollection base;
+    private final GraphLens graphLens;
     private final InitClassLens initClassLens;
     private final NamingLens namingLens;
 
@@ -673,8 +706,12 @@
     private final Set<DexMethodHandle> methodHandles = new LinkedHashSet<>();
 
     private IndexedItemTransaction(
-        VirtualFileIndexedItemCollection base, InitClassLens initClassLens, NamingLens namingLens) {
+        VirtualFileIndexedItemCollection base,
+        GraphLens graphLens,
+        InitClassLens initClassLens,
+        NamingLens namingLens) {
       this.base = base;
+      this.graphLens = graphLens;
       this.initClassLens = initClassLens;
       this.namingLens = namingLens;
     }
@@ -732,6 +769,11 @@
     }
 
     @Override
+    public GraphLens getGraphLens() {
+      return graphLens;
+    }
+
+    @Override
     public InitClassLens getInitClassLens() {
       return initClassLens;
     }
@@ -813,6 +855,7 @@
   static class VirtualFileCycler {
 
     private final List<VirtualFile> files;
+    private final GraphLens graphLens;
     private final InitClassLens initClassLens;
     private final NamingLens namingLens;
 
@@ -823,10 +866,12 @@
 
     VirtualFileCycler(
         List<VirtualFile> files,
+        GraphLens graphLens,
         InitClassLens initClassLens,
         NamingLens namingLens,
         int fileIndexOffset) {
       this.files = files;
+      this.graphLens = graphLens;
       this.initClassLens = initClassLens;
       this.namingLens = namingLens;
 
@@ -859,7 +904,7 @@
         return activeFiles.next();
       } else {
         VirtualFile newFile =
-            new VirtualFile(nextFileId++, initClassLens, namingLens, featuresplit);
+            new VirtualFile(nextFileId++, graphLens, initClassLens, namingLens, featuresplit);
         files.add(newFile);
         allFilesCyclic = Iterators.cycle(files);
         return newFile;
@@ -890,7 +935,8 @@
     }
 
     VirtualFile addFile() {
-      VirtualFile newFile = new VirtualFile(nextFileId++, initClassLens, namingLens, featuresplit);
+      VirtualFile newFile =
+          new VirtualFile(nextFileId++, graphLens, initClassLens, namingLens, featuresplit);
       files.add(newFile);
 
       reset();
@@ -936,6 +982,7 @@
         DexItemFactory dexItemFactory,
         FillStrategy fillStrategy,
         int fileIndexOffset,
+        GraphLens graphLens,
         InitClassLens initClassLens,
         NamingLens namingLens,
         InternalOptions options) {
@@ -944,7 +991,8 @@
       this.dexItemFactory = dexItemFactory;
       this.fillStrategy = fillStrategy;
       this.options = options;
-      this.cycler = new VirtualFileCycler(files, initClassLens, namingLens, fileIndexOffset);
+      this.cycler =
+          new VirtualFileCycler(files, graphLens, initClassLens, namingLens, fileIndexOffset);
     }
 
     static boolean coveredByPrefix(String originalName, String currentPrefix) {
diff --git a/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java b/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java
index a5080d3..be068d9 100644
--- a/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java
+++ b/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java
@@ -6,9 +6,12 @@
 /**
  * Exception to signal an unexpected state internally to the compiler.
  *
- * Exceptions of this type always represent an bug in the compiler.
- * For expected errors, such as invalid input, the compiler should generate a CompilationError.
-  */
+ * <p>Exceptions of this type always represent an bug in the compiler.
+ *
+ * <p>For expected errors, such as invalid input, the compiler should generate a CompilationError.
+ *
+ * <p>For internal recoverable exceptions (eg, control operators) this base should not be used.
+ */
 public class InternalCompilerError extends IllegalStateException {
 
   public InternalCompilerError() {
diff --git a/src/main/java/com/android/tools/r8/errors/InvalidDebugInfoException.java b/src/main/java/com/android/tools/r8/errors/InvalidDebugInfoException.java
index f3f99e2..faf93cd 100644
--- a/src/main/java/com/android/tools/r8/errors/InvalidDebugInfoException.java
+++ b/src/main/java/com/android/tools/r8/errors/InvalidDebugInfoException.java
@@ -3,7 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.errors;
 
-public class InvalidDebugInfoException extends InternalCompilerError {
+/** An always caught exception to abort/restart IR construction due to invalid locals. */
+public class InvalidDebugInfoException extends RuntimeException {
   public InvalidDebugInfoException(String message) {
     super(message);
   }
diff --git a/src/main/java/com/android/tools/r8/errors/InvalidDescriptorException.java b/src/main/java/com/android/tools/r8/errors/InvalidDescriptorException.java
index f6702b7..1b426bc 100644
--- a/src/main/java/com/android/tools/r8/errors/InvalidDescriptorException.java
+++ b/src/main/java/com/android/tools/r8/errors/InvalidDescriptorException.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.errors;
 
-public class InvalidDescriptorException extends InternalCompilerError {
+public class InvalidDescriptorException extends RuntimeException {
   public InvalidDescriptorException(String message) {
     super(message);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 66b00b8..59239db 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -115,9 +115,8 @@
       return dependent;
     }
     DexClass definition = definitionFor(type);
-    if (definition != null && !definition.isLibraryClass() && dependent.isProgramClass()) {
-      InterfaceMethodRewriter.reportDependencyEdge(
-          dependent.asProgramClass(), definition, options());
+    if (definition != null && !definition.isLibraryClass() && !dependent.isLibraryClass()) {
+      InterfaceMethodRewriter.reportDependencyEdge(dependent, definition, options());
     }
     return definition;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index c6bc6b4..d0a3643 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -478,6 +478,9 @@
   }
 
   public OptionalBool isSubtype(DexType subtype, DexType supertype) {
+    // Even if we can compute isSubtype by having class hierarchy we may not be allowed to ask the
+    // question for all code paths in D8. Having the check for liveness ensure that we are in R8
+    // territory.
     return appInfo().hasLiveness()
         ? OptionalBool.of(appInfo().withLiveness().isSubtype(subtype, supertype))
         : OptionalBool.unknown();
diff --git a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
index 318b1bc..c9e4d4d 100644
--- a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.utils.MapUtils;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import java.util.IdentityHashMap;
@@ -24,8 +25,11 @@
   private final BiMap<DexType, DexType> originalTypeNames = HashBiMap.create();
   private final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create();
   private final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
-  private final Map<DexMethod, DexMethod> originalMethodSignaturesForBridges =
-      new IdentityHashMap<>();
+
+  // Original method signatures for bridges and companion methods. Due to the synthesis of these
+  // methods, the mapping from original methods to final methods is not one-to-one, but one-to-many,
+  // which is why we need an additional map.
+  private final Map<DexMethod, DexMethod> extraOriginalMethodSignatures = new IdentityHashMap<>();
 
   public AppliedGraphLens(AppView<? extends AppInfoWithClassHierarchy> appView) {
     this.appView = appView;
@@ -58,22 +62,24 @@
       for (DexEncodedMethod encodedMethod : clazz.methods()) {
         DexMethod method = encodedMethod.method;
         DexMethod original = appView.graphLens().getOriginalMethodSignature(method);
-        if (original != method) {
-          DexMethod existing = originalMethodSignatures.inverse().get(original);
-          if (existing == null) {
-            originalMethodSignatures.put(method, original);
+        DexMethod existing = originalMethodSignatures.inverse().get(original);
+        if (existing == null) {
+          originalMethodSignatures.put(method, original);
+        } else {
+          DexMethod renamed = appView.graphLens().getRenamedMethodSignature(original);
+          if (renamed == existing) {
+            extraOriginalMethodSignatures.put(method, original);
           } else {
-            DexMethod renamed = getRenamedMethodSignature(original);
-            if (renamed == existing) {
-              originalMethodSignaturesForBridges.put(method, original);
-            } else {
-              originalMethodSignatures.forcePut(method, original);
-              originalMethodSignaturesForBridges.put(existing, original);
-            }
+            originalMethodSignatures.forcePut(method, original);
+            extraOriginalMethodSignatures.put(existing, original);
           }
         }
       }
     }
+
+    // Trim original method signatures.
+    MapUtils.removeIdentityMappings(originalMethodSignatures);
+    MapUtils.removeIdentityMappings(extraOriginalMethodSignatures);
   }
 
   @Override
@@ -88,8 +94,8 @@
 
   @Override
   public DexMethod getOriginalMethodSignature(DexMethod method) {
-    if (originalMethodSignaturesForBridges.containsKey(method)) {
-      return originalMethodSignaturesForBridges.get(method);
+    if (extraOriginalMethodSignatures.containsKey(method)) {
+      return extraOriginalMethodSignatures.get(method);
     }
     return originalMethodSignatures.getOrDefault(method, method);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 16533fe..c40931a 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -204,12 +204,13 @@
       NamingLens namingLens,
       AppView<?> appView,
       int classFileVersion) {
+    GraphLens graphLens = appView.graphLens();
     InitClassLens initClassLens = appView.initClassLens();
     InternalOptions options = appView.options();
     CfLabel parameterLabel = null;
     if (shouldAddParameterNames(method, appView)) {
       parameterLabel = new CfLabel();
-      parameterLabel.write(visitor, initClassLens, namingLens);
+      parameterLabel.write(visitor, graphLens, initClassLens, namingLens);
     }
     for (CfInstruction instruction : instructions) {
       if (instruction instanceof CfFrame
@@ -217,7 +218,7 @@
               || (classFileVersion == V1_6 && !options.shouldKeepStackMapTable()))) {
         continue;
       }
-      instruction.write(visitor, initClassLens, namingLens);
+      instruction.write(visitor, graphLens, initClassLens, namingLens);
     }
     visitor.visitEnd();
     visitor.visitMaxs(maxStack, maxLocals);
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index bb11c69..1f2a522 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.ir.code.IRCode;
@@ -89,12 +88,6 @@
   }
 
   @Override
-  void collectIndexedItems(IndexedItemCollection collection,
-      DexMethod method, int instructionOffset) {
-    throw new Unreachable();
-  }
-
-  @Override
   void collectMixedSectionItems(MixedSectionCollection collection) {
     throw new Unreachable();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index a409737..ade8d06 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -62,10 +62,8 @@
     return visibility + " " + annotation;
   }
 
-  @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    annotation.collectIndexedItems(indexedItems, method, instructionOffset);
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    annotation.collectIndexedItems(indexedItems);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
index a6b7824..04f59d7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.naming.NamingLens;
@@ -92,12 +91,6 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection collection,
-      DexMethod method, int instructionOffset) {
-    throw new Unreachable();
-  }
-
-  @Override
   public void collectMixedSectionItems(MixedSectionCollection collection) {
     throw new Unreachable();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java
index 10c6535..e1965be 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java
@@ -39,11 +39,9 @@
     return name + "=" + value;
   }
 
-  @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    name.collectIndexedItems(indexedItems, method, instructionOffset);
-    value.collectIndexedItems(indexedItems, method, instructionOffset);
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    name.collectIndexedItems(indexedItems);
+    value.collectIndexedItems(indexedItems);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
index 7371902..e13faf8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -77,10 +77,10 @@
     return false;
   }
 
-  @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    collectAll(indexedItems, annotations);
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    for (DexAnnotation annotation : annotations) {
+      annotation.collectIndexedItems(indexedItems);
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
index 8c76ca1..bcb8873 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -35,7 +35,7 @@
   // Only used for sorting for deterministic output. This is the method and the instruction
   // offset where this DexCallSite ends up in the output.
   private DexMethod method;
-  private int instructionOffset;
+  private int instructionOffset = -1;
 
   DexCallSite(
       DexString methodName,
@@ -84,6 +84,15 @@
     return application.getCallSite(name, desc, bootstrapMethod, bootstrapArgs);
   }
 
+  public void setContext(DexMethod method, int instructionOffset) {
+    assert method != null;
+    assert instructionOffset >= 0;
+    assert this.method == null;
+    assert this.instructionOffset == -1;
+    this.method = method;
+    this.instructionOffset = instructionOffset;
+  }
+
   @Override
   public int computeHashCode() {
     // Call sites are equal only when this == other, which was already computed by the caller of
@@ -115,23 +124,13 @@
     return builder.toString();
   }
 
-  @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    assert method != null;
-    // Since collectIndexedItems() is called in transactions that may be rolled back, we may end up
-    // setting this.method and this.instructionOffset more than once. If we do set them more than
-    // once, then we must have the same values for method and instructionOffset every time.
-    assert this.method == null || this.method == method;
-    assert this.instructionOffset == 0 || this.instructionOffset == instructionOffset;
-    this.method = method;
-    this.instructionOffset = instructionOffset;
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     if (indexedItems.addCallSite(this)) {
-      methodName.collectIndexedItems(indexedItems, method, instructionOffset);
-      methodProto.collectIndexedItems(indexedItems, method, instructionOffset);
-      bootstrapMethod.collectIndexedItems(indexedItems, method, instructionOffset);
+      methodName.collectIndexedItems(indexedItems);
+      methodProto.collectIndexedItems(indexedItems);
+      bootstrapMethod.collectIndexedItems(indexedItems);
       for (DexValue arg : bootstrapArgs) {
-        arg.collectIndexedItems(indexedItems, method, instructionOffset);
+        arg.collectIndexedItems(indexedItems);
       }
     }
   }
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 fe2eabd..528b79b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -113,6 +113,10 @@
     return accessFlags;
   }
 
+  public DexString getSourceFile() {
+    return sourceFile;
+  }
+
   public Iterable<DexEncodedField> fields() {
     return fields(Predicates.alwaysTrue());
   }
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 1e85690..118a04d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.ProgramResource;
 import com.android.tools.r8.ProgramResource.Kind;
-import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
@@ -54,12 +53,6 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    throw new Unreachable();
-  }
-
-  @Override
   public String toString() {
     return type.toString() + "(classpath class)";
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index ae3fe1e..6716297 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -403,14 +403,11 @@
     return builder.toString();
   }
 
-  @Override
-  public void collectIndexedItems(
-      IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
-    assert instructionOffset == -1;
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     highestSortingString = null;
     for (Instruction insn : instructions) {
       assert !insn.isDexItemBasedConstString();
-      insn.collectIndexedItems(indexedItems, method, insn.getOffset());
+      insn.collectIndexedItems(indexedItems);
       if (insn.isConstString()) {
         updateHighestSortingString(insn.asConstString().getString());
       } else if (insn.isConstStringJumbo()) {
@@ -512,12 +509,6 @@
     }
 
     @Override
-    void collectIndexedItems(IndexedItemCollection indexedItems,
-        DexMethod method, int instructionOffset) {
-      // Intentionally left empty.
-    }
-
-    @Override
     void collectMixedSectionItems(MixedSectionCollection mixedItems) {
       // Should never be visited.
       assert false;
@@ -557,10 +548,10 @@
       return false;
     }
 
-    @Override
-    public void collectIndexedItems(IndexedItemCollection indexedItems,
-        DexMethod method, int instructionOffset) {
-      collectAll(indexedItems, pairs);
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      for (TypeAddrPair pair : pairs) {
+        pair.collectIndexedItems(indexedItems);
+      }
     }
 
     @Override
@@ -599,10 +590,8 @@
         this.addr = addr;
       }
 
-      @Override
-      public void collectIndexedItems(IndexedItemCollection indexedItems,
-          DexMethod method, int instructionOffset) {
-        type.collectIndexedItems(indexedItems, method, instructionOffset);
+      public void collectIndexedItems(IndexedItemCollection indexedItems) {
+        type.collectIndexedItems(indexedItems);
       }
 
       @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
index 059f7f6..20fd6e6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -13,9 +13,7 @@
 abstract public class DexDebugEvent extends DexItem {
   public static final DexDebugEvent[] EMPTY_ARRAY = {};
 
-  @Override
-  public void collectIndexedItems(IndexedItemCollection collection,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection collection) {
     // Empty by default.
   }
 
@@ -222,16 +220,15 @@
     }
 
     @Override
-    public void collectIndexedItems(IndexedItemCollection collection,
-        DexMethod method, int instructionOffset) {
+    public void collectIndexedItems(IndexedItemCollection collection) {
       if (name != null) {
-        name.collectIndexedItems(collection, method, instructionOffset);
+        name.collectIndexedItems(collection);
       }
       if (type != null) {
-        type.collectIndexedItems(collection, method, instructionOffset);
+        type.collectIndexedItems(collection);
       }
       if (signature != null) {
-        signature.collectIndexedItems(collection, method, instructionOffset);
+        signature.collectIndexedItems(collection);
       }
     }
 
@@ -362,9 +359,8 @@
     }
 
     @Override
-    public void collectIndexedItems(IndexedItemCollection collection,
-        DexMethod method, int instructionOffset) {
-      fileName.collectIndexedItems(collection, method, instructionOffset);
+    public void collectIndexedItems(IndexedItemCollection collection) {
+      fileName.collectIndexedItems(collection);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
index 9c27243..51ad23c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
@@ -54,11 +54,15 @@
     return false;
   }
 
-  @Override
-  public void collectIndexedItems(IndexedItemCollection collection,
-      DexMethod method, int instructionOffset) {
-    collectAll(collection, parameters);
-    collectAll(collection, events);
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    for (DexString parameter : parameters) {
+      if (parameter != null) {
+        parameter.collectIndexedItems(indexedItems);
+      }
+    }
+    for (DexDebugEvent event : events) {
+      event.collectIndexedItems(indexedItems);
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
index f5c5272..0ed6769 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
@@ -23,11 +23,11 @@
     this.elements = elements;
   }
 
-  @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    type.collectIndexedItems(indexedItems, method, instructionOffset);
-    collectAll(indexedItems, elements);
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    type.collectIndexedItems(indexedItems);
+    for (DexAnnotationElement element : elements) {
+      element.collectIndexedItems(indexedItems);
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedArray.java b/src/main/java/com/android/tools/r8/graph/DexEncodedArray.java
index fac96c9..19ec6c2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedArray.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedArray.java
@@ -15,10 +15,10 @@
     this.values = values;
   }
 
-  @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    collectAll(indexedItems, values);
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    for (DexValue value : values) {
+      value.collectIndexedItems(indexedItems);
+    }
   }
 
   @Override
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 5d9fa45..4b6fd50 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -87,13 +87,11 @@
     this.kotlinMemberInfo = kotlinMemberInfo;
   }
 
-  @Override
-  public void collectIndexedItems(
-      IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
-    field.collectIndexedItems(indexedItems, method, instructionOffset);
-    annotations().collectIndexedItems(indexedItems, method, instructionOffset);
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    field.collectIndexedItems(indexedItems);
+    annotations().collectIndexedItems(indexedItems);
     if (accessFlags.isStatic()) {
-      getStaticValue().collectIndexedItems(indexedItems, method, instructionOffset);
+      getStaticValue().collectIndexedItems(indexedItems);
     }
   }
 
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 ff7e38c..ce6934a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -648,13 +648,15 @@
     return "Encoded method " + method;
   }
 
-  @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     checkIfObsolete();
     this.method.collectIndexedItems(indexedItems);
     if (code != null) {
-      code.collectIndexedItems(indexedItems, this.method);
+      if (code.isDexCode()) {
+        code.asDexCode().collectIndexedItems(indexedItems);
+      } else {
+        assert false;
+      }
     }
     annotations().collectIndexedItems(indexedItems);
     parameterAnnotationsList.collectIndexedItems(indexedItems);
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 84d581e..19b793d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -79,13 +79,11 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     if (indexedItems.addField(this)) {
-      holder.collectIndexedItems(indexedItems, method, instructionOffset);
-      type.collectIndexedItems(indexedItems, method, instructionOffset);
-      indexedItems.getRenamedName(this).collectIndexedItems(
-          indexedItems, method, instructionOffset);
+      holder.collectIndexedItems(indexedItems);
+      type.collectIndexedItems(indexedItems);
+      indexedItems.getRenamedName(this).collectIndexedItems(indexedItems);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexItem.java b/src/main/java/com/android/tools/r8/graph/DexItem.java
index eb8e150..f26e41a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItem.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItem.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import java.util.Collection;
 import java.util.function.Consumer;
@@ -11,17 +10,13 @@
 
 public abstract class DexItem {
 
-  static <T extends DexItem> void collectAll(IndexedItemCollection indexedItems, T[] items) {
-    consumeArray(items, (T item) -> item.collectIndexedItems(indexedItems));
-  }
-
   public static <T extends DexItem> void collectAll(MixedSectionCollection mixedItems, T[] items) {
-    consumeArray(items, (T item) -> item.collectMixedSectionItems(mixedItems));
+    consumeArray(items, item -> item.collectMixedSectionItems(mixedItems));
   }
 
   public static <T extends DexItem> void collectAll(MixedSectionCollection mixedItems,
       Collection<T> items) {
-    items.forEach((T item) -> item.collectMixedSectionItems(mixedItems));
+    items.forEach(item -> item.collectMixedSectionItems(mixedItems));
   }
 
   /**
@@ -39,17 +34,6 @@
     }
   }
 
-  abstract void collectIndexedItems(IndexedItemCollection collection,
-      DexMethod method, int instructionOffset);
-
-  public void collectIndexedItems(IndexedItemCollection collection) {
-    collectIndexedItems(collection, null, -1);
-  }
-
-  public void collectIndexedItems(IndexedItemCollection collection, DexMethod method) {
-    collectIndexedItems(collection, method, -1);
-  }
-
   abstract void collectMixedSectionItems(MixedSectionCollection collection);
 
   protected void flushCachedValues() {
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 7711eb3..22920eb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.ProgramResource;
 import com.android.tools.r8.ProgramResource.Kind;
-import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
@@ -81,12 +80,6 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    throw new Unreachable();
-  }
-
-  @Override
   public String toString() {
     return type.toString() + "(library class)";
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexMemberAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexMemberAnnotation.java
index d9868a5..5c9dad2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMemberAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMemberAnnotation.java
@@ -17,13 +17,6 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    item.collectIndexedItems(indexedItems, method, instructionOffset);
-    annotations.collectIndexedItems(indexedItems, method, instructionOffset);
-  }
-
-  @Override
   void collectMixedSectionItems(MixedSectionCollection mixedItems) {
     annotations.collectMixedSectionItems(mixedItems);
   }
@@ -50,6 +43,11 @@
     public DexFieldAnnotation(DexField item, DexAnnotationSet annotations) {
       super(item, annotations);
     }
+
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      item.collectIndexedItems(indexedItems);
+      annotations.collectIndexedItems(indexedItems);
+    }
   }
 
   public static class DexMethodAnnotation extends DexMemberAnnotation<DexMethod, DexAnnotationSet> {
@@ -57,6 +55,11 @@
     public DexMethodAnnotation(DexMethod item, DexAnnotationSet annotations) {
       super(item, annotations);
     }
+
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      item.collectIndexedItems(indexedItems);
+      annotations.collectIndexedItems(indexedItems);
+    }
   }
 
   public static class DexParameterAnnotation extends
@@ -65,5 +68,10 @@
     public DexParameterAnnotation(DexMethod item, ParameterAnnotationsList annotations) {
       super(item, annotations);
     }
+
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      item.collectIndexedItems(indexedItems);
+      annotations.collectIndexedItems(indexedItems);
+    }
   }
 }
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 fce08d8..999f261 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -31,6 +31,10 @@
     }
   }
 
+  public DexTypeList getParameters() {
+    return proto.parameters;
+  }
+
   public DexType getReturnType() {
     return proto.returnType;
   }
@@ -92,26 +96,23 @@
   }
 
   @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    if (collectIndexedItemsExceptName(indexedItems, method, instructionOffset)) {
-      collectIndexedItemsName(indexedItems, method, instructionOffset);
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    if (collectIndexedItemsExceptName(indexedItems)) {
+      collectIndexedItemsName(indexedItems);
     }
   }
 
-  public boolean collectIndexedItemsExceptName(
-      IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
+  boolean collectIndexedItemsExceptName(IndexedItemCollection indexedItems) {
     if (indexedItems.addMethod(this)) {
-      holder.collectIndexedItems(indexedItems, method, instructionOffset);
-      proto.collectIndexedItems(indexedItems, method, instructionOffset);
+      holder.collectIndexedItems(indexedItems);
+      proto.collectIndexedItems(indexedItems);
       return true;
     }
     return false;
   }
 
-  public void collectIndexedItemsName(
-      IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
-    indexedItems.getRenamedName(this).collectIndexedItems(indexedItems, method, instructionOffset);
+  void collectIndexedItemsName(IndexedItemCollection indexedItems) {
+    indexedItems.getRenamedName(this).collectIndexedItems(indexedItems);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index 6c37bc9..80a3cb9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -187,7 +187,7 @@
   public final MethodHandleType type;
 
   // Field or method that the method handle is targeting.
-  public final DexMember<? extends DexItem, ? extends DexMember<?, ?>> fieldOrMethod;
+  public final DexMember<? extends DexItem, ? extends DexMember<?, ?>> member;
 
   public final boolean isInterface;
 
@@ -204,18 +204,18 @@
 
   public DexMethodHandle(
       MethodHandleType type,
-      DexMember<? extends DexItem, ? extends DexMember<?, ?>> fieldOrMethod,
+      DexMember<? extends DexItem, ? extends DexMember<?, ?>> member,
       boolean isInterface) {
-    this(type, fieldOrMethod, isInterface, null);
+    this(type, member, isInterface, null);
   }
 
   public DexMethodHandle(
       MethodHandleType type,
-      DexMember<? extends DexItem, ? extends DexMember<?, ?>> fieldOrMethod,
+      DexMember<? extends DexItem, ? extends DexMember<?, ?>> member,
       boolean isInterface,
       DexMethod rewrittenTarget) {
     this.type = type;
-    this.fieldOrMethod = fieldOrMethod;
+    this.member = member;
     this.isInterface = isInterface;
     this.rewrittenTarget = rewrittenTarget;
   }
@@ -232,7 +232,7 @@
 
   @Override
   public int computeHashCode() {
-    return Objects.hash(type, fieldOrMethod.computeHashCode(), isInterface, rewrittenTarget);
+    return Objects.hash(type, member.computeHashCode(), isInterface, rewrittenTarget);
   }
 
   @Override
@@ -240,7 +240,7 @@
     if (other instanceof DexMethodHandle) {
       DexMethodHandle o = (DexMethodHandle) other;
       return type.equals(o.type)
-          && fieldOrMethod.equals(o.fieldOrMethod)
+          && member.equals(o.member)
           && (isInterface == o.isInterface)
           && Objects.equals(rewrittenTarget, o.rewrittenTarget);
     }
@@ -249,28 +249,32 @@
 
   @Override
   public String toString() {
-    StringBuilder builder = new StringBuilder("MethodHandle: {")
+    StringBuilder builder =
+        new StringBuilder("MethodHandle: {")
             .append(type)
             .append(", ")
-            .append(fieldOrMethod.toSourceString())
+            .append(member.toSourceString())
             .append("}");
     return builder.toString();
   }
 
-  @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     if (indexedItems.addMethodHandle(this)) {
-      if (fieldOrMethod.isDexMethod() && rewrittenTarget != null) {
-        // If there is a rewritten target we need to use that to get the right name of the
-        // targeted method (only member rebound methods take part in naming). The rest of the
-        // indexed items are collected from fieldOrMethod.
-        if (fieldOrMethod.asDexMethod().collectIndexedItemsExceptName(
-            indexedItems, method, instructionOffset)) {
-          rewrittenTarget.collectIndexedItemsName(indexedItems, method, instructionOffset);
-        }
+      if (member.isDexField()) {
+        DexField field = member.asDexField();
+        field.collectIndexedItems(indexedItems);
       } else {
-        fieldOrMethod.collectIndexedItems(indexedItems, method, instructionOffset);
+        DexMethod method = member.asDexMethod();
+        if (rewrittenTarget != null) {
+          // If there is a rewritten target we need to use that to get the right name of the
+          // targeted method (only member rebound methods take part in naming). The rest of the
+          // indexed items are collected from method.
+          if (method.collectIndexedItemsExceptName(indexedItems)) {
+            rewrittenTarget.collectIndexedItemsName(indexedItems);
+          }
+        } else {
+          method.collectIndexedItems(indexedItems);
+        }
       }
     }
   }
@@ -300,12 +304,12 @@
 
   public DexMethod asMethod() {
     assert isMethodHandle();
-    return (DexMethod) fieldOrMethod;
+    return (DexMethod) member;
   }
 
   public DexField asField() {
     assert isFieldHandle();
-    return (DexField) fieldOrMethod;
+    return (DexField) member;
   }
 
   @Override
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 0a5014a..90c37ed 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -221,6 +221,10 @@
                     : TraversalContinuation.CONTINUE);
   }
 
+  public Kind getOriginKind() {
+    return originKind;
+  }
+
   public boolean originatesFromDexResource() {
     return originKind == Kind.DEX;
   }
@@ -229,22 +233,20 @@
     return originKind == Kind.CF;
   }
 
-  @Override
-  public void collectIndexedItems(
-      IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     if (indexedItems.addClass(this)) {
-      type.collectIndexedItems(indexedItems, method, instructionOffset);
+      type.collectIndexedItems(indexedItems);
       if (superType != null) {
-        superType.collectIndexedItems(indexedItems, method, instructionOffset);
+        superType.collectIndexedItems(indexedItems);
       } else {
         assert type.toDescriptorString().equals("Ljava/lang/Object;");
       }
       if (sourceFile != null) {
-        sourceFile.collectIndexedItems(indexedItems, method, instructionOffset);
+        sourceFile.collectIndexedItems(indexedItems);
       }
-      annotations().collectIndexedItems(indexedItems, method, instructionOffset);
+      annotations().collectIndexedItems(indexedItems);
       if (interfaces != null) {
-        interfaces.collectIndexedItems(indexedItems, method, instructionOffset);
+        interfaces.collectIndexedItems(indexedItems);
       }
       if (getEnclosingMethodAttribute() != null) {
         getEnclosingMethodAttribute().collectIndexedItems(indexedItems);
@@ -252,18 +254,8 @@
       for (InnerClassAttribute attribute : getInnerClasses()) {
         attribute.collectIndexedItems(indexedItems);
       }
-      synchronizedCollectAll(indexedItems, staticFields);
-      synchronizedCollectAll(indexedItems, instanceFields);
-      synchronized (methodCollection) {
-        methodCollection.forEachMethod(m -> m.collectIndexedItems(indexedItems));
-      }
-    }
-  }
-
-  private static <T extends DexItem> void synchronizedCollectAll(
-      IndexedItemCollection collection, T[] items) {
-    synchronized (items) {
-      collectAll(collection, items);
+      forEachField(field -> field.collectIndexedItems(indexedItems));
+      forEachMethod(method -> method.collectIndexedItems(indexedItems));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexProto.java b/src/main/java/com/android/tools/r8/graph/DexProto.java
index a9a15f3..77b9c42 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProto.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProto.java
@@ -47,13 +47,11 @@
     return "Proto " + shorty + " " + returnType + " " + parameters;
   }
 
-  @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     if (indexedItems.addProto(this)) {
-      shorty.collectIndexedItems(indexedItems, method, instructionOffset);
-      returnType.collectIndexedItems(indexedItems, method, instructionOffset);
-      parameters.collectIndexedItems(indexedItems, method, instructionOffset);
+      shorty.collectIndexedItems(indexedItems);
+      returnType.collectIndexedItems(indexedItems);
+      parameters.collectIndexedItems(indexedItems);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexReference.java b/src/main/java/com/android/tools/r8/graph/DexReference.java
index c37c9de..b296b43 100644
--- a/src/main/java/com/android/tools/r8/graph/DexReference.java
+++ b/src/main/java/com/android/tools/r8/graph/DexReference.java
@@ -3,11 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.dex.IndexedItemCollection;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.stream.Stream;
 
 /**
  * A common interface for {@link DexType}, {@link DexField}, and {@link DexMethod}.
@@ -30,6 +29,8 @@
       BiConsumer<DexMethod, T> methodConsumer,
       T arg);
 
+  public abstract void collectIndexedItems(IndexedItemCollection indexedItems);
+
   public boolean isDexType() {
     return false;
   }
@@ -61,27 +62,4 @@
   public DexMethod asDexMethod() {
     return null;
   }
-
-  public static Stream<DexReference> filterDexReference(Stream<DexItem> stream) {
-    return DexItem.filter(stream, DexReference.class);
-  }
-
-  private static <T extends DexReference> Stream<T> filter(
-      Stream<DexReference> stream,
-      Predicate<DexReference> pred,
-      Function<DexReference, T> f) {
-    return stream.filter(pred).map(f);
-  }
-
-  public static Stream<DexType> filterDexType(Stream<DexReference> stream) {
-    return filter(stream, DexReference::isDexType, DexReference::asDexType);
-  }
-
-  public static Stream<DexField> filterDexField(Stream<DexReference> stream) {
-    return filter(stream, DexReference::isDexField, DexReference::asDexField);
-  }
-
-  public static Stream<DexMethod> filterDexMethod(Stream<DexReference> stream) {
-    return filter(stream, DexReference::isDexMethod, DexReference::asDexMethod);
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexString.java b/src/main/java/com/android/tools/r8/graph/DexString.java
index 03303f6..602a6b7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexString.java
+++ b/src/main/java/com/android/tools/r8/graph/DexString.java
@@ -239,9 +239,7 @@
     return offset;
   }
 
-  @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
     indexedItems.addString(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 1190570..81c4dc5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -171,12 +171,9 @@
   }
 
   @Override
-  public void collectIndexedItems(
-      IndexedItemCollection collection, DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection collection) {
     if (collection.addType(this)) {
-      collection
-          .getRenamedDescriptor(this)
-          .collectIndexedItems(collection, method, instructionOffset);
+      collection.getRenamedDescriptor(this).collectIndexedItems(collection);
     }
   }
 
@@ -387,6 +384,19 @@
     return newBase.toArrayType(getNumberOfLeadingSquareBrackets(), dexItemFactory);
   }
 
+  public DexType replacePackage(String newPackageDescriptor, DexItemFactory dexItemFactory) {
+    assert isClassType();
+    String descriptorString = toDescriptorString();
+    int lastPackageSeparator = descriptorString.lastIndexOf('/');
+    String newDescriptorString = "L" + newPackageDescriptor + "/";
+    if (lastPackageSeparator >= 0) {
+      newDescriptorString += descriptorString.substring(lastPackageSeparator + 1);
+    } else {
+      newDescriptorString += descriptorString.substring(1);
+    }
+    return dexItemFactory.createType(newDescriptorString);
+  }
+
   public DexType toArrayType(int dimensions, DexItemFactory dexItemFactory) {
     byte[] content = new byte[descriptor.content.length + dimensions];
     Arrays.fill(content, 0, dimensions, (byte) '[');
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeList.java b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
index 2272c98..c61de91 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeList.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.ArrayUtils;
 import java.util.Arrays;
+import java.util.stream.Stream;
 
 public class DexTypeList extends DexItem {
 
@@ -38,11 +39,9 @@
     return Arrays.hashCode(values);
   }
 
-  @Override
-  void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
+  void collectIndexedItems(IndexedItemCollection indexedItems) {
     for (DexType type : values) {
-      type.collectIndexedItems(indexedItems, method, instructionOffset);
+      type.collectIndexedItems(indexedItems);
     }
   }
 
@@ -68,6 +67,10 @@
     return values.length;
   }
 
+  public Stream<DexType> stream() {
+    return Stream.of(values);
+  }
+
   @Override
   public String toString() {
     StringBuilder builder = new StringBuilder();
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index 96b6b5a..2fe8c3f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -307,6 +307,10 @@
     dest.putByte((byte) ((arg << 5) | kind.toByte()));
   }
 
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // Intentionally left empty
+  }
+
   @Override
   void collectMixedSectionItems(MixedSectionCollection mixedItems) {
     // Should never be visited.
@@ -380,12 +384,6 @@
   private abstract static class SimpleDexValue extends DexValue {
 
     @Override
-    public void collectIndexedItems(
-        IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
-      // Intentionally left empty
-    }
-
-    @Override
     public void sort() {
       // Intentionally empty
     }
@@ -1054,12 +1052,6 @@
     }
 
     @Override
-    public void collectIndexedItems(IndexedItemCollection indexedItems,
-        DexMethod method, int instructionOffset) {
-      value.collectIndexedItems(indexedItems, method, instructionOffset);
-    }
-
-    @Override
     public void sort() {
       // Intentionally empty.
     }
@@ -1094,6 +1086,11 @@
     }
 
     @Override
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
+    }
+
+    @Override
     public DexValueString asDexValueString() {
       return this;
     }
@@ -1152,6 +1149,11 @@
       this.nameComputationInfo = nameComputationInfo;
     }
 
+    @Override
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
+    }
+
     public NameComputationInfo<?> getNameComputationInfo() {
       return nameComputationInfo;
     }
@@ -1223,9 +1225,8 @@
     }
 
     @Override
-    public void collectIndexedItems(
-        IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
-      value.collectIndexedItems(indexedItems, method, instructionOffset);
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
     }
 
     @Override
@@ -1256,9 +1257,8 @@
     }
 
     @Override
-    public void collectIndexedItems(
-        IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
-      value.collectIndexedItems(indexedItems, method, instructionOffset);
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
     }
 
     @Override
@@ -1289,9 +1289,8 @@
     }
 
     @Override
-    public void collectIndexedItems(
-        IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
-      value.collectIndexedItems(indexedItems, method, instructionOffset);
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
     }
 
     @Override
@@ -1322,9 +1321,8 @@
     }
 
     @Override
-    public void collectIndexedItems(
-        IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
-      value.collectIndexedItems(indexedItems, method, instructionOffset);
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
     }
 
     @Override
@@ -1365,9 +1363,8 @@
     }
 
     @Override
-    public void collectIndexedItems(
-        IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
-      value.collectIndexedItems(indexedItems, method, instructionOffset);
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
     }
 
     @Override
@@ -1394,9 +1391,10 @@
     }
 
     @Override
-    public void collectIndexedItems(IndexedItemCollection indexedItems,
-        DexMethod method, int instructionOffset) {
-      collectAll(indexedItems, values);
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      for (DexValue value : values) {
+        value.collectIndexedItems(indexedItems);
+      }
     }
 
     @Override
@@ -1492,9 +1490,8 @@
     }
 
     @Override
-    public void collectIndexedItems(IndexedItemCollection indexedItems,
-        DexMethod method, int instructionOffset) {
-      value.collectIndexedItems(indexedItems, method, instructionOffset);
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
     }
 
     @Override
@@ -1736,9 +1733,8 @@
     }
 
     @Override
-    public void collectIndexedItems(
-        IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
-      value.collectIndexedItems(indexedItems, method, instructionOffset);
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index edce8d8..2491193 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.desugar.InterfaceProcessor.InterfaceProcessorNestedGraphLens;
 import com.android.tools.r8.shaking.KeepInfoCollection;
 import com.android.tools.r8.utils.SetUtils;
 import com.google.common.collect.BiMap;
@@ -151,6 +152,12 @@
 
   public abstract DexField getRenamedFieldSignature(DexField originalField);
 
+  public final DexMember<?, ?> getRenamedMemberSignature(DexMember<?, ?> originalMember) {
+    return originalMember.isDexField()
+        ? getRenamedFieldSignature(originalMember.asDexField())
+        : getRenamedMethodSignature(originalMember.asDexMethod());
+  }
+
   public final DexMethod getRenamedMethodSignature(DexMethod originalMethod) {
     return getRenamedMethodSignature(originalMethod, null);
   }
@@ -250,6 +257,22 @@
     return this == getIdentityLens();
   }
 
+  public boolean isInterfaceProcessorLens() {
+    return false;
+  }
+
+  public InterfaceProcessorNestedGraphLens asInterfaceProcessorLens() {
+    return null;
+  }
+
+  public boolean isGraphLensWithPrevious() {
+    return false;
+  }
+
+  public GraphLensWithPrevious asGraphLensWithPrevious() {
+    return null;
+  }
+
   public GraphLens withCodeRewritingsApplied() {
     if (hasCodeRewritings()) {
       return new ClearCodeRewritingGraphLens(this);
@@ -471,7 +494,8 @@
 
   // This lens clears all code rewriting (lookup methods mimics identity lens behavior) but still
   // relies on the previous lens for names (getRenamed/Original methods).
-  public static class ClearCodeRewritingGraphLens extends IdentityGraphLens {
+  public static class ClearCodeRewritingGraphLens extends IdentityGraphLens
+      implements GraphLensWithPrevious {
 
     private final GraphLens previous;
 
@@ -480,6 +504,21 @@
     }
 
     @Override
+    public boolean isGraphLensWithPrevious() {
+      return true;
+    }
+
+    @Override
+    public GraphLensWithPrevious asGraphLensWithPrevious() {
+      return this;
+    }
+
+    @Override
+    public GraphLens getPrevious() {
+      return previous;
+    }
+
+    @Override
     public DexType getOriginalType(DexType type) {
       return previous.getOriginalType(type);
     }
@@ -512,6 +551,11 @@
     }
   }
 
+  public interface GraphLensWithPrevious {
+
+    GraphLens getPrevious();
+  }
+
   /**
    * GraphLens implementation with a parent lens using a simple mapping for type, method and field
    * mapping.
@@ -522,7 +566,7 @@
    * #mapInvocationType(DexMethod, DexMethod, Type)} if the default name mapping applies, and only
    * invocation type might need to change.
    */
-  public static class NestedGraphLens extends GraphLens {
+  public static class NestedGraphLens extends GraphLens implements GraphLensWithPrevious {
 
     protected GraphLens previousLens;
     protected final DexItemFactory dexItemFactory;
@@ -535,7 +579,7 @@
     // Maps that store the original signature of fields and methods that have been affected, for
     // example, by vertical class merging. Needed to generate a correct Proguard map in the end.
     protected final BiMap<DexField, DexField> originalFieldSignatures;
-    protected final BiMap<DexMethod, DexMethod> originalMethodSignatures;
+    protected BiMap<DexMethod, DexMethod> originalMethodSignatures;
 
     // Overrides this if the sub type needs to be a nested lens while it doesn't have any mappings
     // at all, e.g., publicizer lens that changes invocation type only.
@@ -564,6 +608,11 @@
       this.dexItemFactory = dexItemFactory;
     }
 
+    @Override
+    public GraphLens getPrevious() {
+      return previousLens;
+    }
+
     public <T> T withAlternativeParentLens(GraphLens lens, Supplier<T> action) {
       GraphLens oldParent = previousLens;
       previousLens = lens;
@@ -573,6 +622,16 @@
     }
 
     @Override
+    public boolean isGraphLensWithPrevious() {
+      return true;
+    }
+
+    @Override
+    public NestedGraphLens asGraphLensWithPrevious() {
+      return this;
+    }
+
+    @Override
     public DexType getOriginalType(DexType type) {
       return previousLens.getOriginalType(type);
     }
diff --git a/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java b/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java
index 0c115e1..96bfb47 100644
--- a/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java
+++ b/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java
@@ -3,17 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 
 /** Subset of dex items that are referenced by some table index. */
 public abstract class IndexedDexItem extends CachedHashValueDexItem {
 
   @Override
-  public abstract void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset);
-
-  @Override
   void collectMixedSectionItems(MixedSectionCollection mixedItems) {
     // Should never be visited.
     assert false;
diff --git a/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java b/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
index 6ff5c75..4b21c2b 100644
--- a/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
+++ b/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
@@ -82,10 +82,10 @@
     return false;
   }
 
-  @Override
-  public void collectIndexedItems(
-      IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
-    collectAll(indexedItems, values);
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    for (DexAnnotationSet value : values) {
+      value.collectIndexedItems(indexedItems);
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramPackage.java b/src/main/java/com/android/tools/r8/graph/ProgramPackage.java
index 28fc4e2..c244576 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramPackage.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramPackage.java
@@ -23,6 +23,14 @@
     classes.add(clazz);
   }
 
+  public String getLastPackageName() {
+    int index = packageDescriptor.lastIndexOf('/');
+    if (index >= 0) {
+      return packageDescriptor.substring(index + 1);
+    }
+    return packageDescriptor;
+  }
+
   public void forEachClass(Consumer<DexProgramClass> consumer) {
     forEach(consumer);
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index e9aa061..51ef31e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -4,15 +4,24 @@
 
 package com.android.tools.r8.horizontalclassmerging;
 
+import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
 import com.google.common.base.Equivalence.Wrapper;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
+import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
 import java.util.Collection;
 import java.util.IdentityHashMap;
 import java.util.Map;
@@ -28,24 +37,34 @@
   private final Collection<DexProgramClass> toMergeGroup;
   private final DexItemFactory dexItemFactory;
   private final HorizontalClassMergerGraphLens.Builder lensBuilder;
+  private final FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder;
 
+  private final Reference2IntMap<DexType> classIdentifiers = new Reference2IntOpenHashMap<>();
   private final Map<DexProto, ConstructorMerger.Builder> constructorMergers;
+  private final DexField classIdField;
 
   ClassMerger(
       AppView<?> appView,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
+      FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
       DexProgramClass target,
       Collection<DexProgramClass> toMergeGroup) {
     this.appView = appView;
     this.lensBuilder = lensBuilder;
+    this.fieldAccessChangesBuilder = fieldAccessChangesBuilder;
     this.target = target;
     this.toMergeGroup = toMergeGroup;
-    this.constructorMergers = new IdentityHashMap<>();
 
+    this.constructorMergers = new IdentityHashMap<>();
     this.dexItemFactory = appView.dexItemFactory();
+
+    // TODO(b/165498187): ensure the name for the field is fresh
+    classIdField = dexItemFactory.createField(target.type, dexItemFactory.intType, "$r8$classId");
+
+    buildClassIdentifierMap();
   }
 
-  private Wrapper<DexMethod> bySignature(DexMethod method) {
+  Wrapper<DexMethod> bySignature(DexMethod method) {
     return MethodSignatureEquivalence.get().wrap(method);
   }
 
@@ -56,7 +75,14 @@
         .add(method);
   }
 
-  private void merge(DexProgramClass toMerge) {
+  void buildClassIdentifierMap() {
+    classIdentifiers.put(target.type, 0);
+    for (DexProgramClass toMerge : toMergeGroup) {
+      classIdentifiers.put(toMerge.type, classIdentifiers.size());
+    }
+  }
+
+  void merge(DexProgramClass toMerge) {
     toMerge.forEachProgramMethod(
         programMethod -> {
           DexEncodedMethod method = programMethod.getDefinition();
@@ -98,8 +124,8 @@
 
   void mergeConstructors() {
     for (ConstructorMerger.Builder builder : constructorMergers.values()) {
-      ConstructorMerger constructorMerger = builder.build(appView, target);
-      constructorMerger.merge(lensBuilder);
+      ConstructorMerger constructorMerger = builder.build(appView, target, classIdField);
+      constructorMerger.merge(lensBuilder, fieldAccessChangesBuilder, classIdentifiers);
     }
   }
 
@@ -117,8 +143,20 @@
         });
   }
 
+  void appendClassIdField() {
+    DexEncodedField encodedField =
+        new DexEncodedField(
+            classIdField,
+            FieldAccessFlags.fromSharedAccessFlags(
+                Constants.ACC_PUBLIC + Constants.ACC_FINAL + Constants.ACC_SYNTHETIC),
+            DexAnnotationSet.empty(),
+            null);
+    target.appendInstanceField(encodedField);
+  }
+
   public void mergeGroup() {
     addTargetConstructors();
+    appendClassIdField();
 
     for (DexProgramClass clazz : toMergeGroup) {
       merge(clazz);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
index b182afb..ff2a898 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.horizontalclassmerging;
 
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.code.Position;
@@ -12,78 +13,117 @@
 import com.android.tools.r8.ir.conversion.IRBuilder;
 import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
 import com.android.tools.r8.utils.IntBox;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 
 /**
  * Generate code of the form: <code>
  *   MyClass(int constructorId, [args]) {
  *     switch (constructorId) {
- *       case 0:
- *         this.Constructor$A([args]);
- *         return;
  *       case 1:
  *         this.Constructor$B([args]);
  *         return;
  *       ...
  *       default:
- *         throw null;
+ *         this.Constructor$A([args]);
+ *         return;
  *     }
  *   }
  * </code>
  */
 public class ConstructorEntryPoint extends SyntheticSourceCode {
-  private final Collection<DexMethod> typeConstructors;
+  private final DexField classIdField;
+  private final Int2ReferenceSortedMap<DexMethod> typeConstructors;
 
   public ConstructorEntryPoint(
-      Collection<DexMethod> typeConstructors, DexMethod method, Position callerPosition) {
-    super(method.holder, method, callerPosition);
+      Int2ReferenceSortedMap<DexMethod> typeConstructors,
+      DexMethod newConstructor,
+      DexField classIdField,
+      Position callerPosition,
+      DexMethod originalMethod) {
+    super(newConstructor.holder, newConstructor, callerPosition, originalMethod);
 
     this.typeConstructors = typeConstructors;
+    this.classIdField = classIdField;
   }
 
-  @Override
-  protected void prepareInstructions() {
-    int typeConstructorCount = typeConstructors.size();
-    int idRegister = getParamRegister(0);
+  void addConstructorInvoke(DexMethod typeConstructor) {
+    add(
+        builder -> {
+          List<Value> arguments = new ArrayList<>(typeConstructor.getArity() + 1);
+          arguments.add(builder.getReceiverValue());
 
-    int[] keys = new int[typeConstructorCount];
-    int[] offsets = new int[typeConstructorCount];
+          // If there are any arguments add them to the list.
+          for (int i = 0; i < typeConstructor.getArity(); i++) {
+            arguments.add(builder.getArgumentValues().get(i));
+          }
+
+          builder.addInvoke(Type.DIRECT, typeConstructor, typeConstructor.proto, arguments, false);
+        });
+  }
+
+  /** Assign the given register to the class id field. */
+  void addRegisterClassIdAssignment(int idRegister) {
+    add(builder -> builder.addInstancePut(idRegister, getReceiverRegister(), classIdField));
+  }
+
+  /** Assign the given constant integer value to the class id field. */
+  void addConstantRegisterClassIdAssignment(int classId) {
+    int idRegister = nextRegister(ValueType.INT);
+    add(builder -> builder.addIntConst(idRegister, classId));
+    addRegisterClassIdAssignment(idRegister);
+  }
+
+  protected void prepareMultiConstructorInstructions() {
+    int typeConstructorCount = typeConstructors.size();
+    int idRegister = getParamRegister(method.getArity() - 1);
+
+    addRegisterClassIdAssignment(idRegister);
+
+    int[] keys = new int[typeConstructorCount - 1];
+    int[] offsets = new int[typeConstructorCount - 1];
     IntBox fallthrough = new IntBox();
     int switchIndex = lastInstructionIndex();
     add(
         builder -> builder.addSwitch(idRegister, keys, fallthrough.get(), offsets),
         builder -> endsSwitch(builder, switchIndex, fallthrough.get(), offsets));
 
-    fallthrough.set(nextInstructionIndex());
-    int nullRegister = nextRegister(ValueType.OBJECT);
-    add(builder -> builder.addNullConst(nullRegister));
-    add(builder -> builder.addThrow(nullRegister), endsBlock);
-
     int index = 0;
-    for (DexMethod typeConstructor : typeConstructors) {
-      keys[index] = index;
-      offsets[index] = nextInstructionIndex();
+    for (Entry<DexMethod> entry : typeConstructors.int2ReferenceEntrySet()) {
+      int classId = entry.getIntKey();
+      DexMethod typeConstructor = entry.getValue();
 
-      add(
-          builder -> {
-            List<Value> arguments = new ArrayList<>(typeConstructor.getArity());
-            arguments.add(builder.getReceiverValue());
-            int paramIndex = 0;
-            for (Value argument : builder.getArgumentValues()) {
-              if (paramIndex++ >= typeConstructor.getArity()) {
-                break;
-              }
-              arguments.add(argument);
-            }
-            builder.addInvoke(
-                Type.DIRECT, typeConstructor, typeConstructor.proto, arguments, false);
-          });
+      if (index == 0) {
+        // The first constructor is the fallthrough case.
+        fallthrough.set(nextInstructionIndex());
+      } else {
+        // All subsequent constructors are matched on a specific case.
+        keys[index - 1] = classId;
+        offsets[index - 1] = nextInstructionIndex();
+      }
 
+      addConstructorInvoke(typeConstructor);
       add(IRBuilder::addReturn, endsBlock);
 
       index++;
     }
   }
+
+  protected void prepareSingleConstructorInstructions() {
+    Entry<DexMethod> entry = typeConstructors.int2ReferenceEntrySet().first();
+    addConstantRegisterClassIdAssignment(entry.getIntKey());
+    addConstructorInvoke(entry.getValue());
+    add(IRBuilder::addReturn, endsBlock);
+  }
+
+  @Override
+  protected void prepareInstructions() {
+    if (typeConstructors.size() > 1) {
+      prepareMultiConstructorInstructions();
+    } else {
+      prepareSingleConstructorInstructions();
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPointSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPointSynthesizedCode.java
new file mode 100644
index 0000000..7d250bb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPointSynthesizedCode.java
@@ -0,0 +1,48 @@
+// 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.horizontalclassmerging;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
+import java.util.function.Consumer;
+
+public class ConstructorEntryPointSynthesizedCode extends AbstractSynthesizedCode {
+  private final DexMethod newConstructor;
+  private final DexMethod originalMethod;
+  private final DexField classIdField;
+  private final Int2ReferenceSortedMap<DexMethod> typeConstructors;
+
+  public ConstructorEntryPointSynthesizedCode(
+      Int2ReferenceSortedMap<DexMethod> typeConstructors,
+      DexMethod newConstructor,
+      DexField classIdField,
+      DexMethod originalMethod) {
+    this.typeConstructors = typeConstructors;
+    this.newConstructor = newConstructor;
+    this.classIdField = classIdField;
+    this.originalMethod = originalMethod;
+  }
+
+  @Override
+  public SourceCodeProvider getSourceCodeProvider() {
+    return callerPosition ->
+        new ConstructorEntryPoint(
+            typeConstructors, newConstructor, classIdField, callerPosition, originalMethod);
+  }
+
+  @Override
+  public Consumer<UseRegistry> getRegistryCallback() {
+    return this::registerReachableDefinitions;
+  }
+
+  private void registerReachableDefinitions(UseRegistry registry) {
+    for (DexMethod typeConstructor : typeConstructors.values()) {
+      registry.registerInvokeDirect(typeConstructor);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
index aae7b0e..76f4814 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -15,25 +16,31 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.ir.synthetic.SynthesizedCode;
+import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.IdentityHashMap;
 import java.util.List;
-import java.util.Map;
 
 public class ConstructorMerger {
   private final AppView<?> appView;
   private final DexProgramClass target;
   private final Collection<DexEncodedMethod> constructors;
   private final DexItemFactory dexItemFactory;
+  private final DexField classIdField;
 
   ConstructorMerger(
-      AppView<?> appView, DexProgramClass target, Collection<DexEncodedMethod> constructors) {
+      AppView<?> appView,
+      DexProgramClass target,
+      Collection<DexEncodedMethod> constructors,
+      DexField classIdField) {
     this.appView = appView;
     this.target = target;
     this.constructors = constructors;
+    this.classIdField = classIdField;
 
     // Constructors should not be empty and all constructors should have the same prototype.
     assert !constructors.isEmpty();
@@ -54,11 +61,16 @@
       return this;
     }
 
-    public ConstructorMerger build(AppView<?> appView, DexProgramClass target) {
-      return new ConstructorMerger(appView, target, constructors);
+    public ConstructorMerger build(
+        AppView<?> appView, DexProgramClass target, DexField classIdField) {
+      return new ConstructorMerger(appView, target, constructors, classIdField);
     }
   }
 
+  private boolean isTrivialMerge() {
+    return constructors.size() == 1;
+  }
+
   private DexMethod moveConstructor(DexEncodedMethod constructor) {
     DexMethod method =
         dexItemFactory.createFreshMethodName(
@@ -72,7 +84,9 @@
       target.removeMethod(constructor.toReference());
     }
 
-    target.addDirectMethod(constructor.toTypeSubstitutedMethod(method));
+    DexEncodedMethod encodedMethod = constructor.toTypeSubstitutedMethod(method);
+    encodedMethod.getMutableOptimizationInfo().markForceInline();
+    target.addDirectMethod(encodedMethod);
     return method;
   }
 
@@ -80,6 +94,10 @@
     DexEncodedMethod firstConstructor = constructors.stream().findFirst().get();
     DexProto oldProto = firstConstructor.getProto();
 
+    if (isTrivialMerge()) {
+      return oldProto;
+    }
+
     List<DexType> parameters = new ArrayList<>();
     Collections.addAll(parameters, oldProto.parameters.values);
     parameters.add(dexItemFactory.intType);
@@ -93,64 +111,63 @@
   }
 
   /** Synthesize a new method which selects the constructor based on a parameter type. */
-  void mergeMany(HorizontalClassMergerGraphLens.Builder lensBuilder) {
-    Map<DexType, DexMethod> typeConstructors = new IdentityHashMap<>();
+  void merge(
+      HorizontalClassMergerGraphLens.Builder lensBuilder,
+      FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
+      Reference2IntMap<DexType> classIdentifiers) {
+    // Tree map as must be sorted.
+    Int2ReferenceSortedMap<DexMethod> typeConstructorClassMap = new Int2ReferenceAVLTreeMap<>();
 
+    int classFileVersion = -1;
     for (DexEncodedMethod constructor : constructors) {
-      typeConstructors.put(constructor.holder(), moveConstructor(constructor));
+      if (constructor.hasClassFileVersion()) {
+        classFileVersion = Integer.max(classFileVersion, constructor.getClassFileVersion());
+      }
+      DexMethod movedConstructor = moveConstructor(constructor);
+      lensBuilder.recordOriginalSignature(constructor.method, movedConstructor);
+      typeConstructorClassMap.put(
+          classIdentifiers.getInt(constructor.getHolderType()), movedConstructor);
     }
 
     DexProto newProto = getNewConstructorProto();
 
-    DexMethod newConstructor =
+    DexMethod originalConstructorReference = constructors.iterator().next().method;
+    DexMethod newConstructorReference =
         appView.dexItemFactory().createMethod(target.type, newProto, dexItemFactory.initMethodName);
-    SynthesizedCode synthesizedCode =
-        new SynthesizedCode(
-            callerPosition ->
-                new ConstructorEntryPoint(
-                    typeConstructors.values(), newConstructor, callerPosition));
-    DexEncodedMethod newMethod =
+    ConstructorEntryPointSynthesizedCode synthesizedCode =
+        new ConstructorEntryPointSynthesizedCode(
+            typeConstructorClassMap,
+            newConstructorReference,
+            classIdField,
+            originalConstructorReference);
+    DexEncodedMethod newConstructor =
         new DexEncodedMethod(
-            newConstructor,
+            newConstructorReference,
             getAccessFlags(),
             DexAnnotationSet.empty(),
             ParameterAnnotationsList.empty(),
-            synthesizedCode);
+            synthesizedCode,
+            classFileVersion,
+            true);
 
-    // Map each old constructor to the newly synthesized constructor in the graph lens.
-    int constructorId = 0;
-    for (DexEncodedMethod constructor : constructors) {
-      lensBuilder.mapConstructor(constructor.method, newMethod.method, constructorId++);
-    }
-
-    target.addDirectMethod(newMethod);
-  }
-
-  /**
-   * The constructor does not conflict with any other constructors. Add the constructor (if any) to
-   * the target directly.
-   */
-  void mergeTrivial(HorizontalClassMergerGraphLens.Builder lensBuilder) {
-    assert constructors.size() <= 1;
-
-    if (!constructors.isEmpty()) {
-      DexEncodedMethod constructor = constructors.iterator().next();
-
-      // Only move the constructor if it is not already in the target type.
-      if (constructor.holder() != target.type) {
-        DexEncodedMethod newConstructor =
-            constructor.toRenamedHolderMethod(target.type, dexItemFactory);
-        target.addDirectMethod(constructor);
-        lensBuilder.moveConstructor(constructor.method, newConstructor.method);
+    if (isTrivialMerge()) {
+      // The constructor does not require the additional argument, just map it like a regular
+      // method.
+      lensBuilder.mapMethod(constructors.iterator().next().method, newConstructorReference);
+    } else {
+      // Map each old constructor to the newly synthesized constructor in the graph lens.
+      for (DexEncodedMethod oldConstructor : constructors) {
+        lensBuilder.mapMergedConstructor(
+            oldConstructor.method,
+            newConstructorReference,
+            classIdentifiers.getInt(oldConstructor.getHolderType()));
       }
     }
-  }
+    // Map the first constructor to the newly synthesized constructor.
+    lensBuilder.recordExtraOriginalSignature(originalConstructorReference, newConstructorReference);
 
-  public void merge(HorizontalClassMergerGraphLens.Builder lensBuilder) {
-    if (constructors.size() <= 1) {
-      mergeTrivial(lensBuilder);
-    } else {
-      mergeMany(lensBuilder);
-    }
+    target.addDirectMethod(newConstructor);
+
+    fieldAccessChangesBuilder.fieldWrittenByMethod(classIdField, newConstructorReference);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 60949e5..f7ac858 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -10,11 +10,13 @@
 import com.android.tools.r8.horizontalclassmerging.policies.NoFields;
 import com.android.tools.r8.horizontalclassmerging.policies.NoInterfaces;
 import com.android.tools.r8.horizontalclassmerging.policies.NoInternalUtilityClasses;
-import com.android.tools.r8.horizontalclassmerging.policies.NoOverlappingConstructors;
+import com.android.tools.r8.horizontalclassmerging.policies.NoRuntimeTypeChecks;
 import com.android.tools.r8.horizontalclassmerging.policies.NoStaticClassInitializer;
 import com.android.tools.r8.horizontalclassmerging.policies.NotEntryPoint;
 import com.android.tools.r8.horizontalclassmerging.policies.SameParentClass;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.ClassMergingEnqueuerExtension;
+import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
 import com.android.tools.r8.shaking.MainDexClasses;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
@@ -25,26 +27,25 @@
 import java.util.Map;
 
 public class HorizontalClassMerger {
-
   private final AppView<AppInfoWithLiveness> appView;
-  private final MainDexClasses mainDexClasses;
-
   private final PolicyExecutor policyExecutor;
 
   public HorizontalClassMerger(
-      AppView<AppInfoWithLiveness> appView, MainDexClasses mainDexClasses) {
+      AppView<AppInfoWithLiveness> appView,
+      MainDexClasses mainDexClasses,
+      ClassMergingEnqueuerExtension classMergingEnqueuerExtension) {
     this.appView = appView;
-    this.mainDexClasses = mainDexClasses;
 
     List<Policy> policies =
         ImmutableList.of(
             new NoFields(),
+            // TODO(b/166071504): Allow merging of classes that implement interfaces.
             new NoInterfaces(),
             new NoStaticClassInitializer(),
+            new NoRuntimeTypeChecks(classMergingEnqueuerExtension),
             new NotEntryPoint(appView.dexItemFactory()),
             new NoInternalUtilityClasses(appView.dexItemFactory()),
-            new SameParentClass(),
-            new NoOverlappingConstructors()
+            new SameParentClass()
             // TODO: add policies
             );
 
@@ -76,6 +77,8 @@
     Map<DexType, DexType> mergedClasses = new IdentityHashMap<>();
     HorizontalClassMergerGraphLens.Builder lensBuilder =
         new HorizontalClassMergerGraphLens.Builder();
+    FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder =
+        new FieldAccessInfoCollectionModifier.Builder();
 
     // TODO(b/166577694): Replace Collection<DexProgramClass> with MergeGroup
     for (Collection<DexProgramClass> group : groups) {
@@ -88,12 +91,14 @@
         mergedClasses.put(clazz.type, target.type);
       }
 
-      ClassMerger merger = new ClassMerger(appView, lensBuilder, target, group);
+      ClassMerger merger =
+          new ClassMerger(appView, lensBuilder, fieldAccessChangesBuilder, target, group);
       merger.mergeGroup();
     }
 
     HorizontalClassMergerGraphLens lens =
-        new TreeFixer(appView, lensBuilder, mergedClasses).fixupTypeReferences();
+        new TreeFixer(appView, lensBuilder, fieldAccessChangesBuilder, mergedClasses)
+            .fixupTypeReferences();
     return lens;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index 451e550..4389282 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -10,6 +10,8 @@
 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.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.ExtraConstantIntParameter;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import java.util.Collections;
@@ -20,14 +22,18 @@
 
 public class HorizontalClassMergerGraphLens extends NestedGraphLens {
   private final AppView<?> appView;
+  private final Map<DexMethod, Integer> constructorIds;
+  private final Map<DexMethod, DexMethod> originalConstructorSignatures;
 
   private HorizontalClassMergerGraphLens(
       AppView<?> appView,
+      Map<DexMethod, Integer> constructorIds,
       Map<DexType, DexType> typeMap,
       Map<DexField, DexField> fieldMap,
       Map<DexMethod, DexMethod> methodMap,
       BiMap<DexField, DexField> originalFieldSignatures,
       BiMap<DexMethod, DexMethod> originalMethodSignatures,
+      Map<DexMethod, DexMethod> originalConstructorSignatures,
       GraphLens previousLens) {
     super(
         typeMap,
@@ -38,12 +44,44 @@
         previousLens,
         appView.dexItemFactory());
     this.appView = appView;
+    this.constructorIds = constructorIds;
+    this.originalConstructorSignatures = originalConstructorSignatures;
+  }
+
+  @Override
+  public DexMethod getOriginalMethodSignature(DexMethod method) {
+    DexMethod originalConstructor = originalConstructorSignatures.get(method);
+    if (originalConstructor == null) {
+      return super.getOriginalMethodSignature(method);
+    }
+    return previousLens.getOriginalMethodSignature(originalConstructor);
   }
 
   public HorizontallyMergedClasses getHorizontallyMergedClasses() {
     return new HorizontallyMergedClasses(this.typeMap);
   }
 
+  /**
+   * If an overloaded constructor is requested, add the constructor id as a parameter to the
+   * constructor. Otherwise return the lookup on the underlying graph lens.
+   */
+  @Override
+  public GraphLensLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
+    Integer constructorId = constructorIds.get(method);
+    GraphLensLookupResult lookup = super.lookupMethod(method, context, type);
+    if (constructorId != null) {
+      DexMethod newMethod = lookup.getMethod();
+      return new GraphLensLookupResult(
+          newMethod,
+          mapInvocationType(newMethod, method, lookup.getType()),
+          lookup
+              .getPrototypeChanges()
+              .withExtraParameter(new ExtraConstantIntParameter(constructorId)));
+    } else {
+      return lookup;
+    }
+  }
+
   public static class Builder {
     protected final BiMap<DexField, DexField> fieldMap = HashBiMap.create();
     protected final Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
@@ -51,6 +89,7 @@
         new IdentityHashMap<>();
 
     private final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
+    private final Map<DexMethod, DexMethod> extraOriginalMethodSignatures = new IdentityHashMap<>();
 
     private final Map<DexMethod, Integer> constructorIds = new IdentityHashMap<>();
 
@@ -64,11 +103,13 @@
         BiMap<DexField, DexField> originalFieldSignatures = fieldMap.inverse();
         return new HorizontalClassMergerGraphLens(
             appView,
+            constructorIds,
             mergedClasses,
             fieldMap,
             methodMap,
             originalFieldSignatures,
             originalMethodSignatures,
+            extraOriginalMethodSignatures,
             appView.graphLens());
       }
     }
@@ -76,16 +117,23 @@
     /** Bidirectional mapping from one method to another. */
     public Builder moveMethod(DexMethod from, DexMethod to) {
       mapMethod(from, to);
-      originalMethodSignatures.put(to, from);
+      recordOriginalSignature(from, to);
       return this;
     }
 
-    /**
-     * Unidirectional mapping from one method to another. This seems to only be used by synthesized
-     * constructors so is private for now. See {@link Builder#moveConstructor(DexMethod,
-     * DexMethod)}.
-     */
-    private Builder mapMethod(DexMethod from, DexMethod to) {
+    public Builder recordOriginalSignature(DexMethod from, DexMethod to) {
+      originalMethodSignatures.forcePut(to, originalMethodSignatures.getOrDefault(from, from));
+      return this;
+    }
+
+    /** Unidirectional mapping from one method to another. */
+    public Builder recordExtraOriginalSignature(DexMethod from, DexMethod to) {
+      extraOriginalMethodSignatures.put(to, extraOriginalMethodSignatures.getOrDefault(from, from));
+      return this;
+    }
+
+    /** Unidirectional mapping from one method to another. */
+    public Builder mapMethod(DexMethod from, DexMethod to) {
       for (DexMethod existingFrom :
           completeInverseMethodMap.getOrDefault(from, Collections.emptySet())) {
         methodMap.put(existingFrom, to);
@@ -100,6 +148,10 @@
       return this;
     }
 
+    public boolean hasExtraSignatureMappingFor(DexMethod method) {
+      return extraOriginalMethodSignatures.containsKey(method);
+    }
+
     public boolean hasOriginalSignatureMappingFor(DexMethod method) {
       return originalMethodSignatures.containsKey(method);
     }
@@ -112,18 +164,10 @@
      * @param constructorId The id that must be appended to the constructor call to ensure the
      *     correct constructor is called.
      */
-    public Builder mapConstructor(DexMethod from, DexMethod to, int constructorId) {
+    public Builder mapMergedConstructor(DexMethod from, DexMethod to, int constructorId) {
       mapMethod(from, to);
       constructorIds.put(from, constructorId);
       return this;
     }
-
-    /**
-     * Bidirectional mapping from one constructor to another. When a single constructor is simply
-     * moved from one class to another, we can uniquely map the new constructor back to the old one.
-     */
-    public Builder moveConstructor(DexMethod from, DexMethod to) {
-      return moveMethod(from, to);
-    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index cd2cabf..7d0d5c0 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.shaking.AnnotationFixer;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
 import com.android.tools.r8.utils.OptionalBool;
 import java.util.IdentityHashMap;
 import java.util.List;
@@ -30,14 +31,17 @@
   private final Map<DexType, DexType> mergedClasses;
   private final Map<DexProto, DexProto> protoFixupCache = new IdentityHashMap<>();
   private final HorizontalClassMergerGraphLens.Builder lensBuilder;
+  private final FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder;
   private final AppView<AppInfoWithLiveness> appView;
 
   public TreeFixer(
       AppView<AppInfoWithLiveness> appView,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
+      FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
       Map<DexType, DexType> mergedClasses) {
     this.mergedClasses = mergedClasses;
     this.lensBuilder = lensBuilder;
+    this.fieldAccessChangesBuilder = fieldAccessChangesBuilder;
     this.appView = appView;
   }
 
@@ -50,6 +54,8 @@
     }
     HorizontalClassMergerGraphLens lens = lensBuilder.build(appView, mergedClasses);
 
+    fieldAccessChangesBuilder.build(this::fixupMethod).modify(appView);
+
     if (lens != null) {
       new AnnotationFixer(lens).run(appView.appInfo().classes());
     }
@@ -63,7 +69,15 @@
       return method;
     }
 
-    lensBuilder.moveMethod(methodReference, newMethodReference);
+    // If the method is a synthesized method, then don't record the original signature.
+    if ((method.getCode() instanceof ConstructorEntryPointSynthesizedCode)) {
+      assert lensBuilder.hasExtraSignatureMappingFor(methodReference);
+      lensBuilder.recordExtraOriginalSignature(methodReference, newMethodReference);
+      lensBuilder.mapMethod(methodReference, newMethodReference);
+    } else {
+      lensBuilder.moveMethod(methodReference, newMethodReference);
+    }
+
     DexEncodedMethod newMethod = method.toTypeSubstitutedMethod(newMethodReference);
     if (newMethod.isNonPrivateVirtualMethod()) {
       // Since we changed the return type or one of the parameters, this method cannot be a
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java
new file mode 100644
index 0000000..dff9967
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java
@@ -0,0 +1,24 @@
+// 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.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
+import com.android.tools.r8.shaking.ClassMergingEnqueuerExtension;
+
+public class NoRuntimeTypeChecks extends SingleClassPolicy {
+  private final ClassMergingEnqueuerExtension classMergingEnqueuerExtension;
+
+  public NoRuntimeTypeChecks(ClassMergingEnqueuerExtension classMergingEnqueuerExtension) {
+    this.classMergingEnqueuerExtension = classMergingEnqueuerExtension;
+  }
+
+  @Override
+  public boolean canMerge(DexProgramClass clazz) {
+    // We currently assume we only merge classes that implement the same set of interfaces.
+    return !(this.classMergingEnqueuerExtension.isCheckCastType(clazz)
+        || this.classMergingEnqueuerExtension.isInstanceOfType(clazz));
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 7dc7daa..3f40cba 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -274,7 +274,7 @@
   // the 'addForward' call-back is called with the target of the forward.
   private void resolveForwardForSignature(
       DexClass clazz, DexMethod method, BiConsumer<DexClass, DexEncodedMethod> addForward) {
-    // Resolve the default method at base type as the symbolic holder at call sites is not known.
+    // Resolve the default method with base type as the symbolic holder as call sites are not known.
     // The dispatch target is then looked up from the possible "instance" class.
     // Doing so can cause an invalid invoke to become valid (at runtime resolution at a subtype
     // might have failed which is hidden by the insertion of the forward method). However, not doing
@@ -298,7 +298,7 @@
 
     DexEncodedMethod target = virtualDispatchTarget.getDefinition();
     DexClass targetHolder = virtualDispatchTarget.getHolder();
-    // Don-t forward if the target is explicitly marked as 'dont-rewrite'
+    // Don't forward if the target is explicitly marked as 'dont-rewrite'
     if (dontRewrite(targetHolder, target)) {
       return;
     }
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 2167964..6f46dc6 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
@@ -26,7 +26,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
@@ -1032,7 +1031,8 @@
   }
 
   private Map<DexType, DexProgramClass> processInterfaces(Builder<?> builder, Flavor flavour) {
-    NestedGraphLens.Builder graphLensBuilder = InterfaceProcessorNestedGraphLens.builder();
+    InterfaceProcessorNestedGraphLens.Builder graphLensBuilder =
+        InterfaceProcessorNestedGraphLens.builder();
     InterfaceProcessor processor = new InterfaceProcessor(appView, this);
     for (DexProgramClass clazz : builder.getProgramClasses()) {
       if (shouldProcess(clazz, flavour, true)) {
@@ -1215,21 +1215,29 @@
   }
 
   public static void reportDependencyEdge(
-      DexProgramClass dependent, DexClass dependency, InternalOptions options) {
+      DexClass dependent, DexClass dependency, InternalOptions options) {
+    assert !dependent.isLibraryClass();
     assert !dependency.isLibraryClass();
     DesugarGraphConsumer consumer = options.desugarGraphConsumer;
     if (consumer != null) {
       Origin dependencyOrigin = dependency.getOrigin();
-      java.util.Collection<DexProgramClass> dependents = dependent.getSynthesizedFrom();
+      java.util.Collection<DexProgramClass> dependents =
+          dependent.isProgramClass() ? dependent.asProgramClass().getSynthesizedFrom() : null;
       if (dependents == null || dependents.isEmpty()) {
-        dependents = Collections.singletonList(dependent);
-      }
-      for (DexProgramClass clazz : dependents) {
-        Origin dependentOrigin = clazz.getOrigin();
-        if (dependentOrigin != dependencyOrigin) {
-          consumer.accept(dependentOrigin, dependencyOrigin);
+        reportDependencyEdge(consumer, dependencyOrigin, dependent);
+      } else {
+        for (DexClass clazz : dependents) {
+          reportDependencyEdge(consumer, dependencyOrigin, clazz);
         }
       }
     }
   }
+
+  private static void reportDependencyEdge(
+      DesugarGraphConsumer consumer, Origin dependencyOrigin, DexClass clazz) {
+    Origin dependentOrigin = clazz.getOrigin();
+    if (dependentOrigin != dependencyOrigin) {
+      consumer.accept(dependentOrigin, dependencyOrigin);
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index f400369..6864127 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -36,6 +36,7 @@
 import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.utils.Pair;
 import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -52,7 +53,7 @@
 // a companion class. Removes bridge default methods.
 //
 // Also moves static interface methods into a companion class.
-final class InterfaceProcessor {
+public final class InterfaceProcessor {
 
   private final AppView<?> appView;
   private final InterfaceMethodRewriter rewriter;
@@ -66,7 +67,7 @@
     this.rewriter = rewriter;
   }
 
-  void process(DexProgramClass iface, NestedGraphLens.Builder graphLensBuilder) {
+  void process(DexProgramClass iface, InterfaceProcessorNestedGraphLens.Builder graphLensBuilder) {
     assert iface.isInterface();
     // The list of methods to be created in companion class.
     List<DexEncodedMethod> companionMethods = new ArrayList<>();
@@ -105,7 +106,7 @@
         implMethod.copyMetadata(virtual);
         virtual.setDefaultInterfaceMethodImplementation(implMethod);
         companionMethods.add(implMethod);
-        graphLensBuilder.move(virtual.method, implMethod.method);
+        graphLensBuilder.recordOrigin(implMethod.method, virtual.method);
       }
 
       // Remove bridge methods.
@@ -385,12 +386,15 @@
   // are to static companion methods.
   public static class InterfaceProcessorNestedGraphLens extends NestedGraphLens {
 
+    private BiMap<DexMethod, DexMethod> extraOriginalMethodSignatures;
+
     public InterfaceProcessorNestedGraphLens(
         Map<DexType, DexType> typeMap,
         Map<DexMethod, DexMethod> methodMap,
         Map<DexField, DexField> fieldMap,
         BiMap<DexField, DexField> originalFieldSignatures,
         BiMap<DexMethod, DexMethod> originalMethodSignatures,
+        BiMap<DexMethod, DexMethod> extraOriginalMethodSignatures,
         GraphLens previousLens,
         DexItemFactory dexItemFactory) {
       super(
@@ -401,6 +405,54 @@
           originalMethodSignatures,
           previousLens,
           dexItemFactory);
+      this.extraOriginalMethodSignatures = extraOriginalMethodSignatures;
+    }
+
+    public static InterfaceProcessorNestedGraphLens find(GraphLens lens) {
+      if (lens.isInterfaceProcessorLens()) {
+        return lens.asInterfaceProcessorLens();
+      }
+      if (lens.isIdentityLens()) {
+        return null;
+      }
+      if (lens.isGraphLensWithPrevious()) {
+        return find(lens.asGraphLensWithPrevious().getPrevious());
+      }
+      assert false;
+      return null;
+    }
+
+    public void toggleMappingToExtraMethods() {
+      BiMap<DexMethod, DexMethod> tmp = originalMethodSignatures;
+      this.originalMethodSignatures = extraOriginalMethodSignatures;
+      this.extraOriginalMethodSignatures = tmp;
+    }
+
+    @Override
+    public boolean isInterfaceProcessorLens() {
+      return true;
+    }
+
+    @Override
+    public InterfaceProcessorNestedGraphLens asInterfaceProcessorLens() {
+      return this;
+    }
+
+    @Override
+    public boolean isLegitimateToHaveEmptyMappings() {
+      return true;
+    }
+
+    @Override
+    public DexMethod getOriginalMethodSignature(DexMethod method) {
+      DexMethod originalMethod = extraOriginalMethodSignatures.get(method);
+      if (originalMethod == null) {
+        originalMethod =
+            originalMethodSignatures != null
+                ? originalMethodSignatures.getOrDefault(method, method)
+                : method;
+      }
+      return previousLens.getOriginalMethodSignature(originalMethod);
     }
 
     @Override
@@ -408,14 +460,26 @@
       return Type.STATIC;
     }
 
-    public static GraphLens.Builder builder() {
+    public static Builder builder() {
       return new Builder();
     }
 
     public static class Builder extends NestedGraphLens.Builder {
+
+      private final BiMap<DexMethod, DexMethod> extraOriginalMethodSignatures = HashBiMap.create();
+
+      public void recordOrigin(DexMethod method, DexMethod origin) {
+        if (method == origin) {
+          return;
+        }
+        extraOriginalMethodSignatures.put(method, origin);
+      }
+
       @Override
       public GraphLens build(DexItemFactory dexItemFactory, GraphLens previousLens) {
-        if (originalFieldSignatures.isEmpty() && originalMethodSignatures.isEmpty()) {
+        if (originalFieldSignatures.isEmpty()
+            && originalMethodSignatures.isEmpty()
+            && extraOriginalMethodSignatures.isEmpty()) {
           return previousLens;
         }
         return new InterfaceProcessorNestedGraphLens(
@@ -424,6 +488,7 @@
             fieldMap,
             originalFieldSignatures,
             originalMethodSignatures,
+            extraOriginalMethodSignatures,
             previousLens,
             dexItemFactory);
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index bc3c320..d28ee6d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -16,6 +16,8 @@
 import com.android.tools.r8.cf.code.CfCmp;
 import com.android.tools.r8.cf.code.CfConstNumber;
 import com.android.tools.r8.cf.code.CfConstString;
+import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
 import com.android.tools.r8.cf.code.CfGoto;
 import com.android.tools.r8.cf.code.CfIf;
 import com.android.tools.r8.cf.code.CfIfCmp;
@@ -45,6 +47,8 @@
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
+import java.util.Arrays;
 
 public final class BackportedMethods {
 
@@ -52,6 +56,7 @@
     factory.createSynthesizedType("Ljava/lang/ArithmeticException;");
     factory.createSynthesizedType("Ljava/lang/AssertionError;");
     factory.createSynthesizedType("Ljava/lang/Double;");
+    factory.createSynthesizedType("Ljava/lang/Exception;");
     factory.createSynthesizedType("Ljava/lang/ExceptionInInitializerError;");
     factory.createSynthesizedType("Ljava/lang/Float;");
     factory.createSynthesizedType("Ljava/lang/IllegalAccessException;");
@@ -97,8 +102,10 @@
     factory.createSynthesizedType("Ljava/util/stream/IntStream;");
     factory.createSynthesizedType("Ljava/util/stream/LongStream;");
     factory.createSynthesizedType("Ljava/util/stream/Stream;");
+    factory.createSynthesizedType("[Ljava/lang/CharSequence;");
     factory.createSynthesizedType("[Ljava/lang/Class;");
     factory.createSynthesizedType("[Ljava/lang/Object;");
+    factory.createSynthesizedType("[Ljava/util/Map$Entry;");
   }
 
   public static CfCode BooleanMethods_compare(InternalOptions options, DexMethod method) {
@@ -119,13 +126,37 @@
             new CfConstNumber(0, ValueType.INT),
             new CfGoto(label3),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 0),
             new CfIf(If.Type.EQ, ValueType.INT, label2),
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label3),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(-1, ValueType.INT),
             label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label4),
         ImmutableList.of(),
@@ -148,8 +179,18 @@
             new CfConstNumber(1231, ValueType.INT),
             new CfGoto(label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.intType)}),
+                Arrays.asList()),
             new CfConstNumber(1237, ValueType.INT),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.intType)}),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label3),
         ImmutableList.of(),
@@ -281,6 +322,16 @@
             new CfConstNumber(0, ValueType.INT),
             new CfReturn(ValueType.INT),
             label4,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.charSequenceType),
+                      FrameType.initialized(options.itemFactory.charSequenceType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             new CfStore(ValueType.INT, 4),
             label5,
@@ -298,6 +349,18 @@
                 false),
             new CfStore(ValueType.INT, 5),
             label6,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.charSequenceType),
+                      FrameType.initialized(options.itemFactory.charSequenceType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 4),
             new CfLoad(ValueType.INT, 5),
             new CfIfCmp(If.Type.GE, ValueType.INT, label12),
@@ -335,9 +398,31 @@
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
             new CfReturn(ValueType.INT),
             label11,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.charSequenceType),
+                      FrameType.initialized(options.itemFactory.charSequenceType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfIinc(4, 1),
             new CfGoto(label6),
             label12,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.charSequenceType),
+                      FrameType.initialized(options.itemFactory.charSequenceType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 2),
             new CfLoad(ValueType.INT, 3),
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
@@ -441,6 +526,14 @@
                 true),
             new CfGoto(label11),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.throwableType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 1),
             new CfInvoke(
                 182,
@@ -482,6 +575,16 @@
             label4,
             new CfGoto(label11),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.throwableType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList(
+                    FrameType.initialized(
+                        options.itemFactory.createType("Ljava/lang/Exception;")))),
             new CfStore(ValueType.OBJECT, 2),
             label6,
             new CfNew(options.itemFactory.createType("Ljava/lang/AssertionError;")),
@@ -540,6 +643,14 @@
                 false),
             new CfThrow(),
             label7,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.throwableType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.throwableType))),
             new CfStore(ValueType.OBJECT, 2),
             label8,
             new CfNew(options.itemFactory.createType("Ljava/lang/AssertionError;")),
@@ -598,6 +709,17 @@
                 false),
             new CfThrow(),
             label9,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.throwableType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList(
+                    FrameType.initialized(
+                        options.itemFactory.createType(
+                            "Ljava/lang/reflect/InvocationTargetException;")))),
             new CfStore(ValueType.OBJECT, 2),
             label10,
             new CfLoad(ValueType.OBJECT, 2),
@@ -610,8 +732,24 @@
                 false),
             new CfThrow(),
             label11,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.throwableType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfGoto(label16),
             label12,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.throwableType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.throwableType))),
             new CfStore(ValueType.OBJECT, 2),
             label13,
             new CfLoad(ValueType.OBJECT, 0),
@@ -619,10 +757,36 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfGoto(label15),
             label14,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.throwableType),
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.throwableType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 2),
             label15,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.throwableType),
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.throwableType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.throwableType))),
             new CfThrow(),
             label16,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.throwableType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfReturnVoid(),
             label17),
         ImmutableList.of(
@@ -706,6 +870,18 @@
             new CfConstNumber(0, ValueType.INT),
             new CfStore(ValueType.INT, 4),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/ArrayList;")),
+                      FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 4),
             new CfLoad(ValueType.INT, 3),
             new CfIfCmp(If.Type.GE, ValueType.INT, label5),
@@ -737,6 +913,14 @@
             new CfIinc(4, 1),
             new CfGoto(label2),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/ArrayList;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 1),
             new CfInvoke(
                 184,
@@ -844,6 +1028,19 @@
             new CfConstNumber(0, ValueType.INT),
             new CfStore(ValueType.INT, 4),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("[Ljava/util/Map$Entry;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/HashMap;")),
+                      FrameType.initialized(
+                          options.itemFactory.createType("[Ljava/util/Map$Entry;")),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 4),
             new CfLoad(ValueType.INT, 3),
             new CfIfCmp(If.Type.GE, ValueType.INT, label8),
@@ -949,9 +1146,31 @@
                 false),
             new CfThrow(),
             label7,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("[Ljava/util/Map$Entry;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/HashMap;")),
+                      FrameType.initialized(
+                          options.itemFactory.createType("[Ljava/util/Map$Entry;")),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfIinc(4, 1),
             new CfGoto(label2),
             label8,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("[Ljava/util/Map$Entry;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/HashMap;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 1),
             new CfInvoke(
                 184,
@@ -1005,6 +1224,17 @@
             new CfConstNumber(0, ValueType.INT),
             new CfStore(ValueType.INT, 4),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/HashSet;")),
+                      FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 4),
             new CfLoad(ValueType.INT, 3),
             new CfIfCmp(If.Type.GE, ValueType.INT, label6),
@@ -1079,9 +1309,28 @@
                 false),
             new CfThrow(),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/HashSet;")),
+                      FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfIinc(4, 1),
             new CfGoto(label2),
             label6,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/HashSet;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 1),
             new CfInvoke(
                 184,
@@ -1143,6 +1392,17 @@
                 true),
             new CfStore(ValueType.OBJECT, 2),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/Collection;")),
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/ArrayList;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/Iterator;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 2),
             new CfInvoke(
                 185,
@@ -1184,6 +1444,15 @@
             label4,
             new CfGoto(label2),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/Collection;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/ArrayList;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 1),
             new CfInvoke(
                 184,
@@ -1256,6 +1525,15 @@
                 true),
             new CfStore(ValueType.OBJECT, 2),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/Map;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/HashMap;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/Iterator;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 2),
             new CfInvoke(
                 185,
@@ -1326,6 +1604,14 @@
             label7,
             new CfGoto(label2),
             label8,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/Map;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/HashMap;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 1),
             new CfInvoke(
                 184,
@@ -1387,6 +1673,16 @@
                 true),
             new CfStore(ValueType.OBJECT, 2),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/Collection;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/HashSet;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/Iterator;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 2),
             new CfInvoke(
                 185,
@@ -1428,6 +1724,15 @@
             label4,
             new CfGoto(label2),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/Collection;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/HashSet;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 1),
             new CfInvoke(
                 184,
@@ -1601,8 +1906,18 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.doubleType)}),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.doubleType)}),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label3),
         ImmutableList.of(),
@@ -1643,8 +1958,18 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.floatType)}),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.floatType)}),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label3),
         ImmutableList.of(),
@@ -1669,14 +1994,38 @@
             new CfConstNumber(0, ValueType.INT),
             new CfGoto(label3),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 0),
             new CfLoad(ValueType.INT, 1),
             new CfIfCmp(If.Type.GE, ValueType.INT, label2),
             new CfConstNumber(-1, ValueType.INT),
             new CfGoto(label3),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(1, ValueType.INT),
             label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label4),
         ImmutableList.of(),
@@ -1832,6 +2181,14 @@
                 false),
             new CfStore(ValueType.OBJECT, 0),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfLoad(ValueType.INT, 1),
             new CfInvoke(
@@ -1927,6 +2284,15 @@
                 false),
             new CfThrow(),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 2),
             new CfNumberConversion(NumericType.LONG, NumericType.INT),
             new CfReturn(ValueType.INT),
@@ -2134,9 +2500,27 @@
             new CfConstNumber(0, ValueType.LONG),
             new CfReturn(ValueType.LONG),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4, 6},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(1, ValueType.LONG),
             new CfReturn(ValueType.LONG),
             label6,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 0),
             new CfConstNumber(0, ValueType.LONG),
             new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
@@ -2147,6 +2531,14 @@
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Div, NumericType.LONG),
             new CfReturn(ValueType.LONG),
             label8,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 0),
             new CfConstNumber(1, ValueType.INT),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Ushr, NumericType.LONG),
@@ -2181,8 +2573,34 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label14),
             label13,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4, 6, 8, 10},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.longType))),
             new CfConstNumber(0, ValueType.INT),
             label14,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4, 6, 8, 10},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList(
+                    FrameType.initialized(options.itemFactory.longType),
+                    FrameType.initialized(options.itemFactory.intType))),
             new CfNumberConversion(NumericType.INT, NumericType.LONG),
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.LONG),
             new CfReturn(ValueType.LONG),
@@ -2295,6 +2713,15 @@
                 false),
             new CfThrow(),
             label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 1),
             new CfConstNumber(2, ValueType.INT),
             new CfIfCmp(If.Type.LT, ValueType.INT, label4),
@@ -2302,6 +2729,15 @@
             new CfConstNumber(36, ValueType.INT),
             new CfIfCmp(If.Type.LE, ValueType.INT, label5),
             label4,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfNew(options.itemFactory.createType("Ljava/lang/NumberFormatException;")),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfConstString(options.itemFactory.createString("illegal radix: ")),
@@ -2332,6 +2768,15 @@
                 false),
             new CfThrow(),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(-1, ValueType.LONG),
             new CfLoad(ValueType.INT, 1),
             new CfNumberConversion(NumericType.INT, NumericType.LONG),
@@ -2365,8 +2810,28 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label8),
             label7,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label8,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfStore(ValueType.INT, 5),
             label9,
             new CfConstNumber(0, ValueType.LONG),
@@ -2375,6 +2840,19 @@
             new CfLoad(ValueType.INT, 5),
             new CfStore(ValueType.INT, 8),
             label11,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 5, 6, 8},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 8),
             new CfLoad(ValueType.INT, 2),
             new CfIfCmp(If.Type.GE, ValueType.INT, label20),
@@ -2419,6 +2897,20 @@
                 false),
             new CfThrow(),
             label15,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 5, 6, 8, 9},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 6),
             new CfConstNumber(0, ValueType.LONG),
             new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
@@ -2449,6 +2941,20 @@
             new CfNumberConversion(NumericType.LONG, NumericType.INT),
             new CfIfCmp(If.Type.LE, ValueType.INT, label18),
             label17,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 5, 6, 8, 9},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfNew(options.itemFactory.createType("Ljava/lang/NumberFormatException;")),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfConstString(options.itemFactory.createString("Too large for unsigned long: ")),
@@ -2471,6 +2977,20 @@
                 false),
             new CfThrow(),
             label18,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 5, 6, 8, 9},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 6),
             new CfLoad(ValueType.INT, 1),
             new CfNumberConversion(NumericType.INT, NumericType.LONG),
@@ -2483,6 +3003,18 @@
             new CfIinc(8, 1),
             new CfGoto(label11),
             label20,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 5, 6},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 6),
             new CfReturn(ValueType.LONG),
             label21),
@@ -2536,11 +3068,29 @@
             new CfLoad(ValueType.LONG, 0),
             new CfReturn(ValueType.LONG),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4, 6},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 0),
             new CfLoad(ValueType.LONG, 2),
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.LONG),
             new CfReturn(ValueType.LONG),
             label6,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 0),
             new CfConstNumber(0, ValueType.LONG),
             new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
@@ -2551,6 +3101,14 @@
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Rem, NumericType.LONG),
             new CfReturn(ValueType.LONG),
             label8,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 0),
             new CfConstNumber(1, ValueType.INT),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Ushr, NumericType.LONG),
@@ -2585,8 +3143,34 @@
             new CfLoad(ValueType.LONG, 2),
             new CfGoto(label14),
             label13,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4, 6, 8, 10},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.longType))),
             new CfConstNumber(0, ValueType.LONG),
             label14,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4, 6, 8, 10},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList(
+                    FrameType.initialized(options.itemFactory.longType),
+                    FrameType.initialized(options.itemFactory.longType))),
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.LONG),
             new CfReturn(ValueType.LONG),
             label15),
@@ -2664,6 +3248,14 @@
             new CfConstString(options.itemFactory.createString("0")),
             new CfReturn(ValueType.OBJECT),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 0),
             new CfConstNumber(0, ValueType.LONG),
             new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
@@ -2683,6 +3275,14 @@
                 false),
             new CfReturn(ValueType.OBJECT),
             label4,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 2),
             new CfConstNumber(2, ValueType.INT),
             new CfIfCmp(If.Type.LT, ValueType.INT, label5),
@@ -2690,9 +3290,25 @@
             new CfConstNumber(36, ValueType.INT),
             new CfIfCmp(If.Type.LE, ValueType.INT, label6),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(10, ValueType.INT),
             new CfStore(ValueType.INT, 2),
             label6,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(64, ValueType.INT),
             new CfNewArray(options.itemFactory.charArrayType),
             new CfStore(ValueType.OBJECT, 3),
@@ -2724,6 +3340,18 @@
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
             new CfStore(ValueType.INT, 6),
             label11,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 3, 4, 5, 6},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.charArrayType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 3),
             new CfIinc(4, -1),
             new CfLoad(ValueType.INT, 4),
@@ -2756,6 +3384,16 @@
             label14,
             new CfGoto(label25),
             label15,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 3, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.charArrayType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 2),
             new CfConstNumber(1, ValueType.INT),
             new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.INT),
@@ -2773,6 +3411,16 @@
             label17,
             new CfGoto(label19),
             label18,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 3, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.charArrayType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 0),
             new CfLoad(ValueType.INT, 2),
             new CfNumberConversion(NumericType.INT, NumericType.LONG),
@@ -2788,6 +3436,17 @@
                 false),
             new CfStore(ValueType.LONG, 5),
             label19,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 3, 4, 5},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.charArrayType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 0),
             new CfLoad(ValueType.LONG, 5),
             new CfLoad(ValueType.INT, 2),
@@ -2817,6 +3476,18 @@
             new CfLoad(ValueType.LONG, 5),
             new CfStore(ValueType.LONG, 0),
             label22,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 3, 4, 5, 7},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.charArrayType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 0),
             new CfConstNumber(0, ValueType.LONG),
             new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
@@ -2850,6 +3521,16 @@
             new CfStore(ValueType.LONG, 0),
             new CfGoto(label22),
             label25,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 3, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.charArrayType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfNew(options.itemFactory.stringType),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfLoad(ValueType.OBJECT, 3),
@@ -2908,6 +3589,16 @@
             new CfLoad(ValueType.INT, 4),
             new CfReturn(ValueType.INT),
             label4,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfInvoke(
@@ -2953,8 +3644,26 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label3),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfLoad(ValueType.LONG, 0),
             new CfLoad(ValueType.LONG, 4),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
@@ -2964,14 +3673,43 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label5),
             label4,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfConstNumber(0, ValueType.INT),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList(
+                    FrameType.initialized(options.itemFactory.intType),
+                    FrameType.initialized(options.itemFactory.intType))),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Or, NumericType.INT),
             new CfIf(If.Type.EQ, ValueType.INT, label7),
             label6,
             new CfLoad(ValueType.LONG, 4),
             new CfReturn(ValueType.LONG),
             label7,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfInvoke(
@@ -3013,6 +3751,11 @@
                 false),
             new CfThrow(),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.intType)}),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 0),
             new CfConstNumber(1, ValueType.INT),
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
@@ -3049,6 +3792,11 @@
                 false),
             new CfThrow(),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.longType)}),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 0),
             new CfConstNumber(1, ValueType.LONG),
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.LONG),
@@ -3092,6 +3840,16 @@
             new CfLoad(ValueType.INT, 2),
             new CfReturn(ValueType.INT),
             label4,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(1, ValueType.INT),
             new CfLoad(ValueType.INT, 0),
             new CfLoad(ValueType.INT, 1),
@@ -3108,8 +3866,30 @@
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
             new CfGoto(label7),
             label6,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 2),
             label7,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label8),
         ImmutableList.of(),
@@ -3152,6 +3932,16 @@
             new CfLoad(ValueType.LONG, 4),
             new CfReturn(ValueType.LONG),
             label4,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4, 6},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(1, ValueType.LONG),
             new CfLoad(ValueType.LONG, 0),
             new CfLoad(ValueType.LONG, 2),
@@ -3170,8 +3960,30 @@
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.LONG),
             new CfGoto(label7),
             label6,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4, 6, 8},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 4),
             label7,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4, 6, 8},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.longType))),
             new CfReturn(ValueType.LONG),
             label8),
         ImmutableList.of(),
@@ -3232,6 +4044,15 @@
             new CfConstNumber(0, ValueType.INT),
             new CfReturn(ValueType.INT),
             label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(1, ValueType.INT),
             new CfLoad(ValueType.INT, 0),
             new CfLoad(ValueType.INT, 1),
@@ -3246,10 +4067,30 @@
             new CfLoad(ValueType.INT, 2),
             new CfGoto(label6),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 2),
             new CfLoad(ValueType.INT, 1),
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
             label6,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label7),
         ImmutableList.of(),
@@ -3284,6 +4125,15 @@
             new CfConstNumber(0, ValueType.LONG),
             new CfReturn(ValueType.LONG),
             label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(1, ValueType.LONG),
             new CfLoad(ValueType.LONG, 0),
             new CfLoad(ValueType.LONG, 2),
@@ -3300,10 +4150,30 @@
             new CfLoad(ValueType.LONG, 4),
             new CfGoto(label6),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4, 6},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 4),
             new CfLoad(ValueType.LONG, 2),
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.LONG),
             label6,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4, 6},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.longType))),
             new CfReturn(ValueType.LONG),
             label7),
         ImmutableList.of(),
@@ -3365,6 +4235,11 @@
                 false),
             new CfThrow(),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.intType)}),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 0),
             new CfConstNumber(1, ValueType.INT),
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
@@ -3401,6 +4276,11 @@
                 false),
             new CfThrow(),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.longType)}),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 0),
             new CfConstNumber(1, ValueType.LONG),
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.LONG),
@@ -3443,6 +4323,16 @@
             new CfLoad(ValueType.INT, 4),
             new CfReturn(ValueType.INT),
             label4,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfInvoke(
@@ -3540,6 +4430,15 @@
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Mul, NumericType.LONG),
             new CfReturn(ValueType.LONG),
             label7,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 4),
             new CfConstNumber(64, ValueType.INT),
             new CfIfCmp(If.Type.LT, ValueType.INT, label15),
@@ -3550,8 +4449,26 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label9),
             label8,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label9,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfLoad(ValueType.LONG, 2),
             new CfConstNumber(-9223372036854775808L, ValueType.LONG),
             new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
@@ -3559,8 +4476,28 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label11),
             label10,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfConstNumber(0, ValueType.INT),
             label11,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList(
+                    FrameType.initialized(options.itemFactory.intType),
+                    FrameType.initialized(options.itemFactory.intType))),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Or, NumericType.INT),
             new CfIf(If.Type.EQ, ValueType.INT, label15),
             label12,
@@ -3580,9 +4517,28 @@
             new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
             new CfIf(If.Type.NE, ValueType.INT, label15),
             label14,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4, 5},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 5),
             new CfReturn(ValueType.LONG),
             label15,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfInvoke(
@@ -3776,6 +4732,11 @@
                 false),
             new CfThrow(),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.intType)}),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 0),
             new CfNeg(NumericType.INT),
             new CfReturn(ValueType.INT),
@@ -3811,6 +4772,11 @@
                 false),
             new CfThrow(),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.longType)}),
+                Arrays.asList()),
             new CfLoad(ValueType.LONG, 0),
             new CfNeg(NumericType.LONG),
             new CfReturn(ValueType.LONG),
@@ -3904,6 +4870,16 @@
             new CfLoad(ValueType.INT, 4),
             new CfReturn(ValueType.INT),
             label4,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfInvoke(
@@ -3949,8 +4925,26 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label3),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfLoad(ValueType.LONG, 0),
             new CfLoad(ValueType.LONG, 4),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
@@ -3960,14 +4954,43 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label5),
             label4,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfConstNumber(0, ValueType.INT),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList(
+                    FrameType.initialized(options.itemFactory.intType),
+                    FrameType.initialized(options.itemFactory.intType))),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Or, NumericType.INT),
             new CfIf(If.Type.EQ, ValueType.INT, label7),
             label6,
             new CfLoad(ValueType.LONG, 4),
             new CfReturn(ValueType.LONG),
             label7,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.longType)
+                    }),
+                Arrays.asList()),
             new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfInvoke(
@@ -4016,6 +5039,14 @@
                 false),
             new CfThrow(),
             label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 2),
             new CfReturn(ValueType.INT),
             label4),
@@ -4047,6 +5078,15 @@
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
             new CfIfCmp(If.Type.LE, ValueType.INT, label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfNew(options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;")),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfNew(options.itemFactory.stringBuilderType),
@@ -4147,6 +5187,15 @@
                 false),
             new CfThrow(),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 0),
             new CfReturn(ValueType.INT),
             label3),
@@ -4174,6 +5223,15 @@
             new CfLoad(ValueType.INT, 2),
             new CfIfCmp(If.Type.LE, ValueType.INT, label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfNew(options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;")),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfNew(options.itemFactory.stringBuilderType),
@@ -4256,6 +5314,15 @@
                 false),
             new CfThrow(),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 0),
             new CfReturn(ValueType.INT),
             label3),
@@ -4280,6 +5347,14 @@
             new CfLoad(ValueType.INT, 1),
             new CfIfCmp(If.Type.LT, ValueType.INT, label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfNew(options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;")),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfNew(options.itemFactory.stringBuilderType),
@@ -4344,6 +5419,14 @@
                 false),
             new CfThrow(),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 0),
             new CfReturn(ValueType.INT),
             label3),
@@ -4368,6 +5451,16 @@
             new CfConstNumber(0, ValueType.INT),
             new CfGoto(label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/Comparator;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 2),
             new CfLoad(ValueType.OBJECT, 0),
             new CfLoad(ValueType.OBJECT, 1),
@@ -4382,6 +5475,16 @@
                     options.itemFactory.createString("compare")),
                 true),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/Comparator;"))
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label3),
         ImmutableList.of(),
@@ -4441,11 +5544,27 @@
             new CfConstNumber(1, ValueType.INT),
             new CfReturn(ValueType.INT),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfIf(If.Type.NE, ValueType.OBJECT, label2),
             new CfConstNumber(0, ValueType.INT),
             new CfReturn(ValueType.INT),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceOf(options.itemFactory.booleanArrayType),
             new CfIf(If.Type.EQ, ValueType.INT, label6),
@@ -4471,10 +5590,34 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label5),
             label4,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label6,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceOf(options.itemFactory.byteArrayType),
             new CfIf(If.Type.EQ, ValueType.INT, label10),
@@ -4500,10 +5643,34 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label9),
             label8,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label9,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label10,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceOf(options.itemFactory.charArrayType),
             new CfIf(If.Type.EQ, ValueType.INT, label14),
@@ -4529,10 +5696,34 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label13),
             label12,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label13,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label14,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceOf(options.itemFactory.doubleArrayType),
             new CfIf(If.Type.EQ, ValueType.INT, label18),
@@ -4558,10 +5749,34 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label17),
             label16,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label17,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label18,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceOf(options.itemFactory.floatArrayType),
             new CfIf(If.Type.EQ, ValueType.INT, label22),
@@ -4587,10 +5802,34 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label21),
             label20,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label21,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label22,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceOf(options.itemFactory.intArrayType),
             new CfIf(If.Type.EQ, ValueType.INT, label26),
@@ -4616,10 +5855,34 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label25),
             label24,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label25,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label26,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceOf(options.itemFactory.longArrayType),
             new CfIf(If.Type.EQ, ValueType.INT, label30),
@@ -4645,10 +5908,34 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label29),
             label28,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label29,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label30,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceOf(options.itemFactory.shortArrayType),
             new CfIf(If.Type.EQ, ValueType.INT, label34),
@@ -4674,10 +5961,34 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label33),
             label32,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label33,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label34,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceOf(options.itemFactory.createType("[Ljava/lang/Object;")),
             new CfIf(If.Type.EQ, ValueType.INT, label38),
@@ -4703,10 +6014,34 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label37),
             label36,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label37,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label38,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfLoad(ValueType.OBJECT, 1),
             new CfInvoke(
@@ -4752,11 +6087,35 @@
                 false),
             new CfIf(If.Type.EQ, ValueType.INT, label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label3),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label4),
         ImmutableList.of(),
@@ -4779,6 +6138,11 @@
             new CfConstNumber(0, ValueType.INT),
             new CfGoto(label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInvoke(
                 182,
@@ -4788,6 +6152,11 @@
                     options.itemFactory.createString("hashCode")),
                 false),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label3),
         ImmutableList.of(),
@@ -4810,8 +6179,18 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label3),
         ImmutableList.of(),
@@ -4834,8 +6213,18 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label3),
         ImmutableList.of(),
@@ -4858,6 +6247,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfReturn(ValueType.OBJECT),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 1),
             new CfConstString(options.itemFactory.createString("defaultObj")),
             new CfInvoke(
@@ -4893,6 +6290,15 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfReturn(ValueType.OBJECT),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/function/Supplier;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 1),
             new CfConstString(options.itemFactory.createString("supplier")),
             new CfInvoke(
@@ -4961,6 +6367,14 @@
                 false),
             new CfThrow(),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.stringType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfReturn(ValueType.OBJECT),
             label3),
@@ -5011,6 +6425,14 @@
             new CfLoad(ValueType.OBJECT, 1),
             new CfGoto(label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.stringType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInvoke(
                 182,
@@ -5020,6 +6442,14 @@
                     options.itemFactory.createString("toString")),
                 false),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.stringType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.stringType))),
             new CfReturn(ValueType.OBJECT),
             label3),
         ImmutableList.of(),
@@ -5067,6 +6497,16 @@
                 true),
             new CfGoto(label3),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/Optional;")),
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/function/Consumer;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 2),
             new CfInvoke(
                 185,
@@ -5076,6 +6516,16 @@
                     options.itemFactory.createString("run")),
                 true),
             label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/Optional;")),
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/function/Consumer;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
+                    }),
+                Arrays.asList()),
             new CfReturnVoid(),
             label4),
         ImmutableList.of(),
@@ -5124,6 +6574,17 @@
                 true),
             new CfGoto(label3),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/OptionalDouble;")),
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/function/DoubleConsumer;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 2),
             new CfInvoke(
                 185,
@@ -5133,6 +6594,17 @@
                     options.itemFactory.createString("run")),
                 true),
             label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/OptionalDouble;")),
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/function/DoubleConsumer;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
+                    }),
+                Arrays.asList()),
             new CfReturnVoid(),
             label4),
         ImmutableList.of(),
@@ -5181,6 +6653,17 @@
                 true),
             new CfGoto(label3),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/OptionalInt;")),
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/function/IntConsumer;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 2),
             new CfInvoke(
                 185,
@@ -5190,6 +6673,17 @@
                     options.itemFactory.createString("run")),
                 true),
             label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/OptionalInt;")),
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/function/IntConsumer;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
+                    }),
+                Arrays.asList()),
             new CfReturnVoid(),
             label4),
         ImmutableList.of(),
@@ -5238,6 +6732,17 @@
                 true),
             new CfGoto(label3),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/OptionalLong;")),
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/function/LongConsumer;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 2),
             new CfInvoke(
                 185,
@@ -5247,6 +6752,17 @@
                     options.itemFactory.createString("run")),
                 true),
             label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/OptionalLong;")),
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/function/LongConsumer;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
+                    }),
+                Arrays.asList()),
             new CfReturnVoid(),
             label4),
         ImmutableList.of(),
@@ -5276,8 +6792,22 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/Optional;"))
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/Optional;"))
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label3),
         ImmutableList.of(),
@@ -5307,8 +6837,24 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/OptionalDouble;"))
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/OptionalDouble;"))
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label3),
         ImmutableList.of(),
@@ -5338,8 +6884,24 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/OptionalInt;"))
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/OptionalInt;"))
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label3),
         ImmutableList.of(),
@@ -5369,8 +6931,24 @@
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/OptionalLong;"))
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/OptionalLong;"))
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label3),
         ImmutableList.of(),
@@ -5414,6 +6992,15 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfReturn(ValueType.OBJECT),
             label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/Optional;")),
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/function/Supplier;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 1),
             new CfInvoke(
                 185,
@@ -5481,6 +7068,13 @@
                 true),
             new CfReturn(ValueType.OBJECT),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/Optional;"))
+                    }),
+                Arrays.asList()),
             new CfInvoke(
                 184,
                 options.itemFactory.createMethod(
@@ -5535,6 +7129,14 @@
                 true),
             new CfReturn(ValueType.OBJECT),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/OptionalDouble;"))
+                    }),
+                Arrays.asList()),
             new CfInvoke(
                 184,
                 options.itemFactory.createMethod(
@@ -5589,6 +7191,14 @@
                 true),
             new CfReturn(ValueType.OBJECT),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/OptionalInt;"))
+                    }),
+                Arrays.asList()),
             new CfInvoke(
                 184,
                 options.itemFactory.createMethod(
@@ -5643,6 +7253,14 @@
                 true),
             new CfReturn(ValueType.OBJECT),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType("Ljava/util/OptionalLong;"))
+                    }),
+                Arrays.asList()),
             new CfInvoke(
                 184,
                 options.itemFactory.createMethod(
@@ -5757,6 +7375,11 @@
                 true),
             new CfGoto(label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInvoke(
                 184,
@@ -5768,6 +7391,13 @@
                     options.itemFactory.createString("of")),
                 true),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
+                Arrays.asList(
+                    FrameType.initialized(
+                        options.itemFactory.createType("Ljava/util/stream/Stream;")))),
             new CfReturn(ValueType.OBJECT),
             label3),
         ImmutableList.of(),
@@ -5804,6 +7434,15 @@
                 false),
             new CfStore(ValueType.INT, 2),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 1),
             new CfLoad(ValueType.INT, 2),
             new CfIfCmp(If.Type.GE, ValueType.INT, label8),
@@ -5834,6 +7473,16 @@
             new CfConstNumber(0, ValueType.INT),
             new CfReturn(ValueType.INT),
             label6,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 1),
             new CfLoad(ValueType.INT, 3),
             new CfInvoke(
@@ -5849,6 +7498,11 @@
             label7,
             new CfGoto(label2),
             label8,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.stringType)}),
+                Arrays.asList()),
             new CfConstNumber(1, ValueType.INT),
             new CfReturn(ValueType.INT),
             label9),
@@ -5889,6 +7543,15 @@
                 false),
             new CfThrow(),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.charSequenceType),
+                      FrameType.initialized(
+                          options.itemFactory.createType("[Ljava/lang/CharSequence;"))
+                    }),
+                Arrays.asList()),
             new CfNew(options.itemFactory.stringBuilderType),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfInvoke(
@@ -5922,6 +7585,17 @@
             new CfConstNumber(1, ValueType.INT),
             new CfStore(ValueType.INT, 3),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.charSequenceType),
+                      FrameType.initialized(
+                          options.itemFactory.createType("[Ljava/lang/CharSequence;")),
+                      FrameType.initialized(options.itemFactory.stringBuilderType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 3),
             new CfLoad(ValueType.OBJECT, 1),
             new CfArrayLength(),
@@ -5958,6 +7632,16 @@
             new CfIinc(3, 1),
             new CfGoto(label5),
             label9,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.charSequenceType),
+                      FrameType.initialized(
+                          options.itemFactory.createType("[Ljava/lang/CharSequence;")),
+                      FrameType.initialized(options.itemFactory.stringBuilderType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 2),
             new CfInvoke(
                 182,
@@ -6004,6 +7688,14 @@
                 false),
             new CfThrow(),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.charSequenceType),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Iterable;"))
+                    }),
+                Arrays.asList()),
             new CfNew(options.itemFactory.stringBuilderType),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfInvoke(
@@ -6057,6 +7749,16 @@
                 false),
             new CfStackInstruction(CfStackInstruction.Opcode.Pop),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.charSequenceType),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Iterable;")),
+                      FrameType.initialized(options.itemFactory.stringBuilderType),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/Iterator;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 3),
             new CfInvoke(
                 185,
@@ -6102,6 +7804,16 @@
             new CfStackInstruction(CfStackInstruction.Opcode.Pop),
             new CfGoto(label5),
             label8,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.charSequenceType),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Iterable;")),
+                      FrameType.initialized(options.itemFactory.stringBuilderType),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/util/Iterator;"))
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 2),
             new CfInvoke(
                 182,
@@ -6186,6 +7898,14 @@
                 false),
             new CfThrow(),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInvoke(
                 182,
@@ -6201,9 +7921,27 @@
             new CfLoad(ValueType.INT, 2),
             new CfIf(If.Type.NE, ValueType.INT, label5),
             label4,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfConstString(options.itemFactory.createString("")),
             new CfReturn(ValueType.OBJECT),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 1),
             new CfConstNumber(1, ValueType.INT),
             new CfIfCmp(If.Type.NE, ValueType.INT, label7),
@@ -6211,6 +7949,15 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfReturn(ValueType.OBJECT),
             label7,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfNew(options.itemFactory.stringBuilderType),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfLoad(ValueType.INT, 2),
@@ -6229,6 +7976,17 @@
             new CfConstNumber(0, ValueType.INT),
             new CfStore(ValueType.INT, 4),
             label9,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.stringBuilderType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 4),
             new CfLoad(ValueType.INT, 1),
             new CfIfCmp(If.Type.GE, ValueType.INT, label12),
@@ -6248,6 +8006,16 @@
             new CfIinc(4, 1),
             new CfGoto(label9),
             label12,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.stringBuilderType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 3),
             new CfInvoke(
                 182,
@@ -6298,6 +8066,15 @@
                 false),
             new CfStore(ValueType.INT, 2),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 1),
             new CfLoad(ValueType.INT, 2),
             new CfIfCmp(If.Type.GE, ValueType.INT, label8),
@@ -6327,6 +8104,16 @@
             label5,
             new CfGoto(label8),
             label6,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 1),
             new CfLoad(ValueType.INT, 3),
             new CfInvoke(
@@ -6342,6 +8129,15 @@
             label7,
             new CfGoto(label2),
             label8,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 2),
             new CfLoad(ValueType.INT, 1),
             new CfIfCmp(If.Type.LE, ValueType.INT, label14),
@@ -6373,6 +8169,16 @@
             label11,
             new CfGoto(label14),
             label12,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 2),
             new CfLoad(ValueType.INT, 3),
             new CfInvoke(
@@ -6388,6 +8194,15 @@
             label13,
             new CfGoto(label8),
             label14,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfLoad(ValueType.INT, 1),
             new CfLoad(ValueType.INT, 2),
@@ -6437,6 +8252,15 @@
                 false),
             new CfStore(ValueType.INT, 2),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 1),
             new CfLoad(ValueType.INT, 2),
             new CfIfCmp(If.Type.GE, ValueType.INT, label8),
@@ -6466,6 +8290,16 @@
             label5,
             new CfGoto(label8),
             label6,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 1),
             new CfLoad(ValueType.INT, 3),
             new CfInvoke(
@@ -6481,6 +8315,15 @@
             label7,
             new CfGoto(label2),
             label8,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfLoad(ValueType.INT, 1),
             new CfLoad(ValueType.INT, 2),
@@ -6526,6 +8369,14 @@
                 false),
             new CfStore(ValueType.INT, 1),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 1),
             new CfIf(If.Type.LE, ValueType.INT, label7),
             label2,
@@ -6556,6 +8407,15 @@
             label4,
             new CfGoto(label7),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 1),
             new CfLoad(ValueType.INT, 2),
             new CfInvoke(
@@ -6571,6 +8431,14 @@
             label6,
             new CfGoto(label1),
             label7,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.stringType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 0),
             new CfConstNumber(0, ValueType.INT),
             new CfLoad(ValueType.INT, 1),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index 1d32c0b..0968624 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -5,23 +5,24 @@
 package com.android.tools.r8.ir.optimize;
 
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMember;
 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.DexReference;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldResolutionResult;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ResolutionResult;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.function.BiFunction;
+import com.android.tools.r8.utils.TriFunction;
 
 // Computes the inlining constraint for a given instruction.
 public class InliningConstraints {
@@ -131,8 +132,7 @@
   }
 
   public ConstraintWithTarget forInstanceGet(DexField field, DexProgramClass context) {
-    DexField lookup = graphLens.lookupField(field);
-    return forFieldInstruction(lookup, appView.appInfo().lookupInstanceTarget(lookup), context);
+    return forFieldInstruction(field, context);
   }
 
   public ConstraintWithTarget forInstanceOf(DexType type, DexProgramClass context) {
@@ -140,8 +140,7 @@
   }
 
   public ConstraintWithTarget forInstancePut(DexField field, DexProgramClass context) {
-    DexField lookup = graphLens.lookupField(field);
-    return forFieldInstruction(lookup, appView.appInfo().lookupInstanceTarget(lookup), context);
+    return forFieldInstruction(field, context);
   }
 
   public ConstraintWithTarget forInvoke(DexMethod method, Type type, DexProgramClass context) {
@@ -170,43 +169,16 @@
     return ConstraintWithTarget.NEVER;
   }
 
-  private DexEncodedMethod lookupWhileVerticalClassMerging(
-      DexMethod method,
-      DexProgramClass context,
-      BiFunction<SingleResolutionResult, DexProgramClass, DexEncodedMethod> lookupFunction) {
-    SingleResolutionResult singleResolutionResult =
-        appView.appInfo().unsafeResolveMethodDueToDexFormat(method).asSingleResolution();
-    if (singleResolutionResult == null) {
-      return null;
-    }
-    DexEncodedMethod dexEncodedMethod = lookupFunction.apply(singleResolutionResult, context);
-    if (dexEncodedMethod != null) {
-      return dexEncodedMethod;
-    }
-    assert graphLens.lookupType(context.superType) == context.type;
-    DexProgramClass superContext = appView.definitionForProgramType(context.superType);
-    if (superContext == null) {
-      return null;
-    }
-    DexEncodedMethod alternativeDexEncodedMethod =
-        lookupFunction.apply(singleResolutionResult, superContext);
-    if (alternativeDexEncodedMethod != null
-        && alternativeDexEncodedMethod.holder() == superContext.type) {
-      return alternativeDexEncodedMethod;
-    }
-    return null;
-  }
-
   public ConstraintWithTarget forInvokeDirect(DexMethod method, DexProgramClass context) {
     DexMethod lookup = graphLens.lookupMethod(method);
+    if (lookup.holder.isArrayType()) {
+      return ConstraintWithTarget.ALWAYS;
+    }
+    ResolutionResult resolutionResult = appView.appInfo().unsafeResolveMethodDueToDexFormat(lookup);
     DexEncodedMethod target =
-        isVerticalClassMerging()
-            ? lookupWhileVerticalClassMerging(
-                lookup,
-                context,
-                (res, ctxt) -> res.lookupInvokeDirectTarget(ctxt, appView.appInfo()))
-            : appView.appInfo().lookupDirectTarget(lookup, context);
-    return forSingleTargetInvoke(lookup, target, context);
+        singleTargetWhileVerticalClassMerging(
+            resolutionResult, context, ResolutionResult::lookupInvokeDirectTarget);
+    return forResolvedMember(resolutionResult.getInitialResolutionHolder(), context, target);
   }
 
   public ConstraintWithTarget forInvokeInterface(DexMethod method, DexProgramClass context) {
@@ -228,14 +200,49 @@
 
   public ConstraintWithTarget forInvokeStatic(DexMethod method, DexProgramClass context) {
     DexMethod lookup = graphLens.lookupMethod(method);
+    if (lookup.holder.isArrayType()) {
+      return ConstraintWithTarget.ALWAYS;
+    }
+    ResolutionResult resolutionResult = appView.appInfo().unsafeResolveMethodDueToDexFormat(lookup);
     DexEncodedMethod target =
-        isVerticalClassMerging()
-            ? lookupWhileVerticalClassMerging(
-                lookup,
-                context,
-                (res, ctxt) -> res.lookupInvokeStaticTarget(ctxt, appView.appInfo()))
-            : appView.appInfo().lookupStaticTarget(lookup, context);
-    return forSingleTargetInvoke(lookup, target, context);
+        singleTargetWhileVerticalClassMerging(
+            resolutionResult, context, ResolutionResult::lookupInvokeStaticTarget);
+    if (!allowStaticInterfaceMethodCalls && target != null) {
+      // See b/120121170.
+      DexClass methodClass = appView.definitionFor(graphLens.lookupType(target.holder()));
+      if (methodClass != null && methodClass.isInterface() && target.hasCode()) {
+        return ConstraintWithTarget.NEVER;
+      }
+    }
+    return forResolvedMember(resolutionResult.getInitialResolutionHolder(), context, target);
+  }
+
+  @SuppressWarnings("ConstantConditions")
+  private DexEncodedMethod singleTargetWhileVerticalClassMerging(
+      ResolutionResult resolutionResult,
+      DexProgramClass context,
+      TriFunction<ResolutionResult, DexProgramClass, AppInfoWithClassHierarchy, DexEncodedMethod>
+          lookup) {
+    if (!resolutionResult.isSingleResolution()) {
+      return null;
+    }
+    DexEncodedMethod dexEncodedMethod = lookup.apply(resolutionResult, context, appView.appInfo());
+    if (!isVerticalClassMerging() || dexEncodedMethod != null) {
+      return dexEncodedMethod;
+    }
+    assert isVerticalClassMerging();
+    assert graphLens.lookupType(context.superType) == context.type;
+    DexProgramClass superContext = appView.programDefinitionFor(context.superType, context);
+    if (superContext == null) {
+      return null;
+    }
+    DexEncodedMethod alternativeDexEncodedMethod =
+        lookup.apply(resolutionResult, superContext, appView.appInfo());
+    if (alternativeDexEncodedMethod != null
+        && alternativeDexEncodedMethod.holder() == superContext.type) {
+      return alternativeDexEncodedMethod;
+    }
+    return null;
   }
 
   public ConstraintWithTarget forInvokeSuper(DexMethod method, DexProgramClass context) {
@@ -293,13 +300,11 @@
   }
 
   public ConstraintWithTarget forStaticGet(DexField field, DexProgramClass context) {
-    DexField lookup = graphLens.lookupField(field);
-    return forFieldInstruction(lookup, appView.appInfo().lookupStaticTarget(lookup), context);
+    return forFieldInstruction(field, context);
   }
 
   public ConstraintWithTarget forStaticPut(DexField field, DexProgramClass context) {
-    DexField lookup = graphLens.lookupField(field);
-    return forFieldInstruction(lookup, appView.appInfo().lookupStaticTarget(lookup), context);
+    return forFieldInstruction(field, context);
   }
 
   public ConstraintWithTarget forStore() {
@@ -326,64 +331,13 @@
     return ConstraintWithTarget.NEVER;
   }
 
-  private ConstraintWithTarget forFieldInstruction(
-      DexField field, DexEncodedField target, DexProgramClass context) {
-    // Resolve the field if possible and decide whether the instruction can inlined.
-    DexType fieldHolder = graphLens.lookupType(field.holder);
-    DexClass fieldClass = appView.definitionFor(fieldHolder);
-    if (target != null && fieldClass != null) {
-      ConstraintWithTarget fieldConstraintWithTarget =
-          ConstraintWithTarget.deriveConstraint(context, fieldHolder, target.accessFlags, appView);
-
-      // If the field has not been member-rebound, then we also need to make sure that the
-      // `context` has access to the definition of the field.
-      //
-      // See, for example, InlineNonReboundFieldTest (b/128604123).
-      if (field.holder != target.holder()) {
-        DexType actualFieldHolder = graphLens.lookupType(target.holder());
-        fieldConstraintWithTarget =
-            ConstraintWithTarget.meet(
-                fieldConstraintWithTarget,
-                ConstraintWithTarget.deriveConstraint(
-                    context, actualFieldHolder, target.accessFlags, appView),
-                appView);
-      }
-
-      ConstraintWithTarget classConstraintWithTarget =
-          ConstraintWithTarget.deriveConstraint(
-              context, fieldHolder, fieldClass.accessFlags, appView);
-      return ConstraintWithTarget.meet(
-          fieldConstraintWithTarget, classConstraintWithTarget, appView);
-    }
-    return ConstraintWithTarget.NEVER;
-  }
-
-  private ConstraintWithTarget forSingleTargetInvoke(
-      DexMethod method, DexEncodedMethod target, DexProgramClass context) {
-    if (method.holder.isArrayType()) {
-      return ConstraintWithTarget.ALWAYS;
-    }
-    if (target != null) {
-      DexType methodHolder = graphLens.lookupType(target.holder());
-      DexClass methodClass = appView.definitionFor(methodHolder);
-      if (methodClass != null) {
-        if (!allowStaticInterfaceMethodCalls && methodClass.isInterface() && target.hasCode()) {
-          // See b/120121170.
-          return ConstraintWithTarget.NEVER;
-        }
-
-        ConstraintWithTarget methodConstraintWithTarget =
-            ConstraintWithTarget.deriveConstraint(
-                context, methodHolder, target.accessFlags, appView);
-        // We also have to take the constraint of the enclosing class into account.
-        ConstraintWithTarget classConstraintWithTarget =
-            ConstraintWithTarget.deriveConstraint(
-                context, methodHolder, methodClass.accessFlags, appView);
-        return ConstraintWithTarget.meet(
-            methodConstraintWithTarget, classConstraintWithTarget, appView);
-      }
-    }
-    return ConstraintWithTarget.NEVER;
+  private ConstraintWithTarget forFieldInstruction(DexField field, DexProgramClass context) {
+    DexField lookup = graphLens.lookupField(field);
+    FieldResolutionResult fieldResolutionResult = appView.appInfo().resolveField(lookup);
+    return forResolvedMember(
+        fieldResolutionResult.getInitialResolutionHolder(),
+        context,
+        fieldResolutionResult.getResolvedField());
   }
 
   private ConstraintWithTarget forVirtualInvoke(
@@ -399,27 +353,28 @@
       return ConstraintWithTarget.NEVER;
     }
 
-    DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
-    if (resolutionTarget == null) {
+    return forResolvedMember(
+        resolutionResult.getInitialResolutionHolder(), context, resolutionResult.getSingleTarget());
+  }
+
+  private ConstraintWithTarget forResolvedMember(
+      DexClass initialResolutionHolder,
+      DexProgramClass context,
+      DexEncodedMember<?, ?> resolvedMember) {
+    if (resolvedMember == null) {
       // This will fail at runtime.
       return ConstraintWithTarget.NEVER;
     }
-
-    DexType methodHolder = graphLens.lookupType(resolutionTarget.holder());
-    DexClass methodClass = appView.definitionFor(methodHolder);
-    assert methodClass != null;
-    ConstraintWithTarget methodConstraintWithTarget =
+    DexType resolvedHolder = graphLens.lookupType(resolvedMember.holder());
+    assert initialResolutionHolder != null;
+    ConstraintWithTarget memberConstraintWithTarget =
         ConstraintWithTarget.deriveConstraint(
-            context, methodHolder, resolutionTarget.accessFlags, appView);
-    // We also have to take the constraint of the enclosing class of the resolution result
-    // into account. We do not allow inlining this method if it is calling something that
-    // is inaccessible. Inlining in that case could move the code to another package making a
-    // call succeed that should not succeed. Conversely, if the resolution result is accessible,
-    // we have to make sure that inlining cannot make it inaccessible.
+            context, resolvedHolder, resolvedMember.getAccessFlags(), appView);
+    // We also have to take the constraint of the initial resolution holder into account.
     ConstraintWithTarget classConstraintWithTarget =
         ConstraintWithTarget.deriveConstraint(
-            context, methodHolder, methodClass.accessFlags, appView);
+            context, initialResolutionHolder.type, initialResolutionHolder.accessFlags, appView);
     return ConstraintWithTarget.meet(
-        methodConstraintWithTarget, classConstraintWithTarget, appView);
+        classConstraintWithTarget, memberConstraintWithTarget, appView);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index c7e7b6b..7287edd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -58,6 +58,7 @@
 import com.android.tools.r8.ir.code.ValueTypeConstraint;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 import com.android.tools.r8.ir.conversion.SourceCode;
+import com.android.tools.r8.ir.desugar.InterfaceProcessor.InterfaceProcessorNestedGraphLens;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.origin.Origin;
@@ -1313,12 +1314,29 @@
   public ProgramMethodSet selectMethodsForOutlining() {
     ProgramMethodSet methodsSelectedForOutlining = ProgramMethodSet.create();
     assert outlineSites.isEmpty();
+
+    // TODO(b/167345026): This is needed to ensure that default interface methods are mapped to
+    //  the corresponding companion methods that contain the code objects. This should be removed
+    //  once default interface methods are desugared prior to the first optimization pass.
+    InterfaceProcessorNestedGraphLens interfaceProcessorLens =
+        InterfaceProcessorNestedGraphLens.find(appView.graphLens());
+    if (interfaceProcessorLens != null) {
+      interfaceProcessorLens.toggleMappingToExtraMethods();
+    }
+
     for (LongLivedProgramMethodMultisetBuilder outlineMethods : candidateMethodLists) {
       if (outlineMethods.size() >= appView.options().outline.threshold) {
         ProgramMethodMultiset multiset = outlineMethods.build(appView);
         multiset.forEachEntry((method, ignore) -> methodsSelectedForOutlining.add(method));
       }
     }
+
+    // TODO(b/167345026): Remove once default interface methods are desugared prior to the first
+    //  optimization pass.
+    if (interfaceProcessorLens != null) {
+      interfaceProcessorLens.toggleMappingToExtraMethods();
+    }
+
     candidateMethodLists.clear();
     return methodsSelectedForOutlining;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 12b42aa..3d7d397 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -283,8 +283,11 @@
 
   private void analyzeConstClass(ConstClass constClass, Set<DexType> eligibleEnums) {
     // We are using the ConstClass of an enum, which typically means the enum cannot be unboxed.
-    // We however allow unboxing if the ConstClass is only used as an argument to Enum#valueOf, to
-    // allow unboxing of: MyEnum a = Enum.valueOf(MyEnum.class, "A");.
+    // We however allow unboxing if the ConstClass is used only:
+    // - as an argument to Enum#valueOf, to allow unboxing of:
+    //    MyEnum a = Enum.valueOf(MyEnum.class, "A");
+    // - as a receiver for a name method, to allow unboxing of:
+    //    MyEnum.class.getName();
     DexType enumType = constClass.getValue();
     if (!enumsUnboxingCandidates.containsKey(enumType)) {
       return;
@@ -299,6 +302,10 @@
       return;
     }
     for (Instruction user : constClass.outValue().uniqueUsers()) {
+      if (user.isInvokeVirtual()
+          && isUnboxableNameMethod(user.asInvokeVirtual().getInvokedMethod())) {
+        continue;
+      }
       if (!(user.isInvokeStatic()
           && user.asInvokeStatic().getInvokedMethod() == factory.enumMembers.valueOf)) {
         markEnumAsUnboxable(Reason.CONST_CLASS, enumClass);
@@ -311,6 +318,12 @@
     eligibleEnums.add(enumType);
   }
 
+  private boolean isUnboxableNameMethod(DexMethod method) {
+    return method == factory.classMethods.getName
+        || method == factory.classMethods.getCanonicalName
+        || method == factory.classMethods.getSimpleName;
+  }
+
   private void addNullDependencies(IRCode code, Set<Instruction> uses, Set<DexType> eligibleEnums) {
     for (Instruction use : uses) {
       if (use.isInvokeMethod()) {
@@ -372,7 +385,7 @@
     }
     ImmutableSet<DexType> enumsToUnbox = ImmutableSet.copyOf(this.enumsUnboxingCandidates.keySet());
     // Update keep info on any of the enum methods of the removed classes.
-    updatePinnedItems(enumsToUnbox);
+    updateKeepInfo(enumsToUnbox);
     enumUnboxerRewriter = new EnumUnboxingRewriter(appView, enumsToUnbox, enumInstanceFieldDataMap);
     DirectMappedDexApplication.Builder appBuilder = appView.appInfo().app().asDirect().builder();
     Map<DexType, DexType> newMethodLocation = synthesizeUnboxedEnumsMethodsLocations(appBuilder);
@@ -480,8 +493,7 @@
     return syntheticClass;
   }
 
-
-  private void updatePinnedItems(Set<DexType> enumsToUnbox) {
+  private void updateKeepInfo(Set<DexType> enumsToUnbox) {
     appView
         .appInfo()
         .getKeepInfo()
@@ -490,8 +502,16 @@
               for (DexType type : enumsToUnbox) {
                 DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(type));
                 assert !keepInfo.getClassInfo(clazz).isPinned();
-                clazz.forEachProgramMethod(keepInfo::unsafeUnpinMethod);
-                clazz.forEachField(field -> keepInfo.unsafeUnpinField(clazz, field));
+                clazz.forEachProgramMethod(
+                    method -> {
+                      keepInfo.unsafeAllowMinificationOfMethod(method);
+                      keepInfo.unsafeUnpinMethod(method);
+                    });
+                clazz.forEachProgramField(
+                    field -> {
+                      keepInfo.unsafeAllowMinificationOfField(field);
+                      keepInfo.unsafeUnpinField(field);
+                    });
               }
             });
   }
@@ -852,9 +872,20 @@
       assert dexClass.isLibraryClass();
       if (dexClass.type != factory.enumType) {
         // System.identityHashCode(Object) is supported for proto enums.
+        // Object#getClass without outValue and Objects.requireNonNull are supported since R8
+        // rewrites explicit null checks to such instructions.
         if (singleTarget == factory.javaLangSystemMethods.identityHashCode) {
           return Reason.ELIGIBLE;
         }
+        if (singleTarget == factory.objectMembers.getClass
+            && (!invokeMethod.hasOutValue() || !invokeMethod.outValue().hasAnyUsers())) {
+          // This is a hidden null check.
+          return Reason.ELIGIBLE;
+        }
+        if (singleTarget == factory.objectsMethods.requireNonNull
+            || singleTarget == factory.objectsMethods.requireNonNullWithMessage) {
+          return Reason.ELIGIBLE;
+        }
         return Reason.UNSUPPORTED_LIBRARY_CALL;
       }
       // TODO(b/147860220): EnumSet and EnumMap may be interesting to model.
@@ -873,9 +904,7 @@
         return Reason.ELIGIBLE;
       } else if (singleTarget == factory.enumMembers.constructor) {
         // Enum constructor call is allowed only if first call of an enum initializer.
-        if (code.method().isInstanceInitializer()
-            && code.method().holder() == enumClass.type
-            && isFirstInstructionAfterArguments(invokeMethod, code)) {
+        if (code.method().isInstanceInitializer() && code.method().holder() == enumClass.type) {
           return Reason.ELIGIBLE;
         }
       }
@@ -1076,16 +1105,6 @@
     return ObjectState.empty();
   }
 
-  private boolean isFirstInstructionAfterArguments(InvokeMethod invokeMethod, IRCode code) {
-    BasicBlock basicBlock = code.entryBlock();
-    for (Instruction instruction : basicBlock.getInstructions()) {
-      if (!instruction.isArgument()) {
-        return instruction == invokeMethod;
-      }
-    }
-    return false;
-  }
-
   private void reportEnumsAnalysis() {
     assert debugLogEnabled;
     Reporter reporter = appView.options().reporter;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java
index a09ea37..922b5f0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java
@@ -12,6 +12,8 @@
 import com.android.tools.r8.cf.code.CfArrayLength;
 import com.android.tools.r8.cf.code.CfArrayStore;
 import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
 import com.android.tools.r8.cf.code.CfGoto;
 import com.android.tools.r8.cf.code.CfIf;
 import com.android.tools.r8.cf.code.CfIfCmp;
@@ -22,6 +24,7 @@
 import com.android.tools.r8.cf.code.CfNew;
 import com.android.tools.r8.cf.code.CfNewArray;
 import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfReturnVoid;
 import com.android.tools.r8.cf.code.CfStackInstruction;
 import com.android.tools.r8.cf.code.CfStore;
 import com.android.tools.r8.cf.code.CfThrow;
@@ -34,6 +37,8 @@
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
+import java.util.Arrays;
 
 public final class EnumUnboxingCfMethods {
 
@@ -57,6 +62,14 @@
             new CfLoad(ValueType.INT, 1),
             new CfIf(If.Type.NE, ValueType.INT, label2),
             label1,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfInvoke(
@@ -68,6 +81,14 @@
                 false),
             new CfThrow(),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 0),
             new CfLoad(ValueType.INT, 1),
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
@@ -104,14 +125,38 @@
                 false),
             new CfThrow(),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 0),
             new CfLoad(ValueType.INT, 1),
             new CfIfCmp(If.Type.NE, ValueType.INT, label3),
             new CfConstNumber(1, ValueType.INT),
             new CfGoto(label4),
             label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfConstNumber(0, ValueType.INT),
             label4,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
             new CfReturn(ValueType.INT),
             label5),
         ImmutableList.of(),
@@ -143,6 +188,11 @@
                 false),
             new CfThrow(),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.intType)}),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 0),
             new CfConstNumber(1, ValueType.INT),
             new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
@@ -173,6 +223,15 @@
             new CfConstNumber(0, ValueType.INT),
             new CfStore(ValueType.INT, 2),
             label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intArrayType),
+                      FrameType.initialized(options.itemFactory.intType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.INT, 2),
             new CfLoad(ValueType.OBJECT, 1),
             new CfArrayLength(),
@@ -188,10 +247,96 @@
             new CfIinc(2, 1),
             new CfGoto(label2),
             label5,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.intArrayType)
+                    }),
+                Arrays.asList()),
             new CfLoad(ValueType.OBJECT, 1),
             new CfReturn(ValueType.OBJECT),
             label6),
         ImmutableList.of(),
         ImmutableList.of());
   }
+
+  public static CfCode EnumUnboxingMethods_zeroCheck(InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        2,
+        1,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.INT, 0),
+            new CfIf(If.Type.NE, ValueType.INT, label2),
+            label1,
+            new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfInvoke(
+                183,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/NullPointerException;"),
+                    options.itemFactory.createProto(options.itemFactory.voidType),
+                    options.itemFactory.createString("<init>")),
+                false),
+            new CfThrow(),
+            label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.intType)}),
+                Arrays.asList()),
+            new CfReturnVoid(),
+            label3),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode EnumUnboxingMethods_zeroCheckMessage(
+      InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        2,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.INT, 0),
+            new CfIf(If.Type.NE, ValueType.INT, label2),
+            label1,
+            new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfInvoke(
+                183,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/NullPointerException;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.voidType, options.itemFactory.stringType),
+                    options.itemFactory.createString("<init>")),
+                false),
+            new CfThrow(),
+            label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.stringType)
+                    }),
+                Arrays.asList()),
+            new CfReturnVoid(),
+            label3),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 54d9aaf..ca106ab 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -77,6 +77,8 @@
   private final DexMethod equalsUtilityMethod;
   private final DexMethod compareToUtilityMethod;
   private final DexMethod valuesUtilityMethod;
+  private final DexMethod zeroCheckMethod;
+  private final DexMethod zeroCheckMessageMethod;
 
   EnumUnboxingRewriter(
       AppView<AppInfoWithLiveness> appView,
@@ -114,6 +116,17 @@
             factory.enumUnboxingUtilityType,
             factory.createProto(factory.intArrayType, factory.intType),
             ENUM_UNBOXING_UTILITY_METHOD_PREFIX + "values");
+    // Custom methods for Object#getClass without outValue and Objects.requireNonNull.
+    this.zeroCheckMethod =
+        factory.createMethod(
+            factory.enumUnboxingUtilityType,
+            factory.createProto(factory.voidType, factory.intType),
+            ENUM_UNBOXING_UTILITY_METHOD_PREFIX + "zeroCheck");
+    this.zeroCheckMessageMethod =
+        factory.createMethod(
+            factory.enumUnboxingUtilityType,
+            factory.createProto(factory.voidType, factory.intType, factory.stringType),
+            ENUM_UNBOXING_UTILITY_METHOD_PREFIX + "zeroCheckMessage");
   }
 
   public EnumValueInfoMapCollection getEnumsToUnbox() {
@@ -160,6 +173,10 @@
                 new InvokeStatic(
                     toStringMethod, invokeMethod.outValue(), invokeMethod.arguments()));
             continue;
+          } else if (invokedMethod == factory.objectMembers.getClass) {
+            assert !invokeMethod.hasOutValue() || !invokeMethod.outValue().hasAnyUsers();
+            replaceEnumInvoke(
+                iterator, invokeMethod, zeroCheckMethod, m -> synthesizeZeroCheckMethod());
           }
         }
         // TODO(b/147860220): rewrite also other enum methods.
@@ -195,6 +212,25 @@
             invokeStatic.outValue().replaceUsers(argument);
             iterator.removeOrReplaceByDebugLocalRead();
           }
+        } else if (invokedMethod == factory.objectsMethods.requireNonNull) {
+          assert invokeStatic.inValues().size() == 1;
+          Value argument = invokeStatic.getArgument(0);
+          DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+          if (enumType != null) {
+            replaceEnumInvoke(
+                iterator, invokeStatic, zeroCheckMethod, m -> synthesizeZeroCheckMethod());
+          }
+        } else if (invokedMethod == factory.objectsMethods.requireNonNullWithMessage) {
+          assert invokeStatic.inValues().size() == 2;
+          Value argument = invokeStatic.getArgument(0);
+          DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+          if (enumType != null) {
+            replaceEnumInvoke(
+                iterator,
+                invokeStatic,
+                zeroCheckMessageMethod,
+                m -> synthesizeZeroCheckMessageMethod());
+          }
         }
       }
       if (instruction.isStaticGet()) {
@@ -497,6 +533,19 @@
     return false;
   }
 
+  private DexEncodedMethod synthesizeZeroCheckMethod() {
+    CfCode cfCode =
+        EnumUnboxingCfMethods.EnumUnboxingMethods_zeroCheck(appView.options(), zeroCheckMethod);
+    return synthesizeUtilityMethod(cfCode, zeroCheckMethod, false);
+  }
+
+  private DexEncodedMethod synthesizeZeroCheckMessageMethod() {
+    CfCode cfCode =
+        EnumUnboxingCfMethods.EnumUnboxingMethods_zeroCheckMessage(
+            appView.options(), zeroCheckMessageMethod);
+    return synthesizeUtilityMethod(cfCode, zeroCheckMessageMethod, false);
+  }
+
   private DexEncodedMethod synthesizeOrdinalMethod() {
     CfCode cfCode =
         EnumUnboxingCfMethods.EnumUnboxingMethods_ordinal(appView.options(), ordinalUtilityMethod);
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index 48f0d33..6f5a765 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
@@ -73,10 +74,19 @@
     this.keepInnerClassStructure = options.keepInnerClassStructure();
 
     // Initialize top-level naming state.
-    topLevelState =
-        new Namespace(
-            getPackageBinaryNameFromJavaType(
-                options.getProguardConfiguration().getPackagePrefix()));
+    if (options.testing.enableExperimentalRepackaging) {
+      topLevelState = new Namespace("");
+      String newPackageDescriptor =
+          StringUtils.replaceAll(options.getProguardConfiguration().getPackagePrefix(), ".", "/");
+      if (!newPackageDescriptor.isEmpty()) {
+        registerPackagePrefixesAsUsed(newPackageDescriptor, false);
+      }
+    } else {
+      topLevelState =
+          new Namespace(
+              getPackageBinaryNameFromJavaType(
+                  options.getProguardConfiguration().getPackagePrefix()));
+    }
 
     states.put("", topLevelState);
 
@@ -199,7 +209,8 @@
   private void registerClassAsUsed(DexType type, DexString descriptor) {
     renaming.put(type, descriptor);
     registerPackagePrefixesAsUsed(
-        getParentPackagePrefix(getClassBinaryNameFromDescriptor(descriptor.toSourceString())));
+        getParentPackagePrefix(getClassBinaryNameFromDescriptor(descriptor.toSourceString())),
+        isAccessModificationAllowed);
     setUsedTypeName(descriptor.toString());
     if (keepInnerClassStructure) {
       DexType outerClass = getOutClassForType(type);
@@ -215,10 +226,10 @@
   }
 
   /** Registers the given package prefix and all of parent packages as used. */
-  private void registerPackagePrefixesAsUsed(String packagePrefix) {
+  private void registerPackagePrefixesAsUsed(String packagePrefix, boolean isMinificationAllowed) {
     // If -allowaccessmodification is not set, we may keep classes in their original packages,
     // accounting for package-private accesses.
-    if (!isAccessModificationAllowed) {
+    if (!isMinificationAllowed) {
       noObfuscationPrefixes.add(packagePrefix);
     }
     String usedPrefix = packagePrefix;
diff --git a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
index 4077f36..5163a17 100644
--- a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
+++ b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
@@ -5,12 +5,18 @@
 package com.android.tools.r8.repackaging;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
 import com.android.tools.r8.graph.ProgramPackage;
 import com.android.tools.r8.graph.ProgramPackageCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
+import java.util.IdentityHashMap;
+import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 
@@ -29,34 +35,52 @@
 public class Repackaging {
 
   private final AppView<AppInfoWithLiveness> appView;
+  private final DexItemFactory dexItemFactory;
   private final ProguardConfiguration proguardConfiguration;
 
   public Repackaging(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
+    this.dexItemFactory = appView.dexItemFactory();
     this.proguardConfiguration = appView.options().getProguardConfiguration();
   }
 
-  public void run(ExecutorService executorService, Timing timing) throws ExecutionException {
+  public RepackagingLens run(
+      DirectMappedDexApplication.Builder appBuilder, ExecutorService executorService, Timing timing)
+      throws ExecutionException {
     timing.begin("Repackage classes");
-    run(executorService);
+    RepackagingLens lens = run(appBuilder, executorService);
     timing.end();
+    return lens;
   }
 
-  private void run(ExecutorService executorService) throws ExecutionException {
+  private RepackagingLens run(
+      DirectMappedDexApplication.Builder appBuilder, ExecutorService executorService)
+      throws ExecutionException {
     if (proguardConfiguration.getPackageObfuscationMode().isNone()) {
-      return;
+      return null;
     }
 
     // For each package, find the set of classes that can be repackaged, and move them to the
     // desired namespace.
-    ProgramPackageCollection packages = ProgramPackageCollection.create(appView);
-    for (ProgramPackage pkg : packages) {
+    Map<DexType, DexType> mappings = new IdentityHashMap<>();
+    for (ProgramPackage pkg : ProgramPackageCollection.create(appView)) {
       Iterable<DexProgramClass> classesToRepackage =
           computeClassesToRepackage(pkg, executorService);
-      // TODO(b/165783399): Move each class in `classesToRepackage`.
+      String newPackageDescriptor = getNewPackageDescriptor(pkg);
+      for (DexProgramClass classToRepackage : classesToRepackage) {
+        // TODO(b/165783399): Handle class collisions when different packages are repackaged into
+        //  the same package.
+        DexType newType =
+            classToRepackage.getType().replacePackage(newPackageDescriptor, dexItemFactory);
+        mappings.put(classToRepackage.getType(), newType);
+      }
       // TODO(b/165783399): Investigate if repackaging can lead to different dynamic dispatch. See,
       //  for example, CrossPackageInvokeSuperToPackagePrivateMethodTest.
     }
+    if (mappings.isEmpty()) {
+      return null;
+    }
+    return new RepackagingTreeFixer(appBuilder, appView, mappings).run();
   }
 
   private Iterable<DexProgramClass> computeClassesToRepackage(
@@ -69,4 +93,14 @@
     constraintGraph.populateConstraints(executorService);
     return constraintGraph.computeClassesToRepackage();
   }
+
+  private String getNewPackageDescriptor(ProgramPackage pkg) {
+    String newPackageDescriptor =
+        StringUtils.replaceAll(proguardConfiguration.getPackagePrefix(), ".", "/");
+    if (proguardConfiguration.getPackageObfuscationMode().isFlattenPackageHierarchy()) {
+      // TODO(b/165783399): Handle collisions among package names.
+      newPackageDescriptor += "/" + pkg.getLastPackageName();
+    }
+    return newPackageDescriptor;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
index 9619f5a..33bbc11 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
@@ -14,9 +14,11 @@
 import com.android.tools.r8.graph.ProgramPackage;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.WorkList;
 import com.google.common.collect.Sets;
-import java.util.Collections;
+import java.util.ArrayList;
 import java.util.IdentityHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
@@ -37,6 +39,7 @@
   private final AppView<AppInfoWithLiveness> appView;
   private final ProgramPackage pkg;
   private final Map<DexDefinition, Node> nodes = new IdentityHashMap<>();
+  private final Set<Node> pinnedNodes = Sets.newIdentityHashSet();
 
   public RepackagingConstraintGraph(AppView<AppInfoWithLiveness> appView, ProgramPackage pkg) {
     this.appView = appView;
@@ -48,19 +51,26 @@
     // Add all the items in the package into the graph. This way we know which items belong to the
     // package without having to extract package descriptor strings and comparing them with the
     // package descriptor.
-    boolean hasPackagePrivateOrProtectedItem = false;
     boolean hasPinnedItem = false;
     for (DexProgramClass clazz : pkg) {
       boolean isPinned = !appView.appInfo().isMinificationAllowed(clazz.getType());
-      hasPinnedItem |= isPinned;
-      nodes.put(clazz, new Node(clazz));
-      hasPackagePrivateOrProtectedItem |= clazz.getAccessFlags().isPackagePrivateOrProtected();
-      for (DexEncodedMember<?, ?> member : clazz.members()) {
-        nodes.put(member, new Node(member));
-        hasPackagePrivateOrProtectedItem |= member.getAccessFlags().isPackagePrivateOrProtected();
+      Node classNode = createNode(clazz);
+      if (isPinned) {
+        pinnedNodes.add(classNode);
       }
+      for (DexEncodedMember<?, ?> member : clazz.members()) {
+        Node memberNode = createNode(member);
+        classNode.addNeighbor(memberNode);
+      }
+      hasPinnedItem |= isPinned;
     }
-    return !hasPinnedItem || !hasPackagePrivateOrProtectedItem;
+    return !hasPinnedItem;
+  }
+
+  private Node createNode(DexDefinition definition) {
+    Node node = new Node(definition);
+    nodes.put(definition, node);
+    return node;
   }
 
   Node getNode(DexDefinition definition) {
@@ -101,9 +111,18 @@
   }
 
   public Iterable<DexProgramClass> computeClassesToRepackage() {
-    // TODO(b/165783399): From each node in the graph that cannot be moved elsewhere due to a -keep
-    //  rule, mark all neighbors as pinned, and repeat.
-    return Collections.emptyList();
+    WorkList<Node> worklist = WorkList.newIdentityWorkList(pinnedNodes);
+    while (worklist.hasNext()) {
+      worklist.addIfNotSeen(worklist.next().getNeighbors());
+    }
+    Set<Node> pinnedNodes = worklist.getSeenSet();
+    List<DexProgramClass> classesToRepackage = new ArrayList<>();
+    for (DexProgramClass clazz : pkg) {
+      if (!pinnedNodes.contains(getNode(clazz))) {
+        classesToRepackage.add(clazz);
+      }
+    }
+    return classesToRepackage;
   }
 
   static class Node {
@@ -120,5 +139,9 @@
       neighbors.add(neighbor);
       neighbor.neighbors.add(this);
     }
+
+    public Set<Node> getNeighbors() {
+      return neighbors;
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
new file mode 100644
index 0000000..7c19b0b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
@@ -0,0 +1,66 @@
+// 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.repackaging;
+
+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.NestedGraphLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
+public class RepackagingLens extends NestedGraphLens {
+
+  private final BiMap<DexType, DexType> originalTypes;
+
+  private RepackagingLens(
+      AppView<AppInfoWithLiveness> appView,
+      BiMap<DexField, DexField> originalFieldSignatures,
+      BiMap<DexMethod, DexMethod> originalMethodSignatures,
+      BiMap<DexType, DexType> originalTypes) {
+    super(
+        originalTypes.inverse(),
+        originalMethodSignatures.inverse(),
+        originalFieldSignatures.inverse(),
+        originalFieldSignatures,
+        originalMethodSignatures,
+        appView.graphLens(),
+        appView.dexItemFactory());
+    this.originalTypes = originalTypes;
+  }
+
+  @Override
+  public DexType getOriginalType(DexType type) {
+    DexType previous = originalTypes.getOrDefault(type, type);
+    return previousLens.getOriginalType(previous);
+  }
+
+  public static class Builder {
+
+    protected final BiMap<DexType, DexType> originalTypes = HashBiMap.create();
+    protected final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create();
+    protected final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
+
+    public void recordMove(DexField from, DexField to) {
+      originalFieldSignatures.put(to, from);
+    }
+
+    public void recordMove(DexMethod from, DexMethod to) {
+      originalMethodSignatures.put(to, from);
+    }
+
+    public void recordMove(DexType from, DexType to) {
+      originalTypes.put(to, from);
+    }
+
+    public RepackagingLens build(AppView<AppInfoWithLiveness> appView) {
+      assert !originalTypes.isEmpty();
+      return new RepackagingLens(
+          appView, originalFieldSignatures, originalMethodSignatures, originalTypes);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
new file mode 100644
index 0000000..3591ff7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
@@ -0,0 +1,281 @@
+// 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.repackaging;
+
+import com.android.tools.r8.graph.AppView;
+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.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.graph.NestHostClassAttribute;
+import com.android.tools.r8.graph.NestMemberClassAttribute;
+import com.android.tools.r8.shaking.AnnotationFixer;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RepackagingTreeFixer {
+
+  private final DirectMappedDexApplication.Builder appBuilder;
+  private final AppView<AppInfoWithLiveness> appView;
+  private final DexItemFactory dexItemFactory;
+  private final RepackagingLens.Builder lensBuilder = new RepackagingLens.Builder();
+  private final Map<DexType, DexType> repackagedClasses;
+
+  private final Map<DexType, DexProgramClass> newProgramClasses = new IdentityHashMap<>();
+  private final Map<DexProto, DexProto> protoFixupCache = new IdentityHashMap<>();
+
+  public RepackagingTreeFixer(
+      DirectMappedDexApplication.Builder appBuilder,
+      AppView<AppInfoWithLiveness> appView,
+      Map<DexType, DexType> repackagedClasses) {
+    this.appBuilder = appBuilder;
+    this.appView = appView;
+    this.dexItemFactory = appView.dexItemFactory();
+    this.repackagedClasses = repackagedClasses;
+  }
+
+  public RepackagingLens run() {
+    // Globally substitute repackaged class types.
+    for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
+      newProgramClasses.computeIfAbsent(clazz.getType(), ignore -> fixupClass(clazz));
+    }
+    appBuilder.replaceProgramClasses(new ArrayList<>(newProgramClasses.values()));
+    RepackagingLens lens = lensBuilder.build(appView);
+    new AnnotationFixer(lens).run(appView.appInfo().classes());
+    return lens;
+  }
+
+  private DexProgramClass fixupClass(DexProgramClass clazz) {
+    DexProgramClass newClass =
+        new DexProgramClass(
+            fixupType(clazz.getType()),
+            clazz.getOriginKind(),
+            clazz.getOrigin(),
+            clazz.getAccessFlags(),
+            fixupType(clazz.superType),
+            fixupTypeList(clazz.interfaces),
+            clazz.getSourceFile(),
+            fixupNestHost(clazz.getNestHost()),
+            fixupNestMemberAttributes(clazz.getNestMembersClassAttributes()),
+            fixupEnclosingMethodAttribute(clazz.getEnclosingMethodAttribute()),
+            fixupInnerClassAttributes(clazz.getInnerClasses()),
+            clazz.annotations(),
+            DexEncodedField.EMPTY_ARRAY,
+            DexEncodedField.EMPTY_ARRAY,
+            DexEncodedMethod.EMPTY_ARRAY,
+            DexEncodedMethod.EMPTY_ARRAY,
+            dexItemFactory.getSkipNameValidationForTesting(),
+            DexProgramClass::checksumFromType,
+            fixupSynthesizedFrom(clazz.getSynthesizedFrom()));
+    newClass.setInstanceFields(fixupFields(clazz.instanceFields()));
+    newClass.setStaticFields(fixupFields(clazz.staticFields()));
+    newClass.setDirectMethods(
+        fixupMethods(
+            clazz.getMethodCollection().directMethods(),
+            clazz.getMethodCollection().numberOfDirectMethods()));
+    newClass.setVirtualMethods(
+        fixupMethods(
+            clazz.getMethodCollection().virtualMethods(),
+            clazz.getMethodCollection().numberOfVirtualMethods()));
+    if (newClass.getType() != clazz.getType()) {
+      lensBuilder.recordMove(clazz.getType(), newClass.getType());
+    }
+    return newClass;
+  }
+
+  private EnclosingMethodAttribute fixupEnclosingMethodAttribute(
+      EnclosingMethodAttribute enclosingMethodAttribute) {
+    if (enclosingMethodAttribute == null) {
+      return null;
+    }
+    DexType enclosingClassType = enclosingMethodAttribute.getEnclosingClass();
+    if (enclosingClassType != null) {
+      DexType newEnclosingClassType = fixupType(enclosingClassType);
+      return newEnclosingClassType != enclosingClassType
+          ? new EnclosingMethodAttribute(newEnclosingClassType)
+          : enclosingMethodAttribute;
+    }
+    DexMethod enclosingMethod = enclosingMethodAttribute.getEnclosingMethod();
+    assert enclosingMethod != null;
+    DexMethod newEnclosingMethod =
+        fixupMethodReference(enclosingMethodAttribute.getEnclosingMethod());
+    return newEnclosingMethod != enclosingMethod
+        ? new EnclosingMethodAttribute(newEnclosingMethod)
+        : enclosingMethodAttribute;
+  }
+
+  private DexEncodedField[] fixupFields(List<DexEncodedField> fields) {
+    if (fields == null) {
+      return DexEncodedField.EMPTY_ARRAY;
+    }
+    DexEncodedField[] newFields = new DexEncodedField[fields.size()];
+    for (int i = 0; i < fields.size(); i++) {
+      newFields[i] = fixupField(fields.get(i));
+    }
+    return newFields;
+  }
+
+  private DexEncodedField fixupField(DexEncodedField field) {
+    DexField fieldReference = field.toReference();
+    DexField newFieldReference = fixupFieldReference(fieldReference);
+    if (newFieldReference != fieldReference) {
+      lensBuilder.recordMove(fieldReference, newFieldReference);
+      return field.toTypeSubstitutedField(newFieldReference);
+    }
+    return field;
+  }
+
+  private DexField fixupFieldReference(DexField field) {
+    DexType newType = fixupType(field.type);
+    DexType newHolder = fixupType(field.holder);
+    return dexItemFactory.createField(newHolder, newType, field.name);
+  }
+
+  private List<InnerClassAttribute> fixupInnerClassAttributes(
+      List<InnerClassAttribute> innerClassAttributes) {
+    if (innerClassAttributes.isEmpty()) {
+      return innerClassAttributes;
+    }
+    boolean changed = false;
+    List<InnerClassAttribute> newInnerClassAttributes = new ArrayList<>();
+    for (InnerClassAttribute innerClassAttribute : innerClassAttributes) {
+      DexType innerClassType = innerClassAttribute.getInner();
+      DexType newInnerClassType = fixupType(innerClassType);
+      DexType outerClassType = innerClassAttribute.getOuter();
+      DexType newOuterClassType = fixupType(outerClassType);
+      newInnerClassAttributes.add(
+          new InnerClassAttribute(
+              innerClassAttribute.getAccess(),
+              newInnerClassType,
+              newOuterClassType,
+              innerClassAttribute.getInnerName()));
+      changed |= newInnerClassType != innerClassType || newOuterClassType != outerClassType;
+    }
+    return changed ? newInnerClassAttributes : innerClassAttributes;
+  }
+
+  private DexEncodedMethod[] fixupMethods(Iterable<DexEncodedMethod> methods, int size) {
+    if (size == 0) {
+      return DexEncodedMethod.EMPTY_ARRAY;
+    }
+    int i = 0;
+    DexEncodedMethod[] newMethods = new DexEncodedMethod[size];
+    for (DexEncodedMethod method : methods) {
+      newMethods[i++] = fixupMethod(method);
+    }
+    return newMethods;
+  }
+
+  private DexEncodedMethod fixupMethod(DexEncodedMethod method) {
+    DexMethod methodReference = method.toReference();
+    DexMethod newMethodReference = fixupMethodReference(methodReference);
+    if (newMethodReference != methodReference) {
+      lensBuilder.recordMove(methodReference, newMethodReference);
+      return method.toTypeSubstitutedMethod(newMethodReference);
+    }
+    return method;
+  }
+
+  private DexMethod fixupMethodReference(DexMethod method) {
+    return appView
+        .dexItemFactory()
+        .createMethod(fixupType(method.holder), fixupProto(method.proto), method.name);
+  }
+
+  private NestHostClassAttribute fixupNestHost(DexType type) {
+    return type != null ? new NestHostClassAttribute(fixupType(type)) : null;
+  }
+
+  private List<NestMemberClassAttribute> fixupNestMemberAttributes(
+      List<NestMemberClassAttribute> nestMemberAttributes) {
+    if (nestMemberAttributes.isEmpty()) {
+      return nestMemberAttributes;
+    }
+    boolean changed = false;
+    List<NestMemberClassAttribute> newNestMemberAttributes =
+        new ArrayList<>(nestMemberAttributes.size());
+    for (NestMemberClassAttribute nestMemberAttribute : nestMemberAttributes) {
+      DexType nestMemberType = nestMemberAttribute.getNestMember();
+      DexType newNestMemberType = fixupType(nestMemberType);
+      newNestMemberAttributes.add(new NestMemberClassAttribute(newNestMemberType));
+      changed |= newNestMemberType != nestMemberType;
+    }
+    return changed ? newNestMemberAttributes : nestMemberAttributes;
+  }
+
+  private DexProto fixupProto(DexProto proto) {
+    DexProto result = protoFixupCache.get(proto);
+    if (result == null) {
+      DexType returnType = fixupType(proto.returnType);
+      DexType[] arguments = fixupTypes(proto.parameters.values);
+      result = dexItemFactory.createProto(returnType, arguments);
+      protoFixupCache.put(proto, result);
+    }
+    return result;
+  }
+
+  private Collection<DexProgramClass> fixupSynthesizedFrom(
+      Collection<DexProgramClass> synthesizedFrom) {
+    if (synthesizedFrom.isEmpty()) {
+      return synthesizedFrom;
+    }
+    boolean changed = false;
+    List<DexProgramClass> newSynthesizedFrom = new ArrayList<>(synthesizedFrom.size());
+    for (DexProgramClass clazz : synthesizedFrom) {
+      // TODO(b/165783399): What do we want to put here if the class that this was synthesized from
+      //  is no longer in the application?
+      assert appView.definitionFor(clazz.type) != null;
+      DexProgramClass newClass =
+          newProgramClasses.computeIfAbsent(clazz.getType(), ignore -> fixupClass(clazz));
+      newSynthesizedFrom.add(newClass);
+      changed |= newClass != clazz;
+    }
+    return changed ? newSynthesizedFrom : synthesizedFrom;
+  }
+
+  private DexType fixupType(DexType type) {
+    if (type.isArrayType()) {
+      DexType base = type.toBaseType(dexItemFactory);
+      DexType fixed = fixupType(base);
+      if (base == fixed) {
+        return type;
+      }
+      return type.replaceBaseType(fixed, dexItemFactory);
+    }
+    if (type.isClassType()) {
+      return repackagedClasses.getOrDefault(type, type);
+    }
+    return type;
+  }
+
+  private DexType[] fixupTypes(DexType[] types) {
+    boolean changed = false;
+    DexType[] newTypes = new DexType[types.length];
+    for (int i = 0; i < newTypes.length; i++) {
+      DexType type = types[i];
+      DexType newType = fixupType(types[i]);
+      newTypes[i] = newType;
+      changed |= newType != type;
+    }
+    return changed ? newTypes : types;
+  }
+
+  private DexTypeList fixupTypeList(DexTypeList types) {
+    DexType[] newTypes = fixupTypes(types.values);
+    return newTypes != types.values ? new DexTypeList(newTypes) : types;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 6768368..05c6f6c 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -1025,7 +1025,7 @@
         // TODO(sgjesse): Rewrite call sites as well? Right now they are only used by minification
         //   after second tree shaking.
         callSites,
-        keepInfo.rewrite(lens),
+        keepInfo.rewrite(lens, application.options),
         lens.rewriteReferenceKeys(mayHaveSideEffects),
         lens.rewriteReferenceKeys(noSideEffects),
         lens.rewriteReferenceKeys(assumedValues),
diff --git a/src/main/java/com/android/tools/r8/shaking/FieldAccessInfoCollectionModifier.java b/src/main/java/com/android/tools/r8/shaking/FieldAccessInfoCollectionModifier.java
new file mode 100644
index 0000000..4e9d977
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/FieldAccessInfoCollectionModifier.java
@@ -0,0 +1,96 @@
+// 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.shaking;
+
+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.FieldAccessInfoCollectionImpl;
+import com.android.tools.r8.graph.FieldAccessInfoImpl;
+import com.android.tools.r8.graph.ProgramMethod;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+public class FieldAccessInfoCollectionModifier {
+
+  static class FieldReferences {
+    final List<DexMethod> writeContexts = new ArrayList<>();
+    final List<DexMethod> readContexts = new ArrayList<>();
+
+    void fixUpMethods(List<DexMethod> methods, Function<DexMethod, DexMethod> fixUpMethod) {
+      for (int i = 0; i < methods.size(); i++) {
+        DexMethod method = methods.get(i);
+        DexMethod newMethod = fixUpMethod.apply(method);
+        if (method != newMethod) {
+          methods.set(i, newMethod);
+        }
+      }
+    }
+
+    void fixUp(Function<DexMethod, DexMethod> fixUpMethod) {
+      fixUpMethods(writeContexts, fixUpMethod);
+      fixUpMethods(readContexts, fixUpMethod);
+    }
+  }
+
+  final Map<DexField, FieldReferences> newFieldAccesses;
+
+  FieldAccessInfoCollectionModifier(Map<DexField, FieldReferences> newFieldAccesses) {
+    this.newFieldAccesses = newFieldAccesses;
+  }
+
+  void forEachFieldAccess(
+      AppView<?> appView,
+      Collection<DexMethod> methods,
+      DexField field,
+      BiConsumer<DexField, ProgramMethod> record) {
+    for (DexMethod method : methods) {
+      ProgramMethod programMethod =
+          appView.definitionFor(method.holder).asProgramClass().lookupProgramMethod(method);
+      record.accept(field, programMethod);
+    }
+  }
+
+  public void modify(AppView<AppInfoWithLiveness> appView) {
+    FieldAccessInfoCollectionImpl impl = appView.appInfo().getMutableFieldAccessInfoCollection();
+    newFieldAccesses.forEach(
+        (field, info) -> {
+          FieldAccessInfoImpl fieldAccessInfo = new FieldAccessInfoImpl(field);
+          forEachFieldAccess(appView, info.readContexts, field, fieldAccessInfo::recordRead);
+          forEachFieldAccess(appView, info.writeContexts, field, fieldAccessInfo::recordWrite);
+          impl.extend(field, fieldAccessInfo);
+        });
+  }
+
+  public static class Builder {
+    final Map<DexField, FieldReferences> newFieldAccesses = new IdentityHashMap<>();
+
+    public Builder() {}
+
+    public FieldAccessInfoCollectionModifier build(Function<DexMethod, DexMethod> fixupMethod) {
+      for (FieldReferences fieldReference : newFieldAccesses.values()) {
+        fieldReference.fixUp(fixupMethod);
+      }
+      return new FieldAccessInfoCollectionModifier(newFieldAccesses);
+    }
+
+    FieldReferences getFieldReferences(DexField field) {
+      return newFieldAccesses.computeIfAbsent(field, ignore -> new FieldReferences());
+    }
+
+    public void fieldReadByMethod(DexField field, DexMethod method) {
+      getFieldReferences(field).readContexts.add(method);
+    }
+
+    public void fieldWrittenByMethod(DexField field, DexMethod method) {
+      getFieldReferences(field).writeContexts.add(method);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index ee30fce..efc610a 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -20,6 +20,8 @@
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.KeepFieldInfo.Joiner;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.Streams;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.IdentityHashMap;
@@ -160,7 +162,7 @@
   @Deprecated
   public abstract void forEachPinnedField(Consumer<DexField> consumer);
 
-  public abstract KeepInfoCollection rewrite(NestedGraphLens lens);
+  public abstract KeepInfoCollection rewrite(NestedGraphLens lens, InternalOptions options);
 
   public abstract KeepInfoCollection mutate(Consumer<MutableKeepInfoCollection> mutator);
 
@@ -196,26 +198,39 @@
     }
 
     @Override
-    public KeepInfoCollection rewrite(NestedGraphLens lens) {
+    public KeepInfoCollection rewrite(NestedGraphLens lens, InternalOptions options) {
       Map<DexType, KeepClassInfo> newClassInfo = new IdentityHashMap<>(keepClassInfo.size());
       keepClassInfo.forEach(
           (type, info) -> {
             DexType newType = lens.lookupType(type);
-            assert !info.isPinned() || type == newType;
+            assert newType == type || !info.isPinned() || info.isMinificationAllowed(options);
             newClassInfo.put(newType, info);
           });
       Map<DexMethod, KeepMethodInfo> newMethodInfo = new IdentityHashMap<>(keepMethodInfo.size());
       keepMethodInfo.forEach(
           (method, info) -> {
             DexMethod newMethod = lens.getRenamedMethodSignature(method);
-            assert !info.isPinned() || method == newMethod;
+            assert !info.isPinned()
+                || info.isMinificationAllowed(options)
+                || newMethod.name == method.name;
+            assert !info.isPinned() || newMethod.getArity() == method.getArity();
+            assert !info.isPinned()
+                || Streams.zip(
+                        newMethod.getParameters().stream(),
+                        method.getParameters().stream().map(lens::lookupType),
+                        Object::equals)
+                    .allMatch(x -> x);
+            assert !info.isPinned()
+                || newMethod.getReturnType() == lens.lookupType(method.getReturnType());
             newMethodInfo.put(newMethod, info);
           });
       Map<DexField, KeepFieldInfo> newFieldInfo = new IdentityHashMap<>(keepFieldInfo.size());
       keepFieldInfo.forEach(
           (field, info) -> {
             DexField newField = lens.getRenamedFieldSignature(field);
-            assert !info.isPinned() || field == newField;
+            assert newField.name == field.name
+                || !info.isPinned()
+                || info.isMinificationAllowed(options);
             newFieldInfo.put(newField, info);
           });
       Map<DexReference, List<Consumer<KeepInfo.Joiner<?, ?, ?>>>> newRuleInstances =
@@ -353,6 +368,13 @@
       joinMethod(holder, method, KeepInfo.Joiner::pin);
     }
 
+    public void unsafeAllowMinificationOfMethod(ProgramMethod method) {
+      KeepMethodInfo info = keepMethodInfo.get(method.getReference());
+      if (info != null && !info.internalIsMinificationAllowed()) {
+        keepMethodInfo.put(method.getReference(), info.builder().allowMinification().build());
+      }
+    }
+
     // Unpinning a method represents a non-monotonic change to the keep info of that item.
     // This is generally unsound as it requires additional analysis to determine that a method that
     // was pinned no longer is. A known sound example is the enum analysis that will identify
@@ -409,12 +431,19 @@
       }
     }
 
-    public void unsafeUnpinField(DexProgramClass holder, DexEncodedField field) {
-      assert holder.type == field.holder();
-      assert !getClassInfo(holder).isPinned();
-      KeepFieldInfo info = this.keepFieldInfo.get(field.toReference());
+    public void unsafeAllowMinificationOfField(ProgramField field) {
+      assert !getClassInfo(field.getHolder()).isPinned();
+      KeepFieldInfo info = keepFieldInfo.get(field.getReference());
+      if (info != null && !info.internalIsMinificationAllowed()) {
+        keepFieldInfo.put(field.getReference(), info.builder().allowAccessModification().build());
+      }
+    }
+
+    public void unsafeUnpinField(ProgramField field) {
+      assert !getClassInfo(field.getHolder()).isPinned();
+      KeepFieldInfo info = this.keepFieldInfo.get(field.getReference());
       if (info != null && info.isPinned()) {
-        keepFieldInfo.put(field.toReference(), info.builder().unpin().build());
+        keepFieldInfo.put(field.getReference(), info.builder().unpin().build());
       }
     }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 08830cc..db43676 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -8,13 +8,11 @@
 import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.BottomUpClassHierarchyTraversal;
 import com.android.tools.r8.graph.DexAnnotation;
 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.DexDefinition;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
@@ -29,6 +27,7 @@
 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.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.graph.SubtypingInfo;
@@ -1962,26 +1961,30 @@
       return false;
     }
 
-    public boolean verifyKeptItemsAreKept(DexApplication application, AppInfo appInfo) {
+    public boolean verifyKeptItemsAreKept(AppView<? extends AppInfoWithClassHierarchy> appView) {
+      AppInfoWithClassHierarchy appInfo = appView.appInfo();
+      GraphLens lens = appView.graphLens();
       // Create a mapping from each required type to the set of required members on that type.
       Map<DexType, Set<DexMember<?, ?>>> requiredMembersPerType = new IdentityHashMap<>();
       noShrinking.forEachClass(
           type -> {
-            assert !appInfo.hasLiveness() || appInfo.withLiveness().isPinned(type)
-                : "Expected reference `" + type.toSourceString() + "` to be pinned";
-            requiredMembersPerType.computeIfAbsent(type, key -> Sets.newIdentityHashSet());
+            DexType rewrittenType = lens.lookupType(type);
+            assert !appInfo.hasLiveness() || appInfo.withLiveness().isPinned(rewrittenType)
+                : "Expected reference `" + rewrittenType.toSourceString() + "` to be pinned";
+            requiredMembersPerType.computeIfAbsent(rewrittenType, key -> Sets.newIdentityHashSet());
           });
       noShrinking.forEachMember(
           member -> {
-            assert !appInfo.hasLiveness() || appInfo.withLiveness().isPinned(member)
-                : "Expected reference `" + member.toSourceString() + "` to be pinned";
+            DexMember<?, ?> rewrittenMember = lens.getRenamedMemberSignature(member);
+            assert !appInfo.hasLiveness() || appInfo.withLiveness().isPinned(rewrittenMember)
+                : "Expected reference `" + rewrittenMember.toSourceString() + "` to be pinned";
             requiredMembersPerType
-                .computeIfAbsent(member.holder, key -> Sets.newIdentityHashSet())
-                .add(member);
+                .computeIfAbsent(rewrittenMember.holder, key -> Sets.newIdentityHashSet())
+                .add(rewrittenMember);
           });
 
       // Run through each class in the program and check that it has members it must have.
-      for (DexProgramClass clazz : application.classes()) {
+      for (DexProgramClass clazz : appView.appInfo().classes()) {
         Set<DexMember<?, ?>> requiredMembers =
             requiredMembersPerType.getOrDefault(clazz.type, ImmutableSet.of());
 
@@ -2023,7 +2026,7 @@
       // If the map is non-empty, then a type in the root set was not in the application.
       if (!requiredMembersPerType.isEmpty()) {
         DexType type = requiredMembersPerType.keySet().iterator().next();
-        DexClass clazz = application.definitionFor(type);
+        DexClass clazz = appView.definitionFor(type);
         assert clazz == null || clazz.isProgramClass()
             : "Unexpected library type in root set: `" + type + "`";
         assert requiredMembersPerType.isEmpty()
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
new file mode 100644
index 0000000..3929e87
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -0,0 +1,14 @@
+// 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.utils;
+
+import java.util.Map;
+
+public class MapUtils {
+
+  public static <T> void removeIdentityMappings(Map<T, T> map) {
+    map.entrySet().removeIf(entry -> entry.getKey() == entry.getValue());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/AsmTestBase.java b/src/test/java/com/android/tools/r8/AsmTestBase.java
index 6195ea0..4a24331 100644
--- a/src/test/java/com/android/tools/r8/AsmTestBase.java
+++ b/src/test/java/com/android/tools/r8/AsmTestBase.java
@@ -21,18 +21,6 @@
 
 public class AsmTestBase extends TestBase {
 
-  protected void ensureException(String main, Class<? extends Throwable> exceptionClass,
-      byte[]... classes) throws Exception {
-    ensureExceptionThrown(runOnJavaRaw(main, classes), exceptionClass);
-    AndroidApp app = buildAndroidApp(classes);
-    ensureExceptionThrown(runOnArtRaw(compileWithD8(app), main), exceptionClass);
-    ensureExceptionThrown(runOnArtRaw(compileWithR8(app), main), exceptionClass);
-    ensureExceptionThrown(
-        runOnArtRaw(compileWithR8(app, keepMainProguardConfiguration(main) + "-dontobfuscate\n"),
-            main),
-        exceptionClass);
-  }
-
   protected void ensureSameOutput(String main, AndroidApiLevel apiLevel, byte[]... classes)
       throws Exception {
     ensureSameOutput(main, apiLevel, Collections.emptyList(), classes);
diff --git a/src/test/java/com/android/tools/r8/JvmTestBuilder.java b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
index e59ea66..8698d61 100644
--- a/src/test/java/com/android/tools/r8/JvmTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
@@ -167,6 +167,10 @@
     return self();
   }
 
+  public JvmTestBuilder noVerify() {
+    return addVmArguments("-noverify");
+  }
+
   public JvmTestBuilder addAndroidBuildVersion() {
     addVmArguments("-D" + AndroidBuildVersion.PROPERTY + "=10000");
     return addProgramClasses(AndroidBuildVersion.class);
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 1631830..c83cd38 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -4,6 +4,8 @@
 package com.android.tools.r8;
 
 import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.MarkerMatcher.assertMarkersMatch;
+import static com.android.tools.r8.MarkerMatcher.markerTool;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -14,11 +16,13 @@
 import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
 import com.android.tools.r8.AssertionsConfiguration.AssertionTransformationScope;
 import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.ImmutableList;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -26,7 +30,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -66,7 +69,7 @@
   }
 
   @Test
-  public void testMarker() throws Throwable {
+  public void testDexMarker() throws Throwable {
     Path output = temp.newFolder().toPath().resolve("desugar_jdk_libs.zip");
     L8.run(
         L8Command.builder()
@@ -77,14 +80,28 @@
                 StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
             .setOutput(output, OutputMode.DexIndexed)
             .build());
-    Collection<Marker> markers = ExtractMarker.extractMarkerFromDexFile(output);
-    // TODO(b/134732760): Shouldn't we remove the D8/R8 marker?
-    assertEquals(2, markers.size());
-    Marker marker = markers.iterator().next();
+    assertMarkersMatch(
+        ExtractMarker.extractMarkerFromDexFile(output),
+        ImmutableList.of(markerTool(Tool.L8), markerTool(Tool.D8)));
   }
 
   @Test
-  public void testMarkerCommandLine() throws Throwable {
+  public void testClassFileMarker() throws Throwable {
+    Path output = temp.newFolder().toPath().resolve("desugar_jdk_libs.zip");
+    L8.run(
+        L8Command.builder()
+            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+            .addProgramFiles(ToolHelper.getDesugarJDKLibs())
+            .setMinApiLevel(20)
+            .addDesugaredLibraryConfiguration(
+                StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+            .setOutput(output, OutputMode.ClassFile)
+            .build());
+    assertMarkersMatch(ExtractMarker.extractMarkerFromDexFile(output), markerTool(Tool.L8));
+  }
+
+  @Test
+  public void testDexMarkerCommandLine() throws Throwable {
     Path output = temp.newFolder().toPath().resolve("desugar_jdk_libs.zip");
     L8Command l8Command =
         parse(
@@ -99,9 +116,28 @@
             output.toString());
     L8.run(l8Command);
     Collection<Marker> markers = ExtractMarker.extractMarkerFromDexFile(output);
-    // TODO(b/134732760): Shouldn't we remove the D8/R8 marker?
-    assertEquals(2, markers.size());
-    Marker marker = markers.iterator().next();
+    assertMarkersMatch(
+        ExtractMarker.extractMarkerFromDexFile(output),
+        ImmutableList.of(markerTool(Tool.L8), markerTool(Tool.D8)));
+  }
+
+  @Test
+  public void testClassFileMarkerCommandLine() throws Throwable {
+    Path output = temp.newFolder().toPath().resolve("desugar_jdk_libs.zip");
+    L8Command l8Command =
+        parse(
+            ToolHelper.getDesugarJDKLibs().toString(),
+            "--lib",
+            ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
+            "--min-api",
+            "20",
+            "--desugared-lib",
+            ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString(),
+            "--output",
+            output.toString(),
+            "--classfile");
+    L8.run(l8Command);
+    assertMarkersMatch(ExtractMarker.extractMarkerFromDexFile(output), markerTool(Tool.L8));
   }
 
   @Test
@@ -118,6 +154,26 @@
   }
 
   @Test
+  public void testFlagPgConfWithClassFile() throws Exception {
+    TestDiagnosticMessagesImpl diagnostics = new TestDiagnosticMessagesImpl();
+    try {
+      Path pgconf = temp.newFolder().toPath().resolve("pg.conf");
+      FileUtils.writeTextFile(pgconf, "");
+      parse(
+          diagnostics,
+          "--desugared-lib",
+          ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString(),
+          "--pg-conf",
+          pgconf.toString(),
+          "--classfile");
+      fail("Expected failure");
+    } catch (CompilationFailedException e) {
+      diagnostics.assertErrorsMatch(
+          diagnosticMessage(containsString("not support shrinking when generating class files")));
+    }
+  }
+
+  @Test
   public void testFlagPgConfMissingParameter() {
     TestDiagnosticMessagesImpl diagnostics = new TestDiagnosticMessagesImpl();
     try {
@@ -162,14 +218,6 @@
   }
 
   @Test(expected = CompilationFailedException.class)
-  public void classFileOutputNotSupported() throws Throwable {
-    DiagnosticsChecker.checkErrorsContains(
-        "L8 does not support compiling to class files",
-        (handler) ->
-            prepareBuilder(handler).setProgramConsumer(ClassFileConsumer.emptyConsumer()).build());
-  }
-
-  @Test(expected = CompilationFailedException.class)
   public void desugaredLibraryConfigurationRequired() throws Throwable {
     DiagnosticsChecker.checkErrorsContains(
         "L8 requires a desugared library configuration",
@@ -177,15 +225,14 @@
             prepareBuilder(handler).setProgramConsumer(ClassFileConsumer.emptyConsumer()).build());
   }
 
-  @Test
-  @Ignore("b/143431384: Re-enable shrinking")
-  public void addProguardConfigurationString() throws Throwable {
+  private void addProguardConfigurationString(
+      DiagnosticsHandler diagnostics, ProgramConsumer programConsumer) throws Throwable {
     String keepRule = "-keep class java.time.*";
     List<String> keepRules = new ArrayList<>();
     keepRules.add(keepRule);
     L8Command.Builder builder =
-        prepareBuilder(new TestDiagnosticMessagesImpl())
-            .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+        prepareBuilder(diagnostics)
+            .setProgramConsumer(programConsumer)
             .addDesugaredLibraryConfiguration(
                 StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
             .addProguardConfiguration(keepRules, Origin.unknown());
@@ -194,15 +241,32 @@
   }
 
   @Test
-  @Ignore("b/143431384: Re-enable shrinking")
-  public void addProguardConfigurationFile() throws Throwable {
+  public void addProguardConfigurationStringWithDex() throws Throwable {
+    addProguardConfigurationString(
+        new TestDiagnosticMessagesImpl(), DexIndexedConsumer.emptyConsumer());
+  }
+
+  @Test
+  public void addProguardConfigurationStringWithClassFile() throws Throwable {
+    TestDiagnosticMessagesImpl diagnostics = new TestDiagnosticMessagesImpl();
+    try {
+      addProguardConfigurationString(diagnostics, ClassFileConsumer.emptyConsumer());
+      fail("Expected failure");
+    } catch (CompilationFailedException e) {
+      diagnostics.assertErrorsMatch(
+          diagnosticMessage(containsString("not support shrinking when generating class files")));
+    }
+  }
+
+  private void addProguardConfigurationFile(
+      DiagnosticsHandler diagnostics, ProgramConsumer programConsumer) throws Throwable {
     String keepRule = "-keep class java.time.*";
     Path keepRuleFile = temp.newFile("keepRuleFile.txt").toPath();
     Files.write(keepRuleFile, Collections.singletonList(keepRule), StandardCharsets.UTF_8);
 
     L8Command.Builder builder1 =
-        prepareBuilder(new TestDiagnosticMessagesImpl())
-            .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+        prepareBuilder(diagnostics)
+            .setProgramConsumer(programConsumer)
             .addDesugaredLibraryConfiguration(
                 StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
             .addProguardConfigurationFiles(keepRuleFile);
@@ -222,6 +286,24 @@
   }
 
   @Test
+  public void addProguardConfigurationFileDex() throws Throwable {
+    addProguardConfigurationFile(
+        new TestDiagnosticMessagesImpl(), DexIndexedConsumer.emptyConsumer());
+  }
+
+  @Test
+  public void addProguardConfigurationFileClassFile() throws Throwable {
+    TestDiagnosticMessagesImpl diagnostics = new TestDiagnosticMessagesImpl();
+    try {
+      addProguardConfigurationFile(diagnostics, ClassFileConsumer.emptyConsumer());
+      fail("Expected failure");
+    } catch (CompilationFailedException e) {
+      diagnostics.assertErrorsMatch(
+          diagnosticMessage(containsString("not support shrinking when generating class files")));
+    }
+  }
+
+  @Test
   public void desugaredLibrary() throws CompilationFailedException {
     L8Command l8Command =
         parse("--desugared-lib", ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString());
diff --git a/src/test/java/com/android/tools/r8/SingleTestRunResult.java b/src/test/java/com/android/tools/r8/SingleTestRunResult.java
index 4d72d22..c03c8e05 100644
--- a/src/test/java/com/android/tools/r8/SingleTestRunResult.java
+++ b/src/test/java/com/android/tools/r8/SingleTestRunResult.java
@@ -118,6 +118,7 @@
     return self();
   }
 
+  @Override
   public RR disassemble() throws IOException, ExecutionException {
     return disassemble(System.out);
   }
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 1bd188a..9aa9789 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -120,7 +120,11 @@
 
   public enum Backend {
     CF,
-    DEX
+    DEX;
+
+    public static Backend fromConsumer(ProgramConsumer consumer) {
+      return consumer instanceof ClassFileConsumer ? CF : DEX;
+    }
   }
 
   public static R8FullTestBuilder testForR8(TemporaryFolder temp, Backend backend) {
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 6f6263a..43b5478 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -300,6 +300,7 @@
 
   public T setProgramConsumer(ProgramConsumer programConsumer) {
     assert programConsumer != null;
+    assert backend == Backend.fromConsumer(programConsumer);
     this.programConsumer = programConsumer;
     return self();
   }
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index 75b7336..06fd76e 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -4,9 +4,11 @@
 package com.android.tools.r8;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.TestRuntime.NoneRuntime;
+import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.utils.AndroidApiLevel;
 
 // Actual test parameters for a specific configuration. Currently just the runtime configuration.
@@ -76,4 +78,9 @@
   public void assertNoneRuntime() {
     assertEquals(NoneRuntime.getInstance(), runtime);
   }
+
+  public DexVm.Version getDexRuntimeVersion() {
+    assertTrue(isDexRuntime());
+    return getRuntime().asDex().getVm().getVersion();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 3069351..98881e9 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -10,6 +10,8 @@
 import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.StringUtils;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Arrays;
@@ -75,6 +77,12 @@
     return minification(false);
   }
 
+  public T addClassObfuscationDictionary(String... names) throws IOException {
+    Path path = getState().getNewTempFolder().resolve("classobfuscationdictionary.txt");
+    FileUtils.writeTextFile(path, StringUtils.join(" ", names));
+    return addKeepRules("-classobfuscationdictionary " + path.toString());
+  }
+
   public abstract T addDataEntryResources(DataEntryResource... resources);
 
   public abstract T addKeepRuleFiles(List<Path> files);
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationProguardCompatTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationProguardCompatTest.java
index 3efb9b1..11bf740 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationProguardCompatTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationProguardCompatTest.java
@@ -10,17 +10,32 @@
 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 com.android.tools.r8.utils.codeinspector.FieldSubject;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
 /** Tests that Proguard may change the visibility of a field or method that is explicitly kept. */
+@RunWith(Parameterized.class)
 public class AccessRelaxationProguardCompatTest extends TestBase {
 
   private static Class<?> clazz = AccessRelaxationProguardCompatTestClass.class;
   private static Class<?> clazzWithGetter = TestClassWithGetter.class;
 
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public AccessRelaxationProguardCompatTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
   @Test
   public void r8Test() throws Exception {
     testForR8(Backend.DEX)
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/InvokeTypeConversionTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/InvokeTypeConversionTest.java
index 8c2adf3..209aa18 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/InvokeTypeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/InvokeTypeConversionTest.java
@@ -6,37 +6,46 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.R8Command;
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.VmTestRunner;
 import com.android.tools.r8.code.InvokeDirect;
 import com.android.tools.r8.code.InvokeVirtual;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.smali.SmaliBuilder;
 import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
 import com.android.tools.r8.smali.SmaliTestBase;
-import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
-import java.util.List;
 import java.util.function.Consumer;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
-@RunWith(VmTestRunner.class)
+@RunWith(Parameterized.class)
 public class InvokeTypeConversionTest extends SmaliTestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
   private final String CLASS_NAME = "Example";
   private MethodSignature main;
 
+  private final TestParameters parameters;
+
+  public InvokeTypeConversionTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
   private SmaliBuilder buildTestClass(String invokeLine) {
     SmaliBuilder builder = new SmaliBuilder(CLASS_NAME);
     builder.addDefaultConstructor();
@@ -63,29 +72,23 @@
       SmaliBuilder builder,
       String expectedException,
       Consumer<CodeInspector> inspectorConsumer) throws Exception {
-    AndroidApp app = buildApplication(builder);
-    List<String> pgConfigs =
-        ImmutableList.of(
-            keepMainProguardConfiguration(CLASS_NAME),
-            // We're testing lens-based invocation type conversions.
-            "-dontoptimize",
-            "-dontobfuscate",
-            "-allowaccessmodification");
-    R8Command.Builder command = ToolHelper.prepareR8CommandBuilder(app);
-    command.addProguardConfiguration(pgConfigs, Origin.unknown());
-    AndroidApp processedApp = ToolHelper.runR8(command.build(), o -> {
-      o.enableInlining = false;
-    });
-    ProcessResult artResult = runOnArtRaw(processedApp, CLASS_NAME);
+    R8TestRunResult result =
+        testForR8(parameters.getBackend())
+            .addProgramDexFileData(builder.compile())
+            .addKeepMainRule(CLASS_NAME)
+            .addKeepRules(
+                // We're testing lens-based invocation type conversions.
+                "-dontoptimize", "-dontobfuscate", "-allowaccessmodification")
+            .addOptionsModification(o -> o.enableInlining = false)
+            .setMinApi(parameters.getApiLevel())
+            .run(parameters.getRuntime(), CLASS_NAME);
     if (expectedException == null) {
-      assertEquals(0, artResult.exitCode);
-      assertEquals("0", artResult.stdout);
+      result.assertSuccessWithOutput("0");
+      result.inspect(inspectorConsumer::accept);
     } else {
-      assertEquals(1, artResult.exitCode);
-      assertThat(artResult.stderr, containsString(expectedException));
+      result.assertFailureWithErrorThatMatches(containsString(expectedException));
+      result.inspectFailure(inspectorConsumer::accept);
     }
-    CodeInspector inspector = new CodeInspector(processedApp);
-    inspectorConsumer.accept(inspector);
   }
 
   // The following test checks invoke-direct, which refers to the private static method, is *not*
@@ -105,8 +108,9 @@
     SmaliBuilder builder = buildTestClass(
         "invoke-direct { v1 }, L" + CLASS_NAME + ";->bar()I");
     String expectedError =
-        ToolHelper.getDexVm().getVersion().isOlderThanOrEqual(Version.V4_4_4)
-            ? "VerifyError" : "IncompatibleClassChangeError";
+        parameters.getRuntime().asDex().getVm().getVersion().isOlderThanOrEqual(Version.V4_4_4)
+            ? "VerifyError"
+            : "IncompatibleClassChangeError";
     run(builder, expectedError, dexInspector -> {
       ClassSubject clazz = dexInspector.clazz(CLASS_NAME);
       assertThat(clazz, isPresent());
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
index 164259f..e190074 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
@@ -28,6 +28,7 @@
 import java.util.Calendar;
 import java.util.List;
 import java.util.TreeSet;
+import java.util.function.Consumer;
 
 public abstract class MethodGenerationBase extends TestBase {
 
@@ -74,17 +75,18 @@
   }
 
   // Running this method will regenerate / overwrite the content of the generated class.
-  protected void generateMethodsAndWriteThemToFile() throws IOException {
-    FileUtils.writeToFile(getGeneratedFile(), null, generateMethods().getBytes());
+  protected void generateMethodsAndWriteThemToFile(Consumer<InternalOptions> optionsConsumer)
+      throws IOException {
+    FileUtils.writeToFile(getGeneratedFile(), null, generateMethods(optionsConsumer).getBytes());
   }
 
   // Running this method generate the content of the generated class but does not overwrite it.
-  protected String generateMethods() throws IOException {
+  protected String generateMethods(Consumer<InternalOptions> optionsConsumer) throws IOException {
     CfCodePrinter codePrinter = new CfCodePrinter();
 
     File tempFile = File.createTempFile("output-", ".java");
 
-    readMethodTemplatesInto(codePrinter);
+    readMethodTemplatesInto(codePrinter, optionsConsumer);
     generateRawOutput(codePrinter, tempFile.toPath());
     String result = formatRawOutput(tempFile.toPath());
 
@@ -92,8 +94,10 @@
     return result;
   }
 
-  private void readMethodTemplatesInto(CfCodePrinter codePrinter) throws IOException {
+  private void readMethodTemplatesInto(
+      CfCodePrinter codePrinter, Consumer<InternalOptions> optionsConsumer) throws IOException {
     InternalOptions options = new InternalOptions();
+    optionsConsumer.accept(options);
     JarClassFileReader reader =
         new JarClassFileReader(
             new JarApplicationReader(options),
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectCheckCastTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectCheckCastTest.java
new file mode 100644
index 0000000..745fc26
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectCheckCastTest.java
@@ -0,0 +1,75 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class ClassesDistinguishedByDirectCheckCastTest extends HorizontalClassMergingTestBase {
+  public ClassesDistinguishedByDirectCheckCastTest(
+      TestParameters parameters, boolean enableHorizontalClassMerging) {
+    super(parameters, enableHorizontalClassMerging);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("fail", "bar")
+        .inspect(
+            codeInspector -> {
+              // The two classes should not be merged.
+              assertThat(codeInspector.clazz(A.class), isPresent());
+              assertThat(codeInspector.clazz(B.class), isPresent());
+            });
+  }
+
+  @NeverClassInline
+  public static class A {
+    @NeverInline
+    public void foo() {
+      System.out.println("foo");
+    }
+  }
+
+  @NeverClassInline
+  public static class B {
+    @NeverInline
+    public void bar() {
+      System.out.println("bar");
+    }
+  }
+
+  public static class Main {
+    @NeverInline
+    public static void checkObject(Object o) {
+      try {
+        B b = (B) o;
+        b.bar();
+      } catch (ClassCastException ex) {
+        System.out.println("fail");
+      }
+    }
+
+    public static void main(String[] args) {
+      A a = new A();
+      B b = new B();
+      checkObject(a);
+      checkObject(b);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectInstanceOfTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectInstanceOfTest.java
new file mode 100644
index 0000000..6a61d2f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectInstanceOfTest.java
@@ -0,0 +1,74 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class ClassesDistinguishedByDirectInstanceOfTest extends HorizontalClassMergingTestBase {
+  public ClassesDistinguishedByDirectInstanceOfTest(
+      TestParameters parameters, boolean enableHorizontalClassMerging) {
+    super(parameters, enableHorizontalClassMerging);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("foo", "bar", "false", "true")
+        .inspect(
+            codeInspector -> {
+              // The two classes should not be merged.
+              assertThat(codeInspector.clazz(A.class), isPresent());
+              assertThat(codeInspector.clazz(B.class), isPresent());
+            });
+  }
+
+  @NeverClassInline
+  public static class A {
+    @NeverInline
+    public void foo() {
+      System.out.println("foo");
+    }
+  }
+
+  @NeverClassInline
+  public static class B {
+    public B(String a) {}
+
+    @NeverInline
+    public void bar() {
+      System.out.println("bar");
+    }
+  }
+
+  public static class Main {
+    @NeverInline
+    public static void checkObject(Object o) {
+      System.out.println(o instanceof B);
+    }
+
+    public static void main(String[] args) {
+      A a = new A();
+      a.foo();
+      B b = new B("hello");
+      b.bar();
+      checkObject(a);
+      checkObject(b);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByIndirectCheckCastToInterfaceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByIndirectCheckCastToInterfaceTest.java
new file mode 100644
index 0000000..edd84cb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByIndirectCheckCastToInterfaceTest.java
@@ -0,0 +1,84 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class ClassesDistinguishedByIndirectCheckCastToInterfaceTest
+    extends HorizontalClassMergingTestBase {
+  public ClassesDistinguishedByIndirectCheckCastToInterfaceTest(
+      TestParameters parameters, boolean enableHorizontalClassMerging) {
+    super(parameters, enableHorizontalClassMerging);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+        .enableMergeAnnotations()
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("fail", "bar")
+        .inspect(
+            codeInspector -> {
+              // The two classes should not be merged.
+              assertThat(codeInspector.clazz(A.class), isPresent());
+              assertThat(codeInspector.clazz(B.class), isPresent());
+            });
+  }
+
+  @NeverMerge
+  @NeverClassInline
+  public interface I {
+    void bar();
+  }
+
+  @NeverClassInline
+  public static class A {
+    @NeverInline
+    public void foo() {
+      System.out.println("foo");
+    }
+  }
+
+  @NeverClassInline
+  public static class B implements I {
+    @NeverInline
+    public void bar() {
+      System.out.println("bar");
+    }
+  }
+
+  public static class Main {
+    @NeverInline
+    public static void checkObject(Object o) {
+      try {
+        I b = (I) o;
+        b.bar();
+      } catch (ClassCastException ex) {
+        System.out.println("fail");
+      }
+    }
+
+    public static void main(String[] args) {
+      A a = new A();
+      B b = new B();
+      checkObject(a);
+      checkObject(b);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistuingishedByIndirectInstanceOfInterfaceCheckCast.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistuingishedByIndirectInstanceOfInterfaceCheckCast.java
new file mode 100644
index 0000000..c6f58af
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistuingishedByIndirectInstanceOfInterfaceCheckCast.java
@@ -0,0 +1,79 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class ClassesDistuingishedByIndirectInstanceOfInterfaceCheckCast
+    extends HorizontalClassMergingTestBase {
+  public ClassesDistuingishedByIndirectInstanceOfInterfaceCheckCast(
+      TestParameters parameters, boolean enableHorizontalClassMerging) {
+    super(parameters, enableHorizontalClassMerging);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+        .enableInliningAnnotations()
+        .enableMergeAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("false", "true")
+        .inspect(
+            codeInspector -> {
+              // The two classes should not be merged.
+              assertThat(codeInspector.clazz(A.class), isPresent());
+              assertThat(codeInspector.clazz(B.class), isPresent());
+            });
+  }
+
+  @NeverClassInline
+  @NeverMerge
+  public interface I {
+    void bar();
+  }
+
+  @NeverClassInline
+  public static class A {
+    @NeverInline
+    public void foo() {
+      System.out.println("foo");
+    }
+  }
+
+  @NeverClassInline
+  public static class B implements I {
+    @NeverInline
+    public void bar() {
+      System.out.println("bar");
+    }
+  }
+
+  public static class Main {
+    @NeverInline
+    public static void checkObject(Object o) {
+      System.out.println(o instanceof I);
+    }
+
+    public static void main(String[] args) {
+      A a = new A();
+      B b = new B();
+      checkObject(a);
+      checkObject(b);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTest.java
new file mode 100644
index 0000000..b1d77c2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTest.java
@@ -0,0 +1,64 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNot.not;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class ConstructorMergingTest extends HorizontalClassMergingTestBase {
+  public ConstructorMergingTest(TestParameters parameters, boolean enableHorizontalClassMerging) {
+    super(parameters, enableHorizontalClassMerging);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("foo", "bar")
+        .inspect(
+            codeInspector -> {
+              if (enableHorizontalClassMerging) {
+                assertThat(codeInspector.clazz(A.class), isPresent());
+                assertThat(codeInspector.clazz(B.class), not(isPresent()));
+                // TODO(b/165517236): Explicitly check classes have been merged.
+              } else {
+                assertThat(codeInspector.clazz(A.class), isPresent());
+                assertThat(codeInspector.clazz(B.class), isPresent());
+              }
+            });
+  }
+
+  @NeverClassInline
+  public static class A {
+    public A() {
+      System.out.println("foo");
+    }
+  }
+
+  @NeverClassInline
+  public static class B {
+    public B() {
+      System.out.println("bar");
+    }
+  }
+
+  public static class Main {
+    public static void main(String[] args) {
+      A a = new A();
+      B b = new B();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithArgumentsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithArgumentsTest.java
new file mode 100644
index 0000000..a1b2d1a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithArgumentsTest.java
@@ -0,0 +1,65 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNot.not;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class ConstructorMergingWithArgumentsTest extends HorizontalClassMergingTestBase {
+  public ConstructorMergingWithArgumentsTest(
+      TestParameters parameters, boolean enableHorizontalClassMerging) {
+    super(parameters, enableHorizontalClassMerging);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("foo hello", "bar world")
+        .inspect(
+            codeInspector -> {
+              if (enableHorizontalClassMerging) {
+                assertThat(codeInspector.clazz(A.class), isPresent());
+                assertThat(codeInspector.clazz(B.class), not(isPresent()));
+                // TODO(b/165517236): Explicitly check classes have been merged.
+              } else {
+                assertThat(codeInspector.clazz(A.class), isPresent());
+                assertThat(codeInspector.clazz(B.class), isPresent());
+              }
+            });
+  }
+
+  @NeverClassInline
+  public static class A {
+    public A(String arg) {
+      System.out.println("foo " + arg);
+    }
+  }
+
+  @NeverClassInline
+  public static class B {
+    public B(String arg) {
+      System.out.println("bar " + arg);
+    }
+  }
+
+  public static class Main {
+    public static void main(String[] args) {
+      A a = new A("hello");
+      B b = new B("world");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/horizontalclassmerging/EmptyClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EmptyClassTest.java
similarity index 70%
rename from src/test/java/com/android/tools/r8/shaking/horizontalclassmerging/EmptyClassTest.java
rename to src/test/java/com/android/tools/r8/classmerging/horizontal/EmptyClassTest.java
index e0d9a33..ffdae33 100644
--- a/src/test/java/com/android/tools/r8/shaking/horizontalclassmerging/EmptyClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EmptyClassTest.java
@@ -2,41 +2,25 @@
 // 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.shaking.horizontalclassmerging;
+package com.android.tools.r8.classmerging.horizontal;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.IsNot.not;
 
 import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.BooleanUtils;
-import java.util.List;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
-@RunWith(Parameterized.class)
-public class EmptyClassTest extends TestBase {
-  private final TestParameters parameters;
-  private final boolean enableHorizontalClassMerging;
-
+public class EmptyClassTest extends HorizontalClassMergingTestBase {
   public EmptyClassTest(TestParameters parameters, boolean enableHorizontalClassMerging) {
-    this.parameters = parameters;
-    this.enableHorizontalClassMerging = enableHorizontalClassMerging;
-  }
-
-  @Parameterized.Parameters(name = "{0}, horizontalClassMerging:{1}")
-  public static List<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+    super(parameters, enableHorizontalClassMerging);
   }
 
   @Test
   public void testR8() throws Exception {
     testForR8(parameters.getBackend())
-        .addInnerClasses(EmptyClassTest.class)
+        .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
             options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingTestBase.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingTestBase.java
new file mode 100644
index 0000000..72e016b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingTestBase.java
@@ -0,0 +1,30 @@
+// 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.classmerging.horizontal;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public abstract class HorizontalClassMergingTestBase extends TestBase {
+  protected final TestParameters parameters;
+  protected final boolean enableHorizontalClassMerging;
+
+  protected HorizontalClassMergingTestBase(
+      TestParameters parameters, boolean enableHorizontalClassMerging) {
+    this.parameters = parameters;
+    this.enableHorizontalClassMerging = enableHorizontalClassMerging;
+  }
+
+  @Parameterized.Parameters(name = "{0}, horizontalClassMerging:{1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/horizontalclassmerging/IdenticalFieldMembersTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/IdenticalFieldMembersTest.java
similarity index 74%
rename from src/test/java/com/android/tools/r8/shaking/horizontalclassmerging/IdenticalFieldMembersTest.java
rename to src/test/java/com/android/tools/r8/classmerging/horizontal/IdenticalFieldMembersTest.java
index 7935860..1cb2d2d 100644
--- a/src/test/java/com/android/tools/r8/shaking/horizontalclassmerging/IdenticalFieldMembersTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/IdenticalFieldMembersTest.java
@@ -2,39 +2,24 @@
 // 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.shaking.horizontalclassmerging;
+package com.android.tools.r8.classmerging.horizontal;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.*;
-import com.android.tools.r8.utils.BooleanUtils;
-import java.util.List;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
-@RunWith(Parameterized.class)
-public class IdenticalFieldMembersTest extends TestBase {
-  private final TestParameters parameters;
-  private final boolean enableHorizontalClassMerging;
-
+public class IdenticalFieldMembersTest extends HorizontalClassMergingTestBase {
   public IdenticalFieldMembersTest(
       TestParameters parameters, boolean enableHorizontalClassMerging) {
-    this.parameters = parameters;
-    this.enableHorizontalClassMerging = enableHorizontalClassMerging;
-  }
-
-  @Parameterized.Parameters(name = "{0}, horizontalClassMerging:{1}")
-  public static List<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+    super(parameters, enableHorizontalClassMerging);
   }
 
   @Test
   public void testR8() throws Exception {
     testForR8(parameters.getBackend())
-        .addInnerClasses(IdenticalFieldMembersTest.class)
+        .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
             options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
new file mode 100644
index 0000000..226af98
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
@@ -0,0 +1,79 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNot.not;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.ConstructorMergingTest.A;
+import com.android.tools.r8.classmerging.horizontal.ConstructorMergingTest.B;
+import com.android.tools.r8.classmerging.horizontal.ConstructorMergingTest.Main;
+import org.junit.Test;
+
+public class MergedConstructorForwardingTest extends HorizontalClassMergingTestBase {
+
+  public MergedConstructorForwardingTest(
+      TestParameters parameters, boolean enableHorizontalClassMerging) {
+    super(parameters, enableHorizontalClassMerging);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("42", "13", "21", "39")
+        .inspect(
+            codeInspector -> {
+              if (enableHorizontalClassMerging) {
+                assertThat(codeInspector.clazz(A.class), isPresent());
+                assertThat(codeInspector.clazz(B.class), not(isPresent()));
+                // TODO(b/165517236): Explicitly check classes have been merged.
+              } else {
+                assertThat(codeInspector.clazz(A.class), isPresent());
+                assertThat(codeInspector.clazz(B.class), isPresent());
+              }
+            });
+  }
+
+  @NeverClassInline
+  public static class A {
+    public A() {
+      this(42);
+    }
+
+    public A(long x) {
+      System.out.println(x);
+    }
+  }
+
+  @NeverClassInline
+  public static class B {
+    public B() {
+      this(7);
+    }
+
+    public B(long y) {
+      System.out.println(y * 3);
+    }
+  }
+
+  public static class Main {
+    public static void main(String[] args) {
+      A a = new A();
+      a = new A(13);
+      B b = new B();
+      b = new B(13);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/horizontalclassmerging/NoOverlappingConstructorsPolicyTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/OverlappingConstructorsTest.java
similarity index 65%
rename from src/test/java/com/android/tools/r8/shaking/horizontalclassmerging/NoOverlappingConstructorsPolicyTest.java
rename to src/test/java/com/android/tools/r8/classmerging/horizontal/OverlappingConstructorsTest.java
index e646087..fbd587d 100644
--- a/src/test/java/com/android/tools/r8/shaking/horizontalclassmerging/NoOverlappingConstructorsPolicyTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/OverlappingConstructorsTest.java
@@ -2,45 +2,27 @@
 // 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.shaking.horizontalclassmerging;
+package com.android.tools.r8.classmerging.horizontal;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.IsNot.not;
 
 import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.shaking.horizontalclassmerging.EmptyClassTest.A;
-import com.android.tools.r8.shaking.horizontalclassmerging.EmptyClassTest.B;
-import com.android.tools.r8.shaking.horizontalclassmerging.EmptyClassTest.Main;
-import com.android.tools.r8.utils.BooleanUtils;
-import java.util.List;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
-@RunWith(Parameterized.class)
-public class NoOverlappingConstructorsPolicyTest extends TestBase {
-  private final TestParameters parameters;
-  private final boolean enableHorizontalClassMerging;
+public class OverlappingConstructorsTest extends HorizontalClassMergingTestBase {
 
-  public NoOverlappingConstructorsPolicyTest(
+  public OverlappingConstructorsTest(
       TestParameters parameters, boolean enableHorizontalClassMerging) {
-    this.parameters = parameters;
-    this.enableHorizontalClassMerging = enableHorizontalClassMerging;
-  }
-
-  @Parameterized.Parameters(name = "{0}, horizontalClassMerging:{1}")
-  public static List<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+    super(parameters, enableHorizontalClassMerging);
   }
 
   @Test
   public void testR8() throws Exception {
     testForR8(parameters.getBackend())
-        .addInnerClasses(this.getClass())
+        .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
             options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
@@ -51,7 +33,7 @@
             codeInspector -> {
               if (enableHorizontalClassMerging) {
                 assertThat(codeInspector.clazz(A.class), isPresent());
-                assertThat(codeInspector.clazz(B.class), isPresent());
+                assertThat(codeInspector.clazz(B.class), not(isPresent()));
                 assertThat(codeInspector.clazz(C.class), not(isPresent()));
                 // TODO(b/165517236): Explicitly check classes have been merged.
               } else {
diff --git a/src/test/java/com/android/tools/r8/shaking/horizontalclassmerging/RemapMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java
similarity index 73%
rename from src/test/java/com/android/tools/r8/shaking/horizontalclassmerging/RemapMethodTest.java
rename to src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java
index 97df366..f0814bd 100644
--- a/src/test/java/com/android/tools/r8/shaking/horizontalclassmerging/RemapMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java
@@ -2,7 +2,7 @@
 // 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.shaking.horizontalclassmerging;
+package com.android.tools.r8.classmerging.horizontal;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -10,46 +10,29 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.BooleanUtils;
-import java.util.List;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
-@RunWith(Parameterized.class)
-public class RemapMethodTest extends TestBase {
-  private final TestParameters parameters;
-  private final boolean enableHorizontalClassMerging;
-
+public class RemapMethodTest extends HorizontalClassMergingTestBase {
   public RemapMethodTest(TestParameters parameters, boolean enableHorizontalClassMerging) {
-    this.parameters = parameters;
-    this.enableHorizontalClassMerging = enableHorizontalClassMerging;
-  }
-
-  @Parameterized.Parameters(name = "{0}, horizontalClassMerging:{1}")
-  public static List<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+    super(parameters, enableHorizontalClassMerging);
   }
 
   @Test
   public void testR8() throws Exception {
     testForR8(parameters.getBackend())
-        .addInnerClasses(this.getClass())
+        .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
             options -> {
               options.enableHorizontalClassMerging = enableHorizontalClassMerging;
-              options.enableVerticalClassMerging = false;
             })
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
-        // .run(parameters.getRuntime(), Main.class)
-        // .assertSuccessWithOutputLines("foo", "foo", "bar", "bar")
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("foo", "foo", "bar", "bar")
         .inspect(
             codeInspector -> {
               assertThat(codeInspector.clazz(A.class), isPresent());
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticConstructorArgumentsMerged.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticConstructorArgumentsMerged.java
new file mode 100644
index 0000000..59ea0b2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticConstructorArgumentsMerged.java
@@ -0,0 +1,78 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNot.not;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class SyntheticConstructorArgumentsMerged extends HorizontalClassMergingTestBase {
+  public SyntheticConstructorArgumentsMerged(
+      TestParameters parameters, boolean enableHorizontalClassMerging) {
+    super(parameters, enableHorizontalClassMerging);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options -> {
+              options.enableHorizontalClassMerging = enableHorizontalClassMerging;
+            })
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("5", "42")
+        .inspect(
+            codeInspector -> {
+              if (enableHorizontalClassMerging) {
+                assertThat(codeInspector.clazz(A.class), isPresent());
+                assertThat(codeInspector.clazz(B.class), not(isPresent()));
+                // TODO(b/165517236): Explicitly check classes have been merged.
+              } else {
+                assertThat(codeInspector.clazz(A.class), isPresent());
+                assertThat(codeInspector.clazz(B.class), isPresent());
+              }
+            });
+  }
+
+  @NeverClassInline
+  public static class A {
+    public A(B b) {
+      b.print(42);
+    }
+  }
+
+  @NeverClassInline
+  public static class B {
+    public B(B b) {
+      if (b != null) {
+        b.print(5);
+      }
+    }
+
+    @NeverInline
+    void print(int v) {
+      System.out.println(v);
+    }
+  }
+
+  public static class Main {
+    public static void main(String[] args) {
+      B b = new B(null);
+      B b1 = new B(b);
+      A a = new A(b);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/horizontalclassmerging/VirtualMethodNotOverlappingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodNotOverlappingTest.java
similarity index 72%
rename from src/test/java/com/android/tools/r8/shaking/horizontalclassmerging/VirtualMethodNotOverlappingTest.java
rename to src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodNotOverlappingTest.java
index 711f217..c80c1ef 100644
--- a/src/test/java/com/android/tools/r8/shaking/horizontalclassmerging/VirtualMethodNotOverlappingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodNotOverlappingTest.java
@@ -2,40 +2,25 @@
 // 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.shaking.horizontalclassmerging;
+package com.android.tools.r8.classmerging.horizontal;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.IsNot.not;
 
 import com.android.tools.r8.*;
-import com.android.tools.r8.utils.BooleanUtils;
-import java.util.*;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
-@RunWith(Parameterized.class)
-public class VirtualMethodNotOverlappingTest extends TestBase {
-  private final TestParameters parameters;
-  private final boolean enableHorizontalClassMerging;
-
+public class VirtualMethodNotOverlappingTest extends HorizontalClassMergingTestBase {
   public VirtualMethodNotOverlappingTest(
       TestParameters parameters, boolean enableHorizontalClassMerging) {
-    this.parameters = parameters;
-    this.enableHorizontalClassMerging = enableHorizontalClassMerging;
-  }
-
-  @Parameterized.Parameters(name = "{0}, horizontalClassMerging:{1}")
-  public static List<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+    super(parameters, enableHorizontalClassMerging);
   }
 
   @Test
   public void testR8() throws Exception {
     testForR8(parameters.getBackend())
-        .addInnerClasses(VirtualMethodNotOverlappingTest.class)
+        .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
             options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
index e42f33d..db4d3df 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
@@ -5,11 +5,18 @@
 package com.android.tools.r8.desugar.desugaredlibrary;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.L8Command;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.StringResource;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
@@ -27,6 +34,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
+import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -51,8 +59,8 @@
     return buildParameters(
         BooleanUtils.values(),
         getTestParameters()
-            .withDexRuntimes()
-            .withAllApiLevels()
+            .withAllRuntimes()
+            .withAllApiLevelsAlsoForCf()
             .withApiLevel(AndroidApiLevel.N)
             .build());
   }
@@ -126,30 +134,62 @@
             .inspect(this::checkRewrittenInvokes)
             .writeToZip();
 
-    // Collection keep rules is only implemented in the DEX writer.
-    String desugaredLibraryKeepRules = keepRuleConsumer.get();
-    if (desugaredLibraryKeepRules != null) {
-      assertEquals(0, desugaredLibraryKeepRules.length());
-      desugaredLibraryKeepRules = "-keep class * { *; }";
-    }
+    if (parameters.getRuntime().isDex()) {
+      // Collection keep rules is only implemented in the DEX writer.
+      String desugaredLibraryKeepRules = keepRuleConsumer.get();
+      if (desugaredLibraryKeepRules != null) {
+        assertEquals(0, desugaredLibraryKeepRules.length());
+        desugaredLibraryKeepRules = "-keep class * { *; }";
+      }
 
-    // Convert to DEX without desugaring.
-    testForD8()
-        .addProgramFiles(jar)
-        .setMinApi(parameters.getApiLevel())
-        .disableDesugaring()
-        .compile()
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibrary,
-            parameters.getApiLevel(),
-            desugaredLibraryKeepRules,
-            shrinkDesugaredLibrary)
-        .run(parameters.getRuntime(), TestClass.class)
-        .assertSuccessWithOutput(expectedOutput);
+      // Convert to DEX without desugaring and run.
+      testForD8()
+          .addProgramFiles(jar)
+          .setMinApi(parameters.getApiLevel())
+          .disableDesugaring()
+          .compile()
+          .addDesugaredCoreLibraryRunClassPath(
+              this::buildDesugaredLibrary,
+              parameters.getApiLevel(),
+              desugaredLibraryKeepRules,
+              shrinkDesugaredLibrary)
+          .run(parameters.getRuntime(), TestClass.class)
+          .assertSuccessWithOutput(expectedOutput);
+    } else {
+      // Build the desugared library in class file format.
+      Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs.jar");
+      L8Command.Builder l8Builder =
+          L8Command.builder()
+              .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+              .addProgramFiles(ToolHelper.getDesugarJDKLibs())
+              .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
+              .setMode(CompilationMode.DEBUG)
+              .addDesugaredLibraryConfiguration(
+                  StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+              .setMinApiLevel(parameters.getApiLevel().getLevel())
+              .setOutput(desugaredLib, OutputMode.ClassFile);
+      ToolHelper.runL8(l8Builder.build());
+
+      // Run on the JVM with desuagred library on classpath.
+      TestRunResult result =
+          testForJvm()
+              .addProgramFiles(jar)
+              .addRunClasspathFiles(desugaredLib)
+              .run(parameters.getRuntime(), TestClass.class);
+      if (parameters.getApiLevel().isGreaterThan(AndroidApiLevel.N_MR1)) {
+        // java.time is present from O, so the desugared library classes are not loaded.
+        result.assertSuccessWithOutput(expectedOutput);
+      } else {
+        // TODO(b/164396438): Produce correct stack map.
+        result.assertFailureWithErrorThatMatches(
+            containsString("java.lang.VerifyError: Bad type on operand stack"));
+      }
+    }
   }
 
   @Test
   public void testTimeD8() throws Exception {
+    Assume.assumeTrue(parameters.getRuntime().isDex());
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForD8()
         .addInnerClasses(JavaTimeTest.class)
@@ -168,6 +208,7 @@
 
   @Test
   public void testTimeR8() throws Exception {
+    Assume.assumeTrue(parameters.getRuntime().isDex());
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForR8(parameters.getBackend())
         .addInnerClasses(JavaTimeTest.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java
index 2275a44..2b84919 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java
@@ -66,7 +66,6 @@
   }
 
   private Path buildSplitDesugaredLibraryPart2() throws Exception {
-    Path outputCf = temp.newFolder().toPath().resolve("merger-input-split-cf.zip");
     Path outputDex = temp.newFolder().toPath().resolve("merger-input-split-dex.zip");
     L8.run(
         L8Command.builder()
@@ -76,13 +75,8 @@
             .addDesugaredLibraryConfiguration(
                 StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
             .setMinApiLevel(AndroidApiLevel.B.getLevel())
-            .setOutput(outputCf, OutputMode.DexIndexed)
+            .setOutput(outputDex, OutputMode.DexIndexed)
             .build());
-    testForD8()
-        .addProgramFiles(outputCf)
-        .setMinApi(AndroidApiLevel.B)
-        .compile()
-        .writeToZip(outputDex);
     return outputDex;
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphTestConsumer.java b/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphTestConsumer.java
index 186b01b..cb0da77 100644
--- a/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphTestConsumer.java
+++ b/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphTestConsumer.java
@@ -8,26 +8,74 @@
 
 import com.android.tools.r8.DesugarGraphConsumer;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import com.android.tools.r8.utils.WorkList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Function;
 
 public class DesugarGraphTestConsumer implements DesugarGraphConsumer {
 
   private boolean finished = false;
-  private Map<Origin, Set<Origin>> edges = new HashMap<>();
+
+  // Map from a dependency to its immediate dependents.
+  private final Map<Origin, Set<Origin>> dependents = new HashMap<>();
+
+  // Map from a dependent to its immedate dependencies.
+  private Map<Origin, Set<Origin>> dependencies = null;
+
+  @Override
+  public String toString() {
+    if (dependents.isEmpty()) {
+      return "<empty>";
+    }
+    StringBuilder builder = new StringBuilder();
+    dependents.forEach(
+        (k, vs) ->
+            StringUtils.append(builder.append(k).append(" -> "), vs, ", ", BraceType.TUBORG)
+                .append("\n"));
+    return builder.toString();
+  }
+
+  public Set<Origin> getDirectDependencies(Origin dependent) {
+    return Collections.unmodifiableSet(
+        dependencies.getOrDefault(dependent, Collections.emptySet()));
+  }
+
+  public Set<Origin> getDirectDependents(Origin dependency) {
+    return Collections.unmodifiableSet(dependents.getOrDefault(dependency, Collections.emptySet()));
+  }
+
+  public Set<Origin> getTransitiveDependencies(Origin dependent) {
+    return getTransitiveClosure(dependent, this::getDirectDependencies);
+  }
+
+  public Set<Origin> getTransitiveDependents(Origin dependency) {
+    return getTransitiveClosure(dependency, this::getDirectDependents);
+  }
+
+  private static Set<Origin> getTransitiveClosure(
+      Origin item, Function<Origin, Set<Origin>> edges) {
+    WorkList<Origin> worklist = WorkList.newEqualityWorkList(edges.apply(item));
+    while (worklist.hasNext()) {
+      worklist.addIfNotSeen(edges.apply(worklist.next()));
+    }
+    return worklist.getSeenSet();
+  }
 
   public boolean contains(Origin dependency, Origin dependent) {
     assertTrue(finished);
-    return edges.getOrDefault(dependency, Collections.emptySet()).contains(dependent);
+    return dependents.getOrDefault(dependency, Collections.emptySet()).contains(dependent);
   }
 
   public int totalEdgeCount() {
     assertTrue(finished);
     int count = 0;
-    for (Set<Origin> dependents : edges.values()) {
+    for (Set<Origin> dependents : dependents.values()) {
       count += dependents.size();
     }
     return count;
@@ -36,12 +84,18 @@
   @Override
   public synchronized void accept(Origin dependent, Origin dependency) {
     assertFalse(finished);
-    edges.computeIfAbsent(dependency, s -> new HashSet<>()).add(dependent);
+    dependents.computeIfAbsent(dependency, s -> new HashSet<>()).add(dependent);
   }
 
   @Override
   public void finished() {
     assertFalse(finished);
     finished = true;
+    dependencies = new HashMap<>();
+    dependents.forEach(
+        (dependency, dependents) ->
+            dependents.forEach(
+                dependent ->
+                    dependencies.computeIfAbsent(dependent, k -> new HashSet<>()).add(dependency)));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphUtils.java b/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphUtils.java
index 937540e..480f81d 100644
--- a/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphUtils.java
+++ b/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphUtils.java
@@ -3,10 +3,18 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.desugar.graph;
 
+import com.android.tools.r8.ClassFileResourceProvider;
 import com.android.tools.r8.D8TestBuilder;
+import com.android.tools.r8.ProgramResource;
+import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.DescriptorUtils;
 import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
 
 public class DesugarGraphUtils {
 
@@ -21,6 +29,8 @@
     return origin;
   }
 
+  private final Map<String, Origin> origins = new HashMap<>();
+
   private static Origin makeOrigin(String name) {
     return new Origin(Origin.root()) {
       @Override
@@ -29,4 +39,46 @@
       }
     };
   }
+
+  public Origin origin(String typeName) {
+    return origins.computeIfAbsent(typeName, DesugarGraphUtils::makeOrigin);
+  }
+
+  public Origin origin(Class<?> clazz) {
+    return origin(clazz.getTypeName());
+  }
+
+  public void addProgramClasses(D8TestBuilder builder, Class<?>... classes) throws IOException {
+    for (Class<?> clazz : classes) {
+      builder.getBuilder().addClassProgramData(ToolHelper.getClassAsBytes(clazz), origin(clazz));
+    }
+  }
+
+  public void addClasspathClass(D8TestBuilder builder, Class<?>... classes) throws IOException {
+    Map<String, ProgramResource> map = new HashMap<>();
+    for (Class<?> clazz : classes) {
+      String descriptor = DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName());
+      map.put(
+          descriptor,
+          ProgramResource.fromBytes(
+              origin(clazz),
+              Kind.CF,
+              ToolHelper.getClassAsBytes(clazz),
+              Collections.singleton(descriptor)));
+    }
+    builder
+        .getBuilder()
+        .addClasspathResourceProvider(
+            new ClassFileResourceProvider() {
+              @Override
+              public Set<String> getClassDescriptors() {
+                return map.keySet();
+              }
+
+              @Override
+              public ProgramResource getProgramResource(String descriptor) {
+                return map.get(descriptor);
+              }
+            });
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/Regress167562221Test.java b/src/test/java/com/android/tools/r8/desugar/graph/Regress167562221Test.java
new file mode 100644
index 0000000..650338c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/graph/Regress167562221Test.java
@@ -0,0 +1,74 @@
+// 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.desugar.graph;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableSet;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress167562221Test extends TestBase {
+
+  // Classpath classes:
+
+  public interface I {}
+
+  public interface J extends I {}
+
+  // Does not contribute to C so won't be in any edges.
+  public interface K extends I {}
+
+  public static class A implements J {}
+
+  // Does not contribute to C so won't be in any edges.
+  public static class B implements K {}
+
+  // Program class:
+
+  public static class C extends A {}
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public Regress167562221Test(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
+  @Test
+  public void repro() throws Throwable {
+    DesugarGraphUtils utils = new DesugarGraphUtils();
+    DesugarGraphTestConsumer consumer = new DesugarGraphTestConsumer();
+    testForD8()
+        .apply(b -> b.getBuilder().setDesugarGraphConsumer(consumer))
+        .apply(b -> utils.addClasspathClass(b, I.class, J.class, K.class, A.class, B.class))
+        .apply(b -> utils.addProgramClasses(b, C.class))
+        .setMinApi(AndroidApiLevel.B)
+        .compile();
+
+    assertEquals(
+        ImmutableSet.of(utils.origin(A.class)),
+        consumer.getDirectDependencies(utils.origin(C.class)));
+
+    assertEquals(
+        ImmutableSet.of(utils.origin(A.class), utils.origin(J.class), utils.origin(I.class)),
+        consumer.getTransitiveDependencies(utils.origin(C.class)));
+
+    // Check that the unrelated CP types are not in the graph.
+
+    assertEquals(ImmutableSet.of(), consumer.getTransitiveDependencies(utils.origin(K.class)));
+    assertEquals(ImmutableSet.of(), consumer.getTransitiveDependencies(utils.origin(B.class)));
+
+    assertEquals(ImmutableSet.of(), consumer.getTransitiveDependents(utils.origin(K.class)));
+    assertEquals(ImmutableSet.of(), consumer.getTransitiveDependents(utils.origin(B.class)));
+  }
+}
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 4106cd58..f147d3b 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
@@ -3,15 +3,58 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.desugar.staticinterfacemethod;
 
-import com.android.tools.r8.AsmTestBase;
-import org.junit.Ignore;
+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;
+import org.junit.runners.Parameterized.Parameters;
 
-public class MissingMethodTest extends AsmTestBase {
+@RunWith(Parameterized.class)
+public class MissingMethodTest extends TestBase {
 
-  @Ignore("b/69835274")
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  private final TestParameters parameters;
+
+  public MissingMethodTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
   @Test
-  public void testDesugarInvokeMissingMethod() throws Exception {
-    ensureException("Main", NoSuchMethodError.class, InterfaceDump.dump(), MainDump.dump());
+  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);
+    }
+  }
+
+  @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);
+    }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
index 49e7256..5cb94de 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -9,6 +9,8 @@
 import com.android.tools.r8.code.ConstString;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexApplication;
@@ -152,7 +154,7 @@
     ApplicationWriter writer =
         new ApplicationWriter(
             application,
-            null,
+            AppView.createForD8(AppInfo.createInitialAppInfo(application)),
             options,
             null,
             GraphLens.getIdentityLens(),
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ClassAccessEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ClassAccessEnumUnboxingTest.java
new file mode 100644
index 0000000..a39e30f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ClassAccessEnumUnboxingTest.java
@@ -0,0 +1,155 @@
+// 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.enumunboxing;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ClassAccessEnumUnboxingTest extends EnumUnboxingTestBase {
+
+  private final TestParameters parameters;
+  private final boolean enumValueOptimization;
+  private final EnumKeepRules enumKeepRules;
+
+  @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+  public static List<Object[]> data() {
+    return enumUnboxingTestParameters();
+  }
+
+  public ClassAccessEnumUnboxingTest(
+      TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
+    this.parameters = parameters;
+    this.enumValueOptimization = enumValueOptimization;
+    this.enumKeepRules = enumKeepRules;
+  }
+
+  @Test
+  public void testEnumUnboxing() throws Exception {
+    Assume.assumeTrue("studio rules required to use valueOf", enumKeepRules.isStudio());
+    R8TestRunResult run =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(ClassAccessEnumUnboxingTest.class)
+            .addKeepMainRule(Main.class)
+            .addKeepRules(enumKeepRules.getKeepRules())
+            .enableNeverClassInliningAnnotations()
+            .enableInliningAnnotations()
+            .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+            .allowDiagnosticInfoMessages()
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .inspectDiagnosticMessages(
+                m -> {
+                  assertEnumIsUnboxed(ProtoEnumLike.class, ProtoEnumLike.class.getSimpleName(), m);
+                  assertEnumIsUnboxed(UnboxableEnum.class, UnboxableEnum.class.getSimpleName(), m);
+                  assertEnumIsBoxed(EscapingEnum1.class, EscapingEnum1.class.getSimpleName(), m);
+                  assertEnumIsBoxed(EscapingEnum2.class, EscapingEnum2.class.getSimpleName(), m);
+                })
+            .run(parameters.getRuntime(), Main.class)
+            .assertSuccess();
+    assertLines2By2Correct(run.getStdOut());
+  }
+
+  @NeverClassInline
+  enum ProtoEnumLike {
+    A,
+    B,
+    C;
+
+    @Override
+    @NeverInline
+    public String toString() {
+      return "an instance of " + this.getClass().getName();
+    }
+  }
+
+  @NeverClassInline
+  enum UnboxableEnum {
+    A,
+    B,
+    C;
+
+    @Override
+    @NeverInline
+    public String toString() {
+      return "an instance of "
+          + this.getClass().getName()
+          + this.getClass().getSimpleName()
+          + this.getClass().getCanonicalName();
+    }
+  }
+
+  @NeverClassInline
+  enum EscapingEnum1 {
+    A,
+    B,
+    C;
+  }
+
+  @NeverClassInline
+  enum EscapingEnum2 {
+    A,
+    B,
+    C;
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      unboxableProtoCase();
+      unboxableCase();
+      nonUnboxableCase();
+    }
+
+    @NeverInline
+    private static void unboxableCase() {
+      System.out.println(UnboxableEnum.A.ordinal());
+      System.out.println(0);
+      System.out.println(UnboxableEnum.A.toString());
+      System.out.println("an instance of intintint");
+    }
+
+    @NeverInline
+    private static void unboxableProtoCase() {
+      System.out.println(ProtoEnumLike.A.ordinal());
+      System.out.println(0);
+      System.out.println(ProtoEnumLike.A.toString());
+      System.out.println("an instance of int");
+    }
+
+    @NeverInline
+    private static void nonUnboxableCase() {
+      System.out.println(EscapingEnum1.A.ordinal());
+      System.out.println(0);
+      System.out.println(EscapingEnum2.A.ordinal());
+      System.out.println(0);
+      System.out.println(EscapingEnum1.B.ordinal());
+      System.out.println(1);
+      System.out.println(EscapingEnum2.B.ordinal());
+      System.out.println(1);
+      System.out.println(getEnum(EscapingEnum1.class, "A").ordinal());
+      System.out.println(0);
+      System.out.println(getEnum(EscapingEnum2.class, "A").ordinal());
+      System.out.println(0);
+      System.out.println(getEnum(EscapingEnum1.class, "B").ordinal());
+      System.out.println(1);
+      System.out.println(getEnum(EscapingEnum2.class, "B").ordinal());
+      System.out.println(1);
+    }
+
+    @NeverInline
+    private static <T extends Enum<T>> Enum<T> getEnum(Class<T> enumClass, String value) {
+      return Enum.valueOf(enumClass, value);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingMethods.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingMethods.java
index e1d51cc..cc2aa0c 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingMethods.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingMethods.java
@@ -51,4 +51,17 @@
     }
     return unboxedEnum1 == unboxedEnum2;
   }
+
+  // Methods zeroCheck and zeroCheckMessage are used to replace null checks on unboxed enums.
+  public static void zeroCheck(int unboxedEnum) {
+    if (unboxedEnum == 0) {
+      throw new NullPointerException();
+    }
+  }
+
+  public static void zeroCheckMessage(int unboxedEnum, String message) {
+    if (unboxedEnum == 0) {
+      throw new NullPointerException(message);
+    }
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/GenerateEnumUnboxingMethods.java b/src/test/java/com/android/tools/r8/enumunboxing/GenerateEnumUnboxingMethods.java
index 706cdc7..4abe533 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/GenerateEnumUnboxingMethods.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/GenerateEnumUnboxingMethods.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.cfmethodgeneration.MethodGenerationBase;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
@@ -50,16 +51,22 @@
     return METHOD_TEMPLATE_CLASSES;
   }
 
+  private static void setReadInputStackMap(InternalOptions options) {
+    options.testing.readInputStackMaps = true;
+  }
+
   @Test
   public void testEnumUtilityMethodsGenerated() throws Exception {
     ArrayList<Class<?>> sorted = new ArrayList<>(getMethodTemplateClasses());
     sorted.sort(Comparator.comparing(Class::getTypeName));
     assertEquals("Classes should be listed in sorted order", sorted, getMethodTemplateClasses());
     assertEquals(
-        FileUtils.readTextFile(getGeneratedFile(), StandardCharsets.UTF_8), generateMethods());
+        FileUtils.readTextFile(getGeneratedFile(), StandardCharsets.UTF_8),
+        generateMethods(GenerateEnumUnboxingMethods::setReadInputStackMap));
   }
 
   public static void main(String[] args) throws Exception {
-    new GenerateEnumUnboxingMethods(null).generateMethodsAndWriteThemToFile();
+    new GenerateEnumUnboxingMethods(null)
+        .generateMethodsAndWriteThemToFile(GenerateEnumUnboxingMethods::setReadInputStackMap);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/InstanceFieldsEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/InstanceFieldsEnumUnboxingTest.java
index e91be2b..dc21961 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/InstanceFieldsEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/InstanceFieldsEnumUnboxingTest.java
@@ -33,7 +33,7 @@
     SuccessIntFieldOrdinal.class,
     SuccessIntFieldInitializerInit.class,
     SuccessStringField.class,
-    SuccessMultiConstructorIntField.class
+    SuccessMultiConstructorIntField.class,
   };
 
   private final TestParameters parameters;
@@ -71,7 +71,7 @@
       testClass(compile, failure, true);
     }
     for (Class<?> success : SUCCESSES) {
-      testClass(compile, success, success != SuccessUnusedField.class);
+      testClass(compile, success, false);
     }
   }
 
@@ -423,8 +423,7 @@
       int field1;
 
       EnumField(int i0) {
-        this.field0 = i0;
-        this.field1 = -1;
+        this(i0, -1);
       }
 
       EnumField(int i0, int i1) {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/NullCheckEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/NullCheckEnumUnboxingTest.java
new file mode 100644
index 0000000..25a3434
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/NullCheckEnumUnboxingTest.java
@@ -0,0 +1,197 @@
+// 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.enumunboxing;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.List;
+import java.util.Objects;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class NullCheckEnumUnboxingTest extends EnumUnboxingTestBase {
+
+  private final TestParameters parameters;
+  private final boolean enumValueOptimization;
+  private final EnumKeepRules enumKeepRules;
+
+  @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+  public static List<Object[]> data() {
+    return enumUnboxingTestParameters();
+  }
+
+  public NullCheckEnumUnboxingTest(
+      TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
+    this.parameters = parameters;
+    this.enumValueOptimization = enumValueOptimization;
+    this.enumKeepRules = enumKeepRules;
+  }
+
+  @Test
+  public void testEnumUnboxing() throws Exception {
+    R8TestRunResult run =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(NullCheckEnumUnboxingTest.class)
+            .addKeepMainRule(MainNullTest.class)
+            .addKeepRules(enumKeepRules.getKeepRules())
+            .enableNeverClassInliningAnnotations()
+            .enableInliningAnnotations()
+            .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+            .allowDiagnosticMessages()
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .inspectDiagnosticMessages(
+                m -> {
+                  assertEnumIsUnboxed(MyEnum.class, MyEnum.class.getSimpleName(), m);
+                  // MyEnum19 is unboxed only if minAPI > 19 because Objects#requiredNonNull is then
+                  // present.
+                  if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K)) {
+                    assertEnumIsUnboxed(MyEnum19.class, MyEnum19.class.getSimpleName(), m);
+                  } else {
+                    assertEnumIsBoxed(MyEnum19.class, MyEnum19.class.getSimpleName(), m);
+                  }
+                })
+            .run(parameters.getRuntime(), MainNullTest.class)
+            .assertSuccess();
+    assertLines2By2Correct(run.getStdOut());
+  }
+
+  @NeverClassInline
+  enum MyEnum {
+    A,
+    B,
+    C
+  }
+
+  @NeverClassInline
+  enum MyEnum19 {
+    A,
+    B,
+    C
+  }
+
+  static class MainNullTest {
+
+    public static void main(String[] args) {
+      nullCheckTests();
+      nullCheckMessageTests();
+    }
+
+    private static void nullCheckTests() {
+      nullCheck0Test(MyEnum.A, false);
+      nullCheck0Test(MyEnum.B, false);
+      nullCheck0Test(null, true);
+
+      nullCheck1Test(MyEnum.A, false);
+      nullCheck1Test(MyEnum.B, false);
+      nullCheck1Test(null, true);
+
+      nullCheck2Test(MyEnum19.A, false);
+      nullCheck2Test(MyEnum19.B, false);
+      nullCheck2Test(null, true);
+    }
+
+    private static void nullCheckMessageTests() {
+      nullCheckMessage0Test(MyEnum.A, "myMessageA", false);
+      nullCheckMessage0Test(MyEnum.B, "myMessageB", false);
+      nullCheckMessage0Test(null, "myMessageN", true);
+
+      nullCheckMessage1Test(MyEnum19.A, "myMessageA", false);
+      nullCheckMessage1Test(MyEnum19.B, "myMessageB", false);
+      nullCheckMessage1Test(null, "myMessageN", true);
+    }
+
+    private static void nullCheck0Test(MyEnum input, boolean isNull) {
+      String result = "pass";
+      try {
+        nullCheck0(input);
+      } catch (NullPointerException ex8) {
+        result = "fail";
+      }
+      System.out.println(result);
+      System.out.println(isNull ? "fail" : "pass");
+    }
+
+    private static void nullCheck1Test(MyEnum input, boolean isNull) {
+      String result = "pass";
+      try {
+        nullCheck1(input);
+      } catch (NullPointerException ex8) {
+        result = "fail";
+      }
+      System.out.println(result);
+      System.out.println(isNull ? "fail" : "pass");
+    }
+
+    private static void nullCheck2Test(MyEnum19 input, boolean isNull) {
+      String result = "pass";
+      try {
+        nullCheck2(input);
+      } catch (NullPointerException ex) {
+        result = "fail";
+      }
+      System.out.println(result);
+      System.out.println(isNull ? "fail" : "pass");
+    }
+
+    private static void nullCheckMessage0Test(MyEnum input, String message, boolean isNull) {
+      String result = "pass";
+      try {
+        nullCheckMessage0(input, message);
+      } catch (NullPointerException ex) {
+        result = ex.getMessage();
+      }
+      System.out.println(result);
+      System.out.println(isNull ? message : "pass");
+    }
+
+    private static void nullCheckMessage1Test(MyEnum19 input, String message, boolean isNull) {
+      String result = "pass";
+      try {
+        nullCheckMessage1(input, message);
+      } catch (NullPointerException ex) {
+        result = ex.getMessage();
+      }
+      System.out.println(result);
+      System.out.println(isNull ? message : "pass");
+    }
+
+    @NeverInline
+    private static void nullCheck0(MyEnum e) {
+      if (e == null) {
+        throw new NullPointerException();
+      }
+    }
+
+    @SuppressWarnings("ResultOfMethodCallIgnored")
+    @NeverInline
+    private static void nullCheck1(MyEnum e) {
+      e.getClass();
+    }
+
+    @NeverInline
+    private static void nullCheck2(MyEnum19 e) {
+      Objects.requireNonNull(e);
+    }
+
+    @NeverInline
+    private static void nullCheckMessage0(MyEnum e, String message) {
+      if (e == null) {
+        throw new NullPointerException(message);
+      }
+    }
+
+    @NeverInline
+    private static void nullCheckMessage1(MyEnum19 e, String message) {
+      Objects.requireNonNull(e, message);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index 05c11c4..cd417d1 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.cfmethodgeneration.MethodGenerationBase;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
@@ -68,16 +69,22 @@
     return METHOD_TEMPLATE_CLASSES;
   }
 
+  private static void setReadInputStackMap(InternalOptions options) {
+    options.testing.readInputStackMaps = true;
+  }
+
   @Test
   public void testBackportsGenerated() throws Exception {
     ArrayList<Class<?>> sorted = new ArrayList<>(getMethodTemplateClasses());
     sorted.sort(Comparator.comparing(Class::getTypeName));
     assertEquals("Classes should be listed in sorted order", sorted, getMethodTemplateClasses());
     assertEquals(
-        FileUtils.readTextFile(getGeneratedFile(), StandardCharsets.UTF_8), generateMethods());
+        FileUtils.readTextFile(getGeneratedFile(), StandardCharsets.UTF_8),
+        generateMethods(GenerateBackportMethods::setReadInputStackMap));
   }
 
   public static void main(String[] args) throws Exception {
-    new GenerateBackportMethods(null).generateMethodsAndWriteThemToFile();
+    new GenerateBackportMethods(null)
+        .generateMethodsAndWriteThemToFile(GenerateBackportMethods::setReadInputStackMap);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index e4e0b53..fdc2ab9 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.Code;
@@ -866,7 +867,7 @@
     ApplicationWriter writer =
         new ApplicationWriter(
             application,
-            null,
+            AppView.createForD8(AppInfo.createInitialAppInfo(application)),
             options,
             null,
             GraphLens.getIdentityLens(),
diff --git a/src/test/java/com/android/tools/r8/naming/arraytypes/ArrayTypesTest.java b/src/test/java/com/android/tools/r8/naming/arraytypes/ArrayTypesTest.java
index adfbcec..8100e16 100644
--- a/src/test/java/com/android/tools/r8/naming/arraytypes/ArrayTypesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/arraytypes/ArrayTypesTest.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.naming.arraytypes;
 
-import static org.junit.Assume.assumeTrue;
-
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -41,7 +39,7 @@
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   @BeforeClass
@@ -75,7 +73,7 @@
         .addProgramClassFileData(generateTestClass())
         .addKeepMainRule(Main.class)
         .addKeepRules("-keep class " + generatedTestClassName + " { test(...); }")
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutput(expectedOutput);
   }
@@ -106,20 +104,17 @@
         .addKeepRules("-applymapping " + mappingFile.toAbsolutePath())
         .noMinification()
         .noTreeShaking()
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutput(expectedOutput);
   }
 
   @Test
-  public void testD8() throws Exception {
-    assumeTrue(parameters.isDexRuntime());
-    testForD8()
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters)
         .addProgramClasses(Main.class, A.class)
         .addProgramClassFileData(generateTestClass())
-        .setMinApi(parameters.getRuntime())
         .run(parameters.getRuntime(), Main.class)
-        .writeProcessResult(System.out)
         .assertSuccessWithOutput(expectedOutput);
   }
 
@@ -129,7 +124,7 @@
     MethodVisitor mv;
 
     classWriter.visit(
-        Opcodes.V1_8,
+        Opcodes.V1_6,
         Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SUPER | Opcodes.ACC_ENUM,
         DescriptorUtils.getBinaryNameFromJavaType(generatedTestClassName),
         null,
@@ -165,15 +160,19 @@
         mv.visitVarInsn(Opcodes.ALOAD, 2);
       });
 
-      printCompareIntergers("Hashcode: ", mv, (mvCopy) -> {
-        assert mv == mvCopy;
-        // Invoke hashCode using both java.lang.Object and array type an holder.
-        mv.visitVarInsn(Opcodes.ALOAD, 0);
-        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I", false);
+      printCompareIntegers(
+          "Hashcode: ",
+          mv,
+          (mvCopy) -> {
+            assert mv == mvCopy;
+            // Invoke hashCode using both java.lang.Object and array type an holder.
+            mv.visitVarInsn(Opcodes.ALOAD, 0);
+            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I", false);
 
-        mv.visitVarInsn(Opcodes.ALOAD, 0);
-        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, arrayTypeDescriptor, "hashCode", "()I", false);
-      });
+            mv.visitVarInsn(Opcodes.ALOAD, 0);
+            mv.visitMethodInsn(
+                Opcodes.INVOKEVIRTUAL, arrayTypeDescriptor, "hashCode", "()I", false);
+          });
 
       printBoolean("Compare with Object: ", mv, (mvCopy) -> {
         assert mv == mvCopy;
@@ -213,7 +212,7 @@
           Opcodes.INVOKEVIRTUAL, arrayTypeDescriptor, "notPresent", "()Ljava/lang/String;", false);
 
       mv.visitInsn(Opcodes.RETURN);
-      mv.visitMaxs(-1, -1);
+      mv.visitMaxs(10, 10);
       mv.visitEnd();
     }
 
@@ -221,7 +220,8 @@
 
     return classWriter.toByteArray();
   }
-  public static void printCompareIntergers(
+
+  public static void printCompareIntegers(
       String header, MethodVisitor mv, Consumer<MethodVisitor> consumer) {
     mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
     mv.visitLdcInsn(header);
diff --git a/src/test/java/com/android/tools/r8/regress/b152973695/CompileToInvalidFileTest.java b/src/test/java/com/android/tools/r8/regress/b152973695/CompileToInvalidFileTest.java
index c96cbda..745d166 100644
--- a/src/test/java/com/android/tools/r8/regress/b152973695/CompileToInvalidFileTest.java
+++ b/src/test/java/com/android/tools/r8/regress/b152973695/CompileToInvalidFileTest.java
@@ -51,7 +51,7 @@
             ? new ClassFileConsumer.ArchiveConsumer(INVALID_FILE)
             : new ArchiveConsumer(INVALID_FILE);
     try {
-      testForD8()
+      testForD8(Backend.fromConsumer(programConsumer))
           .addProgramClasses(Main.class)
           .setProgramConsumer(programConsumer)
           .compileWithExpectedDiagnostics(diagnostics -> checkDiagnostics(diagnostics, true));
@@ -69,7 +69,7 @@
             ? new ClassFileConsumer.ArchiveConsumer(INVALID_FILE)
             : new ArchiveConsumer(INVALID_FILE);
     try {
-      testForR8(classFileConsumer ? Backend.CF : Backend.DEX)
+      testForR8(Backend.fromConsumer(programConsumer))
           .addProgramClasses(Main.class)
           .addKeepMainRule(Main.class)
           .setProgramConsumer(programConsumer)
diff --git a/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java b/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
index 8f3eea0..0afec27 100644
--- a/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
+++ b/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
@@ -3,73 +3,106 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.regress.b78493232;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.AsmTestBase;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.D8TestRunResult;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRunResult;
 import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
 // Variant of Regress78493232, but where the new-instance is forced to flow to a non-trivial phi
 // function prior to the call to <init>. Due to the non-trivial phi this JVM code will not pass
 // the verifier. This test is kept to allow inspection of the code path hit in D8/R8 for such
 // inputs, but besides that just documents the behaviour on the various VMs.
+@RunWith(Parameterized.class)
 public class Regress78493232_WithPhi extends AsmTestBase {
 
-  static final String expected =
+  private static final String EXPECTED =
       StringUtils.lines(
           "After 0 iterations, expected \"java.security.SecureRandom\", but got \"null\"");
 
+  private static final String MAIN = Regress78493232Dump_WithPhi.CLASS_NAME;
+  private static final List<Class<?>> CLASSES = ImmutableList.of(Regress78493232Utils.class);
+  private static final List<byte[]> CLASS_BYTES =
+      ImmutableList.of(Regress78493232Dump_WithPhi.dump());
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public Regress78493232_WithPhi(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
   @Test
-  public void test() throws Exception {
-    AndroidApp app =
-        buildAndroidApp(
-            Regress78493232Dump_WithPhi.dump(),
-            ToolHelper.getClassAsBytes(Regress78493232Utils.class));
-    ProcessResult javaResult =
-        runOnJavaRawNoVerify(
-            Regress78493232Dump_WithPhi.CLASS_NAME,
-            Regress78493232Dump_WithPhi.dump(),
-            ToolHelper.getClassAsBytes(Regress78493232Utils.class));
-    ProcessResult d8Result =
-        runOnArtRaw(compileWithD8(app), Regress78493232Dump_WithPhi.CLASS_NAME);
-    ProcessResult r8Result =
-        runOnArtRaw(
-            compileWithR8(app, "-dontshrink\n-dontobfuscate\n"),
-            Regress78493232Dump_WithPhi.CLASS_NAME);
-    String proguardConfig =
-        keepMainProguardConfiguration(Regress78493232Dump_WithPhi.CLASS_NAME) + "-dontobfuscate\n";
-    ProcessResult r8ShakenResult =
-        runOnArtRaw(compileWithR8(app, proguardConfig), Regress78493232Dump_WithPhi.CLASS_NAME);
-    assertEquals(expected, javaResult.stdout);
-    assertEquals(0, javaResult.exitCode);
-    switch (ToolHelper.getDexVm().getVersion()) {
+  public void testReference() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForJvm()
+        .noVerify()
+        .addProgramClassFileData(CLASS_BYTES)
+        .addProgramClasses(CLASSES)
+        .run(parameters.getRuntime(), MAIN)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    D8TestRunResult result =
+        testForD8()
+            .addProgramClasses(CLASSES)
+            .addProgramClassFileData(CLASS_BYTES)
+            .setMinApi(parameters.getApiLevel())
+            .run(parameters.getRuntime(), MAIN);
+    checkResult(result);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testR8(true);
+  }
+
+  @Test
+  public void testNoTreeShakingR8() throws Exception {
+    testR8(false);
+  }
+
+  private void testR8(boolean treeShake) throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    R8TestRunResult result =
+        testForR8(parameters.getBackend())
+            .addProgramClasses(CLASSES)
+            .addProgramClassFileData(CLASS_BYTES)
+            .treeShaking(treeShake)
+            .noMinification()
+            .setMinApi(parameters.getApiLevel())
+            .addKeepMainRule(MAIN)
+            .run(parameters.getRuntime(), MAIN);
+    checkResult(result);
+  }
+
+  private void checkResult(TestRunResult<?> result) {
+    switch (parameters.getDexRuntimeVersion()) {
       case V6_0_1:
-        assertEquals("Completed successfully after 1000 iterations\n", d8Result.stdout);
-        assertEquals("Completed successfully after 1000 iterations\n", r8Result.stdout);
-        assertEquals("Completed successfully after 1000 iterations\n", r8ShakenResult.stdout);
-        assertEquals(0, d8Result.exitCode);
-        assertEquals(0, r8Result.exitCode);
-        assertEquals(0, r8ShakenResult.exitCode);
+        result.assertSuccessWithOutput("Completed successfully after 1000 iterations\n");
         break;
       case V5_1_1:
-        assertEquals(expected, d8Result.stdout);
-        assertEquals(expected, r8Result.stdout);
-        assertEquals(expected, r8ShakenResult.stdout);
-        assertEquals(0, d8Result.exitCode);
-        assertEquals(0, r8Result.exitCode);
-        assertEquals(0, r8ShakenResult.exitCode);
+        result.assertSuccessWithOutput(EXPECTED);
         break;
       default:
-        assertNotEquals(-1, d8Result.stderr.indexOf("java.lang.VerifyError"));
-        assertNotEquals(-1, r8Result.stderr.indexOf("java.lang.VerifyError"));
-        assertNotEquals(-1, r8ShakenResult.stderr.indexOf("java.lang.VerifyError"));
-        assertEquals(1, d8Result.exitCode);
-        assertEquals(1, r8Result.exitCode);
-        assertEquals(1, r8ShakenResult.exitCode);
+        result.assertFailureWithErrorThatThrows(VerifyError.class);
         break;
     }
   }
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageAfterCollisionWithPackagePrivateSignatureTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageAfterCollisionWithPackagePrivateSignatureTest.java
new file mode 100644
index 0000000..47157aa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageAfterCollisionWithPackagePrivateSignatureTest.java
@@ -0,0 +1,101 @@
+// 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.repackage;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.repackage.testclasses.repackagetest.TestClass;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RepackageAfterCollisionWithPackagePrivateSignatureTest extends TestBase {
+
+  private static final String FLATTEN_PACKAGE_HIERARCHY = "flattenpackagehierarchy";
+  private static final String REPACKAGE_CLASSES = "repackageclasses";
+  private static final String REPACKAGE_DIR = "foo";
+
+  private final String flattenPackageHierarchyOrRepackageClasses;
+  private final TestParameters parameters;
+
+  @Parameters(name = "{1}, kind: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+        getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  public RepackageAfterCollisionWithPackagePrivateSignatureTest(
+      String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+    this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForR8(parameters.getBackend())
+        .addInnerClasses(RepackageAfterCollisionWithPackagePrivateSignatureTest.class)
+        .addKeepClassAndMembersRules(TestClass.class)
+        .addKeepRules(
+            "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_DIR + "\"")
+        .addClassObfuscationDictionary("a")
+        .addOptionsModification(options -> options.testing.enableExperimentalRepackaging = true)
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject repackageClassSubject = inspector.clazz(RepackageCandidate.class);
+    assertThat(repackageClassSubject, isPresent());
+    assertEquals(
+        flattenPackageHierarchyOrRepackageClasses.equals(FLATTEN_PACKAGE_HIERARCHY)
+            ? REPACKAGE_DIR + ".a"
+            : REPACKAGE_DIR,
+        repackageClassSubject.getDexProgramClass().getType().getPackageName());
+  }
+
+  public static class TestClass {
+
+    public static void main(String[] args) {
+      RepackageCandidate.foo(0);
+      RepackageCandidate.foo((int) System.currentTimeMillis(), 0);
+    }
+
+    static void restrictToCurrentPackage() {
+      System.out.print("Hello");
+    }
+  }
+
+  public static class RepackageCandidate {
+
+    public static void foo(int unused) {
+      TestClass.restrictToCurrentPackage();
+    }
+
+    @NeverInline
+    public static void foo(int used, int unused) {
+      if (used >= 0) {
+        System.out.println(" world!");
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
index 6d468f4..69a88d0 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.TestBase;
@@ -47,6 +48,8 @@
 @RunWith(Parameterized.class)
 public class RepackageTest extends TestBase {
 
+  private static final String FLATTEN_PACKAGE_HIERARCHY = "flattenpackagehierarchy";
+  private static final String REPACKAGE_CLASSES = "repackageclasses";
   private static final String REPACKAGE_DIR = "foo";
 
   private static final List<String> EXPECTED =
@@ -68,28 +71,35 @@
           "ReachableClass.packagePrivateMethod()");
 
   private final boolean allowAccessModification;
+  private final boolean enableExperimentalRepackaging;
   private final String flattenPackageHierarchyOrRepackageClasses;
   private final TestParameters parameters;
 
-  @Parameters(name = "{1}, allow access modification: {0}")
+  @Parameters(name = "{3}, allow access modification: {0}, experimental: {1}, kind: {2}")
   public static List<Object[]> data() {
     return buildParameters(
         BooleanUtils.values(),
-        ImmutableList.of("flattenpackagehierarchy", "repackageclasses"),
+        BooleanUtils.values(),
+        ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
         getTestParameters().withAllRuntimesAndApiLevels().build());
   }
 
   public RepackageTest(
       boolean allowAccessModification,
+      boolean enableExperimentalRepackaging,
       String flattenPackageHierarchyOrRepackageClasses,
       TestParameters parameters) {
     this.allowAccessModification = allowAccessModification;
+    this.enableExperimentalRepackaging = enableExperimentalRepackaging;
     this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
     this.parameters = parameters;
   }
 
   @Test
   public void testJvm() throws Exception {
+    assumeFalse(allowAccessModification);
+    assumeFalse(enableExperimentalRepackaging);
+    assumeTrue(flattenPackageHierarchyOrRepackageClasses.equals(FLATTEN_PACKAGE_HIERARCHY));
     assumeTrue(parameters.isCfRuntime());
     testForJvm()
         .addTestClasspath()
@@ -99,6 +109,7 @@
 
   @Test
   public void testR8() throws Exception {
+    assumeTrue(!enableExperimentalRepackaging || parameters.isCfRuntime());
     testForR8(parameters.getBackend())
         .addProgramFiles(ToolHelper.getClassFilesForTestPackage(TestClass.class.getPackage()))
         .addKeepMainRule(TestClass.class)
@@ -115,6 +126,9 @@
             "  <methods>;",
             "}")
         .allowAccessModification(allowAccessModification)
+        .addOptionsModification(
+            options ->
+                options.testing.enableExperimentalRepackaging = enableExperimentalRepackaging)
         .enableInliningAnnotations()
         .enableMergeAnnotations()
         .setMinApi(parameters.getApiLevel())
@@ -132,9 +146,9 @@
           if (eligibleForRepackaging) {
             assertEquals(
                 clazz.getTypeName(),
-                flattenPackageHierarchyOrRepackageClasses.equals("flattenpackagehierarchy")
+                flattenPackageHierarchyOrRepackageClasses.equals(FLATTEN_PACKAGE_HIERARCHY)
                     ? REPACKAGE_DIR + ".a"
-                    : REPACKAGE_DIR + "",
+                    : REPACKAGE_DIR,
                 subject.getDexProgramClass().getType().getPackageName());
           } else {
             assertEquals(
@@ -154,7 +168,7 @@
     //  the consumer, since these classes should be repackaged independent of
     //  -allowaccessmodification.
     Consumer<Class<?>> markShouldAlwaysBeEligible =
-        clazz -> consumer.accept(clazz, allowAccessModification);
+        clazz -> consumer.accept(clazz, allowAccessModification || enableExperimentalRepackaging);
     Consumer<Class<?>> markEligibleWithAllowAccessModification =
         clazz -> consumer.accept(clazz, allowAccessModification);
 
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassDirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassDirect.java
index a7af296..8089eb2 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassDirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassDirect.java
@@ -11,7 +11,7 @@
 public class AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassDirect {
 
   @NeverInline
-  static void test() {
+  public static void test() {
     ReachableClassWithKeptMethodAllowRenaming.packagePrivateMethod();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassIndirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassIndirect.java
index 9310128..c7c3695 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassIndirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassIndirect.java
@@ -11,7 +11,16 @@
 public class AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassIndirect {
 
   @NeverInline
-  static void test() {
-    AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassDirect.test();
+  public static void test() {
+    Helper.test();
+  }
+
+  @NeverMerge
+  public static class Helper {
+
+    @NeverInline
+    static void test() {
+      ReachableClassWithKeptMethodAllowRenaming.packagePrivateMethod();
+    }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodOnReachableClassDirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodOnReachableClassDirect.java
index 1c159f0..6948b55 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodOnReachableClassDirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodOnReachableClassDirect.java
@@ -11,7 +11,7 @@
 public class AccessPackagePrivateKeptMethodOnReachableClassDirect {
 
   @NeverInline
-  static void test() {
+  public static void test() {
     ReachableClassWithKeptMethod.packagePrivateMethod();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodOnReachableClassIndirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodOnReachableClassIndirect.java
index 043c43a..7d5baae 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodOnReachableClassIndirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodOnReachableClassIndirect.java
@@ -11,7 +11,16 @@
 public class AccessPackagePrivateKeptMethodOnReachableClassIndirect {
 
   @NeverInline
-  static void test() {
-    AccessPackagePrivateKeptMethodOnReachableClassDirect.test();
+  public static void test() {
+    Helper.test();
+  }
+
+  @NeverMerge
+  public static class Helper {
+
+    @NeverInline
+    static void test() {
+      ReachableClassWithKeptMethod.packagePrivateMethod();
+    }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassAllowRenamingDirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassAllowRenamingDirect.java
index 45049a6..f3a90e6 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassAllowRenamingDirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassAllowRenamingDirect.java
@@ -11,7 +11,7 @@
 public class AccessPackagePrivateMethodOnKeptClassAllowRenamingDirect {
 
   @NeverInline
-  static void test() {
+  public static void test() {
     KeptClassAllowRenaming.packagePrivateMethod();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassAllowRenamingIndirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassAllowRenamingIndirect.java
index 8ff327f..ddfd95b 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassAllowRenamingIndirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassAllowRenamingIndirect.java
@@ -11,7 +11,16 @@
 public class AccessPackagePrivateMethodOnKeptClassAllowRenamingIndirect {
 
   @NeverInline
-  static void test() {
-    AccessPackagePrivateMethodOnKeptClassAllowRenamingDirect.test();
+  public static void test() {
+    Helper.test();
+  }
+
+  @NeverMerge
+  public static class Helper {
+
+    @NeverInline
+    static void test() {
+      KeptClassAllowRenaming.packagePrivateMethod();
+    }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassDirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassDirect.java
index 8ae7f71..11110bd 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassDirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassDirect.java
@@ -11,7 +11,7 @@
 public class AccessPackagePrivateMethodOnKeptClassDirect {
 
   @NeverInline
-  static void test() {
+  public static void test() {
     KeptClass.packagePrivateMethod();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassIndirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassIndirect.java
index 6e769dd..0b737ff 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassIndirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassIndirect.java
@@ -11,7 +11,16 @@
 public class AccessPackagePrivateMethodOnKeptClassIndirect {
 
   @NeverInline
-  static void test() {
-    AccessPackagePrivateMethodOnKeptClassDirect.test();
+  public static void test() {
+    Helper.test();
+  }
+
+  @NeverMerge
+  public static class Helper {
+
+    @NeverInline
+    static void test() {
+      KeptClass.packagePrivateMethod();
+    }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnReachableClassDirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnReachableClassDirect.java
index 8ef5364..f9a5b76 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnReachableClassDirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnReachableClassDirect.java
@@ -11,7 +11,7 @@
 public class AccessPackagePrivateMethodOnReachableClassDirect {
 
   @NeverInline
-  static void test() {
+  public static void test() {
     ReachableClass.packagePrivateMethod();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnReachableClassIndirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnReachableClassIndirect.java
index 63d0072..2c0a1b2 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnReachableClassIndirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnReachableClassIndirect.java
@@ -11,7 +11,16 @@
 public class AccessPackagePrivateMethodOnReachableClassIndirect {
 
   @NeverInline
-  static void test() {
-    AccessPackagePrivateMethodOnReachableClassDirect.test();
+  public static void test() {
+    Helper.test();
+  }
+
+  @NeverMerge
+  public static class Helper {
+
+    @NeverInline
+    static void test() {
+      ReachableClass.packagePrivateMethod();
+    }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicKeptMethodAllowRenamingOnReachableClass.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicKeptMethodAllowRenamingOnReachableClass.java
index 460e3e1..0f54397 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicKeptMethodAllowRenamingOnReachableClass.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicKeptMethodAllowRenamingOnReachableClass.java
@@ -11,7 +11,7 @@
 public class AccessPublicKeptMethodAllowRenamingOnReachableClass {
 
   @NeverInline
-  static void test() {
+  public static void test() {
     ReachableClassWithKeptMethodAllowRenaming.publicMethod();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicKeptMethodOnReachableClass.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicKeptMethodOnReachableClass.java
index be42852..dcd6aa4 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicKeptMethodOnReachableClass.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicKeptMethodOnReachableClass.java
@@ -11,7 +11,7 @@
 public class AccessPublicKeptMethodOnReachableClass {
 
   @NeverInline
-  static void test() {
+  public static void test() {
     ReachableClassWithKeptMethod.publicMethod();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnKeptClass.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnKeptClass.java
index 54f999d..f6cf98a 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnKeptClass.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnKeptClass.java
@@ -11,7 +11,7 @@
 public class AccessPublicMethodOnKeptClass {
 
   @NeverInline
-  static void test() {
+  public static void test() {
     KeptClass.publicMethod();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnKeptClassAllowRenaming.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnKeptClassAllowRenaming.java
index 16acfb5..05f943c 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnKeptClassAllowRenaming.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnKeptClassAllowRenaming.java
@@ -11,7 +11,7 @@
 public class AccessPublicMethodOnKeptClassAllowRenaming {
 
   @NeverInline
-  static void test() {
+  public static void test() {
     KeptClassAllowRenaming.publicMethod();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnReachableClass.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnReachableClass.java
index 4414fd6..0c21cef 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnReachableClass.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnReachableClass.java
@@ -11,7 +11,7 @@
 public class AccessPublicMethodOnReachableClass {
 
   @NeverInline
-  static void test() {
+  public static void test() {
     ReachableClass.publicMethod();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/resolution/AsmBasedTests.java b/src/test/java/com/android/tools/r8/resolution/AsmBasedTests.java
deleted file mode 100644
index 7264dd7..0000000
--- a/src/test/java/com/android/tools/r8/resolution/AsmBasedTests.java
+++ /dev/null
@@ -1,34 +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.resolution;
-
-import com.android.tools.r8.AsmTestBase;
-import com.android.tools.r8.resolution.shadowing1.AClassDump;
-import com.android.tools.r8.resolution.shadowing1.InterfaceDump;
-import com.android.tools.r8.resolution.shadowing1.MainDump;
-import com.android.tools.r8.resolution.shadowing1.SubInterfaceDump;
-import org.junit.Ignore;
-import org.junit.Test;
-
-public class AsmBasedTests extends AsmTestBase {
-
-  @Test
-  @Ignore("b/69356146")
-  public void defaultMethodShadowedByStatic() throws Exception {
-    ensureException("Main", IncompatibleClassChangeError.class,
-        InterfaceDump.dump(),
-        SubInterfaceDump.dump(),
-        AClassDump.dump(),
-        MainDump.dump());
-  }
-
-  @Test
-  @Ignore("b/69356146")
-  public void invokeDefaultMethodViaStatic() throws Exception {
-    ensureException("Main", IncompatibleClassChangeError.class,
-        com.android.tools.r8.resolution.invokestaticinterfacedefault.InterfaceDump.dump(),
-        com.android.tools.r8.resolution.invokestaticinterfacedefault.MainDump.dump());
-
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/resolution/DefaultMethodShadowedByStaticTest.java b/src/test/java/com/android/tools/r8/resolution/DefaultMethodShadowedByStaticTest.java
new file mode 100644
index 0000000..19424d8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/DefaultMethodShadowedByStaticTest.java
@@ -0,0 +1,69 @@
+// 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.resolution;
+
+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 com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.resolution.shadowing1.AClassDump;
+import com.android.tools.r8.resolution.shadowing1.InterfaceDump;
+import com.android.tools.r8.resolution.shadowing1.MainDump;
+import com.android.tools.r8.resolution.shadowing1.SubInterfaceDump;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DefaultMethodShadowedByStaticTest extends TestBase {
+
+  private final List<byte[]> CLASSES =
+      ImmutableList.of(
+          InterfaceDump.dump(), SubInterfaceDump.dump(), AClassDump.dump(), MainDump.dump());
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  private final TestParameters parameters;
+
+  public DefaultMethodShadowedByStaticTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    TestRunResult<?> result =
+        testForRuntime(parameters)
+            .addProgramClassFileData(CLASSES)
+            .run(parameters.getRuntime(), "Main");
+    if (parameters.isDexRuntime()
+        && (parameters.getApiLevel().isLessThan(apiLevelWithStaticInterfaceMethodsSupport())
+            || parameters.getDexRuntimeVersion().equals(Version.V7_0_0))) {
+      // TODO(b/167535447): Desugaring should preserve the error.
+      result.assertSuccessWithOutputLines("42");
+    } else if (parameters.isDexRuntime()
+        && parameters.getDexRuntimeVersion().equals(Version.V7_0_0)) {
+      // Note: VM 7.0.0 without desugaring of defaults will incorrectly allow the virtual dispatch.
+      result.assertSuccessWithOutputLines("42");
+    } else {
+      result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+    }
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(CLASSES)
+        .addKeepMainRule("Main")
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), "Main")
+        .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/InvokeDefaultMethodViaStaticTest.java b/src/test/java/com/android/tools/r8/resolution/InvokeDefaultMethodViaStaticTest.java
new file mode 100644
index 0000000..2080b42
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/InvokeDefaultMethodViaStaticTest.java
@@ -0,0 +1,68 @@
+// 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.resolution;
+
+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 com.android.tools.r8.resolution.invokestaticinterfacedefault.InterfaceDump;
+import com.android.tools.r8.resolution.invokestaticinterfacedefault.MainDump;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InvokeDefaultMethodViaStaticTest extends TestBase {
+
+  private static final List<byte[]> CLASSES =
+      ImmutableList.of(InterfaceDump.dump(), MainDump.dump());
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  private final TestParameters parameters;
+
+  public InvokeDefaultMethodViaStaticTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    TestRunResult<?> result =
+        testForRuntime(parameters)
+            .addProgramClassFileData(CLASSES)
+            .run(parameters.getRuntime(), "Main");
+    if (parameters.isDexRuntime()
+        && parameters.getApiLevel().isLessThan(apiLevelWithDefaultInterfaceMethodsSupport())) {
+      // TODO(b/167535447): Desugaring should preserve the error.
+      result.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
+    } else {
+      result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+    }
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    TestRunResult<?> result =
+        testForR8(parameters.getBackend())
+            .addProgramClassFileData(CLASSES)
+            .addKeepMainRule("Main")
+            .setMinApi(parameters.getApiLevel())
+            .addOptionsModification(o -> o.testing.allowInvokeErrors = true)
+            .run(parameters.getRuntime(), "Main");
+    if (parameters.isDexRuntime()
+        && parameters.getApiLevel().isLessThan(apiLevelWithDefaultInterfaceMethodsSupport())) {
+      // TODO(b/167535447): Desugaring should preserve the error.
+      result.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
+    } else {
+      result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/invokestaticinterfacedefault/InterfaceDump.java b/src/test/java/com/android/tools/r8/resolution/invokestaticinterfacedefault/InterfaceDump.java
index 56d67aa..37a6f2b 100644
--- a/src/test/java/com/android/tools/r8/resolution/invokestaticinterfacedefault/InterfaceDump.java
+++ b/src/test/java/com/android/tools/r8/resolution/invokestaticinterfacedefault/InterfaceDump.java
@@ -22,7 +22,7 @@
  */
 public class InterfaceDump implements Opcodes {
 
-  public static byte[] dump() throws Exception {
+  public static byte[] dump() {
 
     ClassWriter cw = new ClassWriter(0);
     FieldVisitor fv;
diff --git a/src/test/java/com/android/tools/r8/resolution/invokestaticinterfacedefault/MainDump.java b/src/test/java/com/android/tools/r8/resolution/invokestaticinterfacedefault/MainDump.java
index 15f12b9..0de68a4 100644
--- a/src/test/java/com/android/tools/r8/resolution/invokestaticinterfacedefault/MainDump.java
+++ b/src/test/java/com/android/tools/r8/resolution/invokestaticinterfacedefault/MainDump.java
@@ -14,7 +14,7 @@
  */
 public class MainDump implements Opcodes {
 
-  public static byte[] dump() throws Exception {
+  public static byte[] dump() {
 
     ClassWriter cw = new ClassWriter(0);
     FieldVisitor fv;
diff --git a/src/test/java/com/android/tools/r8/resolution/shadowing1/AClassDump.java b/src/test/java/com/android/tools/r8/resolution/shadowing1/AClassDump.java
index 9701db0..7f1be60 100644
--- a/src/test/java/com/android/tools/r8/resolution/shadowing1/AClassDump.java
+++ b/src/test/java/com/android/tools/r8/resolution/shadowing1/AClassDump.java
@@ -19,7 +19,7 @@
  */
 public class AClassDump implements Opcodes {
 
-  public static byte[] dump() throws Exception {
+  public static byte[] dump() {
 
     ClassWriter cw = new ClassWriter(0);
     FieldVisitor fv;
diff --git a/src/test/java/com/android/tools/r8/resolution/shadowing1/InterfaceDump.java b/src/test/java/com/android/tools/r8/resolution/shadowing1/InterfaceDump.java
index 603071a..cc8b019 100644
--- a/src/test/java/com/android/tools/r8/resolution/shadowing1/InterfaceDump.java
+++ b/src/test/java/com/android/tools/r8/resolution/shadowing1/InterfaceDump.java
@@ -11,7 +11,7 @@
 
 public class InterfaceDump implements Opcodes {
 
-  public static byte[] dump() throws Exception {
+  public static byte[] dump() {
 
     ClassWriter cw = new ClassWriter(0);
     FieldVisitor fv;
diff --git a/src/test/java/com/android/tools/r8/resolution/shadowing1/MainDump.java b/src/test/java/com/android/tools/r8/resolution/shadowing1/MainDump.java
index 09bd3b2..b21c15f 100644
--- a/src/test/java/com/android/tools/r8/resolution/shadowing1/MainDump.java
+++ b/src/test/java/com/android/tools/r8/resolution/shadowing1/MainDump.java
@@ -23,7 +23,7 @@
  */
 public class MainDump implements Opcodes {
 
-  public static byte[] dump() throws Exception {
+  public static byte[] dump() {
 
     ClassWriter cw = new ClassWriter(0);
     FieldVisitor fv;
diff --git a/src/test/java/com/android/tools/r8/resolution/shadowing1/SubInterfaceDump.java b/src/test/java/com/android/tools/r8/resolution/shadowing1/SubInterfaceDump.java
index f70cf4f..7e51c28 100644
--- a/src/test/java/com/android/tools/r8/resolution/shadowing1/SubInterfaceDump.java
+++ b/src/test/java/com/android/tools/r8/resolution/shadowing1/SubInterfaceDump.java
@@ -11,7 +11,7 @@
 
 public class SubInterfaceDump implements Opcodes {
 
-  public static byte[] dump() throws Exception {
+  public static byte[] dump() {
 
     ClassWriter cw = new ClassWriter(0);
     FieldVisitor fv;
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
index 2bb72cd..ac64695 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -8,6 +8,8 @@
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
@@ -114,7 +116,7 @@
       ApplicationWriter writer =
           new ApplicationWriter(
               dexApp,
-              null,
+              AppView.createForD8(AppInfo.createInitialAppInfo(dexApp)),
               options,
               null,
               GraphLens.getIdentityLens(),
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index 59c8760..3125afe 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -183,4 +183,9 @@
   public ClassNamingForNameMapper getNaming() {
     return null;
   }
+
+  @Override
+  public void disassembleUsingJavap(boolean verbose) throws Exception {
+    throw new Unreachable("Cannot disassembly an absent class");
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index b2266ce..28fd839 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -214,4 +214,6 @@
   }
 
   public abstract ClassNamingForNameMapper getNaming();
+
+  public abstract void disassembleUsingJavap(boolean verbose) throws Exception;
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index 0b1d950..f12840f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -7,6 +7,9 @@
 import static com.android.tools.r8.KotlinTestBase.METADATA_TYPE;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -27,6 +30,7 @@
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.function.Consumer;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -408,4 +412,25 @@
   public ClassNamingForNameMapper getNaming() {
     return naming;
   }
+
+  @Override
+  public void disassembleUsingJavap(boolean verbose) throws Exception {
+    assert dexClass.origin != null;
+    List<String> command = new ArrayList<>();
+    command.add(
+        CfRuntime.getCheckedInJdk9().getJavaHome().resolve("bin").resolve("javap").toString());
+    if (verbose) {
+      command.add("-v");
+      command.add("-c");
+      command.add("-p");
+    }
+    command.add("-cp");
+    List<String> parts = dexClass.origin.parts();
+    assert parts.size() == 2;
+    command.add(parts.get(0));
+    command.add(parts.get(1).replace(".class", ""));
+    ProcessResult processResult = ToolHelper.runProcess(new ProcessBuilder(command));
+    assert processResult.exitCode == 0;
+    System.out.println(processResult.stdout);
+  }
 }