Merge commit '8d24db72a19fc0420001e20db9765ebf277d188d' into dev-release
diff --git a/.gitignore b/.gitignore
index c97f9a0..7ad6f32 100644
--- a/.gitignore
+++ b/.gitignore
@@ -127,6 +127,8 @@
 third_party/openjdk/desugar_jdk_libs.tar.gz
 third_party/openjdk/desugar_jdk_libs_11
 third_party/openjdk/desugar_jdk_libs_11.tar.gz
+third_party/openjdk/desugar_jdk_libs_legacy
+third_party/openjdk/desugar_jdk_libs_legacy.tar.gz
 third_party/openjdk/desugar_jdk_libs_releases/1.0.9
 third_party/openjdk/desugar_jdk_libs_releases/1.0.9.tar.gz
 third_party/openjdk/desugar_jdk_libs_releases/1.0.10
diff --git a/build.gradle b/build.gradle
index bd5538a..8a6e218 100644
--- a/build.gradle
+++ b/build.gradle
@@ -367,6 +367,7 @@
                 "openjdk/openjdk-rt-1.8",
                 "openjdk/desugar_jdk_libs",
                 "openjdk/desugar_jdk_libs_11",
+                "openjdk/desugar_jdk_libs_legacy",
                 "openjdk/desugar_jdk_libs_releases/1.0.9",
                 "openjdk/desugar_jdk_libs_releases/1.0.10",
                 "openjdk/desugar_jdk_libs_releases/1.1.0",
@@ -1019,7 +1020,7 @@
         dependsOn r8WithRelocatedDeps
         dependsOn r8Task
         commandLine ([
-                "python", "tools/create_r8lib.py",
+                "python3", "tools/create_r8lib.py",
                 "--r8jar", r8Task.outputs.files[0],
                 "--output", output]
                 + (pgConfs.collectMany { ["--pg-conf", it] })
@@ -1068,7 +1069,7 @@
     inputs.file script
     outputs.dir outputDir
     dependsOn downloadDeps
-    commandLine "python", script
+    commandLine "python3", script
     workingDir = projectDir
 }
 
@@ -1186,7 +1187,7 @@
     inputs.files files("tests/2017-10-04/art.tar.gz", createArtTestsScript)
     outputs.dir outputDir
     dependsOn downloadDeps
-    commandLine "python", createArtTestsScript
+    commandLine "python3", createArtTestsScript
     workingDir = projectDir
 }
 
@@ -1196,7 +1197,7 @@
     inputs.file script
     outputs.dir outputDir
     dependsOn downloadDeps
-    commandLine "python", script
+    commandLine "python3", script
     workingDir = projectDir
 }
 
@@ -1875,7 +1876,7 @@
 def retrace(Throwable exception) {
     def out = new StringBuffer()
     def err = new StringBuffer()
-    def command = "python tools/retrace.py --quiet"
+    def command = "python3 tools/retrace.py --quiet"
     def header = "RETRACED STACKTRACE";
     out.append("\n--------------------------------------\n")
     out.append("${header}\n")
diff --git a/infra/config/global/generated/luci-scheduler.cfg b/infra/config/global/generated/luci-scheduler.cfg
index 629ee37..cb2a721 100644
--- a/infra/config/global/generated/luci-scheduler.cfg
+++ b/infra/config/global/generated/luci-scheduler.cfg
@@ -691,7 +691,7 @@
   triggers: "linux-android-12.0.0_release"
   gitiles {
     repo: "https://r8.googlesource.com/r8"
-    refs: "regexp:refs/heads//([3]\\.[2-9]+(\\.[0-9]+)?|[4-9]\\.[0-9]+(\\.[0-9]+)?)"
+    refs: "regexp:refs/heads/([3]\\.[2-9]+(\\.[0-9]+)?|[4-9]\\.[0-9]+(\\.[0-9]+)?)"
     path_regexps: "src/main/java/com/android/tools/r8/Version.java"
   }
 }
@@ -702,7 +702,7 @@
   triggers: "linux-android-13.0.0_release"
   gitiles {
     repo: "https://r8.googlesource.com/r8"
-    refs: "regexp:refs/heads//([3]\\.[3-9]+(\\.[0-9]+)?|[4-9]\\.[0-9]+(\\.[0-9]+)?)"
+    refs: "regexp:refs/heads/([3]\\.[3-9]+(\\.[0-9]+)?|[4-9]\\.[0-9]+(\\.[0-9]+)?)"
     path_regexps: "src/main/java/com/android/tools/r8/Version.java"
   }
 }
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index 6c59f26..d6eb25a 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -112,7 +112,7 @@
   name = "branch-gitiles-3.3-forward",
   bucket = "ci",
   repo = "https://r8.googlesource.com/r8",
-  refs = ["refs/heads//([3]\\.[3-9]+(\\.[0-9]+)?|[4-9]\\.[0-9]+(\\.[0-9]+)?)"],
+  refs = ["refs/heads/([3]\\.[3-9]+(\\.[0-9]+)?|[4-9]\\.[0-9]+(\\.[0-9]+)?)"],
   path_regexps = ["src/main/java/com/android/tools/r8/Version.java"]
 )
 
@@ -120,7 +120,7 @@
   name = "branch-gitiles-3.2-forward",
   bucket = "ci",
   repo = "https://r8.googlesource.com/r8",
-  refs = ["refs/heads//([3]\\.[2-9]+(\\.[0-9]+)?|[4-9]\\.[0-9]+(\\.[0-9]+)?)"],
+  refs = ["refs/heads/([3]\\.[2-9]+(\\.[0-9]+)?|[4-9]\\.[0-9]+(\\.[0-9]+)?)"],
   path_regexps = ["src/main/java/com/android/tools/r8/Version.java"]
 )
 
diff --git a/src/library_desugar/desugar_jdk_libs_alternative_3.json b/src/library_desugar/desugar_jdk_libs_alternative_3.json
deleted file mode 100644
index 2b0ffd9..0000000
--- a/src/library_desugar/desugar_jdk_libs_alternative_3.json
+++ /dev/null
@@ -1,254 +0,0 @@
-{
-  "configuration_format_version": 3,
-  "group_id" : "com.tools.android",
-  "artifact_id" : "desugar_jdk_libs_alternative_3",
-  "version": "1.2.1",
-  "required_compilation_api_level": 26,
-  "synthesized_library_classes_package_prefix": "j$.",
-  "support_all_callbacks_from_library": false,
-  "common_flags": [
-    {
-      "api_level_below_or_equal": 25,
-      "wrapper_conversion": [
-        "java.time.Clock"
-      ]
-    },
-    {
-      "api_level_below_or_equal": 23,
-      "wrapper_conversion": [
-        "java.util.PrimitiveIterator$OfDouble",
-        "java.util.PrimitiveIterator$OfInt",
-        "java.util.PrimitiveIterator$OfLong",
-        "java.util.Spliterator",
-        "java.util.Spliterator$OfDouble",
-        "java.util.Spliterator$OfInt",
-        "java.util.Spliterator$OfLong",
-        "java.util.Spliterator$OfPrimitive",
-        "java.util.function.BiConsumer",
-        "java.util.function.BiFunction",
-        "java.util.function.BiPredicate",
-        "java.util.function.BinaryOperator",
-        "java.util.function.Consumer",
-        "java.util.function.DoubleBinaryOperator",
-        "java.util.function.DoubleConsumer",
-        "java.util.function.DoubleFunction",
-        "java.util.function.DoublePredicate",
-        "java.util.function.DoubleToIntFunction",
-        "java.util.function.DoubleToLongFunction",
-        "java.util.function.DoubleUnaryOperator",
-        "java.util.function.Function",
-        "java.util.function.IntBinaryOperator",
-        "java.util.function.IntConsumer",
-        "java.util.function.IntFunction",
-        "java.util.function.IntPredicate",
-        "java.util.function.IntToDoubleFunction",
-        "java.util.function.IntToLongFunction",
-        "java.util.function.IntUnaryOperator",
-        "java.util.function.LongBinaryOperator",
-        "java.util.function.LongConsumer",
-        "java.util.function.LongFunction",
-        "java.util.function.LongPredicate",
-        "java.util.function.LongToDoubleFunction",
-        "java.util.function.LongToIntFunction",
-        "java.util.function.LongUnaryOperator",
-        "java.util.function.ObjDoubleConsumer",
-        "java.util.function.ObjIntConsumer",
-        "java.util.function.ObjLongConsumer",
-        "java.util.function.Predicate",
-        "java.util.function.Supplier",
-        "java.util.function.ToDoubleFunction",
-        "java.util.function.ToIntFunction",
-        "java.util.function.ToLongFunction",
-        "java.util.function.UnaryOperator",
-        "java.util.stream.BaseStream",
-        "java.util.stream.Collector",
-        "java.util.stream.DoubleStream",
-        "java.util.stream.IntStream",
-        "java.util.stream.LongStream",
-        "java.util.stream.Stream"
-      ]
-    }
-  ],
-  "library_flags": [
-    {
-      "api_level_below_or_equal": 25,
-      "rewrite_prefix": {
-        "j$.time.": "java.time.",
-        "java.time.": "j$.time.",
-        "java.util.Desugar": "j$.util.Desugar"
-      },
-      "retarget_lib_member": {
-        "java.util.Date#toInstant": "java.util.DesugarDate",
-        "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar",
-        "java.util.TimeZone#toZoneId": "java.util.DesugarTimeZone"
-      },
-      "custom_conversion": {
-        "java.time.ZonedDateTime": "java.time.TimeConversions",
-        "java.time.LocalDate": "java.time.TimeConversions",
-        "java.time.Duration": "java.time.TimeConversions",
-        "java.time.ZoneId": "java.time.TimeConversions",
-        "java.time.MonthDay": "java.time.TimeConversions",
-        "java.time.Instant": "java.time.TimeConversions"
-      }
-    },
-    {
-      "api_level_below_or_equal": 23,
-      "rewrite_prefix": {
-        "j$.util.Optional": "java.util.Optional",
-        "j$.util.LongSummaryStatistics": "java.util.LongSummaryStatistics",
-        "j$.util.IntSummaryStatistics": "java.util.IntSummaryStatistics",
-        "j$.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatistics",
-        "java.util.stream.": "j$.util.stream.",
-        "java.util.function.": "j$.util.function.",
-        "java.util.Comparators": "j$.util.Comparators",
-        "java.util.DoubleSummaryStatistics": "j$.util.DoubleSummaryStatistics",
-        "java.util.IntSummaryStatistics": "j$.util.IntSummaryStatistics",
-        "java.util.LongSummaryStatistics": "j$.util.LongSummaryStatistics",
-        "java.util.Objects": "j$.util.Objects",
-        "java.util.Optional": "j$.util.Optional",
-        "java.util.PrimitiveIterator": "j$.util.PrimitiveIterator",
-        "java.util.SortedSet$1": "j$.util.SortedSet$1",
-        "java.util.Spliterator": "j$.util.Spliterator",
-        "java.util.StringJoiner": "j$.util.StringJoiner",
-        "java.util.Tripwire": "j$.util.Tripwire",
-        "java.util.concurrent.DesugarUnsafe": "j$.util.concurrent.DesugarUnsafe",
-        "java.util.concurrent.ThreadLocalRandom": "j$.util.concurrent.ThreadLocalRandom",
-        "java.util.concurrent.atomic.DesugarAtomic": "j$.util.concurrent.atomic.DesugarAtomic",
-        "java.util.concurrent.ConcurrentHashMap": "j$.util.concurrent.ConcurrentHashMap",
-        "java.io.DesugarBufferedReader": "j$.io.DesugarBufferedReader",
-        "java.io.UncheckedIOException": "j$.io.UncheckedIOException"
-      },
-      "retarget_lib_member": {
-        "java.util.Arrays#stream": "java.util.DesugarArrays",
-        "java.util.Arrays#spliterator": "java.util.DesugarArrays",
-        "java.util.LinkedHashSet#spliterator": "java.util.DesugarLinkedHashSet",
-        "java.io.BufferedReader#lines": "java.io.DesugarBufferedReader"
-      },
-      "dont_rewrite": [
-        "java.util.Iterator#remove"
-      ],
-      "emulate_interface": {
-        "java.util.Map$Entry": "j$.util.Map$Entry",
-        "java.util.Collection": "j$.util.Collection",
-        "java.util.Map": "j$.util.Map",
-        "java.util.Iterator": "j$.util.Iterator",
-        "java.util.Comparator": "j$.util.Comparator",
-        "java.util.List": "j$.util.List",
-        "java.util.SortedSet": "j$.util.SortedSet",
-        "java.util.Set": "j$.util.Set",
-        "java.util.concurrent.ConcurrentMap": "j$.util.concurrent.ConcurrentMap"
-      },
-      "custom_conversion": {
-        "java.util.Optional": "java.util.OptionalConversions",
-        "java.util.OptionalDouble": "java.util.OptionalConversions",
-        "java.util.OptionalInt": "java.util.OptionalConversions",
-        "java.util.OptionalLong": "java.util.OptionalConversions",
-        "java.util.LongSummaryStatistics": "java.util.LongSummaryStatisticsConversions",
-        "java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
-        "java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions"
-      }
-    }
-  ],
-  "program_flags": [
-    {
-      "api_level_below_or_equal": 25,
-      "rewrite_prefix": {
-        "java.time.": "j$.time.",
-        "java.util.Desugar": "j$.util.Desugar"
-      },
-      "retarget_lib_member": {
-        "java.util.Calendar#toInstant": "java.util.DesugarCalendar",
-        "java.util.Date#from": "java.util.DesugarDate",
-        "java.util.Date#toInstant": "java.util.DesugarDate",
-        "java.util.GregorianCalendar#from": "java.util.DesugarGregorianCalendar",
-        "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar",
-        "java.util.TimeZone#getTimeZone": "java.util.DesugarTimeZone",
-        "java.util.TimeZone#toZoneId": "java.util.DesugarTimeZone"
-      },
-      "custom_conversion": {
-        "java.time.ZonedDateTime": "java.time.TimeConversions",
-        "java.time.LocalDate": "java.time.TimeConversions",
-        "java.time.Duration": "java.time.TimeConversions",
-        "java.time.ZoneId": "java.time.TimeConversions",
-        "java.time.MonthDay": "java.time.TimeConversions",
-        "java.time.Instant": "java.time.TimeConversions"
-      }
-    },
-    {
-      "api_level_below_or_equal": 23,
-      "rewrite_prefix": {
-        "java.util.stream.": "j$.util.stream.",
-        "java.util.function.": "j$.util.function.",
-        "java.util.DoubleSummaryStatistics": "j$.util.DoubleSummaryStatistics",
-        "java.util.IntSummaryStatistics": "j$.util.IntSummaryStatistics",
-        "java.util.LongSummaryStatistics": "j$.util.LongSummaryStatistics",
-        "java.util.Objects": "j$.util.Objects",
-        "java.util.Optional": "j$.util.Optional",
-        "java.util.PrimitiveIterator": "j$.util.PrimitiveIterator",
-        "java.util.Spliterator": "j$.util.Spliterator",
-        "java.util.StringJoiner": "j$.util.StringJoiner",
-        "java.util.concurrent.ThreadLocalRandom": "j$.util.concurrent.ThreadLocalRandom",
-        "java.util.concurrent.atomic.DesugarAtomic": "j$.util.concurrent.atomic.DesugarAtomic",
-        "java.util.concurrent.ConcurrentHashMap": "j$.util.concurrent.ConcurrentHashMap",
-        "java.io.UncheckedIOException": "j$.io.UncheckedIOException"
-      },
-      "retarget_lib_member": {
-        "java.util.Arrays#stream": "java.util.DesugarArrays",
-        "java.util.Arrays#spliterator": "java.util.DesugarArrays",
-        "java.util.LinkedHashSet#spliterator": "java.util.DesugarLinkedHashSet",
-        "java.util.concurrent.atomic.AtomicInteger#getAndUpdate": "java.util.concurrent.atomic.DesugarAtomicInteger",
-        "java.util.concurrent.atomic.AtomicInteger#updateAndGet": "java.util.concurrent.atomic.DesugarAtomicInteger",
-        "java.util.concurrent.atomic.AtomicInteger#getAndAccumulate": "java.util.concurrent.atomic.DesugarAtomicInteger",
-        "java.util.concurrent.atomic.AtomicInteger#accumulateAndGet": "java.util.concurrent.atomic.DesugarAtomicInteger",
-        "java.util.concurrent.atomic.AtomicLong#getAndUpdate": "java.util.concurrent.atomic.DesugarAtomicLong",
-        "java.util.concurrent.atomic.AtomicLong#updateAndGet": "java.util.concurrent.atomic.DesugarAtomicLong",
-        "java.util.concurrent.atomic.AtomicLong#getAndAccumulate": "java.util.concurrent.atomic.DesugarAtomicLong",
-        "java.util.concurrent.atomic.AtomicLong#accumulateAndGet": "java.util.concurrent.atomic.DesugarAtomicLong",
-        "java.util.concurrent.atomic.AtomicReference#getAndUpdate": "java.util.concurrent.atomic.DesugarAtomicReference",
-        "java.util.concurrent.atomic.AtomicReference#updateAndGet": "java.util.concurrent.atomic.DesugarAtomicReference",
-        "java.util.concurrent.atomic.AtomicReference#getAndAccumulate": "java.util.concurrent.atomic.DesugarAtomicReference",
-        "java.util.concurrent.atomic.AtomicReference#accumulateAndGet": "java.util.concurrent.atomic.DesugarAtomicReference",
-        "java.util.Collections#synchronizedMap": "java.util.DesugarCollections",
-        "java.util.Collections#synchronizedSortedMap": "java.util.DesugarCollections",
-        "java.io.BufferedReader#lines": "java.io.DesugarBufferedReader"
-      },
-      "dont_rewrite": [
-        "java.util.Iterator#remove"
-      ],
-      "emulate_interface": {
-        "java.util.Map$Entry": "j$.util.Map$Entry",
-        "java.util.Collection": "j$.util.Collection",
-        "java.util.Map": "j$.util.Map",
-        "java.util.Iterator": "j$.util.Iterator",
-        "java.util.Comparator": "j$.util.Comparator",
-        "java.util.List": "j$.util.List",
-        "java.util.SortedSet": "j$.util.SortedSet",
-        "java.util.Set": "j$.util.Set",
-        "java.util.concurrent.ConcurrentMap": "j$.util.concurrent.ConcurrentMap"
-      },
-      "custom_conversion": {
-        "java.util.Optional": "java.util.OptionalConversions",
-        "java.util.OptionalDouble": "java.util.OptionalConversions",
-        "java.util.OptionalInt": "java.util.OptionalConversions",
-        "java.util.OptionalLong": "java.util.OptionalConversions",
-        "java.util.LongSummaryStatistics": "java.util.LongSummaryStatisticsConversions",
-        "java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
-        "java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions"
-      }
-    }
-  ],
-  "shrinker_config": [
-    "-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$TreeBin { int lockState; }",
-    "-keepclassmembers class j$.util.concurrent.ConcurrentHashMap { int sizeCtl; int transferIndex; long baseCount; int cellsBusy; }",
-    "-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$CounterCell { long value; }",
-    "-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); public static final !synthetic <fields>; }",
-    "-keeppackagenames j$",
-    "-keepclassmembers class j$.util.IntSummaryStatistics { long count; long sum; int min; int max; }",
-    "-keepclassmembers class j$.util.LongSummaryStatistics { long count; long sum; long min; long max; }",
-    "-keepclassmembers class j$.util.DoubleSummaryStatistics { long count; double sum; double min; double max; }",
-    "-keepattributes Signature",
-    "-keepattributes EnclosingMethod",
-    "-keepattributes InnerClasses",
-    "-dontwarn sun.misc.Unsafe"
-  ]
-}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index 778274a..503352b 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -21,8 +21,12 @@
       "retarget_method": {
         "java.time.Instant java.util.Date#toInstant()": "java.util.DesugarDate",
         "java.time.ZoneId java.util.TimeZone#toZoneId()": "java.util.DesugarTimeZone",
-        "java.time.ZonedDateTime java.util.GregorianCalendar#toZonedDateTime()": "java.util.DesugarGregorianCalendar"
+        "java.time.ZonedDateTime java.util.GregorianCalendar#toZonedDateTime()": "java.util.DesugarGregorianCalendar",
+        "java.util.TimeZone java.util.TimeZone#getTimeZone(java.time.ZoneId)": "java.util.DesugarTimeZone"
       },
+      "wrapper_conversion": [
+        "java.time.Clock"
+      ],
       "custom_conversion": {
         "java.time.Duration": "java.time.TimeConversions",
         "java.time.Instant": "java.time.TimeConversions",
@@ -33,12 +37,6 @@
       }
     },
     {
-      "api_level_below_or_equal": 25,
-      "wrapper_conversion": [
-        "java.time.Clock"
-      ]
-    },
-    {
       "api_level_below_or_equal": 23,
       "rewrite_prefix": {
         "java.util.DoubleSummaryStatistics": "j$.util.DoubleSummaryStatistics",
@@ -154,6 +152,9 @@
       "api_level_below_or_equal": 18,
       "rewrite_prefix": {
         "java.nio.charset.StandardCharsets": "j$.nio.charset.StandardCharsets"
+      },
+      "retarget_method": {
+        "boolean java.lang.Character#isBmpCodePoint(int)": "java.lang.DesugarCharacter"
       }
     }
   ],
@@ -164,8 +165,7 @@
         "java.time.Instant java.util.Calendar#toInstant()": "java.util.DesugarCalendar",
         "java.util.Date java.util.Date#from(java.time.Instant)": "java.util.DesugarDate",
         "java.util.GregorianCalendar java.util.GregorianCalendar#from(java.time.ZonedDateTime)": "java.util.DesugarGregorianCalendar",
-        "java.util.TimeZone java.util.TimeZone#getTimeZone(java.lang.String)": "java.util.DesugarTimeZone",
-        "java.util.TimeZone java.util.TimeZone#getTimeZone(java.time.ZoneId)": "java.util.DesugarTimeZone"
+        "java.util.TimeZone java.util.TimeZone#getTimeZone(java.lang.String)": "java.util.DesugarTimeZone"
       }
     },
     {
@@ -188,9 +188,15 @@
       }
     },
     {
+      "api_level_below_or_equal": 19,
+      "dont_retarget": [
+        "android.support.multidex.MultiDexExtractor$ExtractedDex"
+      ]
+    },
+    {
       "api_level_below_or_equal": 18,
-      "retarget_method": {
-        "boolean java.lang.Character#isBmpCodePoint(int)": "j$.lang.DesugarCharacter"
+      "rewrite_prefix": {
+        "java.lang.DesugarCharacter": "j$.lang.DesugarCharacter"
       }
     }
   ],
@@ -199,10 +205,21 @@
       "api_level_below_or_equal": 10000,
       "rewrite_prefix": {
         "desugar.": "j$.desugar.",
+        "libcore.": "j$.libcore.",
         "java.lang.Desugar": "j$.lang.Desugar",
         "java.lang.ref.Cleaner": "j$.lang.ref.Cleaner",
-        "libcore.": "j$.libcore.",
+        "wrapper." : "j$.wrapper.",
         "sun.security.action.": "j$.sun.security.action."
+      },
+      "rewrite_derived_prefix": {
+        "java.io.": {
+          "__wrapper__.j$.io.": "j$.io.",
+          "__wrapper__.java.io.": "java.io."
+        },
+        "java.nio.": {
+          "__wrapper__.j$.nio.": "j$.nio.",
+          "__wrapper__.java.nio.": "java.nio."
+        }
       }
     },
     {
@@ -223,7 +240,6 @@
       "api_level_below_or_equal": 25,
       "rewrite_prefix": {
         "java.io.DesugarFile": "j$.io.DesugarFile",
-        "java.nio.Desugar": "j$.nio.Desugar",
         "java.nio.channels.AsynchronousChannel": "j$.nio.channels.AsynchronousChannel",
         "java.nio.channels.AsynchronousFileChannel": "j$.nio.channels.AsynchronousFileChannel",
         "java.nio.channels.CompletionHandler": "j$.nio.channels.CompletionHandler",
@@ -240,8 +256,7 @@
       },
       "retarget_method": {
         "boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)": "java.util.DesugarArrays",
-        "java.nio.file.Path java.io.File#toPath()": "java.io.DesugarFile",
-        "java.util.TimeZone java.util.TimeZone#getTimeZone(java.time.ZoneId)": "java.util.DesugarTimeZone"
+        "java.nio.file.Path java.io.File#toPath()": "java.io.DesugarFile"
       },
       "backport": {
         "java.lang.DesugarDouble": "java.lang.Double",
@@ -281,13 +296,13 @@
         "java.util.Optional": {
           "j$.util.Optional": "java.util.Optional"
         }
-      }
-    },
-    {
-      "api_level_below_or_equal": 18,
+      },
       "retarget_method": {
-        "boolean java.lang.Character#isBmpCodePoint(int)": "java.lang.DesugarCharacter"
-      }
+        "long java.io.InputStream#transferTo(java.io.OutputStream)": "java.io.DesugarInputStream"
+      },
+      "amend_library_method": [
+        "public long java.io.InputStream#transferTo(java.io.OutputStream)"
+      ]
     }
   ],
   "shrinker_config": [
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json b/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
index faf4817..ab3c8a6 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
@@ -1,5 +1,5 @@
 {
-  "identifier": "com.tools.android:desugar_jdk_libs_alternative_3:2.0.0",
+  "identifier": "com.tools.android:desugar_jdk_libs_alternative_3:1.0.12",
   "configuration_format_version": 100,
   "required_compilation_api_level": 30,
   "synthesized_library_classes_package_prefix": "j$.",
@@ -21,8 +21,12 @@
       "retarget_method": {
         "java.time.Instant java.util.Date#toInstant()": "java.util.DesugarDate",
         "java.time.ZoneId java.util.TimeZone#toZoneId()": "java.util.DesugarTimeZone",
-        "java.time.ZonedDateTime java.util.GregorianCalendar#toZonedDateTime()": "java.util.DesugarGregorianCalendar"
+        "java.time.ZonedDateTime java.util.GregorianCalendar#toZonedDateTime()": "java.util.DesugarGregorianCalendar",
+        "java.util.TimeZone java.util.TimeZone#getTimeZone(java.time.ZoneId)": "java.util.DesugarTimeZone"
       },
+      "wrapper_conversion": [
+        "java.time.Clock"
+      ],
       "custom_conversion": {
         "java.time.Duration": "java.time.TimeConversions",
         "java.time.Instant": "java.time.TimeConversions",
@@ -33,14 +37,9 @@
       }
     },
     {
-      "api_level_below_or_equal": 25,
-      "wrapper_conversion": [
-        "java.time.Clock"
-      ]
-    },
-    {
       "api_level_below_or_equal": 23,
       "rewrite_prefix": {
+        "java.io.DesugarBufferedReader": "j$.io.DesugarBufferedReader",
         "java.io.UncheckedIOException": "j$.io.UncheckedIOException",
         "java.util.DoubleSummaryStatistics": "j$.util.DoubleSummaryStatistics",
         "java.util.IntSummaryStatistics": "j$.util.IntSummaryStatistics",
@@ -97,15 +96,15 @@
         "java.util.function.IntConsumer",
         "java.util.function.IntBinaryOperator",
         "java.util.function.UnaryOperator",
-        "java.util.function.IntPredicate",
         "java.util.function.DoubleConsumer",
+        "java.util.function.IntPredicate",
         "java.util.Spliterator$OfLong",
         "java.util.stream.Collector",
         "java.util.function.LongPredicate",
         "java.util.function.ToLongFunction",
         "java.util.function.LongToDoubleFunction",
-        "java.util.function.LongToIntFunction",
         "java.util.PrimitiveIterator$OfInt",
+        "java.util.function.LongToIntFunction",
         "java.util.function.Predicate",
         "java.util.Spliterator$OfPrimitive",
         "java.util.function.DoubleToIntFunction",
@@ -119,8 +118,8 @@
         "java.util.Spliterator",
         "java.util.stream.IntStream",
         "java.util.function.LongBinaryOperator",
-        "java.util.function.DoubleFunction",
         "java.util.Spliterator$OfDouble",
+        "java.util.function.DoubleFunction",
         "java.util.function.ObjIntConsumer",
         "java.util.function.Function",
         "java.util.function.Supplier",
@@ -131,8 +130,8 @@
         "java.util.PrimitiveIterator$OfLong",
         "java.util.function.BiConsumer",
         "java.util.function.IntFunction",
-        "java.util.function.IntToDoubleFunction",
         "java.util.stream.LongStream",
+        "java.util.function.IntToDoubleFunction",
         "java.util.function.LongFunction",
         "java.util.function.ToIntFunction",
         "java.util.function.LongConsumer",
@@ -157,6 +156,9 @@
       "api_level_below_or_equal": 18,
       "rewrite_prefix": {
         "java.nio.charset.StandardCharsets": "j$.nio.charset.StandardCharsets"
+      },
+      "retarget_method": {
+        "boolean java.lang.Character#isBmpCodePoint(int)": "java.lang.DesugarCharacter"
       }
     }
   ],
@@ -167,8 +169,7 @@
         "java.time.Instant java.util.Calendar#toInstant()": "java.util.DesugarCalendar",
         "java.util.Date java.util.Date#from(java.time.Instant)": "java.util.DesugarDate",
         "java.util.GregorianCalendar java.util.GregorianCalendar#from(java.time.ZonedDateTime)": "java.util.DesugarGregorianCalendar",
-        "java.util.TimeZone java.util.TimeZone#getTimeZone(java.lang.String)": "java.util.DesugarTimeZone",
-        "java.util.TimeZone java.util.TimeZone#getTimeZone(java.time.ZoneId)": "java.util.DesugarTimeZone"
+        "java.util.TimeZone java.util.TimeZone#getTimeZone(java.lang.String)": "java.util.DesugarTimeZone"
       }
     },
     {
@@ -191,9 +192,15 @@
       }
     },
     {
+      "api_level_below_or_equal": 19,
+      "dont_retarget": [
+        "android.support.multidex.MultiDexExtractor$ExtractedDex"
+      ]
+    },
+    {
       "api_level_below_or_equal": 18,
-      "retarget_method": {
-        "boolean java.lang.Character#isBmpCodePoint(int)": "j$.lang.DesugarCharacter"
+      "rewrite_prefix": {
+        "java.lang.DesugarCharacter": "j$.lang.DesugarCharacter"
       }
     }
   ],
@@ -202,17 +209,41 @@
       "api_level_below_or_equal": 10000,
       "rewrite_prefix": {
         "desugar.": "j$.desugar.",
+        "libcore.": "j$.libcore.",
         "java.lang.Desugar": "j$.lang.Desugar",
         "java.lang.ref.Cleaner": "j$.lang.ref.Cleaner",
-        "libcore.": "j$.libcore.",
+        "wrapper." : "j$.wrapper.",
         "sun.security.action.": "j$.sun.security.action."
+      },
+      "rewrite_derived_prefix": {
+        "java.io.": {
+          "__wrapper__.j$.io.": "j$.io.",
+          "__wrapper__.java.io.": "java.io."
+        },
+        "java.nio.": {
+          "__wrapper__.j$.nio.": "j$.nio.",
+          "__wrapper__.java.nio.": "java.nio."
+        }
       }
     },
     {
       "api_level_below_or_equal": 30,
+      "rewrite_derived_prefix": {
+        "java.time.": {
+          "j$.time.": "java.time."
+        }
+      }
+    },
+    {
+      "api_level_below_or_equal": 29,
+      "rewrite_prefix": {
+        "java.util.concurrent.Flow": "j$.util.concurrent.Flow"
+      }
+    },
+    {
+      "api_level_below_or_equal": 25,
       "rewrite_prefix": {
         "java.io.DesugarFile": "j$.io.DesugarFile",
-        "java.nio.Desugar": "j$.nio.Desugar",
         "java.nio.channels.AsynchronousChannel": "j$.nio.channels.AsynchronousChannel",
         "java.nio.channels.AsynchronousFileChannel": "j$.nio.channels.AsynchronousFileChannel",
         "java.nio.channels.CompletionHandler": "j$.nio.channels.CompletionHandler",
@@ -227,12 +258,8 @@
         "sun.nio.fs.DynamicFileAttributeView": "j$.sun.nio.fs.DynamicFileAttributeView",
         "sun.util.PreHashedMap": "j$.sun.util.PreHashedMap"
       },
-      "rewrite_derived_prefix": {
-        "java.time.": {
-          "j$.time.": "java.time."
-        }
-      },
       "retarget_method": {
+        "boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)": "java.util.DesugarArrays",
         "java.nio.file.Path java.io.File#toPath()": "java.io.DesugarFile"
       },
       "backport": {
@@ -240,18 +267,6 @@
         "java.lang.DesugarInteger": "java.lang.Integer",
         "java.lang.DesugarLong": "java.lang.Long",
         "java.lang.DesugarMath": "java.lang.Math"
-      }
-    },
-    {
-      "api_level_below_or_equal": 29,
-      "rewrite_prefix": {
-        "java.util.concurrent.Flow": "j$.util.concurrent.Flow"
-      }
-    },
-    {
-      "api_level_below_or_equal": 25,
-      "retarget_method": {
-        "boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)": "java.util.DesugarArrays"
       },
       "amend_library_method": [
         "private static boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)"
@@ -260,7 +275,6 @@
     {
       "api_level_below_or_equal": 23,
       "rewrite_prefix": {
-        "java.io.DesugarBufferedReader": "j$.io.DesugarBufferedReader",
         "java.io.DesugarInputStream": "j$.io.DesugarInputStream",
         "java.lang.FunctionalInterface": "j$.lang.FunctionalInterface",
         "java.nio.channels.SeekableByteChannel": "j$.nio.channels.SeekableByteChannel",
@@ -286,18 +300,19 @@
         "java.util.Optional": {
           "j$.util.Optional": "java.util.Optional"
         }
-      }
-    },
-    {
-      "api_level_below_or_equal": 18,
+      },
       "retarget_method": {
-        "boolean java.lang.Character#isBmpCodePoint(int)": "java.lang.DesugarCharacter"
-      }
+        "long java.io.InputStream#transferTo(java.io.OutputStream)": "java.io.DesugarInputStream"
+      },
+      "amend_library_method": [
+        "public long java.io.InputStream#transferTo(java.io.OutputStream)"
+      ]
     }
   ],
   "shrinker_config": [
     "-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$TreeBin { int lockState; }",
     "-keepclassmembers class j$.util.concurrent.ConcurrentHashMap { int sizeCtl; int transferIndex; long baseCount; int cellsBusy; }",
+    "-keepclassmembers class j$.util.concurrent.ConcurrentHashMap { private void readObject(java.io.ObjectInputStream); private void writeObject(java.io.ObjectOutputStream); private void readObjectNoData(); private static final java.io.ObjectStreamField[] serialPersistentFields; private static final long serialVersionUID;}",
     "-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$CounterCell { long value; }",
     "-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); public static final !synthetic <fields>; }",
     "-keeppackagenames j$.**",
diff --git a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
index 1ae2311..9c11062 100644
--- a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
+++ b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
@@ -6,7 +6,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
@@ -248,7 +248,7 @@
 
     DesugaredLibrarySpecification getDesugaredLibraryConfiguration(DexItemFactory factory) {
       if (desugaredLibrarySpecificationResources.isEmpty()) {
-        return LegacyDesugaredLibrarySpecification.empty();
+        return HumanDesugaredLibrarySpecification.empty();
       }
       if (desugaredLibrarySpecificationResources.size() > 1) {
         reporter.fatalError("Only one desugared library configuration is supported.");
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index a474a31..a522278 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.inspector.Inspector;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
@@ -577,7 +577,7 @@
     DesugaredLibrarySpecification getDesugaredLibraryConfiguration(
         DexItemFactory factory, boolean libraryCompilation) {
       if (desugaredLibrarySpecificationResources.isEmpty()) {
-        return LegacyDesugaredLibrarySpecification.empty();
+        return HumanDesugaredLibrarySpecification.empty();
       }
       if (desugaredLibrarySpecificationResources.size() > 1) {
         throw new CompilationError("Only one desugared library configuration is supported.");
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index bc6cb77..051565e 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -87,7 +87,7 @@
         readDesugaredLibraryConfiguration(desugarConfigurationPath);
     Path androidJarPath = getAndroidJarPath(specification.getRequiredCompilationApiLevel());
     this.desugaredLibrarySpecification =
-        specification.toMachineSpecification(options, androidJarPath);
+        specification.toMachineSpecification(options, androidJarPath, Timing.empty());
 
     this.desugaredLibraryImplementation = Paths.get(desugarImplementationPath);
     this.outputDirectory = Paths.get(outputDirectory);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index ac01496..732a73b 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -497,12 +497,14 @@
           && options.getProguardConfiguration().isOptimizing()) {
         if (options.enableVerticalClassMerging) {
           timing.begin("VerticalClassMerger");
-          VerticalClassMerger verticalClassMerger =
+          VerticalClassMergerGraphLens lens =
               new VerticalClassMerger(
-                  getDirectApp(appViewWithLiveness), appViewWithLiveness, executorService, timing);
-          VerticalClassMergerGraphLens lens = verticalClassMerger.run();
+                      getDirectApp(appViewWithLiveness),
+                      appViewWithLiveness,
+                      executorService,
+                      timing)
+                  .run();
           if (lens != null) {
-            appView.rewriteWithLens(lens);
             runtimeTypeCheckInfo = runtimeTypeCheckInfo.rewriteWithLens(lens);
           }
           timing.end();
diff --git a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
deleted file mode 100644
index 20571ba..0000000
--- a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
+++ /dev/null
@@ -1,242 +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.benchmarks;
-
-import static com.android.tools.r8.benchmarks.BenchmarkUtils.printRuntimeNanoseconds;
-
-import com.android.tools.r8.ByteDataView;
-import com.android.tools.r8.ClassFileResourceProvider;
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.D8;
-import com.android.tools.r8.D8Command;
-import com.android.tools.r8.D8Command.Builder;
-import com.android.tools.r8.DexFilePerClassFileConsumer;
-import com.android.tools.r8.DexFilePerClassFileConsumer.ForwardingConsumer;
-import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.ProgramConsumer;
-import com.android.tools.r8.ProgramResource;
-import com.android.tools.r8.ProgramResource.Kind;
-import com.android.tools.r8.ResourceException;
-import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.ZipUtils;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.io.ByteStreams;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-
-public class FrameworkIncrementalDexingBenchmark {
-  private static final int ITERATIONS = 100;
-  private static final int API = 24;
-  private static final Path JAR_DESUGARED =
-      Paths.get("third_party", "framework", "framework_14082017_desugared.jar");
-  private static final Path JAR_NOT_DESUGARED =
-      Paths.get("third_party", "framework", "framework_14082017.jar");
-  private static final Path LIB =
-      Paths.get("third_party", "android_jar", "lib-v" + API, "android.jar");
-
-  static class InMemoryClassPathProvider implements ClassFileResourceProvider {
-    Origin origin;
-    Map<String, byte[]> resources;
-
-    InMemoryClassPathProvider(Path archive) throws IOException {
-      origin = new PathOrigin(archive);
-      ImmutableMap.Builder<String, byte[]> builder = ImmutableMap.builder();
-      ZipUtils.iter(
-          archive.toString(),
-          (entry, stream) -> {
-            String name = entry.getName();
-            if (ZipUtils.isClassFile(name)) {
-              String descriptor = DescriptorUtils.guessTypeDescriptor(name);
-              builder.put(descriptor, ByteStreams.toByteArray(stream));
-            }
-          });
-      resources = builder.build();
-    }
-
-    @Override
-    public Set<String> getClassDescriptors() {
-      return resources.keySet();
-    }
-
-    @Override
-    public ProgramResource getProgramResource(String descriptor) {
-      byte[] bytes = resources.get(descriptor);
-      return bytes == null
-          ? null
-          : ProgramResource.fromBytes(
-              new EntryOrigin(descriptor, origin),
-              Kind.CF,
-              bytes,
-              Collections.singleton(descriptor));
-    }
-  }
-
-  static class EntryOrigin extends Origin {
-    final String descriptor;
-
-    public EntryOrigin(String descriptor, Origin parent) {
-      super(parent);
-      this.descriptor = descriptor;
-    }
-
-    @Override
-    public String part() {
-      return descriptor;
-    }
-  }
-
-  private static String title(String title, boolean desugar) {
-    return "FrameworkIncremental" + (desugar ? title : "NoDesugar" + title);
-  }
-
-  private static void compileAll(
-      Path input,
-      InMemoryClassPathProvider provider,
-      boolean desugar,
-      Map<String, ProgramResource> outputs,
-      ExecutorService executor)
-      throws IOException, CompilationFailedException {
-
-    ProgramConsumer consumer =
-        new DexFilePerClassFileConsumer.ForwardingConsumer(null) {
-          @Override
-          public synchronized void accept(
-              String primaryClassDescriptor,
-              ByteDataView data,
-              Set<String> descriptors,
-              DiagnosticsHandler handler) {
-            ProgramResource resource = ProgramResource.fromBytes(
-                Origin.unknown(), Kind.DEX, data.copyByteData(), descriptors);
-            for (String descriptor : descriptors) {
-              assert !outputs.containsKey(descriptor);
-              if (provider.resources.containsKey(descriptor)) {
-                outputs.put(descriptor, resource);
-              }
-            }
-          }
-        };
-
-    long start = System.nanoTime();
-    D8.run(
-        D8Command.builder()
-            .setMinApiLevel(API)
-            .setIntermediate(true)
-            .setMode(CompilationMode.DEBUG)
-            .addProgramFiles(input)
-            .addLibraryFiles(LIB)
-            .setDisableDesugaring(!desugar)
-            .setProgramConsumer(consumer)
-            .build(),
-        executor);
-    printRuntimeNanoseconds(title("DexAll", desugar), System.nanoTime() - start);
-  }
-
-  private static void compileGroupsOf(
-      int count,
-      List<String> descriptors,
-      InMemoryClassPathProvider provider,
-      boolean desugar,
-      Map<String, ProgramResource> outputs,
-      ExecutorService executor)
-      throws IOException, CompilationFailedException {
-    ProgramConsumer consumer =
-        new ForwardingConsumer(null) {
-          @Override
-          public synchronized void accept(
-              String primaryClassDescriptor,
-              ByteDataView data,
-              Set<String> descriptors,
-              DiagnosticsHandler handler) {
-            ProgramResource resource = ProgramResource.fromBytes(
-                Origin.unknown(), Kind.DEX, data.copyByteData(), descriptors);
-            for (String descriptor : descriptors) {
-              if (provider.resources.containsKey(descriptor)) {
-                outputs.put(descriptor, resource);
-              }
-            }
-          }
-        };
-
-    descriptors.sort(String::compareTo);
-    int increment = descriptors.size() / ITERATIONS;
-    long start = System.nanoTime();
-    for (int iteration = 0; iteration < ITERATIONS; iteration++) {
-      int index = iteration * increment;
-      Builder builder =
-          D8Command.builder()
-              .setMinApiLevel(API)
-              .setIntermediate(true)
-              .setMode(CompilationMode.DEBUG)
-              .addClasspathResourceProvider(provider)
-              .addLibraryFiles(LIB)
-              .setProgramConsumer(consumer)
-              .setDisableDesugaring(!desugar);
-      for (int j = 0; j < count; j++) {
-        builder.addClassProgramData(provider.resources.get(descriptors.get(index + j)),
-            Origin.unknown());
-      }
-      D8.run(builder.build(), executor);
-    }
-    printRuntimeNanoseconds(title("DexGroupsOf" + count, desugar), System.nanoTime() - start);
-  }
-
-  private static void merge(
-      boolean desugar, Map<String, ProgramResource> outputs, ExecutorService executor)
-      throws IOException, CompilationFailedException, ResourceException {
-    Builder builder =
-        D8Command.builder()
-            .setMinApiLevel(API)
-            .setIntermediate(false)
-            .setMode(CompilationMode.DEBUG)
-            .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
-            .setDisableDesugaring(true);
-    for (ProgramResource input : outputs.values()) {
-      try (InputStream inputStream = input.getByteStream()) {
-        builder.addDexProgramData(ByteStreams.toByteArray(inputStream), input.getOrigin());
-      }
-    }
-    long start = System.nanoTime();
-    D8.run(
-        builder // never need to desugar when merging dex.
-            .build(),
-        executor);
-    printRuntimeNanoseconds(title("DexMerge", desugar), System.nanoTime() - start);
-  }
-
-  public static void main(String[] args)
-      throws IOException, CompilationFailedException, ResourceException {
-    boolean desugar = Arrays.asList(args).contains("--desugar");
-    Path input = desugar ? JAR_NOT_DESUGARED : JAR_DESUGARED;
-    InMemoryClassPathProvider provider = new InMemoryClassPathProvider(input);
-    List<String> descriptors = new ArrayList<>(provider.getClassDescriptors());
-    Map<String, ProgramResource> outputs = new HashMap<>(provider.getClassDescriptors().size());
-    int threads = Integer.min(Runtime.getRuntime().availableProcessors(), 16) / 2;
-    ExecutorService executor = ThreadUtils.getExecutorService(threads);
-    try {
-      compileAll(input, provider, desugar, outputs, executor);
-      compileGroupsOf(1, descriptors, provider, desugar, outputs, executor);
-      compileGroupsOf(10, descriptors, provider, desugar, outputs, executor);
-      compileGroupsOf(100, descriptors, provider, desugar, outputs, executor);
-      merge(desugar, outputs, executor);
-      // TODO: We should run dex2oat to verify the compilation.
-    } finally {
-      executor.shutdown();
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java b/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java
deleted file mode 100644
index 477fd63..0000000
--- a/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java
+++ /dev/null
@@ -1,60 +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.benchmarks;
-
-import com.android.tools.r8.ByteDataView;
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.D8;
-import com.android.tools.r8.D8Command;
-import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.utils.ThreadUtils;
-import java.io.IOException;
-import java.nio.file.Paths;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-
-public class IncrementalDexingBenchmark {
-  private static final int ITERATIONS = 1000;
-
-  public static void compile(ExecutorService executor)
-      throws IOException, CompilationFailedException {
-    D8.run(
-        D8Command.builder()
-            .addProgramFiles(Paths.get("build/test/examples/arithmetic.jar"))
-            .setMode(CompilationMode.DEBUG)
-            .setDisableDesugaring(true)
-            .setProgramConsumer(
-                new DexIndexedConsumer.ForwardingConsumer(null) {
-                  @Override
-                  public void accept(
-                      int fileIndex,
-                      ByteDataView data,
-                      Set<String> descriptors,
-                      DiagnosticsHandler handler) {
-                    if (fileIndex != 0) {
-                      throw new RuntimeException("WAT");
-                    }
-                  }
-                })
-            .build(),
-        executor);
-  }
-
-  public static void main(String[] args) throws IOException, CompilationFailedException {
-    int threads = Integer.min(Runtime.getRuntime().availableProcessors(), 16) / 2;
-    ExecutorService executor = ThreadUtils.getExecutorService(threads);
-    try {
-      long start = System.nanoTime();
-      for (int i = 0; i < ITERATIONS; i++) {
-        compile(executor);
-      }
-      double elapsedMs = (System.nanoTime() - start) / 1000000.0;
-      BenchmarkUtils.printRuntimeMilliseconds("IncrementalDexing", elapsedMs);
-    } finally {
-      executor.shutdown();
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/cf/CfVerifierTool.java b/src/main/java/com/android/tools/r8/cf/CfVerifierTool.java
index 524b6bc..fdfba1e 100644
--- a/src/main/java/com/android/tools/r8/cf/CfVerifierTool.java
+++ b/src/main/java/com/android/tools/r8/cf/CfVerifierTool.java
@@ -32,12 +32,7 @@
     appView.setAppServices(AppServices.builder(appView).build());
     for (DexProgramClass clazz : appView.appInfo().classes()) {
       clazz.forEachProgramMethod(
-          method ->
-              method
-                  .getDefinition()
-                  .getCode()
-                  .asCfCode()
-                  .verifyFrames(method.getDefinition(), appView, clazz.getOrigin()));
+          method -> method.getDefinition().getCode().asCfCode().verifyFrames(method, appView));
     }
   }
 }
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 bf66099..efad6db 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
@@ -10,7 +10,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -202,13 +202,12 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., value1, value2 →
     // ..., result
-    FrameType frameType = FrameType.fromNumericType(type, factory);
+    FrameType frameType = FrameType.fromNumericType(type, dexItemFactory);
     frameBuilder.popAndDiscard(frameType, frameType).push(frameType);
   }
 }
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 21e74a3..0af6a11 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
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -77,12 +77,13 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., arrayref →
     // ..., length
-    frameBuilder.popAndDiscardInitialized(factory.objectArrayType).push(factory.intType);
+    frameBuilder
+        .popAndDiscardInitialized(dexItemFactory.objectArrayType)
+        .push(dexItemFactory.intType);
   }
 }
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 3385529..44f0e60 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
@@ -10,7 +10,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -123,13 +123,12 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., arrayref, index →
     // ..., value
-    frameBuilder.popAndDiscardInitialized(factory.objectArrayType, factory.intType);
-    frameBuilder.push(FrameType.fromMemberType(type, factory));
+    frameBuilder.popAndDiscardInitialized(dexItemFactory.objectArrayType, dexItemFactory.intType);
+    frameBuilder.push(FrameType.fromMemberType(type, dexItemFactory));
   }
 }
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 e4350e0..68d6b2d 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
@@ -10,7 +10,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -113,14 +113,13 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., arrayref, index, value →
     // ...
     frameBuilder
-        .popAndDiscard(FrameType.fromMemberType(type, factory))
-        .popAndDiscardInitialized(factory.objectArrayType, factory.intType);
+        .popAndDiscard(FrameType.fromMemberType(type, dexItemFactory))
+        .popAndDiscardInitialized(dexItemFactory.objectArrayType, dexItemFactory.intType);
   }
 }
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 9e198e4..6098e98 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
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
@@ -127,12 +128,11 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., objectref →
     // ..., objectref
-    frameBuilder.popAndDiscardInitialized(factory.objectType).push(type);
+    frameBuilder.popAndDiscardInitialized(dexItemFactory.objectType).push(type);
   }
 }
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 e1a5ce5..c997da1 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
@@ -10,7 +10,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -127,13 +127,12 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., value1, value2 →
     // ..., result
-    FrameType frameType = FrameType.fromNumericType(type, factory);
-    frameBuilder.popAndDiscard(frameType, frameType).push(factory.intType);
+    FrameType frameType = FrameType.fromNumericType(type, dexItemFactory);
+    frameBuilder.popAndDiscard(frameType, frameType).push(dexItemFactory.intType);
   }
 }
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 07cb6d2..c135288 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
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
@@ -146,12 +147,11 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ... →
     // ..., value
-    frameBuilder.push(factory.classType);
+    frameBuilder.push(dexItemFactory.classType);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
index f86c213..4efa8ae 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
@@ -221,12 +221,11 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ... →
     // ..., value
-    frameBuilder.push(factory.classType);
+    frameBuilder.push(dexItemFactory.classType);
   }
 }
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 b495441..5f5837a 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
@@ -9,8 +9,8 @@
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
-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.ProgramMethod;
@@ -98,12 +98,11 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ... →
     // ..., value
-    frameBuilder.push(factory.methodHandleType);
+    frameBuilder.push(dexItemFactory.methodHandleType);
   }
 }
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 3578a70..3ef0e8d 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
@@ -9,8 +9,8 @@
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProto;
-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.ProgramMethod;
@@ -96,12 +96,11 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ... →
     // ..., value
-    frameBuilder.push(factory.methodTypeType);
+    frameBuilder.push(dexItemFactory.methodTypeType);
   }
 }
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 f04dcfe..4d7dbb7 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
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -69,10 +69,9 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ... →
     // ..., value
     frameBuilder.push(DexItemFactory.nullValueType);
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 759568d..ca9cdf0 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
@@ -9,7 +9,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -172,12 +172,11 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ... →
     // ..., value
-    frameBuilder.push(type.toPrimitiveType().toDexType(factory));
+    frameBuilder.push(type.toPrimitiveType().toDexType(dexItemFactory));
   }
 }
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 9a079e7..1b46a48 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
@@ -8,8 +8,8 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 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.ProgramMethod;
@@ -99,12 +99,11 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ... →
     // ..., value
-    frameBuilder.push(factory.stringType);
+    frameBuilder.push(dexItemFactory.stringType);
   }
 }
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 1958b5c..0a6887a 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
@@ -10,8 +10,8 @@
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexReference;
-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.ProgramMethod;
@@ -118,12 +118,11 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ... →
     // ..., value
-    frameBuilder.push(factory.stringType);
+    frameBuilder.push(dexItemFactory.stringType);
   }
 }
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 783a837..99c9151 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
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.CfCompareHelper;
 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.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
@@ -183,10 +184,9 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     switch (opcode) {
       case Opcodes.GETFIELD:
         // ..., objectref →
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 47124c0..31bc71c 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
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.CfCodeStackMapValidatingException;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
@@ -492,10 +493,9 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     frameBuilder.checkFrameAndSet(this);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
index 9272226..0d466f0 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.graph.CfCodeStackMapValidatingException;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.utils.MapUtils;
 import com.android.tools.r8.utils.collections.ImmutableDeque;
 import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
@@ -40,7 +39,6 @@
   private final BiPredicate<DexType, DexType> isJavaAssignable;
   private final DexItemFactory factory;
   private final List<CfTryCatch> tryCatchRanges;
-  private final GraphLens graphLens;
   private final int maxStackHeight;
 
   private final Deque<CfTryCatch> currentCatchRanges = new ArrayDeque<>();
@@ -52,14 +50,12 @@
       List<CfTryCatch> tryCatchRanges,
       BiPredicate<DexType, DexType> isJavaAssignable,
       DexItemFactory factory,
-      GraphLens graphLens,
       int maxStackHeight) {
     this.context = context;
     this.stateMap = stateMap;
     this.tryCatchRanges = tryCatchRanges;
     this.isJavaAssignable = isJavaAssignable;
     this.factory = factory;
-    this.graphLens = graphLens;
     this.maxStackHeight = maxStackHeight;
     throwStack = ImmutableDeque.of(FrameType.initialized(factory.throwableType));
     // Compute all labels that marks a start or end to catch ranges.
@@ -113,6 +109,12 @@
     return frameType;
   }
 
+  public CfFrameVerificationHelper popAndDiscardInitialized(DexType expectedType) {
+    checkFrameIsSet();
+    popInitialized(expectedType);
+    return this;
+  }
+
   public CfFrameVerificationHelper popAndDiscardInitialized(DexType... expectedTypes) {
     checkFrameIsSet();
     for (int i = expectedTypes.length - 1; i >= 0; i--) {
@@ -258,14 +260,14 @@
     if (!source.isUninitializedThis()) {
       return false;
     }
-    return target == factory.objectType || graphLens.lookupClassType(target) == context;
+    return target == factory.objectType || target == context;
   }
 
   public boolean isUninitializedNewAndTarget(FrameType source, DexType target) {
     if (!source.isUninitializedNew()) {
       return false;
     }
-    return target == factory.objectType || graphLens.lookupClassType(target) == context;
+    return target == factory.objectType || target == context;
   }
 
   public boolean isAssignableAndInitialized(FrameType source, DexType target) {
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 6c434c4..eb82f47 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
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -94,10 +94,9 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     frameBuilder.checkTarget(target);
     frameBuilder.setNoFrame();
   }
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 a3ad1cc..9db12ec 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
@@ -9,7 +9,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -130,14 +130,15 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., value →
     // ...
     frameBuilder.popAndDiscardInitialized(
-        type.isObject() ? factory.objectType : type.toPrimitiveType().toDexType(factory));
+        type.isObject()
+            ? dexItemFactory.objectType
+            : type.toPrimitiveType().toDexType(dexItemFactory));
     frameBuilder.checkTarget(target);
   }
 }
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 e42d7b2..2340a89 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
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
@@ -131,14 +132,15 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., value1, value2 →
     // ...
     DexType type =
-        this.type.isObject() ? factory.objectType : this.type.toPrimitiveType().toDexType(factory);
+        this.type.isObject()
+            ? dexItemFactory.objectType
+            : this.type.toPrimitiveType().toDexType(dexItemFactory);
     frameBuilder.popAndDiscardInitialized(type, type);
     frameBuilder.checkTarget(target);
   }
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 ea9e810..735dde8 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
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -91,10 +91,9 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
-    frameBuilder.readLocal(var, factory.intType);
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
+    frameBuilder.readLocal(var, dexItemFactory.intType);
   }
 }
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 4d42276..1092bfe 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
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 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.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
@@ -111,12 +112,11 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., →
     // ..., value
-    frameBuilder.push(factory.intType);
+    frameBuilder.push(dexItemFactory.intType);
   }
 }
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 8b82246..0f93c4d 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
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
@@ -121,12 +122,11 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., objectref →
     // ..., result
-    frameBuilder.popAndDiscardInitialized(factory.objectType).push(factory.intType);
+    frameBuilder.popAndDiscardInitialized(dexItemFactory.objectType).push(dexItemFactory.intType);
   }
 }
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 4427f41..a1b7023 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
@@ -12,7 +12,7 @@
 import com.android.tools.r8.graph.ClasspathMethod;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -309,8 +309,7 @@
 
   public abstract void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens);
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory);
 }
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 292178b..9fb383a 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
@@ -311,23 +311,24 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., objectref, [arg1, [arg2 ...]] →
     // ... [ returnType ]
     // OR, for static method calls:
     // ..., [arg1, [arg2 ...]] →
     // ...
-    frameBuilder.popAndDiscardInitialized(this.method.proto.parameters.values);
-    if (opcode == Opcodes.INVOKESPECIAL && method.isInstanceInitializer(factory)) {
-      frameBuilder.popAndInitialize(context, method.holder);
+    frameBuilder.popAndDiscardInitialized(method.proto.parameters.values);
+    if (opcode == Opcodes.INVOKESPECIAL
+        && (method.isInstanceInitializer(dexItemFactory)
+            || method.mustBeInlinedIntoInstanceInitializer(appView))) {
+      frameBuilder.popAndInitialize(context.getHolderType(), method.holder);
     } else if (opcode != Opcodes.INVOKESTATIC) {
       frameBuilder.popInitialized(method.holder);
     }
-    if (this.method.proto.returnType != factory.voidType) {
-      frameBuilder.push(this.method.proto.returnType);
+    if (!method.getReturnType().isVoidType()) {
+      frameBuilder.push(method.getReturnType());
     }
   }
 
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 644ed7d..2082290 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
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -162,14 +163,13 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., [arg1, [arg2 ...]] →
     // ...
     frameBuilder.popAndDiscardInitialized(callSite.methodProto.parameters.values);
-    if (callSite.methodProto.returnType != factory.voidType) {
+    if (callSite.methodProto.returnType != dexItemFactory.voidType) {
       frameBuilder.push(callSite.methodProto.returnType);
     }
   }
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 0bff416..51829ec 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
@@ -10,7 +10,7 @@
 import com.android.tools.r8.graph.CfCodeStackMapValidatingException;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -80,10 +80,9 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // JSR/RET instructions cannot be verified since we have not type-checking way for addresses
     // on the stack/locals. We have to abandon.
     throw CfCodeStackMapValidatingException.error("Unexpected JSR/RET instruction");
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 028eff7..1f611b1 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
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -92,10 +92,9 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // This is a no-op.
   }
 }
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 7aaf08c..4f9a4db 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
@@ -9,7 +9,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -121,15 +121,16 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ... →
     // ..., objectref
     frameBuilder.push(
         frameBuilder.readLocal(
             getLocalIndex(),
-            type.isObject() ? factory.objectType : type.toPrimitiveType().toDexType(factory)));
+            type.isObject()
+                ? dexItemFactory.objectType
+                : type.toPrimitiveType().toDexType(dexItemFactory)));
   }
 }
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 8a38898..71e31d8 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
@@ -10,7 +10,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -174,13 +174,12 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., value1, value2 →
     // ..., result
-    FrameType value1Type = FrameType.fromNumericType(type, factory);
+    FrameType value1Type = FrameType.fromNumericType(type, dexItemFactory);
     FrameType value2Type;
     switch (opcode) {
       case And:
@@ -189,7 +188,7 @@
         value2Type = value1Type;
         break;
       default:
-        value2Type = FrameType.initialized(factory.intType);
+        value2Type = FrameType.initialized(dexItemFactory.intType);
     }
     frameBuilder.popAndDiscard(value1Type, value2Type).push(value1Type);
   }
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 809317f..557d380 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
@@ -9,7 +9,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -91,12 +91,11 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., objectref →
     // ...
-    frameBuilder.pop(FrameType.initialized(factory.objectType));
+    frameBuilder.pop(FrameType.initialized(dexItemFactory.objectType));
   }
 }
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 b798257..33fed6e 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
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
@@ -124,14 +125,13 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., count1, [count2, ...] →
     // ..., arrayref
     for (int i = 0; i < dimensions; i++) {
-      frameBuilder.popInitialized(factory.intType);
+      frameBuilder.popInitialized(dexItemFactory.intType);
     }
     frameBuilder.push(type);
   }
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 e9a6518..a2621a8 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
@@ -10,7 +10,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -116,13 +116,12 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., value →
     // ..., result
-    FrameType frameType = FrameType.fromNumericType(type, factory);
+    FrameType frameType = FrameType.fromNumericType(type, dexItemFactory);
     frameBuilder.popAndDiscard(frameType).push(frameType);
   }
 }
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 ff87575..95deb22 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
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
@@ -110,10 +111,9 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ... →
     // ..., objectref
     frameBuilder.push(FrameType.uninitializedNew(new CfLabel(), 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 769fe22..50f82d0 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
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
@@ -160,13 +161,12 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., count →
     // ..., arrayref
     assert type.isArrayType();
-    frameBuilder.popAndDiscardInitialized(factory.intType).push(type);
+    frameBuilder.popAndDiscardInitialized(dexItemFactory.intType).push(type);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java b/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java
index 335cce5..0d283d4 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
@@ -113,10 +114,9 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ... →
     // ..., objectref
     frameBuilder.push(FrameType.initialized(type));
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 c7be975..3518f2a 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
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -73,10 +73,9 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // This is an actual 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 9e656f7..13c737d 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
@@ -10,7 +10,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -187,14 +187,13 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., value →
     // ..., result
     frameBuilder
-        .popAndDiscard(FrameType.fromNumericType(from, factory))
-        .push(FrameType.fromNumericType(to, factory));
+        .popAndDiscard(FrameType.fromNumericType(from, dexItemFactory))
+        .push(FrameType.fromNumericType(to, dexItemFactory));
   }
 }
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 0112d3f..e7bf24a 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
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -106,10 +106,9 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // This is a no-op.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfRecordFieldValues.java b/src/main/java/com/android/tools/r8/cf/code/CfRecordFieldValues.java
index b5b2275..ec9b7df 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfRecordFieldValues.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfRecordFieldValues.java
@@ -11,7 +11,7 @@
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -106,13 +106,12 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     for (DexField ignored : fields) {
-      frameBuilder.popInitialized(factory.objectType);
+      frameBuilder.popInitialized(dexItemFactory.objectType);
     }
-    frameBuilder.push(factory.objectArrayType);
+    frameBuilder.push(dexItemFactory.objectArrayType);
   }
 }
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 9a74eaf..68038a4 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
@@ -9,7 +9,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -109,12 +109,11 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
-    assert returnType != null;
-    frameBuilder.popAndDiscardInitialized(returnType);
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
+    assert !context.getReturnType().isVoidType();
+    frameBuilder.popAndDiscardInitialized(context.getReturnType());
     frameBuilder.setNoFrame();
   }
 }
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 84a524d..9072b97 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
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -83,10 +83,9 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     frameBuilder.setNoFrame();
   }
 }
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 911141c..289846f 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
@@ -11,7 +11,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -350,10 +350,9 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
 
     switch (opcode) {
       case Pop:
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 9177bda..b096223 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
@@ -12,7 +12,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -123,10 +123,9 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., ref →
     // ...
     FrameType pop = frameBuilder.pop();
@@ -134,7 +133,7 @@
       case OBJECT:
         frameBuilder.checkIsAssignable(
             pop,
-            factory.objectType,
+            dexItemFactory.objectType,
             or(
                 frameBuilder::isUninitializedThisAndTarget,
                 frameBuilder::isUninitializedNewAndTarget,
@@ -143,25 +142,25 @@
         return;
       case INT:
         frameBuilder.checkIsAssignable(
-            pop, factory.intType, frameBuilder::isAssignableAndInitialized);
-        frameBuilder.storeLocal(var, FrameType.initialized(factory.intType));
+            pop, dexItemFactory.intType, frameBuilder::isAssignableAndInitialized);
+        frameBuilder.storeLocal(var, FrameType.initialized(dexItemFactory.intType));
         return;
       case FLOAT:
         frameBuilder.checkIsAssignable(
-            pop, factory.floatType, frameBuilder::isAssignableAndInitialized);
-        frameBuilder.storeLocal(var, FrameType.initialized(factory.floatType));
+            pop, dexItemFactory.floatType, frameBuilder::isAssignableAndInitialized);
+        frameBuilder.storeLocal(var, FrameType.initialized(dexItemFactory.floatType));
         return;
       case LONG:
         frameBuilder.checkIsAssignable(
-            pop, factory.longType, frameBuilder::isAssignableAndInitialized);
-        frameBuilder.storeLocal(var, FrameType.initialized(factory.longType));
-        frameBuilder.storeLocal(var + 1, FrameType.initialized(factory.longType));
+            pop, dexItemFactory.longType, frameBuilder::isAssignableAndInitialized);
+        frameBuilder.storeLocal(var, FrameType.initialized(dexItemFactory.longType));
+        frameBuilder.storeLocal(var + 1, FrameType.initialized(dexItemFactory.longType));
         return;
       case DOUBLE:
         frameBuilder.checkIsAssignable(
-            pop, factory.doubleType, frameBuilder::isAssignableAndInitialized);
-        frameBuilder.storeLocal(var, FrameType.initialized(factory.doubleType));
-        frameBuilder.storeLocal(var + 1, FrameType.initialized(factory.doubleType));
+            pop, dexItemFactory.doubleType, frameBuilder::isAssignableAndInitialized);
+        frameBuilder.storeLocal(var, FrameType.initialized(dexItemFactory.doubleType));
+        frameBuilder.storeLocal(var + 1, FrameType.initialized(dexItemFactory.doubleType));
         return;
       default:
         throw new Unreachable("Unexpected type " + type);
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 aef40c6..6321c35 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
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -144,13 +144,12 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., index/key →
     // ...
-    frameBuilder.popInitialized(factory.intType);
+    frameBuilder.popInitialized(dexItemFactory.intType);
     frameBuilder.checkTarget(defaultTarget);
     for (CfLabel target : targets) {
       frameBuilder.checkTarget(target);
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 b562faf..fc36a53 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
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCompareHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -90,13 +90,12 @@
   @Override
   public void evaluate(
       CfFrameVerificationHelper frameBuilder,
-      DexType context,
-      DexType returnType,
-      DexItemFactory factory,
-      InitClassLens initClassLens) {
+      DexMethod context,
+      AppView<?> appView,
+      DexItemFactory dexItemFactory) {
     // ..., objectref →
     // objectref
-    frameBuilder.popInitialized(factory.throwableType);
+    frameBuilder.popInitialized(dexItemFactory.throwableType);
     // The exception edges are verified in CfCode since this is a throwing instruction.
     frameBuilder.setNoFrame();
   }
diff --git a/src/main/java/com/android/tools/r8/debuginfo/DebugRepresentation.java b/src/main/java/com/android/tools/r8/debuginfo/DebugRepresentation.java
new file mode 100644
index 0000000..7a33b3c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/debuginfo/DebugRepresentation.java
@@ -0,0 +1,291 @@
+// Copyright (c) 2022, 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.debuginfo;
+
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.dex.VirtualFile;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexDebugInfo.PcBasedDebugInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+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.naming.NamingLens;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.LebUtils;
+import com.android.tools.r8.utils.LineNumberOptimizer;
+import com.android.tools.r8.utils.StringUtils;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import it.unimi.dsi.fastutil.ints.IntIterators;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class DebugRepresentation {
+
+  public interface DebugRepresentationPredicate {
+
+    boolean useDexPcEncoding(DexProgramClass holder, DexEncodedMethod method);
+  }
+
+  public static DebugRepresentationPredicate none(InternalOptions options) {
+    assert !options.canUseDexPc2PcAsDebugInformation();
+    return (holder, method) -> false;
+  }
+
+  public static DebugRepresentationPredicate fromFiles(
+      List<VirtualFile> files, InternalOptions options) {
+    if (!options.canUseDexPc2PcAsDebugInformation()) {
+      return none(options);
+    }
+    if (options.canUseNativeDexPcInsteadOfDebugInfo() || options.testing.forcePcBasedEncoding) {
+      return (holder, method) -> true;
+    }
+    // TODO(b/220999985): Avoid the need to maintain a class-to-file map.
+    Map<DexProgramClass, VirtualFile> classMapping = new IdentityHashMap<>();
+    for (VirtualFile file : files) {
+      file.classes().forEach(c -> classMapping.put(c, file));
+    }
+    return (holder, method) -> {
+      if (!isPcCandidate(method)) {
+        return false;
+      }
+      VirtualFile file = classMapping.get(holder);
+      DebugRepresentation cutoffs = file.getDebugRepresentation();
+      return cutoffs.usesPcEncoding(method);
+    };
+  }
+
+  private final Int2ReferenceMap<CostSummary> paramToInfo;
+
+  private DebugRepresentation(Int2ReferenceMap<CostSummary> paramToInfo) {
+    this.paramToInfo = paramToInfo;
+  }
+
+  public static void computeForFile(
+      VirtualFile file, GraphLens graphLens, NamingLens namingLens, InternalOptions options) {
+    if (!options.canUseDexPc2PcAsDebugInformation()
+        || options.canUseNativeDexPcInsteadOfDebugInfo()
+        || options.testing.forcePcBasedEncoding) {
+      return;
+    }
+    // First collect all of the per-pc costs
+    // (the sum of the normal debug info for all methods sharing the same max pc and param count.)
+    Int2ReferenceMap<CostSummary> paramCountToCosts = new Int2ReferenceOpenHashMap<>();
+    for (DexProgramClass clazz : file.classes()) {
+      IdentityHashMap<DexString, List<DexEncodedMethod>> overloads =
+          LineNumberOptimizer.groupMethodsByRenamedName(graphLens, namingLens, clazz);
+      for (List<DexEncodedMethod> methods : overloads.values()) {
+        if (methods.size() != 1) {
+          // Never use PC info for overloaded methods. They need distinct lines to disambiguate.
+          continue;
+        }
+        DexEncodedMethod method = methods.get(0);
+        if (!isPcCandidate(method)) {
+          continue;
+        }
+        DexCode code = method.getCode().asDexCode();
+        DexDebugInfo debugInfo = code.getDebugInfo();
+        Instruction lastInstruction = getLastExecutableInstruction(code);
+        if (lastInstruction == null) {
+          continue;
+        }
+        int lastPc = lastInstruction.getOffset();
+        int debugInfoCost = estimatedDebugInfoSize(debugInfo);
+        paramCountToCosts
+            .computeIfAbsent(debugInfo.getParameterCount(), DebugRepresentation.CostSummary::new)
+            .addCost(lastPc, debugInfoCost);
+      }
+    }
+    // Second compute the cost of converting to a pc encoding.
+    paramCountToCosts.forEach((ignored, summary) -> summary.computeConversionCosts());
+    // The result is stored on the virtual files for thread safety.
+    // TODO(b/220999985): Consider just passing this to the line number optimizer once fixed.
+    file.setDebugRepresentation(new DebugRepresentation(paramCountToCosts));
+  }
+
+  private boolean usesPcEncoding(DexEncodedMethod method) {
+    DexCode code = method.getCode().asDexCode();
+    DexDebugInfo debugInfo = code.getDebugInfo();
+    int paramCount = debugInfo.getParameterCount();
+    CostSummary conversionInfo = paramToInfo.get(paramCount);
+    if (conversionInfo.cutoff < 0) {
+      return false;
+    }
+    Instruction lastInstruction = getLastExecutableInstruction(code);
+    if (lastInstruction == null) {
+      return false;
+    }
+    int maxPc = lastInstruction.getOffset();
+    return maxPc <= conversionInfo.cutoff;
+  }
+
+  @Override
+  public String toString() {
+    List<CostSummary> sorted = new ArrayList<>(paramToInfo.values());
+    sorted.sort(Comparator.comparing(i -> i.paramCount));
+    return StringUtils.join("\n", sorted, CostSummary::toString);
+  }
+
+  private static boolean isPcCandidate(DexEncodedMethod method) {
+    if (!method.hasCode() || !method.getCode().isDexCode()) {
+      return false;
+    }
+    DexCode code = method.getCode().asDexCode();
+    return LineNumberOptimizer.doesContainPositions(code);
+  }
+
+  /** The cost of representing normal debug info for all methods with this max pc value. */
+  private static class PcNormalCost {
+
+    final int pc;
+    int cost;
+    int methods;
+
+    public PcNormalCost(int pc) {
+      assert pc >= 0;
+      this.pc = pc;
+    }
+
+    void add(int cost) {
+      assert cost >= 0;
+      methods++;
+      this.cost += cost;
+    }
+  }
+
+  /** The summary of normal costs for all debug info with a particular parameter size. */
+  private static class CostSummary {
+
+    private final int paramCount;
+
+    // Values for the normal encoding costs per-pc.
+    private final Int2ReferenceMap<PcNormalCost> pcToCost = new Int2ReferenceOpenHashMap<>();
+    private int minPc = Integer.MAX_VALUE;
+    private int maxPc = Integer.MIN_VALUE;
+
+    // Values for the conversion costs. These are computed only after all per-pc costs are known.
+    private int cutoff;
+    private int normalPreCutoffCost;
+    private int normalPostCutoffCost;
+
+    private CostSummary(int paramCount) {
+      assert paramCount >= 0;
+      this.paramCount = paramCount;
+    }
+
+    private void addCost(int pc, int cost) {
+      assert pc >= 0;
+      pcToCost.computeIfAbsent(pc, PcNormalCost::new).add(cost);
+      minPc = Math.min(minPc, pc);
+      maxPc = Math.max(maxPc, pc);
+    }
+
+    private void computeConversionCosts() {
+      assert !pcToCost.isEmpty();
+      // Point at which it is estimated that conversion to PC-encoding is viable.
+      int currentConvertedPc = -1;
+      // The normal cost of the part that is viable for conversion (this is just for debugging).
+      int normalConvertedCost = 0;
+      // The normal cost of the part that is not yet part of the converted range.
+      int normalOutstandingCost = 0;
+      // Iterate in ascending order as the point conversion cost is the sum of the preceding costs.
+      int[] sortedPcs = new int[pcToCost.size()];
+      IntIterators.unwrap(pcToCost.keySet().iterator(), sortedPcs);
+      Arrays.sort(sortedPcs);
+      for (int currentPc : sortedPcs) {
+        PcNormalCost pcSummary = pcToCost.get(currentPc);
+        // The cost of the debug info unconverted is the sum of the unconverted up to this point.
+        normalOutstandingCost += pcSummary.cost;
+        // The cost of the conversion is the delta between the already converted and the current.
+        // This does not account for the header overhead on converting the first point. However,
+        // the few bytes overhead per param-count should not affect much.
+        int costToConvert = currentPc - currentConvertedPc;
+        // If the estimated cost is larger we convert. The order here could be either way as
+        // both the normal cost and converted cost are estimates. Canonicalization could reduce
+        // the former and compaction could reduce the latter.
+        if (normalOutstandingCost > costToConvert) {
+          normalConvertedCost += normalOutstandingCost;
+          normalOutstandingCost = 0;
+          currentConvertedPc = currentPc;
+        }
+      }
+      cutoff = currentConvertedPc;
+      normalPreCutoffCost = normalConvertedCost;
+      normalPostCutoffCost = normalOutstandingCost;
+      assert cutoff >= -1;
+      assert normalPreCutoffCost >= 0;
+      assert normalPostCutoffCost >= 0;
+      assert preCutoffPcCost() >= 0;
+      assert postCutoffPcCost() >= 0;
+    }
+
+    private int preCutoffPcCost() {
+      return cutoff > 0 ? PcBasedDebugInfo.estimatedWriteSize(paramCount, cutoff) : 0;
+    }
+
+    private int postCutoffPcCost() {
+      return cutoff < maxPc ? PcBasedDebugInfo.estimatedWriteSize(paramCount, maxPc - cutoff) : 0;
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder builder = new StringBuilder();
+      builder
+          .append("p:")
+          .append(paramCount)
+          .append(", c:")
+          .append(cutoff)
+          .append(", m:")
+          .append(maxPc);
+      if (cutoff > 0) {
+        builder
+            .append(", preCutNormal:")
+            .append(normalPreCutoffCost)
+            .append(", preCutPC:")
+            .append(preCutoffPcCost());
+      }
+      if (cutoff < maxPc) {
+        builder
+            .append(", postCutNormal:")
+            .append(normalPostCutoffCost)
+            .append(", postCutPC:")
+            .append(postCutoffPcCost());
+      }
+      return builder.toString();
+    }
+  }
+
+  private static Instruction getLastExecutableInstruction(DexCode code) {
+    Instruction lastInstruction = null;
+    for (Instruction instruction : code.instructions) {
+      if (!instruction.isPayload()) {
+        lastInstruction = instruction;
+      }
+    }
+    return lastInstruction;
+  }
+
+  private static int estimatedDebugInfoSize(DexDebugInfo info) {
+    if (info.isPcBasedInfo()) {
+      return info.asPcBasedInfo().estimatedWriteSize();
+    }
+    // Each event is a single byte so we take the event length as the cost of the info.
+    // Note that the line number optimizer could reduce the line diffs such that deltas are
+    // smaller, but this is likely a very good estimate of the actual cost.
+    int parameterCount = info.getParameterCount();
+    int eventCount = info.asEventBasedInfo().events.length;
+    // Size: startline(0) + paramCount + null-array[paramCount] + eventCount + 1(end-event)
+    return LebUtils.sizeAsUleb128(0)
+        + LebUtils.sizeAsUleb128(parameterCount)
+        + LebUtils.sizeAsUleb128(0) * parameterCount
+        + eventCount
+        + 1;
+  }
+}
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 1eb78d1..0705b78 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -17,6 +17,8 @@
 import com.android.tools.r8.ProgramConsumer;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.SourceFileEnvironment;
+import com.android.tools.r8.debuginfo.DebugRepresentation;
+import com.android.tools.r8.debuginfo.DebugRepresentation.DebugRepresentationPredicate;
 import com.android.tools.r8.dex.FileWriter.ByteBufferResult;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.features.FeatureSplitConfiguration.DataResourceProvidersAndConsumer;
@@ -295,6 +297,7 @@
                   Timing fileTiming = Timing.create("VirtualFile " + virtualFile.getId(), options);
                   computeOffsetMappingAndRewriteJumboStrings(
                       virtualFile, lazyDexStrings, fileTiming);
+                  DebugRepresentation.computeForFile(virtualFile, graphLens, namingLens, options);
                   fileTiming.end();
                   return fileTiming;
                 },
@@ -303,10 +306,15 @@
         merger.end();
       }
 
+
       // Now code offsets are fixed, compute the mapping file content.
       if (willComputeProguardMap()) {
+        // TODO(b/220999985): Refactor line number optimization to be per file and thread it above.
+        DebugRepresentationPredicate representation =
+            DebugRepresentation.fromFiles(virtualFiles, options);
         delayedProguardMapId.set(
-            runAndWriteMap(inputApp, appView, namingLens, timing, originalSourceFiles));
+            runAndWriteMap(
+                inputApp, appView, namingLens, timing, originalSourceFiles, representation));
       }
 
       // With the mapping id/hash known, it is safe to compute the remaining dex strings.
diff --git a/src/main/java/com/android/tools/r8/dex/Constants.java b/src/main/java/com/android/tools/r8/dex/Constants.java
index 8ea7319..7cf3ca9 100644
--- a/src/main/java/com/android/tools/r8/dex/Constants.java
+++ b/src/main/java/com/android/tools/r8/dex/Constants.java
@@ -142,6 +142,7 @@
   public static final String INSTANCE_INITIALIZER_NAME = "<init>";
   public static final String CLASS_INITIALIZER_NAME = "<clinit>";
   public static final String TEMPORARY_INSTANCE_INITIALIZER_PREFIX = "$r8$constructor";
+  public static final String SYNTHETIC_INSTANCE_INITIALIZER_PREFIX = "$r8$init$synthetic";
 
   public static final int MAX_NON_JUMBO_INDEX = U16BIT_MAX;
 
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 e7c7a1d..a085223 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 
 import com.android.tools.r8.FeatureSplit;
+import com.android.tools.r8.debuginfo.DebugRepresentation;
 import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
@@ -59,16 +60,6 @@
 
 public class VirtualFile {
 
-  // The fill strategy determine how to distribute classes into dex files.
-  enum FillStrategy {
-    // Distribute classes in as few dex files as possible filling each dex file as much as possible.
-    FILL_MAX,
-    // Distribute classes keeping some space for future growth. This is mainly useful together with
-    // the package map distribution.
-    LEAVE_SPACE_FOR_GROWTH,
-    // TODO(sgjesse): Does "minimal main dex" combined with "leave space for growth" make sense?
-  }
-
   public static final int MAX_ENTRIES = Constants.U16BIT_MAX + 1;
 
   /**
@@ -83,6 +74,7 @@
   private final FeatureSplit featureSplit;
 
   private final DexProgramClass primaryClass;
+  private DebugRepresentation debugRepresentation;
 
   VirtualFile(
       int id,
@@ -150,6 +142,17 @@
     return primaryClass == null ? null : primaryClass.type.descriptor.toString();
   }
 
+  public void setDebugRepresentation(DebugRepresentation debugRepresentation) {
+    assert debugRepresentation != null;
+    assert this.debugRepresentation == null;
+    this.debugRepresentation = debugRepresentation;
+  }
+
+  public DebugRepresentation getDebugRepresentation() {
+    assert debugRepresentation != null;
+    return debugRepresentation;
+  }
+
   public static String deriveCommonPrefixAndSanityCheck(List<String> fileNames) {
     Iterator<String> nameIterator = fileNames.iterator();
     String first = nameIterator.next();
@@ -278,8 +281,8 @@
             hasMainDexList, transaction.getNumberOfMethods(), transaction.getNumberOfFields()));
   }
 
-  private boolean isFilledEnough(FillStrategy fillStrategy) {
-    return isFull(fillStrategy == FillStrategy.FILL_MAX ? MAX_ENTRIES : MAX_PREFILL_ENTRIES);
+  private boolean isFilledEnough() {
+    return isFull(MAX_ENTRIES);
   }
 
   public void abortTransaction() {
@@ -492,12 +495,6 @@
 
     protected void addFeatureSplitFiles(Map<FeatureSplit, Set<DexProgramClass>> featureSplitClasses)
         throws IOException {
-      addFeatureSplitFiles(featureSplitClasses, FillStrategy.FILL_MAX);
-    }
-
-    protected void addFeatureSplitFiles(
-        Map<FeatureSplit, Set<DexProgramClass>> featureSplitClasses, FillStrategy fillStrategy)
-        throws IOException {
       if (featureSplitClasses.isEmpty()) {
         return;
       }
@@ -524,7 +521,6 @@
                 appView,
                 featureClasses,
                 originalNames,
-                fillStrategy,
                 0,
                 writer.graphLens,
                 writer.initClassLens,
@@ -536,13 +532,11 @@
   }
 
   public static class FillFilesDistributor extends DistributorBase {
-    private final FillStrategy fillStrategy;
     private final ExecutorService executorService;
 
     FillFilesDistributor(ApplicationWriter writer, InternalOptions options,
         ExecutorService executorService) {
       super(writer, options);
-      this.fillStrategy = FillStrategy.FILL_MAX;
       this.executorService = executorService;
     }
 
@@ -594,7 +588,6 @@
                 appView,
                 classes,
                 originalNames,
-                fillStrategy,
                 fileIndexOffset,
                 writer.graphLens,
                 writer.initClassLens,
@@ -602,7 +595,7 @@
                 options)
             .call();
       }
-      addFeatureSplitFiles(featureSplitClasses, fillStrategy);
+      addFeatureSplitFiles(featureSplitClasses);
 
       assert totalClassNumber == virtualFiles.stream().mapToInt(dex -> dex.classes().size()).sum();
       return virtualFiles;
@@ -1028,7 +1021,6 @@
     private final List<DexProgramClass> classes;
     private final Map<DexProgramClass, String> originalNames;
     private final DexItemFactory dexItemFactory;
-    private final FillStrategy fillStrategy;
     private final InternalOptions options;
     private final VirtualFileCycler cycler;
 
@@ -1037,7 +1029,6 @@
         AppView<?> appView,
         Set<DexProgramClass> classes,
         Map<DexProgramClass, String> originalNames,
-        FillStrategy fillStrategy,
         int fileIndexOffset,
         GraphLens graphLens,
         InitClassLens initClassLens,
@@ -1046,7 +1037,6 @@
       this.classes = new ArrayList<>(classes);
       this.originalNames = originalNames;
       this.dexItemFactory = appView.dexItemFactory();
-      this.fillStrategy = fillStrategy;
       this.options = options;
       this.cycler =
           new VirtualFileCycler(
@@ -1170,7 +1160,7 @@
           && current.getNumberOfClasses() > options.testing.limitNumberOfClassesPerDex) {
         return true;
       }
-      return current.isFilledEnough(fillStrategy) || current.isFull();
+      return current.isFull();
     }
 
     private void addNonPackageClasses(
@@ -1179,7 +1169,7 @@
       VirtualFile current;
       current = cycler.next();
       for (DexProgramClass clazz : nonPackageClasses) {
-        if (current.isFilledEnough(fillStrategy)) {
+        if (current.isFull()) {
           current = getVirtualFile(cycler);
         }
         current.addClass(clazz);
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 cbf79f2..6f1ab90 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -433,7 +433,7 @@
     return codeLens;
   }
 
-  private void setCodeLens(GraphLens codeLens) {
+  public void setCodeLens(GraphLens codeLens) {
     this.codeLens = codeLens;
   }
 
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 9ab5faa..4358a27 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -25,8 +25,6 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata;
-import com.android.tools.r8.graph.proto.ArgumentInfo;
-import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.MemberType;
@@ -34,6 +32,7 @@
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Position.SyntheticPosition;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
+import com.android.tools.r8.ir.conversion.ExtraParameter;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -404,8 +403,7 @@
       LensCodeRewriterUtils rewriter,
       MethodVisitor visitor) {
     GraphLens graphLens = appView.graphLens();
-    assert verifyFrames(method.getDefinition(), appView, null).isValid()
-        : "Could not validate stack map frames";
+    assert verifyFrames(method, appView).isValid() : "Could not validate stack map frames";
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     InitClassLens initClassLens = appView.initClassLens();
     InternalOptions options = appView.options();
@@ -506,8 +504,7 @@
 
   @Override
   public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) {
-    verifyFramesOrRemove(
-        method.getDefinition(), appView, origin, IRBuilder.lookupPrototypeChanges(appView, method));
+    verifyFramesOrRemove(method, appView, getCodeLens(appView));
     return internalBuildPossiblyWithLocals(
         method, method, appView, appView.codeLens(), null, null, origin, null);
   }
@@ -525,7 +522,7 @@
     assert valueNumberGenerator != null;
     assert callerPosition != null;
     assert protoChanges != null;
-    verifyFramesOrRemove(method.getDefinition(), appView, origin, protoChanges);
+    verifyFramesOrRemove(method, appView, codeLens);
     return internalBuildPossiblyWithLocals(
         context,
         method,
@@ -537,12 +534,8 @@
         protoChanges);
   }
 
-  private void verifyFramesOrRemove(
-      DexEncodedMethod method,
-      AppView<?> appView,
-      Origin origin,
-      RewrittenPrototypeDescription protoChanges) {
-    stackMapStatus = verifyFrames(method, appView, origin, protoChanges);
+  private void verifyFramesOrRemove(ProgramMethod method, AppView<?> appView, GraphLens codeLens) {
+    stackMapStatus = verifyFrames(method, appView, codeLens);
     if (!stackMapStatus.isValid()) {
       ArrayList<CfInstruction> copy = new ArrayList<>(instructions);
       copy.removeIf(CfInstruction::isFrame);
@@ -874,30 +867,31 @@
         originalHolder, maxStack, maxLocals, newInstructions, tryCatchRanges, localVariables);
   }
 
-  public StackMapStatus verifyFrames(DexEncodedMethod method, AppView<?> appView, Origin origin) {
-    return verifyFrames(method, appView, origin, RewrittenPrototypeDescription.none());
+  public StackMapStatus verifyFrames(ProgramMethod method, AppView<?> appView) {
+    return verifyFrames(method, appView, getCodeLens(appView));
   }
 
-  public StackMapStatus verifyFrames(
-      DexEncodedMethod method,
-      AppView<?> appView,
-      Origin origin,
-      RewrittenPrototypeDescription protoChanges) {
+  public StackMapStatus verifyFrames(ProgramMethod method, AppView<?> appView, GraphLens codeLens) {
+    GraphLens graphLens = appView.graphLens();
+    DexEncodedMethod definition = method.getDefinition();
     if (!appView.options().canUseInputStackMaps()
         || appView.options().testing.disableStackMapVerification) {
       return StackMapStatus.NOT_PRESENT;
     }
-    if (method.hasClassFileVersion() && method.getClassFileVersion().isLessThan(CfVersion.V1_7)) {
+    if (definition.hasClassFileVersion()
+        && definition.getClassFileVersion().isLessThan(CfVersion.V1_7)) {
       return StackMapStatus.NOT_PRESENT;
     }
-    if (!method.isInstanceInitializer()
-        && appView
-            .graphLens()
-            .getOriginalMethodSignature(method.getReference())
-            .isInstanceInitializer(appView.dexItemFactory())) {
-      // We cannot verify instance initializers if they are moved.
-      return StackMapStatus.NOT_PRESENT;
-    }
+
+    RewrittenPrototypeDescription protoChanges =
+        graphLens.lookupPrototypeChangesForMethodDefinition(method.getReference(), codeLens);
+
+    DexMethod previousMethodSignature =
+        graphLens.getOriginalMethodSignature(method.getReference(), codeLens);
+    boolean previousMethodSignatureIsInstance =
+        method.getDefinition().isInstance()
+            || protoChanges.getArgumentInfoCollection().isConvertedToStaticMethod();
+
     // Build a map from labels to frames.
     Map<CfLabel, CfFrame> stateMap = new IdentityHashMap<>();
     List<CfLabel> labels = new ArrayList<>();
@@ -909,10 +903,7 @@
           for (CfLabel label : labels) {
             if (stateMap.containsKey(label)) {
               return reportStackMapError(
-                  CfCodeStackMapValidatingException.multipleFramesForLabel(
-                      origin,
-                      appView.graphLens().getOriginalMethodSignature(method.getReference()),
-                      appView),
+                  CfCodeStackMapValidatingException.multipleFramesForLabel(method, appView),
                   appView);
             }
             stateMap.put(label, frame);
@@ -920,11 +911,7 @@
         } else if (instruction != instructions.get(0)) {
           // From b/168212806, it is possible that the first instruction is a frame.
           return reportStackMapError(
-              CfCodeStackMapValidatingException.unexpectedStackMapFrame(
-                  origin,
-                  appView.graphLens().getOriginalMethodSignature(method.getReference()),
-                  appView),
-              appView);
+              CfCodeStackMapValidatingException.unexpectedStackMapFrame(method, appView), appView);
         }
       }
       // We are trying to map a frame to a label, but we can have positions in between, so skip
@@ -943,32 +930,28 @@
     // If there are no frames but we have seen a jump instruction, we cannot verify the stack map.
     if (requireStackMapFrame && stateMap.isEmpty()) {
       return reportStackMapError(
-          CfCodeStackMapValidatingException.noFramesForMethodWithJumps(
-              origin,
-              appView.graphLens().getOriginalMethodSignature(method.getReference()),
-              appView),
-          appView);
-    }
-    DexType context = appView.graphLens().lookupType(method.getHolderType());
-    DexType returnType = appView.graphLens().lookupType(method.getReference().getReturnType());
-    if (!protoChanges.isEmpty() && protoChanges.getRewrittenReturnInfo() != null) {
-      returnType = protoChanges.getRewrittenReturnInfo().getOldType();
+          CfCodeStackMapValidatingException.noFramesForMethodWithJumps(method, appView), appView);
     }
     CfFrameVerificationHelper builder =
         new CfFrameVerificationHelper(
-            context,
+            previousMethodSignature.getHolderType(),
             stateMap,
             tryCatchRanges,
             isAssignablePredicate(appView),
             appView.dexItemFactory(),
-            appView.graphLens(),
             maxStack);
     if (stateMap.containsKey(null)) {
       assert !shouldComputeInitialFrame();
       builder.checkFrameAndSet(stateMap.get(null));
     } else if (shouldComputeInitialFrame()) {
       builder.checkFrameAndSet(
-          new CfFrame(computeInitialLocals(context, method, protoChanges), new ArrayDeque<>()));
+          new CfFrame(
+              computeInitialLocals(
+                  appView,
+                  previousMethodSignature,
+                  previousMethodSignatureIsInstance,
+                  protoChanges),
+              new ArrayDeque<>()));
     }
     for (int i = 0; i < instructions.size(); i++) {
       CfInstruction instruction = instructions.get(i);
@@ -980,17 +963,11 @@
           assert !instruction.isStore();
           builder.checkExceptionEdges();
         }
-        instruction.evaluate(
-            builder, context, returnType, appView.dexItemFactory(), appView.initClassLens());
+        instruction.evaluate(builder, previousMethodSignature, appView, appView.dexItemFactory());
       } catch (CfCodeStackMapValidatingException ex) {
         return reportStackMapError(
             CfCodeStackMapValidatingException.toDiagnostics(
-                origin,
-                appView.graphLens().getOriginalMethodSignature(method.getReference()),
-                i,
-                instruction,
-                ex.getMessage(),
-                appView),
+                method, i, instruction, ex.getMessage(), appView),
             appView);
       }
     }
@@ -1038,40 +1015,33 @@
   }
 
   private Int2ReferenceSortedMap<FrameType> computeInitialLocals(
-      DexType context, DexEncodedMethod method, RewrittenPrototypeDescription protoTypeChanges) {
+      AppView<?> appView,
+      DexMethod method,
+      boolean isInstance,
+      RewrittenPrototypeDescription prototypeChanges) {
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
     Int2ReferenceSortedMap<FrameType> initialLocals = new Int2ReferenceAVLTreeMap<>();
     int index = 0;
-    if (method.isInstanceInitializer()) {
-      initialLocals.put(index++, FrameType.uninitializedThis());
-    } else if (!method.getAccessFlags().isStatic()) {
-      initialLocals.put(index++, FrameType.initialized(context));
+    if (isInstance) {
+      initialLocals.put(
+          index++,
+          method.isInstanceInitializer(dexItemFactory)
+                  || method.mustBeInlinedIntoInstanceInitializer(appView)
+                  || method.isHorizontallyMergedInstanceInitializer(dexItemFactory)
+              ? FrameType.uninitializedThis()
+              : FrameType.initialized(method.getHolderType()));
     }
-    ArgumentInfoCollection argumentsInfo = protoTypeChanges.getArgumentInfoCollection();
-    DexType[] parameters = method.getReference().proto.parameters.values;
-    int originalNumberOfArguments =
-        parameters.length
-            + argumentsInfo.numberOfRemovedArguments()
-            + initialLocals.size()
-            - protoTypeChanges.numberOfExtraParameters();
-    int argumentIndex = index;
-    int usedArgumentIndex = 0;
-    while (argumentIndex < originalNumberOfArguments) {
-      ArgumentInfo argumentInfo = argumentsInfo.getArgumentInfo(argumentIndex++);
-      DexType localType;
-      if (argumentInfo.isRemovedArgumentInfo()) {
-        localType = argumentInfo.asRemovedArgumentInfo().getType();
-      } else {
-        if (argumentInfo.isRewrittenTypeInfo()) {
-          assert parameters[usedArgumentIndex] == argumentInfo.asRewrittenTypeInfo().getNewType();
-          localType = argumentInfo.asRewrittenTypeInfo().getOldType();
-        } else {
-          localType = parameters[usedArgumentIndex];
-        }
-        usedArgumentIndex++;
-      }
-      FrameType frameType = FrameType.initialized(localType);
+    for (DexType parameter : method.getParameters()) {
+      FrameType frameType = FrameType.initialized(parameter);
       initialLocals.put(index++, frameType);
-      if (localType.isWideType()) {
+      if (frameType.isWide()) {
+        initialLocals.put(index++, frameType);
+      }
+    }
+    for (ExtraParameter extraParameter : prototypeChanges.getExtraParameters()) {
+      FrameType frameType = FrameType.initialized(extraParameter.getType(dexItemFactory));
+      initialLocals.put(index++, frameType);
+      if (frameType.isWide()) {
         initialLocals.put(index++, frameType);
       }
     }
diff --git a/src/main/java/com/android/tools/r8/graph/CfCodeStackMapValidatingException.java b/src/main/java/com/android/tools/r8/graph/CfCodeStackMapValidatingException.java
index b6d1b1e..1a25ce6 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCodeStackMapValidatingException.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCodeStackMapValidatingException.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.origin.Origin;
 
 public class CfCodeStackMapValidatingException extends RuntimeException {
 
@@ -18,36 +17,43 @@
   }
 
   public static CfCodeDiagnostics unexpectedStackMapFrame(
-      Origin origin, DexMethod method, AppView<?> appView) {
+      ProgramMethod method, AppView<?> appView) {
     StringBuilder sb = new StringBuilder("Unexpected stack map frame without target");
     if (appView.enableWholeProgramOptimizations()) {
       sb.append(" In later version of R8, the method may be assumed not reachable.");
     }
-    return new CfCodeDiagnostics(origin, method, sb.toString());
+    return new CfCodeDiagnostics(
+        method.getOrigin(),
+        appView.graphLens().getOriginalMethodSignature(method.getReference()),
+        sb.toString());
   }
 
-  public static CfCodeDiagnostics multipleFramesForLabel(
-      Origin origin, DexMethod method, AppView<?> appView) {
+  public static CfCodeDiagnostics multipleFramesForLabel(ProgramMethod method, AppView<?> appView) {
     StringBuilder sb = new StringBuilder("Multiple frames for label");
     if (appView.enableWholeProgramOptimizations()) {
       sb.append(" In later version of R8, the method may be assumed not reachable.");
     }
-    return new CfCodeDiagnostics(origin, method, sb.toString());
+    return new CfCodeDiagnostics(
+        method.getOrigin(),
+        appView.graphLens().getOriginalMethodSignature(method.getReference()),
+        sb.toString());
   }
 
   public static CfCodeDiagnostics noFramesForMethodWithJumps(
-      Origin origin, DexMethod method, AppView<?> appView) {
+      ProgramMethod method, AppView<?> appView) {
     StringBuilder sb =
         new StringBuilder("Expected stack map table for method with non-linear control flow.");
     if (appView.enableWholeProgramOptimizations()) {
       sb.append(" In later version of R8, the method may be assumed not reachable.");
     }
-    return new CfCodeDiagnostics(origin, method, sb.toString());
+    return new CfCodeDiagnostics(
+        method.getOrigin(),
+        appView.graphLens().getOriginalMethodSignature(method.getReference()),
+        sb.toString());
   }
 
   public static CfCodeDiagnostics toDiagnostics(
-      Origin origin,
-      DexMethod method,
+      ProgramMethod method,
       int instructionIndex,
       CfInstruction instruction,
       String detailMessage,
@@ -63,6 +69,9 @@
     if (appView.enableWholeProgramOptimizations()) {
       sb.append(" In later version of R8, the method may be assumed not reachable.");
     }
-    return new CfCodeDiagnostics(origin, method, sb.toString());
+    return new CfCodeDiagnostics(
+        method.getOrigin(),
+        appView.graphLens().getOriginalMethodSignature(method.getReference()),
+        sb.toString());
   }
 }
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 a260816..0f4233d 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -88,7 +88,11 @@
     return false;
   }
 
-  public boolean isHorizontalClassMergingCode() {
+  public boolean isHorizontalClassMergerCode() {
+    return false;
+  }
+
+  public boolean isIncompleteHorizontalClassMergerCode() {
     return false;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index feedd6c..5dd4a97 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -20,6 +20,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 public abstract class DexApplication implements DexDefinitionSupplier {
@@ -116,6 +117,10 @@
 
   abstract Collection<DexProgramClass> programClasses();
 
+  public abstract void forEachProgramType(Consumer<DexType> consumer);
+
+  public abstract void forEachLibraryType(Consumer<DexType> consumer);
+
   public Collection<DexProgramClass> classes() {
     ReorderBox<DexProgramClass> box = new ReorderBox<>(programClasses());
     assert box.reorderClasses();
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 0beeb2f..09f740e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
@@ -148,8 +148,7 @@
       // No indexed items to collect.
     }
 
-    @Override
-    public int estimatedWriteSize() {
+    public static int estimatedWriteSize(int parameterCount, int maxPc) {
       return LebUtils.sizeAsUleb128(START_LINE)
           + LebUtils.sizeAsUleb128(parameterCount)
           + parameterCount * LebUtils.sizeAsUleb128(0)
@@ -159,6 +158,11 @@
     }
 
     @Override
+    public int estimatedWriteSize() {
+      return estimatedWriteSize(parameterCount, maxPc);
+    }
+
+    @Override
     public void write(
         DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
       writer.putUleb128(START_LINE);
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 3640575..08488c1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -320,6 +320,8 @@
       createString(Constants.CLASS_INITIALIZER_NAME);
   public final DexString temporaryConstructorMethodPrefix =
       createString(Constants.TEMPORARY_INSTANCE_INITIALIZER_PREFIX);
+  public final DexString syntheticConstructorMethodPrefix =
+      createString(Constants.SYNTHETIC_INSTANCE_INITIALIZER_PREFIX);
 
   public final DexString thisName = createString("this");
   public final DexString lambdaInstanceFieldName = createString(LAMBDA_INSTANCE_FIELD_NAME);
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 0cd5a14..466e1cf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -296,8 +296,21 @@
     return factory.isConstructor(this);
   }
 
-  public boolean mustBeInlinedIntoInstanceInitializer(DexItemFactory dexItemFactory) {
-    return getName().startsWith(dexItemFactory.temporaryConstructorMethodPrefix);
+  public boolean mustBeInlinedIntoInstanceInitializer(AppView<?> appView) {
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+    if (getName().startsWith(dexItemFactory.temporaryConstructorMethodPrefix)) {
+      DexClassAndMethod method = appView.definitionFor(this);
+      return method != null
+          && appView
+              .graphLens()
+              .getOriginalMethodSignature(this)
+              .isInstanceInitializer(dexItemFactory);
+    }
+    return false;
+  }
+
+  public boolean isHorizontallyMergedInstanceInitializer(DexItemFactory dexItemFactory) {
+    return getName().startsWith(dexItemFactory.syntheticConstructorMethodPrefix);
   }
 
   public DexMethod withExtraArgumentPrepended(DexType type, DexItemFactory dexItemFactory) {
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index dd95a2d..1f756ee 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -23,6 +23,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
 
 public class DirectMappedDexApplication extends DexApplication {
 
@@ -65,6 +66,16 @@
     return programClasses;
   }
 
+  @Override
+  public void forEachProgramType(Consumer<DexType> consumer) {
+    programClasses.forEach(clazz -> consumer.accept(clazz.type));
+  }
+
+  @Override
+  public void forEachLibraryType(Consumer<DexType> consumer) {
+    libraryClasses.forEach((type, clazz) -> consumer.accept(type));
+  }
+
   public Collection<DexLibraryClass> libraryClasses() {
     return libraryClasses.values();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
index b781f0b..49ef4af 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -54,6 +54,16 @@
   }
 
   @Override
+  public void forEachProgramType(Consumer<DexType> consumer) {
+    programClasses.getAllTypes().forEach(consumer);
+  }
+
+  @Override
+  public void forEachLibraryType(Consumer<DexType> consumer) {
+    libraryClasses.getAllClassProviderTypes().forEach(consumer);
+  }
+
+  @Override
   public ClassResolutionResult contextIndependentDefinitionForWithResolutionResult(DexType type) {
     ClassResolutionResult.Builder builder = ClassResolutionResult.builder();
     if (libraryClasses != null) {
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index 37acbad..3cb1a4c 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -42,7 +42,7 @@
     GraphLens codeLens = appView.graphLens();
     RewrittenPrototypeDescription protoChanges = RewrittenPrototypeDescription.none();
     if (methodProcessor.shouldApplyCodeRewritings(this)) {
-      codeLens = appView.codeLens();
+      codeLens = getDefinition().getCode().getCodeLens(appView);
       protoChanges = appView.graphLens().lookupPrototypeChangesForMethodDefinition(getReference());
     }
     return code.buildInliningIR(
diff --git a/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java b/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java
index cbfa83d..889f21a 100644
--- a/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java
@@ -34,26 +34,31 @@
   private final Int2ObjectSortedMap<ArgumentInfo> argumentInfos;
   private final int argumentInfosSize;
   private final ArgumentPermutation argumentPermutation;
+  private final boolean isConvertedToStaticMethod;
 
   // Specific constructor for empty.
   private ArgumentInfoCollection() {
     this.argumentInfos = EMPTY_MAP;
     this.argumentInfosSize = -1;
     this.argumentPermutation = ArgumentPermutation.getDefault();
+    this.isConvertedToStaticMethod = false;
   }
 
   private ArgumentInfoCollection(
       Int2ObjectSortedMap<ArgumentInfo> argumentInfos,
       int argumentInfosSize,
-      ArgumentPermutation argumentPermutation) {
+      ArgumentPermutation argumentPermutation,
+      boolean isConvertedToStaticMethod) {
     assert argumentInfos != null;
     assert argumentPermutation != null;
     assert !argumentInfos.isEmpty() || argumentInfos == EMPTY_MAP;
-    assert !argumentInfos.isEmpty() || !argumentPermutation.isDefault() : "should use empty.";
+    assert !argumentInfos.isEmpty() || !argumentPermutation.isDefault() || isConvertedToStaticMethod
+        : "should use empty.";
     assert argumentInfosSize >= 0;
     this.argumentInfos = argumentInfos;
     this.argumentInfosSize = argumentInfosSize;
     this.argumentPermutation = argumentPermutation;
+    this.isConvertedToStaticMethod = isConvertedToStaticMethod;
   }
 
   public static ArgumentInfoCollection empty() {
@@ -161,6 +166,10 @@
     return argumentPermutation.getNewArgumentIndex(intermediateArgumentIndex);
   }
 
+  public boolean isConvertedToStaticMethod() {
+    return isConvertedToStaticMethod;
+  }
+
   public int size() {
     assert !isEmpty();
     return argumentInfosSize;
@@ -187,7 +196,10 @@
               builder.addArgumentInfo(argumentIndex, argumentInfo);
             }
           });
-      return builder.setArgumentInfosSize(argumentInfosSize).build();
+      return builder
+          .setArgumentInfosSize(argumentInfosSize)
+          .setIsConvertedToStaticMethod(isConvertedToStaticMethod())
+          .build();
     }
     return this;
   }
@@ -203,12 +215,14 @@
     ArgumentInfoCollection other = (ArgumentInfoCollection) obj;
     return argumentInfos.equals(other.argumentInfos)
         && argumentPermutation.equals(other.argumentPermutation)
-        && argumentInfosSize == other.argumentInfosSize;
+        && argumentInfosSize == other.argumentInfosSize
+        && isConvertedToStaticMethod == other.isConvertedToStaticMethod;
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(argumentInfos, argumentPermutation, argumentInfosSize);
+    return Objects.hash(
+        argumentInfos, argumentPermutation, argumentInfosSize, isConvertedToStaticMethod);
   }
 
   public static Builder builder() {
@@ -220,6 +234,7 @@
     private Int2ObjectSortedMap<ArgumentInfo> argumentInfos = new Int2ObjectRBTreeMap<>();
     private int argumentInfosSize = -1;
     private ArgumentPermutation argumentPermutation = ArgumentPermutation.getDefault();
+    private boolean isConvertedToStaticMethod;
 
     public Builder addArgumentInfo(int argumentIndex, ArgumentInfo argInfo) {
       argumentInfos.put(argumentIndex, argInfo);
@@ -240,7 +255,9 @@
     }
 
     public boolean isEmpty() {
-      return argumentInfos.isEmpty() && argumentPermutation.isDefault();
+      return argumentInfos.isEmpty()
+          && argumentPermutation.isDefault()
+          && !isConvertedToStaticMethod;
     }
 
     public Builder setArgumentInfosSize(int argumentInfosSize) {
@@ -253,6 +270,15 @@
       return this;
     }
 
+    public Builder setIsConvertedToStaticMethod() {
+      return setIsConvertedToStaticMethod(true);
+    }
+
+    public Builder setIsConvertedToStaticMethod(boolean isConvertedToStaticMethod) {
+      this.isConvertedToStaticMethod = isConvertedToStaticMethod;
+      return this;
+    }
+
     public ArgumentInfoCollection build() {
       if (isEmpty()) {
         return empty();
@@ -260,7 +286,7 @@
       Int2ObjectSortedMap<ArgumentInfo> argumentInfosOrEmpty =
           argumentInfos.isEmpty() ? EMPTY_MAP : argumentInfos;
       return new ArgumentInfoCollection(
-          argumentInfosOrEmpty, argumentInfosSize, argumentPermutation);
+          argumentInfosOrEmpty, argumentInfosSize, argumentPermutation, isConvertedToStaticMethod);
     }
   }
 
@@ -315,9 +341,14 @@
         argumentPermutationBuilder.setNewArgumentIndex(argumentIndex, newArgumentIndex);
       }
     }
+    assert BooleanUtils.intValue(isConvertedToStaticMethod())
+            + BooleanUtils.intValue(other.isConvertedToStaticMethod())
+        <= 1;
     return builder
         .setArgumentInfosSize(argumentInfosSize)
         .setArgumentPermutation(argumentPermutationBuilder.build())
+        .setIsConvertedToStaticMethod(
+            isConvertedToStaticMethod() || other.isConvertedToStaticMethod())
         .build();
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/proto/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/proto/RewrittenPrototypeDescription.java
index 8459d85..06c5418 100644
--- a/src/main/java/com/android/tools/r8/graph/proto/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/proto/RewrittenPrototypeDescription.java
@@ -20,7 +20,6 @@
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
 import com.android.tools.r8.ir.conversion.ExtraParameter;
-import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoFixer;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.google.common.collect.ImmutableList;
@@ -252,18 +251,12 @@
         extraParameters, newRewrittenReturnInfo, argumentInfoCollection);
   }
 
-  public RewrittenPrototypeDescription withExtraUnusedNullParameters(
-      int numberOfExtraUnusedNullParameters) {
-    List<ExtraParameter> parameters =
-        Collections.nCopies(numberOfExtraUnusedNullParameters, new ExtraUnusedNullParameter());
-    return withExtraParameters(parameters);
-  }
-
   public RewrittenPrototypeDescription withExtraParameters(ExtraParameter... parameters) {
     return withExtraParameters(Arrays.asList(parameters));
   }
 
-  public RewrittenPrototypeDescription withExtraParameters(List<ExtraParameter> parameters) {
+  public RewrittenPrototypeDescription withExtraParameters(
+      List<? extends ExtraParameter> parameters) {
     if (parameters.isEmpty()) {
       return this;
     }
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 78a8743..303efef 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -143,7 +143,8 @@
     if (!definition.getCode().isCfCode()) {
       assert appView.options().isGeneratingDex();
       assert mode.isFinal();
-      syntheticInitializerConverterBuilder.add(new ProgramMethod(group.getTarget(), definition));
+      syntheticInitializerConverterBuilder.addClassInitializer(
+          new ProgramMethod(group.getTarget(), definition));
     }
   }
 
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 dd7de45..4b79710 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -121,10 +121,7 @@
 
     SyntheticInitializerConverter syntheticInitializerConverter =
         syntheticInitializerConverterBuilder.build();
-    if (!syntheticInitializerConverter.isEmpty()) {
-      assert mode.isFinal();
-      syntheticInitializerConverterBuilder.build().convert(executorService);
-    }
+    syntheticInitializerConverter.convertClassInitializers(executorService);
 
     // Generate the graph lens.
     HorizontallyMergedClasses mergedClasses =
@@ -144,15 +141,18 @@
     // sites, fields accesses, etc. are correctly transferred to the target classes.
     appView.rewriteWithLensAndApplication(
         horizontalClassMergerGraphLens, getNewApplication(mergedClasses));
+    codeProvider.setGraphLens(horizontalClassMergerGraphLens);
 
     // Record where the synthesized $r8$classId fields are read and written.
     if (mode.isInitial()) {
       createFieldAccessInfoCollectionModifier(groups).modify(appView.withLiveness());
-      transformIncompleteCode(groups, horizontalClassMergerGraphLens, executorService);
     } else {
       assert groups.stream().noneMatch(MergeGroup::hasClassIdField);
     }
 
+    transformIncompleteCode(groups, horizontalClassMergerGraphLens, executorService);
+    syntheticInitializerConverter.convertInstanceInitializers(executorService);
+
     appView.pruneItems(
         prunedItems.toBuilder().setPrunedApp(appView.app()).build(), executorService);
 
@@ -197,11 +197,11 @@
       if (group.hasClassIdField()) {
         DexProgramClass target = group.getTarget();
         target.forEachProgramInstanceInitializerMatching(
-            definition -> definition.getCode().isHorizontalClassMergingCode(),
+            definition -> definition.getCode().isHorizontalClassMergerCode(),
             method -> builder.recordFieldWrittenInContext(group.getClassIdField(), method));
         target.forEachProgramVirtualMethodMatching(
             definition ->
-                definition.hasCode() && definition.getCode().isHorizontalClassMergingCode(),
+                definition.hasCode() && definition.getCode().isHorizontalClassMergerCode(),
             method -> builder.recordFieldReadInContext(group.getClassIdField(), method));
       }
     }
@@ -216,20 +216,19 @@
     ThreadUtils.processItems(
         groups,
         group -> {
-          if (group.hasClassIdField()) {
-            DexProgramClass target = group.getTarget();
-            target.forEachProgramVirtualMethodMatching(
-                definition ->
-                    definition.hasCode()
-                        && definition.getCode() instanceof IncompleteVirtuallyMergedMethodCode,
-                method -> {
-                  IncompleteVirtuallyMergedMethodCode code =
-                      (IncompleteVirtuallyMergedMethodCode) method.getDefinition().getCode();
-                  method
-                      .getDefinition()
-                      .setCode(code.toCfCode(method, horizontalClassMergerGraphLens), appView);
-                });
-          }
+          DexProgramClass target = group.getTarget();
+          target.forEachProgramMethodMatching(
+              definition ->
+                  definition.hasCode()
+                      && definition.getCode().isIncompleteHorizontalClassMergerCode(),
+              method -> {
+                IncompleteHorizontalClassMergerCode code =
+                    (IncompleteHorizontalClassMergerCode) method.getDefinition().getCode();
+                method
+                    .getDefinition()
+                    .setCode(
+                        code.toCfCode(appView, method, horizontalClassMergerGraphLens), appView);
+              });
         },
         executorService);
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerCfCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerCfCode.java
deleted file mode 100644
index ce6f6be..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerCfCode.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.horizontalclassmerging;
-
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.DexType;
-import java.util.List;
-
-/**
- * Similar to CfCode, but with a marker that makes it possible to recognize this is synthesized by
- * the horizontal class merger.
- */
-public class HorizontalClassMergerCfCode extends CfCode {
-
-  HorizontalClassMergerCfCode(
-      DexType originalHolder, int maxStack, int maxLocals, List<CfInstruction> instructions) {
-    super(originalHolder, maxStack, maxLocals, instructions);
-  }
-
-  @Override
-  public boolean isHorizontalClassMergingCode() {
-    return true;
-  }
-}
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 75743d6..14465fd 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -272,7 +272,8 @@
       }
     }
 
-    void addExtraParameters(DexMethod methodSignature, List<ExtraParameter> extraParameters) {
+    void addExtraParameters(
+        DexMethod methodSignature, List<? extends ExtraParameter> extraParameters) {
       Set<DexMethod> originalMethodSignatures = methodMap.getKeys(methodSignature);
       if (originalMethodSignatures.isEmpty()) {
         methodExtraParameters
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java
index 6976e5a..9f74d8e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java
@@ -7,6 +7,7 @@
 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.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.IRCode;
 
@@ -23,6 +24,7 @@
     AppView<AppInfo> appViewForConversion =
         AppView.createForD8(AppInfo.createInitialAppInfo(appView.appInfo().app()));
     appViewForConversion.setGraphLens(appView.graphLens());
+    appViewForConversion.setCodeLens(appView.codeLens());
     this.appViewForConversion = appViewForConversion;
   }
 
@@ -32,4 +34,8 @@
         .getCode()
         .buildIR(method, appViewForConversion, method.getOrigin());
   }
+
+  public void setGraphLens(GraphLens graphLens) {
+    appViewForConversion.setGraphLens(graphLens);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteHorizontalClassMergerCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteHorizontalClassMergerCode.java
new file mode 100644
index 0000000..d961949
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteHorizontalClassMergerCode.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2022, 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.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.ClasspathMethod;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.origin.Origin;
+
+public abstract class IncompleteHorizontalClassMergerCode extends Code {
+
+  @Override
+  public boolean isHorizontalClassMergerCode() {
+    return true;
+  }
+
+  @Override
+  public boolean isIncompleteHorizontalClassMergerCode() {
+    return true;
+  }
+
+  public abstract CfCode toCfCode(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      ProgramMethod method,
+      HorizontalClassMergerGraphLens lens);
+
+  // Implement Code.
+
+  @Override
+  public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) {
+    throw new Unreachable();
+  }
+
+  @Override
+  protected boolean computeEquals(Object other) {
+    throw new Unreachable();
+  }
+
+  @Override
+  protected int computeHashCode() {
+    throw new Unreachable();
+  }
+
+  @Override
+  public int estimatedDexCodeSizeUpperBoundInBytes() {
+    throw new Unreachable();
+  }
+
+  @Override
+  public boolean isEmptyVoidMethod() {
+    throw new Unreachable();
+  }
+
+  @Override
+  public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
+    throw new Unreachable();
+  }
+
+  @Override
+  public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) {
+    throw new Unreachable();
+  }
+
+  @Override
+  public abstract String toString();
+
+  @Override
+  public String toString(DexEncodedMethod method, ClassNameMapper naming) {
+    return toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
new file mode 100644
index 0000000..f60f332
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
@@ -0,0 +1,245 @@
+// Copyright (c) 2022, 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.cf.code.CfConstClass;
+import com.android.tools.r8.cf.code.CfConstNull;
+import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfConstString;
+import com.android.tools.r8.cf.code.CfDexItemBasedConstString;
+import com.android.tools.r8.cf.code.CfInstanceFieldWrite;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLabel;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfPosition;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.value.SingleConstValue;
+import com.android.tools.r8.ir.analysis.value.SingleDexItemBasedStringValue;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Position.SyntheticPosition;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.ir.conversion.ExtraParameter;
+import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
+import com.android.tools.r8.utils.IntBox;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.Map;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Similar to CfCode, but with a marker that makes it possible to recognize this is synthesized by
+ * the horizontal class merger.
+ */
+public class IncompleteMergedInstanceInitializerCode extends IncompleteHorizontalClassMergerCode {
+
+  private final DexField classIdField;
+  private final int extraNulls;
+  private final DexMethod originalMethodReference;
+  private final DexMethod syntheticMethodReference;
+
+  private final Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignmentsPre;
+  private final Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignmentsPost;
+
+  private final DexMethod parentConstructor;
+  private final List<InstanceFieldInitializationInfo> parentConstructorArguments;
+
+  IncompleteMergedInstanceInitializerCode(
+      DexField classIdField,
+      int extraNulls,
+      DexMethod originalMethodReference,
+      DexMethod syntheticMethodReference,
+      Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignmentsPre,
+      Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignmentsPost,
+      DexMethod parentConstructor,
+      List<InstanceFieldInitializationInfo> parentConstructorArguments) {
+    this.classIdField = classIdField;
+    this.extraNulls = extraNulls;
+    this.originalMethodReference = originalMethodReference;
+    this.syntheticMethodReference = syntheticMethodReference;
+    this.instanceFieldAssignmentsPre = instanceFieldAssignmentsPre;
+    this.instanceFieldAssignmentsPost = instanceFieldAssignmentsPost;
+    this.parentConstructor = parentConstructor;
+    this.parentConstructorArguments = parentConstructorArguments;
+  }
+
+  @Override
+  public CfCode toCfCode(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      ProgramMethod method,
+      HorizontalClassMergerGraphLens lens) {
+    int[] argumentToLocalIndex = new int[method.getDefinition().getNumberOfArguments()];
+    int maxLocals = 0;
+    for (int argumentIndex = 0; argumentIndex < argumentToLocalIndex.length; argumentIndex++) {
+      argumentToLocalIndex[argumentIndex] = maxLocals;
+      maxLocals += method.getArgumentType(argumentIndex).getRequiredRegisters();
+    }
+
+    IntBox maxStack = new IntBox();
+    ImmutableList.Builder<CfInstruction> instructionBuilder = ImmutableList.builder();
+
+    // Set position.
+    Position callerPosition =
+        SyntheticPosition.builder().setLine(0).setMethod(syntheticMethodReference).build();
+    Position calleePosition =
+        SyntheticPosition.builder()
+            .setLine(0)
+            .setMethod(originalMethodReference)
+            .setCallerPosition(callerPosition)
+            .build();
+    CfPosition position = new CfPosition(new CfLabel(), calleePosition);
+    instructionBuilder.add(position);
+    instructionBuilder.add(position.getLabel());
+
+    // Assign class id.
+    if (classIdField != null) {
+      int classIdLocalIndex = maxLocals - 1 - extraNulls;
+      instructionBuilder.add(new CfLoad(ValueType.OBJECT, 0));
+      instructionBuilder.add(new CfLoad(ValueType.INT, classIdLocalIndex));
+      instructionBuilder.add(
+          new CfInstanceFieldWrite(
+              lens.getRenamedFieldSignature(classIdField, lens.getPrevious())));
+      maxStack.set(2);
+    }
+
+    // Assign each field.
+    addCfInstructionsForInstanceFieldAssignments(
+        instructionBuilder, instanceFieldAssignmentsPre, argumentToLocalIndex, maxStack, lens);
+
+    // Load receiver for parent constructor call.
+    int stackHeightForParentConstructorCall = 1;
+    instructionBuilder.add(new CfLoad(ValueType.OBJECT, 0));
+
+    // Load constructor arguments.
+    MethodLookupResult parentConstructorLookup = lens.lookupInvokeDirect(parentConstructor, method);
+
+    int i = 0;
+    for (InstanceFieldInitializationInfo initializationInfo : parentConstructorArguments) {
+      stackHeightForParentConstructorCall +=
+          addCfInstructionsForInitializationInfo(
+              instructionBuilder,
+              initializationInfo,
+              argumentToLocalIndex,
+              parentConstructorLookup.getReference().getParameter(i));
+      i++;
+    }
+
+    for (ExtraParameter extraParameter :
+        parentConstructorLookup.getPrototypeChanges().getExtraParameters()) {
+      stackHeightForParentConstructorCall +=
+          addCfInstructionsForInitializationInfo(
+              instructionBuilder,
+              extraParameter.getValue(appView),
+              argumentToLocalIndex,
+              parentConstructorLookup.getReference().getParameter(i));
+      i++;
+    }
+
+    assert i == parentConstructorLookup.getReference().getParameters().size();
+
+    // Invoke parent constructor.
+    instructionBuilder.add(
+        new CfInvoke(Opcodes.INVOKESPECIAL, parentConstructorLookup.getReference(), false));
+    maxStack.setMax(stackHeightForParentConstructorCall);
+
+    // Assign each field.
+    addCfInstructionsForInstanceFieldAssignments(
+        instructionBuilder, instanceFieldAssignmentsPost, argumentToLocalIndex, maxStack, lens);
+
+    // Return.
+    instructionBuilder.add(new CfReturnVoid());
+
+    return new CfCode(
+        originalMethodReference.getHolderType(),
+        maxStack.get(),
+        maxLocals,
+        instructionBuilder.build()) {
+
+      @Override
+      public GraphLens getCodeLens(AppView<?> appView) {
+        return lens;
+      }
+    };
+  }
+
+  private static void addCfInstructionsForInstanceFieldAssignments(
+      ImmutableList.Builder<CfInstruction> instructionBuilder,
+      Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignments,
+      int[] argumentToLocalIndex,
+      IntBox maxStack,
+      HorizontalClassMergerGraphLens lens) {
+    instanceFieldAssignments.forEach(
+        (field, initializationInfo) -> {
+          // Load the receiver, the field value, and then set the field.
+          instructionBuilder.add(new CfLoad(ValueType.OBJECT, 0));
+          int stackSizeForInitializationInfo =
+              addCfInstructionsForInitializationInfo(
+                  instructionBuilder, initializationInfo, argumentToLocalIndex, field.getType());
+          instructionBuilder.add(
+              new CfInstanceFieldWrite(lens.getRenamedFieldSignature(field, lens.getPrevious())));
+          maxStack.setMax(stackSizeForInitializationInfo + 1);
+        });
+  }
+
+  private static int addCfInstructionsForInitializationInfo(
+      ImmutableList.Builder<CfInstruction> instructionBuilder,
+      InstanceFieldInitializationInfo initializationInfo,
+      int[] argumentToLocalIndex,
+      DexType type) {
+    if (initializationInfo.isArgumentInitializationInfo()) {
+      int argumentIndex = initializationInfo.asArgumentInitializationInfo().getArgumentIndex();
+      instructionBuilder.add(
+          new CfLoad(ValueType.fromDexType(type), argumentToLocalIndex[argumentIndex]));
+      return type.getRequiredRegisters();
+    }
+
+    assert initializationInfo.isSingleValue();
+    assert initializationInfo.asSingleValue().isSingleConstValue();
+
+    SingleConstValue singleConstValue = initializationInfo.asSingleValue().asSingleConstValue();
+    if (singleConstValue.isSingleConstClassValue()) {
+      instructionBuilder.add(
+          new CfConstClass(singleConstValue.asSingleConstClassValue().getType()));
+      return 1;
+    } else if (singleConstValue.isSingleDexItemBasedStringValue()) {
+      SingleDexItemBasedStringValue dexItemBasedStringValue =
+          singleConstValue.asSingleDexItemBasedStringValue();
+      instructionBuilder.add(
+          new CfDexItemBasedConstString(
+              dexItemBasedStringValue.getItem(), dexItemBasedStringValue.getNameComputationInfo()));
+      return 1;
+    } else if (singleConstValue.isSingleNumberValue()) {
+      if (type.isReferenceType()) {
+        assert singleConstValue.isNull();
+        instructionBuilder.add(new CfConstNull());
+        return 1;
+      } else {
+        instructionBuilder.add(
+            new CfConstNumber(
+                singleConstValue.asSingleNumberValue().getValue(), ValueType.fromDexType(type)));
+        return type.getRequiredRegisters();
+      }
+    } else {
+      assert singleConstValue.isSingleStringValue();
+      instructionBuilder.add(
+          new CfConstString(singleConstValue.asSingleStringValue().getDexString()));
+      return 1;
+    }
+  }
+
+  @Override
+  public String toString() {
+    return "IncompleteMergedInstanceInitializerCode";
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java
index 65aed84..835daa4 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java
@@ -15,22 +15,15 @@
 import com.android.tools.r8.cf.code.CfReturnVoid;
 import com.android.tools.r8.cf.code.CfSwitch;
 import com.android.tools.r8.cf.code.CfSwitch.Kind;
-import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.ClasspathMethod;
-import com.android.tools.r8.graph.Code;
-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.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.IterableUtils;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
@@ -44,9 +37,10 @@
 
 /**
  * A short-lived piece of code that will be converted into {@link CfCode} using the method {@link
- * #toCfCode(ProgramMethod, HorizontalClassMergerGraphLens)}.
+ * IncompleteHorizontalClassMergerCode#toCfCode(AppView, ProgramMethod,
+ * HorizontalClassMergerGraphLens)}.
  */
-public class IncompleteVirtuallyMergedMethodCode extends Code {
+public class IncompleteVirtuallyMergedMethodCode extends IncompleteHorizontalClassMergerCode {
 
   private final DexField classIdField;
   private final Int2ReferenceSortedMap<DexMethod> mappedMethods;
@@ -64,11 +58,6 @@
     this.originalMethod = originalMethod;
   }
 
-  @Override
-  public boolean isHorizontalClassMergingCode() {
-    return true;
-  }
-
   /**
    * Given a mapping from class ids to methods to invoke, this creates a piece of {@link CfCode} on
    * the following form.
@@ -91,7 +80,11 @@
    * methods may be changed as a result of the horizontal class merger's fixup (e.g., if the method
    * signature refers to a horizontally merged type).
    */
-  public CfCode toCfCode(ProgramMethod method, HorizontalClassMergerGraphLens lens) {
+  @Override
+  public CfCode toCfCode(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      ProgramMethod method,
+      HorizontalClassMergerGraphLens lens) {
     // We store each argument in a local.
     int maxLocals = 1 + IterableUtils.sumInt(method.getParameters(), DexType::getRequiredRegisters);
 
@@ -157,13 +150,7 @@
 
       @Override
       public GraphLens getCodeLens(AppView<?> appView) {
-        GraphLens graphLens =
-            appView
-                .graphLens()
-                .asNonIdentityLens()
-                .find(GraphLens::isHorizontalClassMergerGraphLens);
-        assert graphLens != null;
-        return graphLens;
+        return lens;
       }
     };
   }
@@ -195,50 +182,8 @@
     return locals;
   }
 
-  // Implement Code.
-
-  @Override
-  public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) {
-    throw new Unreachable();
-  }
-
-  @Override
-  protected boolean computeEquals(Object other) {
-    throw new Unreachable();
-  }
-
-  @Override
-  protected int computeHashCode() {
-    throw new Unreachable();
-  }
-
-  @Override
-  public int estimatedDexCodeSizeUpperBoundInBytes() {
-    throw new Unreachable();
-  }
-
-  @Override
-  public boolean isEmptyVoidMethod() {
-    throw new Unreachable();
-  }
-
-  @Override
-  public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
-    throw new Unreachable();
-  }
-
-  @Override
-  public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) {
-    throw new Unreachable();
-  }
-
   @Override
   public String toString() {
     return "IncompleteVirtuallyMergedMethodCode";
   }
-
-  @Override
-  public String toString(DexEncodedMethod method, ClassNameMapper naming) {
-    return toString();
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
index 7aa753c..894c017 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
@@ -4,40 +4,18 @@
 
 package com.android.tools.r8.horizontalclassmerging;
 
-import com.android.tools.r8.cf.code.CfConstClass;
-import com.android.tools.r8.cf.code.CfConstNull;
-import com.android.tools.r8.cf.code.CfConstNumber;
-import com.android.tools.r8.cf.code.CfConstString;
-import com.android.tools.r8.cf.code.CfDexItemBasedConstString;
-import com.android.tools.r8.cf.code.CfInstanceFieldWrite;
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.cf.code.CfLabel;
-import com.android.tools.r8.cf.code.CfLoad;
-import com.android.tools.r8.cf.code.CfPosition;
-import com.android.tools.r8.cf.code.CfReturnVoid;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.value.SingleConstValue;
-import com.android.tools.r8.ir.analysis.value.SingleDexItemBasedStringValue;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.Position.SyntheticPosition;
-import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
-import com.android.tools.r8.utils.IntBox;
-import com.google.common.collect.ImmutableList;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import org.objectweb.asm.Opcodes;
 
 /**
  * A simple abstraction of an instance initializer's code, which allows a parent constructor call
@@ -91,146 +69,21 @@
    * @param syntheticMethodReference the original, synthetic reference of the new method reference
    *     ($r8$init$synthetic)
    */
-  public CfCode createCfCode(
-      DexMethod newMethodReference,
+  public IncompleteMergedInstanceInitializerCode createCfCode(
       DexMethod originalMethodReference,
       DexMethod syntheticMethodReference,
       MergeGroup group,
       boolean hasClassId,
       int extraNulls) {
-    int[] argumentToLocalIndex =
-        new int[newMethodReference.getParameters().size() + 1 - extraNulls];
-    int maxLocals = 0;
-    argumentToLocalIndex[0] = maxLocals++;
-    for (int i = 1; i < argumentToLocalIndex.length; i++) {
-      argumentToLocalIndex[i] = maxLocals;
-      maxLocals += newMethodReference.getParameter(i - 1).getRequiredRegisters();
-    }
-
-    IntBox maxStack = new IntBox();
-    ImmutableList.Builder<CfInstruction> instructionBuilder = ImmutableList.builder();
-
-    // Set position.
-    Position callerPosition =
-        SyntheticPosition.builder().setLine(0).setMethod(syntheticMethodReference).build();
-    Position calleePosition =
-        SyntheticPosition.builder()
-            .setLine(0)
-            .setMethod(originalMethodReference)
-            .setCallerPosition(callerPosition)
-            .build();
-    CfPosition position = new CfPosition(new CfLabel(), calleePosition);
-    instructionBuilder.add(position);
-    instructionBuilder.add(position.getLabel());
-
-    // Assign class id.
-    if (group.hasClassIdField()) {
-      assert hasClassId;
-      int classIdLocalIndex = maxLocals - 1;
-      instructionBuilder.add(new CfLoad(ValueType.OBJECT, 0));
-      instructionBuilder.add(new CfLoad(ValueType.INT, classIdLocalIndex));
-      instructionBuilder.add(new CfInstanceFieldWrite(group.getClassIdField()));
-      maxStack.set(2);
-    } else {
-      assert !hasClassId;
-    }
-
-    // Assign each field.
-    addCfInstructionsForInstanceFieldAssignments(
-        instructionBuilder, instanceFieldAssignmentsPre, argumentToLocalIndex, maxStack);
-
-    // Load receiver for parent constructor call.
-    int stackHeightForParentConstructorCall = 1;
-    instructionBuilder.add(new CfLoad(ValueType.OBJECT, 0));
-
-    // Load constructor arguments.
-    int i = 0;
-    for (InstanceFieldInitializationInfo initializationInfo : parentConstructorArguments) {
-      stackHeightForParentConstructorCall +=
-          addCfInstructionsForInitializationInfo(
-              instructionBuilder,
-              initializationInfo,
-              argumentToLocalIndex,
-              parentConstructor.getParameter(i));
-      i++;
-    }
-
-    // Invoke parent constructor.
-    instructionBuilder.add(new CfInvoke(Opcodes.INVOKESPECIAL, parentConstructor, false));
-    maxStack.setMax(stackHeightForParentConstructorCall);
-
-    // Assign each field.
-    addCfInstructionsForInstanceFieldAssignments(
-        instructionBuilder, instanceFieldAssignmentsPost, argumentToLocalIndex, maxStack);
-
-    // Return.
-    instructionBuilder.add(new CfReturnVoid());
-
-    return new HorizontalClassMergerCfCode(
-        newMethodReference.getHolderType(), maxStack.get(), maxLocals, instructionBuilder.build());
-  }
-
-  private static void addCfInstructionsForInstanceFieldAssignments(
-      ImmutableList.Builder<CfInstruction> instructionBuilder,
-      Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignments,
-      int[] argumentToLocalIndex,
-      IntBox maxStack) {
-    instanceFieldAssignments.forEach(
-        (field, initializationInfo) -> {
-          // Load the receiver, the field value, and then set the field.
-          instructionBuilder.add(new CfLoad(ValueType.OBJECT, 0));
-          int stackSizeForInitializationInfo =
-              addCfInstructionsForInitializationInfo(
-                  instructionBuilder, initializationInfo, argumentToLocalIndex, field.getType());
-          instructionBuilder.add(new CfInstanceFieldWrite(field));
-          maxStack.setMax(stackSizeForInitializationInfo + 1);
-        });
-  }
-
-  private static int addCfInstructionsForInitializationInfo(
-      ImmutableList.Builder<CfInstruction> instructionBuilder,
-      InstanceFieldInitializationInfo initializationInfo,
-      int[] argumentToLocalIndex,
-      DexType type) {
-    if (initializationInfo.isArgumentInitializationInfo()) {
-      int argumentIndex = initializationInfo.asArgumentInitializationInfo().getArgumentIndex();
-      instructionBuilder.add(
-          new CfLoad(ValueType.fromDexType(type), argumentToLocalIndex[argumentIndex]));
-      return type.getRequiredRegisters();
-    }
-
-    assert initializationInfo.isSingleValue();
-    assert initializationInfo.asSingleValue().isSingleConstValue();
-
-    SingleConstValue singleConstValue = initializationInfo.asSingleValue().asSingleConstValue();
-    if (singleConstValue.isSingleConstClassValue()) {
-      instructionBuilder.add(
-          new CfConstClass(singleConstValue.asSingleConstClassValue().getType()));
-      return 1;
-    } else if (singleConstValue.isSingleDexItemBasedStringValue()) {
-      SingleDexItemBasedStringValue dexItemBasedStringValue =
-          singleConstValue.asSingleDexItemBasedStringValue();
-      instructionBuilder.add(
-          new CfDexItemBasedConstString(
-              dexItemBasedStringValue.getItem(), dexItemBasedStringValue.getNameComputationInfo()));
-      return 1;
-    } else if (singleConstValue.isSingleNumberValue()) {
-      if (type.isReferenceType()) {
-        assert singleConstValue.isNull();
-        instructionBuilder.add(new CfConstNull());
-        return 1;
-      } else {
-        instructionBuilder.add(
-            new CfConstNumber(
-                singleConstValue.asSingleNumberValue().getValue(), ValueType.fromDexType(type)));
-        return type.getRequiredRegisters();
-      }
-    } else {
-      assert singleConstValue.isSingleStringValue();
-      instructionBuilder.add(
-          new CfConstString(singleConstValue.asSingleStringValue().getDexString()));
-      return 1;
-    }
+    return new IncompleteMergedInstanceInitializerCode(
+        hasClassId ? group.getClassIdField() : null,
+        extraNulls,
+        originalMethodReference,
+        syntheticMethodReference,
+        instanceFieldAssignmentsPre,
+        instanceFieldAssignmentsPost,
+        parentConstructor,
+        parentConstructorArguments);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
index 3c97009..1c3443b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -34,7 +34,6 @@
 import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
 public class InstanceInitializerMerger {
@@ -143,11 +142,11 @@
    * synthesized constructor all-though it by construction doesn't have any unused arguments.
    */
   private DexMethod getSyntheticMethodReference(
-      ClassMethodsBuilder classMethodsBuilder, ProgramMethod representative) {
+      ClassMethodsBuilder classMethodsBuilder, DexMethod newMethodReference) {
     return dexItemFactory.createFreshMethodNameWithoutHolder(
-        "$r8$init$synthetic",
-        representative.getProto(),
-        representative.getHolderType(),
+        Constants.SYNTHETIC_INSTANCE_INITIALIZER_PREFIX,
+        newMethodReference.getProto(),
+        newMethodReference.getHolderType(),
         classMethodsBuilder::isFresh);
   }
 
@@ -276,7 +275,6 @@
       int extraNulls) {
     if (hasInstanceInitializerDescription()) {
       return instanceInitializerDescription.createCfCode(
-          newMethodReference,
           getOriginalMethodReference(),
           syntheticMethodReference,
           group,
@@ -318,7 +316,11 @@
             newMethodReferenceTemplate,
             mode.isInitial() ? syntheticArgumentClass.getArgumentClasses() : ImmutableList.of(),
             classMethodsBuilder::isFresh);
-    int extraNulls = newMethodReference.getArity() - newMethodReferenceTemplate.getArity();
+
+    // Compute the extra unused null parameters.
+    List<ExtraUnusedNullParameter> extraUnusedNullParameters =
+        ExtraUnusedNullParameter.computeExtraUnusedNullParameters(
+            newMethodReferenceTemplate, newMethodReference);
 
     // Verify that the merge is a simple renaming in the final round of merging.
     assert mode.isInitial() || newMethodReference == newMethodReferenceTemplate;
@@ -340,7 +342,7 @@
 
     // Add a mapping from a synthetic name to the synthetic constructor.
     DexMethod syntheticMethodReference =
-        getSyntheticMethodReference(classMethodsBuilder, representative);
+        getSyntheticMethodReference(classMethodsBuilder, newMethodReference);
     if (!isSingleton() || group.hasClassIdField()) {
       lensBuilder.recordNewMethodSignature(syntheticMethodReference, newMethodReference, true);
     }
@@ -352,7 +354,7 @@
         int classIdentifier = classIdentifiers.getInt(instanceInitializer.getHolderType());
         extraParameters.add(new ExtraConstantIntParameter(classIdentifier));
       }
-      extraParameters.addAll(Collections.nCopies(extraNulls, new ExtraUnusedNullParameter()));
+      extraParameters.addAll(extraUnusedNullParameters);
       lensBuilder.mapMergedConstructor(
           instanceInitializer.getReference(), newMethodReference, extraParameters);
     }
@@ -363,7 +365,11 @@
             .setMethod(newMethodReference)
             .setAccessFlags(getNewAccessFlags())
             .setCode(
-                getNewCode(newMethodReference, syntheticMethodReference, needsClassId, extraNulls))
+                getNewCode(
+                    newMethodReference,
+                    syntheticMethodReference,
+                    needsClassId,
+                    extraUnusedNullParameters.size()))
             .setClassFileVersion(getNewClassFileVersion())
             .setApiLevelForDefinition(representativeMethod.getApiLevelForDefinition())
             .setApiLevelForCode(representativeMethod.getApiLevelForCode())
@@ -372,11 +378,12 @@
 
     if (mode.isFinal()) {
       if (appView.options().isGeneratingDex() && !newInstanceInitializer.getCode().isDexCode()) {
-        syntheticInitializerConverterBuilder.add(
+        syntheticInitializerConverterBuilder.addInstanceInitializer(
             new ProgramMethod(group.getTarget(), newInstanceInitializer));
       } else {
         assert appView.options().isGeneratingDex()
-            || newInstanceInitializer.getCode().isCfWritableCode();
+            || newInstanceInitializer.getCode().isCfWritableCode()
+            || newInstanceInitializer.getCode().isIncompleteHorizontalClassMergerCode();
       }
     }
   }
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 0c36573..183664a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -30,7 +30,6 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.IdentityHashMap;
 import java.util.LinkedHashSet;
 import java.util.Map;
@@ -293,10 +292,10 @@
                 newMethodReference,
                 syntheticArgumentClass.getArgumentClasses(),
                 tryMethod -> !newMethods.contains(tryMethod.getSignature()));
-        int extraNulls = newMethodReference.getArity() - originalMethodReference.getArity();
         lensBuilder.addExtraParameters(
             originalMethodReference,
-            Collections.nCopies(extraNulls, new ExtraUnusedNullParameter()));
+            ExtraUnusedNullParameter.computeExtraUnusedNullParameters(
+                originalMethodReference, newMethodReference));
       } else {
         newMethodReference =
             dexItemFactory.createFreshMethodNameWithoutHolder(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
index 3a9f744..60903cc 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
@@ -53,7 +53,7 @@
   }
 
   @Override
-  public boolean isHorizontalClassMergingCode() {
+  public boolean isHorizontalClassMergerCode() {
     return true;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
index d2414ed..446d245 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
@@ -27,15 +27,18 @@
 
   private final AppView<? extends AppInfoWithClassHierarchy> appView;
   private final IRCodeProvider codeProvider;
-  private final List<ProgramMethod> methods;
+  private final List<ProgramMethod> classInitializers;
+  private final List<ProgramMethod> instanceInitializers;
 
   private SyntheticInitializerConverter(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       IRCodeProvider codeProvider,
-      List<ProgramMethod> methods) {
+      List<ProgramMethod> classInitializers,
+      List<ProgramMethod> instanceInitializers) {
     this.appView = appView;
     this.codeProvider = codeProvider;
-    this.methods = methods;
+    this.classInitializers = classInitializers;
+    this.instanceInitializers = instanceInitializers;
   }
 
   public static Builder builder(
@@ -43,7 +46,24 @@
     return new Builder(appView, codeProvider);
   }
 
-  public void convert(ExecutorService executorService) throws ExecutionException {
+  public void convertClassInitializers(ExecutorService executorService) throws ExecutionException {
+    if (!classInitializers.isEmpty()) {
+      IRConverter converter = new IRConverter(createAppViewForConversion(), Timing.empty());
+      ThreadUtils.processItems(
+          classInitializers, method -> processMethod(method, converter), executorService);
+    }
+  }
+
+  public void convertInstanceInitializers(ExecutorService executorService)
+      throws ExecutionException {
+    if (!instanceInitializers.isEmpty()) {
+      IRConverter converter = new IRConverter(createAppViewForConversion(), Timing.empty());
+      ThreadUtils.processItems(
+          instanceInitializers, method -> processMethod(method, converter), executorService);
+    }
+  }
+
+  private AppView<AppInfo> createAppViewForConversion() {
     // At this point the code rewritings described by repackaging and synthetic finalization have
     // not been applied to the code objects. These code rewritings will be applied in the
     // application writer. We therefore simulate that we are in D8, to allow building IR for each of
@@ -52,28 +72,26 @@
     AppView<AppInfo> appViewForConversion =
         AppView.createForD8(AppInfo.createInitialAppInfo(appView.appInfo().app()));
     appViewForConversion.setGraphLens(appView.graphLens());
+    appViewForConversion.setCodeLens(appView.codeLens());
+    return appViewForConversion;
+  }
 
-    // Build IR for each of the class initializers and finalize.
-    IRConverter converter = new IRConverter(appViewForConversion, Timing.empty());
-    ThreadUtils.processItems(
-        methods,
-        method -> {
-          IRCode code = codeProvider.buildIR(method);
-          converter.removeDeadCodeAndFinalizeIR(
-              code, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
-        },
-        executorService);
+  private void processMethod(ProgramMethod method, IRConverter converter) {
+    IRCode code = codeProvider.buildIR(method);
+    converter.removeDeadCodeAndFinalizeIR(
+        code, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
   }
 
   public boolean isEmpty() {
-    return methods.isEmpty();
+    return classInitializers.isEmpty() && instanceInitializers.isEmpty();
   }
 
   public static class Builder {
 
     private final AppView<? extends AppInfoWithClassHierarchy> appView;
     private final IRCodeProvider codeProvider;
-    private final List<ProgramMethod> methods = new ArrayList<>();
+    private final List<ProgramMethod> classInitializers = new ArrayList<>();
+    private final List<ProgramMethod> instanceInitializers = new ArrayList<>();
 
     private Builder(
         AppView<? extends AppInfoWithClassHierarchy> appView, IRCodeProvider codeProvider) {
@@ -81,13 +99,19 @@
       this.codeProvider = codeProvider;
     }
 
-    public Builder add(ProgramMethod method) {
-      this.methods.add(method);
+    public Builder addClassInitializer(ProgramMethod method) {
+      this.classInitializers.add(method);
+      return this;
+    }
+
+    public Builder addInstanceInitializer(ProgramMethod method) {
+      this.instanceInitializers.add(method);
       return this;
     }
 
     public SyntheticInitializerConverter build() {
-      return new SyntheticInitializerConverter(appView, codeProvider, methods);
+      return new SyntheticInitializerConverter(
+          appView, codeProvider, classInitializers, instanceInitializers);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Argument.java b/src/main/java/com/android/tools/r8/ir/code/Argument.java
index e78ade7..edd3468 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Argument.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Argument.java
@@ -37,7 +37,15 @@
   }
 
   public int getIndex() {
-    assert verifyIndex();
+    return getIndex(true);
+  }
+
+  public int getIndexRaw() {
+    return getIndex(false);
+  }
+
+  private int getIndex(boolean verifyIndex) {
+    assert !verifyIndex || verifyIndex();
     return index;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index 358369b..60d87b7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -310,7 +310,7 @@
       DexItemFactory dexItemFactory = appView.dexItemFactory();
       DexMethod invokedMethod = toBeReplaced.asInvokeDirect().getInvokedMethod();
       if (invokedMethod.isInstanceInitializer(dexItemFactory)
-          || invokedMethod.mustBeInlinedIntoInstanceInitializer(dexItemFactory)) {
+          || invokedMethod.mustBeInlinedIntoInstanceInitializer(appView)) {
         return false;
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index d7abfac..02c2319 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -211,7 +211,7 @@
     current.removePhiUser(this);
   }
 
-  void replaceOperand(Value current, Value newValue) {
+  public void replaceOperand(Value current, Value newValue) {
     for (int i = 0; i < operands.size(); i++) {
       if (operands.get(i) == current) {
         operands.set(i, newValue);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 4f4a7b2..e95d612 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -26,9 +26,9 @@
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -81,7 +81,7 @@
   private static final int IINC_PATTERN_SIZE = 4;
 
   public final AppView<?> appView;
-  private final DexEncodedMethod method;
+  private final ProgramMethod method;
   private final IRCode code;
 
   private Map<BasicBlock, CfLabel> labels;
@@ -135,7 +135,7 @@
 
   public CfBuilder(
       AppView<?> appView,
-      DexEncodedMethod method,
+      ProgramMethod method,
       IRCode code,
       BytecodeMetadataProvider bytecodeMetadataProvider) {
     this.appView = appView;
@@ -193,12 +193,12 @@
     DexBuilder.removeRedundantDebugPositions(code);
     CfCode code = buildCfCode();
     assert verifyInvokeInterface(code, appView);
-    assert code.verifyFrames(method, appView, this.code.origin).isValid();
+    assert code.verifyFrames(method, appView, appView.graphLens()).isValid();
     return code;
   }
 
   private Set<UninitializedThisLocalRead> insertUninitializedThisLocalReads() {
-    if (!method.isInstanceInitializer()) {
+    if (!method.getDefinition().isInstanceInitializer()) {
       return Collections.emptySet();
     }
     // Find all non-normal exit blocks.
@@ -249,7 +249,7 @@
       for (Instruction insn : block.getInstructions()) {
         if (insn.isNewInstance()) {
           initializers.put(insn.asNewInstance(), computeInitializers(insn.outValue()));
-        } else if (insn.isArgument() && method.isInstanceInitializer()) {
+        } else if (insn.isArgument() && method.getDefinition().isInstanceInitializer()) {
           if (insn.outValue().isThis()) {
             // By JVM8 §4.10.1.9 (invokespecial), a this() or super() call in a constructor
             // changes the type of `this` from uninitializedThis
@@ -259,7 +259,7 @@
         }
       }
     }
-    assert !(method.isInstanceInitializer() && thisInitializers == null);
+    assert !(method.getDefinition().isInstanceInitializer() && thisInitializers == null);
   }
 
   private List<InvokeDirect> computeInitializers(Value value) {
@@ -383,8 +383,8 @@
     }
     com.android.tools.r8.position.Position diagnosticPosition =
         com.android.tools.r8.position.Position.UNKNOWN;
-    if (method.getCode().isCfCode()) {
-      diagnosticPosition = method.getCode().asCfCode().getDiagnosticPosition();
+    if (method.getDefinition().getCode().isCfCode()) {
+      diagnosticPosition = method.getDefinition().getCode().asCfCode().getDiagnosticPosition();
     }
     return new CfCode(
         method.getHolderType(),
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java b/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java
index 0532a48..cc8a8c7 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java
@@ -6,24 +6,40 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 public class ExtraUnusedNullParameter extends ExtraParameter {
 
   private final DexType type;
 
-  @Deprecated
-  public ExtraUnusedNullParameter() {
-    this(null);
-  }
-
   public ExtraUnusedNullParameter(DexType type) {
     this.type = type;
   }
 
+  public static List<ExtraUnusedNullParameter> computeExtraUnusedNullParameters(
+      DexMethod from, DexMethod to) {
+    int numberOfExtraNullParameters = to.getArity() - from.getArity();
+    if (numberOfExtraNullParameters == 0) {
+      return Collections.emptyList();
+    }
+    List<ExtraUnusedNullParameter> extraUnusedNullParameters =
+        new ArrayList<>(numberOfExtraNullParameters);
+    for (int extraUnusedNullParameterIndex = from.getArity();
+        extraUnusedNullParameterIndex < to.getParameters().size();
+        extraUnusedNullParameterIndex++) {
+      DexType extraUnusedNullParameterType = to.getParameter(extraUnusedNullParameterIndex);
+      extraUnusedNullParameters.add(new ExtraUnusedNullParameter(extraUnusedNullParameterType));
+    }
+    return extraUnusedNullParameters;
+  }
+
   @Override
   public DexType getType(DexItemFactory dexItemFactory) {
     assert type != null;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 664da13..0ac2896 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -74,6 +74,7 @@
 import com.android.tools.r8.ir.optimize.Inliner;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.MemberValuePropagation;
+import com.android.tools.r8.ir.optimize.NaturalIntLoopRemover;
 import com.android.tools.r8.ir.optimize.PeepholeOptimizer;
 import com.android.tools.r8.ir.optimize.RedundantFieldLoadAndStoreElimination;
 import com.android.tools.r8.ir.optimize.ReflectionOptimizer;
@@ -142,6 +143,7 @@
   private final InternalOptions options;
   private final CfgPrinter printer;
   public final CodeRewriter codeRewriter;
+  private final NaturalIntLoopRemover naturalIntLoopRemover = new NaturalIntLoopRemover();
   private final ConstantCanonicalizer constantCanonicalizer;
   public final MemberValuePropagation memberValuePropagation;
   private final LensCodeRewriter lensCodeRewriter;
@@ -1300,6 +1302,9 @@
     timing.begin("Rewrite array length");
     codeRewriter.rewriteKnownArrayLengthCalls(code);
     timing.end();
+    timing.begin("Natural Int Loop Remover");
+    naturalIntLoopRemover.run(code);
+    timing.end();
     timing.begin("Rewrite AssertionError");
     codeRewriter.rewriteAssertionErrorTwoArgumentConstructor(code, options);
     timing.end();
@@ -1638,11 +1643,11 @@
       OptimizationFeedback feedback,
       MethodConversionOptions conversionOptions,
       BytecodeMetadataProvider bytecodeMetadataProvider) {
-    DexEncodedMethod method = code.method();
-    assert !method.getCode().isDexCode();
+    ProgramMethod method = code.context();
+    assert !method.getDefinition().getCode().isDexCode();
     CfBuilder builder = new CfBuilder(appView, method, code, bytecodeMetadataProvider);
     CfCode result = builder.build(deadCodeRemover, conversionOptions);
-    method.setCode(result, appView);
+    method.getDefinition().setCode(result, appView);
     markProcessed(code, feedback);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 613ab36..309cf22 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -885,7 +885,8 @@
           ListIterator<Instruction> argumentPostludeIterator = argumentPostlude.listIterator();
           while (argumentPostludeIterator.hasNext()) {
             Instruction current = argumentPostludeIterator.next();
-            if (!current.isArgument() || replacement.getIndex() < current.asArgument().getIndex()) {
+            if (!current.isArgument()
+                || replacement.getIndexRaw() < current.asArgument().getIndexRaw()) {
               argumentPostludeIterator.previous();
               break;
             }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java
index bd146e9..c943028 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java
@@ -4,11 +4,14 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary;
 
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.utils.Reporter;
 import java.util.Map;
 
 /**
@@ -18,21 +21,34 @@
  */
 public class DesugaredLibraryAmender {
 
-  private final AppView<?> appView;
+  private final DexDefinitionSupplier definitions;
+  private final Reporter reporter;
+  private final ComputedApiLevel minAPILevel;
 
   public static void run(AppView<?> appView) {
-    run(appView, appView.options().machineDesugaredLibrarySpecification.getAmendLibraryMethods());
+    run(
+        appView.options().machineDesugaredLibrarySpecification.getAmendLibraryMethods(),
+        appView,
+        appView.options().reporter,
+        appView.computedMinApiLevel());
   }
 
-  public static void run(AppView<?> appView, Map<DexMethod, MethodAccessFlags> amendLibrary) {
+  public static void run(
+      Map<DexMethod, MethodAccessFlags> amendLibrary,
+      DexDefinitionSupplier definitions,
+      Reporter reporter,
+      ComputedApiLevel minAPILevel) {
     if (amendLibrary.isEmpty()) {
       return;
     }
-    new DesugaredLibraryAmender(appView).run(amendLibrary);
+    new DesugaredLibraryAmender(definitions, reporter, minAPILevel).run(amendLibrary);
   }
 
-  private DesugaredLibraryAmender(AppView<?> appView) {
-    this.appView = appView;
+  private DesugaredLibraryAmender(
+      DexDefinitionSupplier definitions, Reporter reporter, ComputedApiLevel minAPILevel) {
+    this.definitions = definitions;
+    this.reporter = reporter;
+    this.minAPILevel = minAPILevel;
   }
 
   private void run(Map<DexMethod, MethodAccessFlags> amendLibrary) {
@@ -40,17 +56,14 @@
   }
 
   private void amendLibraryMethod(DexMethod method, MethodAccessFlags methodAccessFlags) {
-    DexClass dexClass = appView.contextIndependentDefinitionFor(method.getHolderType());
+    DexClass dexClass = definitions.contextIndependentDefinitionFor(method.getHolderType());
     if (dexClass == null || !dexClass.isLibraryClass()) {
       // Consider just throwing an error.
-      appView
-          .options()
-          .reporter
-          .warning(
-              "Desugared library: Cannot amend library method "
-                  + method
-                  + " because the holder is not a library class"
-                  + (dexClass == null ? "(null)." : "."));
+      reporter.warning(
+          "Desugared library: Cannot amend library method "
+              + method
+              + " because the holder is not a library class"
+              + (dexClass == null ? "(null)." : "."));
       return;
     }
     if (dexClass.lookupMethod(method) != null) {
@@ -61,7 +74,7 @@
             .setMethod(method)
             .setAccessFlags(methodAccessFlags)
             .setCode(null)
-            .setApiLevelForDefinition(appView.computedMinApiLevel())
+            .setApiLevelForDefinition(minAPILevel)
             .build();
     dexClass.getMethodCollection().addMethod(encodedMethod);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java
index f96e5cd..9d3bf12 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java
@@ -4,12 +4,11 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary;
 
-import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.List;
@@ -24,14 +23,6 @@
     return false;
   }
 
-  default LegacyDesugaredLibrarySpecification asLegacyDesugaredLibrarySpecification() {
-    return null;
-  }
-
-  default HumanDesugaredLibrarySpecification asHumanDesugaredLibrarySpecification() {
-    return null;
-  }
-
   boolean isEmpty();
 
   boolean isLibraryCompilation();
@@ -45,14 +36,15 @@
   AndroidApiLevel getRequiredCompilationApiLevel();
 
   MachineDesugaredLibrarySpecification toMachineSpecification(
-      InternalOptions options, AndroidApp app) throws IOException;
+      InternalOptions options, AndroidApp app, Timing timing) throws IOException;
 
   MachineDesugaredLibrarySpecification toMachineSpecification(
-      InternalOptions options, Path library, Path desugaredJDKLib) throws IOException;
+      InternalOptions options, Path library, Timing timing, Path desugaredJDKLib)
+      throws IOException;
 
   default MachineDesugaredLibrarySpecification toMachineSpecification(
-      InternalOptions options, Path library) throws IOException {
+      InternalOptions options, Path library, Timing timing) throws IOException {
     assert !isLibraryCompilation();
-    return toMachineSpecification(options, library, null);
+    return toMachineSpecification(options, library, timing, null);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
index 157dcc6..238ae7b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
@@ -3,22 +3,16 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification;
 
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 public class HumanDesugaredLibrarySpecification implements DesugaredLibrarySpecification {
 
@@ -35,6 +29,11 @@
     this.rewritingFlags = rewritingFlags;
   }
 
+  public static HumanDesugaredLibrarySpecification empty() {
+    return new HumanDesugaredLibrarySpecification(
+        HumanTopLevelFlags.empty(), HumanRewritingFlags.empty(), false);
+  }
+
   @Override
   public boolean isEmpty() {
     return rewritingFlags.isEmpty();
@@ -45,11 +44,6 @@
     return true;
   }
 
-  @Override
-  public HumanDesugaredLibrarySpecification asHumanDesugaredLibrarySpecification() {
-    return this;
-  }
-
   public boolean supportAllCallbacksFromLibrary() {
     return topLevelFlags.supportAllCallbacksFromLibrary();
   }
@@ -81,61 +75,6 @@
     return topLevelFlags.getIdentifier();
   }
 
-  public Map<String, String> getRewritePrefix() {
-    return rewritingFlags.getRewritePrefix();
-  }
-
-  public boolean hasEmulatedLibraryInterfaces() {
-    return !getEmulateLibraryInterface().isEmpty();
-  }
-
-  public Map<DexType, DexType> getEmulateLibraryInterface() {
-    return rewritingFlags.getEmulatedInterfaces();
-  }
-
-  // If the method is retargeted, answers the retargeted method, else null.
-  public DexMethod retargetMethod(DexEncodedMethod method, AppView<?> appView) {
-    Map<DexMethod, DexType> retargetCoreLibMember = rewritingFlags.getRetargetMethod();
-    DexType dexType = retargetCoreLibMember.get(method.getReference());
-    if (dexType != null) {
-      return appView
-          .dexItemFactory()
-          .createMethod(
-              dexType,
-              appView.dexItemFactory().prependHolderToProto(method.getReference()),
-              method.getName());
-    }
-    return null;
-  }
-
-  public DexMethod retargetMethod(DexClassAndMethod method, AppView<?> appView) {
-    return retargetMethod(method.getDefinition(), appView);
-  }
-
-  public Map<DexMethod, DexType> getRetargetCoreLibMember() {
-    return rewritingFlags.getRetargetMethod();
-  }
-
-  public Map<DexType, DexType> getBackportCoreLibraryMember() {
-    return rewritingFlags.getLegacyBackport();
-  }
-
-  public Map<DexType, DexType> getCustomConversions() {
-    return rewritingFlags.getCustomConversions();
-  }
-
-  public Set<DexType> getWrapperConversions() {
-    return rewritingFlags.getWrapperConversions();
-  }
-
-  public Set<DexMethod> getDontRewriteInvocation() {
-    return rewritingFlags.getDontRewriteInvocation();
-  }
-
-  public Set<DexType> getDontRetargetLibMember() {
-    return rewritingFlags.getDontRetarget();
-  }
-
   @Override
   public List<String> getExtraKeepRules() {
     return topLevelFlags.getExtraKeepRules();
@@ -152,19 +91,15 @@
 
   @Override
   public MachineDesugaredLibrarySpecification toMachineSpecification(
-      InternalOptions options, AndroidApp app) throws IOException {
-    return new HumanToMachineSpecificationConverter()
-        .convert(
-            this,
-            isLibraryCompilation() ? app.getProgramResourceProviders() : null,
-            app.getLibraryResourceProviders(),
-            options);
+      InternalOptions options, AndroidApp app, Timing timing) throws IOException {
+    return new HumanToMachineSpecificationConverter(timing).convert(this, app, options);
   }
 
   @Override
   public MachineDesugaredLibrarySpecification toMachineSpecification(
-      InternalOptions options, Path library, Path desugaredJDKLib) throws IOException {
-    return new HumanToMachineSpecificationConverter()
-        .convert(this, isLibraryCompilation() ? desugaredJDKLib : null, library, options);
+      InternalOptions options, Path library, Timing timing, Path desugaredJDKLib)
+      throws IOException {
+    return new HumanToMachineSpecificationConverter(timing)
+        .convertForTesting(this, desugaredJDKLib, library, options);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
index bb4bc68..e52cc02 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
@@ -176,9 +176,11 @@
     if (formatVersion != CURRENT_HUMAN_CONFIGURATION_FORMAT_VERSION) {
       reporter.warning(
           new StringDiagnostic(
-              "Human desugared library specification version mismatches the parser "
-                  + "expected version. This is allowed and should happen only while extending "
-                  + "the specifications.",
+              "Human desugared library specification format version "
+                  + formatVersion
+                  + " mismatches the parser expected version ("
+                  + CURRENT_HUMAN_CONFIGURATION_FORMAT_VERSION
+                  + "). This is allowed and should happen only while extending the specifications.",
               origin));
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
index 71227cf..3daf522 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
@@ -15,7 +15,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.Timing;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.List;
@@ -28,11 +28,6 @@
   private final LegacyTopLevelFlags topLevelFlags;
   private final LegacyRewritingFlags rewritingFlags;
 
-  public static LegacyDesugaredLibrarySpecification empty() {
-    return new LegacyDesugaredLibrarySpecification(
-        LegacyTopLevelFlags.empty(), LegacyRewritingFlags.empty(), false);
-  }
-
   public LegacyDesugaredLibrarySpecification(
       LegacyTopLevelFlags topLevelFlags,
       LegacyRewritingFlags rewritingFlags,
@@ -52,11 +47,6 @@
     return true;
   }
 
-  @Override
-  public LegacyDesugaredLibrarySpecification asLegacyDesugaredLibrarySpecification() {
-    return this;
-  }
-
   public LegacyTopLevelFlags getTopLevelFlags() {
     return topLevelFlags;
   }
@@ -88,18 +78,6 @@
     return topLevelFlags.getIdentifier();
   }
 
-  public Map<String, String> getRewritePrefix() {
-    return rewritingFlags.getRewritePrefix();
-  }
-
-  public boolean hasEmulatedLibraryInterfaces() {
-    return !getEmulateLibraryInterface().isEmpty();
-  }
-
-  public Map<DexType, DexType> getEmulateLibraryInterface() {
-    return rewritingFlags.getEmulateLibraryInterface();
-  }
-
   // If the method is retargeted, answers the retargeted method, else null.
   public DexMethod retargetMethod(DexEncodedMethod method, AppView<?> appView) {
     Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
@@ -120,14 +98,6 @@
     return retargetMethod(method.getDefinition(), appView);
   }
 
-  public Map<DexString, Map<DexType, DexType>> getRetargetCoreLibMember() {
-    return rewritingFlags.getRetargetCoreLibMember();
-  }
-
-  public Map<DexType, DexType> getBackportCoreLibraryMember() {
-    return rewritingFlags.getBackportCoreLibraryMember();
-  }
-
   public Map<DexType, DexType> getCustomConversions() {
     return rewritingFlags.getCustomConversions();
   }
@@ -136,13 +106,6 @@
     return rewritingFlags.getWrapperConversions();
   }
 
-  public List<Pair<DexType, DexString>> getDontRewriteInvocation() {
-    return rewritingFlags.getDontRewriteInvocation();
-  }
-
-  public Set<DexType> getDontRetargetLibMember() {
-    return rewritingFlags.getDontRetargetLibMember();
-  }
 
   @Override
   public List<String> getExtraKeepRules() {
@@ -156,17 +119,18 @@
 
   @Override
   public MachineDesugaredLibrarySpecification toMachineSpecification(
-      InternalOptions options, AndroidApp app) throws IOException {
-    return new LegacyToHumanSpecificationConverter()
-        .convert(this, app.getLibraryResourceProviders(), options)
-        .toMachineSpecification(options, app);
+      InternalOptions options, AndroidApp app, Timing timing) throws IOException {
+    return new LegacyToHumanSpecificationConverter(timing)
+        .convert(this, app, options)
+        .toMachineSpecification(options, app, timing);
   }
 
   @Override
   public MachineDesugaredLibrarySpecification toMachineSpecification(
-      InternalOptions options, Path library, Path desugaredJDKLib) throws IOException {
-    return new LegacyToHumanSpecificationConverter()
-        .convert(this, library, options)
-        .toMachineSpecification(options, library, desugaredJDKLib);
+      InternalOptions options, Path library, Timing timing, Path desugaredJDKLib)
+      throws IOException {
+    return new LegacyToHumanSpecificationConverter(timing)
+        .convertForTesting(this, desugaredJDKLib, library, options)
+        .toMachineSpecification(options, library, timing, desugaredJDKLib);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/AppForSpecConversion.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/AppForSpecConversion.java
new file mode 100644
index 0000000..45f9dae
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/AppForSpecConversion.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion;
+
+import com.android.tools.r8.ClassFileResourceProvider;
+import com.android.tools.r8.ProgramResourceProvider;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.concurrent.ExecutorService;
+
+public class AppForSpecConversion {
+  static DexApplication readApp(
+      AndroidApp inputApp, InternalOptions options, boolean libraryCompilation, Timing timing)
+      throws IOException {
+    timing.begin("Read App");
+    AndroidApp.Builder builder = AndroidApp.builder();
+    for (ClassFileResourceProvider classFileResourceProvider :
+        inputApp.getLibraryResourceProviders()) {
+      builder.addLibraryResourceProvider(classFileResourceProvider);
+    }
+    if (libraryCompilation) {
+      for (ProgramResourceProvider programResourceProvider :
+          inputApp.getProgramResourceProviders()) {
+        builder.addProgramResourceProvider(programResourceProvider);
+      }
+    }
+    DexApplication app = internalReadApp(builder.build(), options, timing);
+    timing.end();
+    return app;
+  }
+
+  static DexApplication readAppForTesting(
+      Path desugaredJDKLib,
+      Path androidLib,
+      InternalOptions options,
+      boolean libraryCompilation,
+      Timing timing)
+      throws IOException {
+    timing.begin("Read App for testing");
+    assert !libraryCompilation || desugaredJDKLib != null;
+    AndroidApp.Builder builder = AndroidApp.builder();
+    if (libraryCompilation) {
+      builder.addProgramFile(desugaredJDKLib);
+    }
+    AndroidApp inputApp = builder.addLibraryFile(androidLib).build();
+    DexApplication app = internalReadApp(inputApp, options, timing);
+    timing.end();
+    return app;
+  }
+
+  private static DexApplication internalReadApp(
+      AndroidApp inputApp, InternalOptions options, Timing timing) throws IOException {
+    timing.begin("Internal Read App");
+    ApplicationReader applicationReader = new ApplicationReader(inputApp, options, timing);
+    ExecutorService executorService = ThreadUtils.getExecutorService(options);
+    assert !options.ignoreJavaLibraryOverride;
+    options.ignoreJavaLibraryOverride = true;
+    DexApplication app = applicationReader.read(executorService);
+    options.ignoreJavaLibraryOverride = false;
+    timing.end();
+    return app;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
index 55ac78c..e0ae560 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion;
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -88,17 +87,13 @@
   }
 
   private void rewriteClasses() {
-    for (DexClass clazz : appInfo.app().asDirect().libraryClasses()) {
-      if (clazz.toString().contains("MonthDay")) {
-        clazz.toString();
-      }
-      registerType(clazz.type);
-      registerDifferentType(clazz.type);
-    }
-    for (DexClass clazz : appInfo.classes()) {
-      registerType(clazz.type);
-      registerDifferentType(clazz.type);
-    }
+    appInfo.app().forEachLibraryType(this::registerClassType);
+    appInfo.app().forEachProgramType(this::registerClassType);
+  }
+
+  private void registerClassType(DexType type) {
+    registerType(type);
+    registerDifferentType(type);
   }
 
   private void registerType(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
index 6268125..a5a27aa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
@@ -11,8 +11,6 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.MethodResolutionResult;
-import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
@@ -28,12 +26,10 @@
 public class HumanToMachineRetargetConverter {
 
   private final AppInfoWithClassHierarchy appInfo;
-  private final SubtypingInfo subtypingInfo;
   private final Set<DexMethod> missingMethods = Sets.newIdentityHashSet();
 
   public HumanToMachineRetargetConverter(AppInfoWithClassHierarchy appInfo) {
     this.appInfo = appInfo;
-    subtypingInfo = SubtypingInfo.create(appInfo);
   }
 
   public void convertRetargetFlags(
@@ -91,31 +87,12 @@
     DerivedMethod dispatchMethod =
         new DerivedMethod(src.getReference(), SyntheticKind.RETARGET_CLASS);
     LinkedHashMap<DexType, DerivedMethod> dispatchCases = new LinkedHashMap<>();
-    assert validateNoOverride(src, appInfo, subtypingInfo);
     builder.putEmulatedVirtualRetarget(
         src.getReference(),
         new EmulatedDispatchMethodDescriptor(
             interfaceMethod, dispatchMethod, forwardingMethod, dispatchCases));
   }
 
-  private boolean validateNoOverride(
-      DexEncodedMethod src, AppInfoWithClassHierarchy appInfo, SubtypingInfo subtypingInfo) {
-    for (DexType subtype : subtypingInfo.subtypes(src.getHolderType())) {
-      DexClass subclass = appInfo.definitionFor(subtype);
-      MethodResolutionResult resolutionResult =
-          appInfo.resolveMethodOn(subclass, src.getReference());
-      // The resolution is not successful when compiling to dex if the method rewritten is missing
-      // in Android.jar.
-      assert !resolutionResult.isSuccessfulMemberResolutionResult()
-          || resolutionResult.getResolvedMethod().getReference() == src.getReference()
-          // There is a difference in the sql library between Android.jar and the JDK which leads
-          // to this resolution when compiling Cf to Cf while the methods do not exist in Android.
-          || (resolutionResult.getResolvedMethod().getHolderType().toString().contains("java.sql")
-              && resolutionResult.getResolvedMethod().getName().toString().equals("toInstant"));
-    }
-    return true;
-  }
-
   private boolean isEmulatedInterfaceDispatch(
       DexEncodedMethod method,
       AppInfoWithClassHierarchy appInfo,
@@ -146,19 +123,10 @@
       DexEncodedMethod foundMethod,
       DexType type,
       AppInfoWithClassHierarchy appInfo,
-      SubtypingInfo subtypingInfo,
       BiConsumer<DexMethod, DexMethod> consumer) {
     DexMethod src = foundMethod.getReference();
     DexMethod dest = src.withHolder(type, appInfo.dexItemFactory());
     consumer.accept(src, dest);
-    for (DexType subtype : subtypingInfo.subtypes(foundMethod.getHolderType())) {
-      DexClass subclass = appInfo.definitionFor(subtype);
-      MethodResolutionResult resolutionResult = appInfo.resolveMethodOn(subclass, src);
-      if (resolutionResult.isSuccessfulMemberResolutionResult()
-          && resolutionResult.getResolvedMethod().getReference() == src) {
-        consumer.accept(src.withHolder(subtype, appInfo.dexItemFactory()), dest);
-      }
-    }
   }
 
   private void convertNonEmulatedVirtualRetarget(
@@ -167,7 +135,6 @@
         foundMethod,
         type,
         appInfo,
-        subtypingInfo,
         (src, dest) ->
             builder.putNonEmulatedVirtualRetarget(
                 src,
@@ -177,7 +144,6 @@
 
   private void convertStaticRetarget(
       MachineRewritingFlags.Builder builder, DexEncodedMethod foundMethod, DexType type) {
-    convertNonEmulatedRetarget(
-        foundMethod, type, appInfo, subtypingInfo, builder::putStaticRetarget);
+    convertNonEmulatedRetarget(foundMethod, type, appInfo, builder::putStaticRetarget);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
index 51516d4..c0bcd82 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
@@ -4,19 +4,15 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion;
 
-import com.android.tools.r8.ClassFileResourceProvider;
-import com.android.tools.r8.ProgramResourceProvider;
-import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 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.DexApplication;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAmender;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
@@ -27,7 +23,7 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineTopLevelFlags;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.Sets;
 import java.io.IOException;
@@ -35,52 +31,43 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.ExecutorService;
 
 public class HumanToMachineSpecificationConverter {
 
-  private AppView<?> appView;
+  private AppInfoWithClassHierarchy appInfo;
+  private Reporter reporter;
   private final Set<DexType> missingCustomConversions = Sets.newIdentityHashSet();
+  private final Timing timing;
 
-  public MachineDesugaredLibrarySpecification convert(
-      HumanDesugaredLibrarySpecification humanSpec,
-      List<ProgramResourceProvider> desugaredJDKLib,
-      List<ClassFileResourceProvider> library,
-      InternalOptions options)
-      throws IOException {
-    assert !humanSpec.isLibraryCompilation() || desugaredJDKLib != null;
-    AndroidApp.Builder builder = AndroidApp.builder();
-    for (ClassFileResourceProvider classFileResourceProvider : library) {
-      builder.addLibraryResourceProvider(classFileResourceProvider);
-    }
-    if (humanSpec.isLibraryCompilation()) {
-      for (ProgramResourceProvider programResourceProvider : desugaredJDKLib) {
-        builder.addProgramResourceProvider(programResourceProvider);
-      }
-    }
-    return internalConvert(humanSpec, builder.build(), options);
+  public HumanToMachineSpecificationConverter(Timing timing) {
+    this.timing = timing;
   }
 
   public MachineDesugaredLibrarySpecification convert(
+      HumanDesugaredLibrarySpecification humanSpec, AndroidApp inputApp, InternalOptions options)
+      throws IOException {
+    DexApplication app =
+        AppForSpecConversion.readApp(inputApp, options, humanSpec.isLibraryCompilation(), timing);
+    return convert(humanSpec, app);
+  }
+
+  public MachineDesugaredLibrarySpecification convertForTesting(
       HumanDesugaredLibrarySpecification humanSpec,
       Path desugaredJDKLib,
       Path androidLib,
       InternalOptions options)
       throws IOException {
-    assert !humanSpec.isLibraryCompilation() || desugaredJDKLib != null;
-    AndroidApp.Builder builder = AndroidApp.builder();
-    if (humanSpec.isLibraryCompilation()) {
-      builder.addProgramFile(desugaredJDKLib);
-    }
-    AndroidApp inputApp = builder.addLibraryFile(androidLib).build();
-    return internalConvert(humanSpec, inputApp, options);
+    DexApplication app =
+        AppForSpecConversion.readAppForTesting(
+            desugaredJDKLib, androidLib, options, humanSpec.isLibraryCompilation(), timing);
+    return convert(humanSpec, app);
   }
 
-  private MachineDesugaredLibrarySpecification internalConvert(
-      HumanDesugaredLibrarySpecification humanSpec, AndroidApp inputApp, InternalOptions options)
-      throws IOException {
-    DexApplication app = readApp(inputApp, options);
-    appView = AppView.createForD8(AppInfo.createInitialAppInfo(app));
+  private MachineDesugaredLibrarySpecification convert(
+      HumanDesugaredLibrarySpecification humanSpec, DexApplication app) {
+    timing.begin("Human to machine convert");
+    reporter = app.options.reporter;
+    appInfo = AppInfoWithClassHierarchy.createForDesugaring(AppInfo.createInitialAppInfo(app));
     LibraryValidator.validate(
         app,
         humanSpec.isLibraryCompilation(),
@@ -89,6 +76,7 @@
         convertRewritingFlags(
             humanSpec.getSynthesizedLibraryClassesPackagePrefix(), humanSpec.getRewritingFlags());
     MachineTopLevelFlags topLevelFlags = convertTopLevelFlags(humanSpec.getTopLevelFlags());
+    timing.end();
     return new MachineDesugaredLibrarySpecification(
         humanSpec.isLibraryCompilation(), topLevelFlags, machineRewritingFlags);
   }
@@ -105,9 +93,10 @@
 
   private MachineRewritingFlags convertRewritingFlags(
       String synthesizedPrefix, HumanRewritingFlags rewritingFlags) {
-    AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
+    timing.begin("convert rewriting flags");
     MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder();
-    DesugaredLibraryAmender.run(appView, rewritingFlags.getAmendLibraryMethod());
+    DesugaredLibraryAmender.run(
+        rewritingFlags.getAmendLibraryMethod(), appInfo, reporter, ComputedApiLevel.unknown());
     rewritingFlags.getAmendLibraryMethod().forEach(builder::amendLibraryMethod);
     new HumanToMachineRetargetConverter(appInfo)
         .convertRetargetFlags(rewritingFlags, builder, this::warnMissingReferences);
@@ -126,7 +115,9 @@
         "Cannot register custom conversion due to missing type: ", missingCustomConversions);
     rewritingFlags.getDontRetarget().forEach(builder::addDontRetarget);
     rewritingFlags.getLegacyBackport().forEach(builder::putLegacyBackport);
-    return builder.build();
+    MachineRewritingFlags machineFlags = builder.build();
+    timing.end();
+    return machineFlags;
   }
 
   private void convertCustomConversion(
@@ -152,16 +143,6 @@
     builder.putCustomConversion(type, new CustomConversionDescriptor(toMethod, fromMethod));
   }
 
-  private DexApplication readApp(AndroidApp inputApp, InternalOptions options) throws IOException {
-    ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
-    ExecutorService executorService = ThreadUtils.getExecutorService(options);
-    assert !options.ignoreJavaLibraryOverride;
-    options.ignoreJavaLibraryOverride = true;
-    DirectMappedDexApplication app = applicationReader.read(executorService).toDirect();
-    options.ignoreJavaLibraryOverride = false;
-    return app;
-  }
-
   void warnMissingReferences(String message, Set<? extends DexReference> missingReferences) {
     List<DexReference> memberList = new ArrayList<>(missingReferences);
     memberList.sort(DexReference::compareTo);
@@ -178,6 +159,6 @@
     if (memberList.isEmpty()) {
       return;
     }
-    appView.options().reporter.warning("Specification conversion: " + message + memberList);
+    reporter.warning("Specification conversion: " + message + memberList);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
index 3a2c990..47ef9e3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
@@ -4,14 +4,13 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion;
 
-import com.android.tools.r8.ClassFileResourceProvider;
 import com.android.tools.r8.StringConsumer;
 import com.android.tools.r8.StringResource;
-import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProto;
@@ -34,8 +33,8 @@
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Pair;
-import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@@ -44,15 +43,22 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ExecutorService;
 
 public class LegacyToHumanSpecificationConverter {
 
-  private static final String wrapperPrefix = "__wrapper__.";
-  private AndroidApiLevel legacyHackLevel = AndroidApiLevel.N_MR1;
+  private static final String WRAPPER_PREFIX = "__wrapper__.";
+  private static final AndroidApiLevel LEGACY_HACK_LEVEL = AndroidApiLevel.N_MR1;
+  private final Timing timing;
+
+  public LegacyToHumanSpecificationConverter(Timing timing) {
+    this.timing = timing;
+  }
 
   public void convertAllAPILevels(
-      StringResource inputSpecification, Path androidLib, StringConsumer output)
+      StringResource inputSpecification,
+      Path desugaredJDKLib,
+      Path androidLib,
+      StringConsumer output)
       throws IOException {
     InternalOptions options = new InternalOptions();
     MultiAPILevelLegacyDesugaredLibrarySpecification legacySpec =
@@ -60,18 +66,21 @@
                 options.dexItemFactory(), options.reporter)
             .parseMultiLevelConfiguration(inputSpecification);
     MultiAPILevelHumanDesugaredLibrarySpecification humanSpec =
-        convertAllAPILevels(legacySpec, androidLib, options);
+        convertAllAPILevels(legacySpec, desugaredJDKLib, androidLib, options);
     MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.export(humanSpec, output);
   }
 
   public MultiAPILevelHumanDesugaredLibrarySpecification convertAllAPILevels(
       MultiAPILevelLegacyDesugaredLibrarySpecification legacySpec,
+      Path desugaredJDKLib,
       Path androidLib,
       InternalOptions options)
       throws IOException {
+    timing.begin("Legacy to human all API convert");
     Origin origin = legacySpec.getOrigin();
-    AndroidApp androidApp = AndroidApp.builder().addLibraryFile(androidLib).build();
-    DexApplication app = readApp(androidApp, options);
+    DexApplication app =
+        AppForSpecConversion.readAppForTesting(desugaredJDKLib, androidLib, options, true, timing);
+
     HumanTopLevelFlags humanTopLevelFlags = convertTopLevelFlags(legacySpec.getTopLevelFlags());
     Int2ObjectArrayMap<HumanRewritingFlags> commonFlags =
         convertRewritingFlagMap(legacySpec.getCommonFlags(), app, origin);
@@ -87,32 +96,34 @@
             origin, humanTopLevelFlags, commonFlags, libraryFlags, programFlags);
     MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.deduplicateFlags(
         humanSpec, options.reporter);
+    timing.end();
     return humanSpec;
   }
 
   public HumanDesugaredLibrarySpecification convert(
+      LegacyDesugaredLibrarySpecification legacySpec, AndroidApp inputApp, InternalOptions options)
+      throws IOException {
+    DexApplication app =
+        AppForSpecConversion.readApp(inputApp, options, legacySpec.isLegacy(), timing);
+    return convert(legacySpec, app, options);
+  }
+
+  public HumanDesugaredLibrarySpecification convertForTesting(
       LegacyDesugaredLibrarySpecification legacySpec,
-      List<ClassFileResourceProvider> library,
+      Path desugaredJDKLib,
+      Path androidLib,
       InternalOptions options)
       throws IOException {
-    AndroidApp.Builder builder = AndroidApp.builder();
-    for (ClassFileResourceProvider classFileResourceProvider : library) {
-      builder.addLibraryResourceProvider(classFileResourceProvider);
-    }
-    return internalConvert(legacySpec, builder.build(), options);
+    DexApplication app =
+        AppForSpecConversion.readAppForTesting(
+            desugaredJDKLib, androidLib, options, legacySpec.isLibraryCompilation(), timing);
+    return convert(legacySpec, app, options);
   }
 
   public HumanDesugaredLibrarySpecification convert(
-      LegacyDesugaredLibrarySpecification legacySpec, Path androidLib, InternalOptions options)
+      LegacyDesugaredLibrarySpecification legacySpec, DexApplication app, InternalOptions options)
       throws IOException {
-    AndroidApp androidApp = AndroidApp.builder().addLibraryFile(androidLib).build();
-    return internalConvert(legacySpec, androidApp, options);
-  }
-
-  public HumanDesugaredLibrarySpecification internalConvert(
-      LegacyDesugaredLibrarySpecification legacySpec, AndroidApp inputApp, InternalOptions options)
-      throws IOException {
-    DexApplication app = readApp(inputApp, options);
+    timing.begin("Legacy to Human convert");
     LibraryValidator.validate(
         app,
         legacySpec.isLibraryCompilation(),
@@ -125,22 +136,27 @@
     Origin origin = Origin.unknown();
     HumanRewritingFlags humanRewritingFlags =
         convertRewritingFlags(legacySpec.getRewritingFlags(), app, origin);
-    if (options.getMinApiLevel().isLessThanOrEqualTo(legacyHackLevel)
+    if (options.getMinApiLevel().isLessThanOrEqualTo(LEGACY_HACK_LEVEL)
         && legacySpec.isLibraryCompilation()) {
+      timing.begin("Legacy hacks");
       HumanRewritingFlags.Builder builder =
           humanRewritingFlags.newBuilder(app.options.reporter, origin);
       legacyLibraryFlagHacks(app.dexItemFactory(), builder);
       humanRewritingFlags = builder.build();
+      timing.end();
     }
+
+    timing.end();
     return new HumanDesugaredLibrarySpecification(
         humanTopLevelFlags, humanRewritingFlags, legacySpec.isLibraryCompilation());
   }
 
   private void legacyLibraryFlagHacks(
       Int2ObjectArrayMap<HumanRewritingFlags> libraryFlags, DexApplication app, Origin origin) {
-    int level = legacyHackLevel.getLevel();
+    int level = LEGACY_HACK_LEVEL.getLevel();
     HumanRewritingFlags humanRewritingFlags = libraryFlags.get(level);
     if (humanRewritingFlags == null) {
+      // Skip CHM only configuration.
       return;
     }
     HumanRewritingFlags.Builder builder =
@@ -180,12 +196,6 @@
     builder.retargetMethod(source, target);
   }
 
-  private DexApplication readApp(AndroidApp inputApp, InternalOptions options) throws IOException {
-    ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
-    ExecutorService executorService = ThreadUtils.getExecutorService(options);
-    return applicationReader.read(executorService).toDirect();
-  }
-
   private Int2ObjectArrayMap<HumanRewritingFlags> convertRewritingFlagMap(
       Int2ObjectMap<LegacyRewritingFlags> libFlags, DexApplication app, Origin origin) {
     Int2ObjectArrayMap<HumanRewritingFlags> map = new Int2ObjectArrayMap<>();
@@ -195,8 +205,8 @@
 
   private HumanRewritingFlags convertRewritingFlags(
       LegacyRewritingFlags flags, DexApplication app, Origin origin) {
+    timing.begin("Convert rewriting flags");
     HumanRewritingFlags.Builder builder = HumanRewritingFlags.builder(app.options.reporter, origin);
-
     flags
         .getRewritePrefix()
         .forEach((prefix, rewritten) -> rewritePrefix(builder, prefix, rewritten));
@@ -205,15 +215,15 @@
     flags.getCustomConversions().forEach(builder::putCustomConversion);
     flags.getDontRetargetLibMember().forEach(builder::addDontRetargetLibMember);
     flags.getWrapperConversions().forEach(builder::addWrapperConversion);
-
     flags
         .getRetargetCoreLibMember()
         .forEach((name, typeMap) -> convertRetargetCoreLibMember(builder, app, name, typeMap));
     flags
         .getDontRewriteInvocation()
         .forEach(pair -> convertDontRewriteInvocation(builder, app, pair));
-
-    return builder.build();
+    HumanRewritingFlags humanFlags = builder.build();
+    timing.end();
+    return humanFlags;
   }
 
   private void rewritePrefix(HumanRewritingFlags.Builder builder, String prefix, String rewritten) {
@@ -224,14 +234,14 @@
       builder.putRewriteDerivedPrefix(rewritten, prefix, rewritten);
       return;
     }
-    if (prefix.equals(wrapperPrefix)) {
+    if (prefix.equals(WRAPPER_PREFIX)) {
       // We hard code here this applies to java.nio and java.io only.
       ImmutableMap<String, String> map =
           ImmutableMap.of("java.nio.", "j$.nio.", "java.io.", "j$.io.");
       map.forEach(
           (k, v) -> {
-            builder.putRewriteDerivedPrefix(k, wrapperPrefix + k, k);
-            builder.putRewriteDerivedPrefix(k, wrapperPrefix + v, v);
+            builder.putRewriteDerivedPrefix(k, WRAPPER_PREFIX + k, k);
+            builder.putRewriteDerivedPrefix(k, WRAPPER_PREFIX + v, v);
           });
       return;
     }
@@ -242,7 +252,8 @@
       HumanRewritingFlags.Builder builder, DexApplication app, Pair<DexType, DexString> pair) {
     DexClass dexClass = app.definitionFor(pair.getFirst());
     assert dexClass != null;
-    List<DexClassAndMethod> methodsWithName = findMethodsWithName(pair.getSecond(), dexClass);
+    List<DexClassAndMethod> methodsWithName =
+        findMethodsWithName(pair.getSecond(), dexClass, builder, app);
     for (DexClassAndMethod dexClassAndMethod : methodsWithName) {
       builder.addDontRewriteInvocation(dexClassAndMethod.getReference());
     }
@@ -257,16 +268,37 @@
         (type, rewrittenType) -> {
           DexClass dexClass = app.definitionFor(type);
           assert dexClass != null;
-          List<DexClassAndMethod> methodsWithName = findMethodsWithName(name, dexClass);
+          List<DexClassAndMethod> methodsWithName =
+              findMethodsWithName(name, dexClass, builder, app);
           for (DexClassAndMethod dexClassAndMethod : methodsWithName) {
             builder.retargetMethod(dexClassAndMethod.getReference(), rewrittenType);
           }
         });
   }
 
-  private List<DexClassAndMethod> findMethodsWithName(DexString methodName, DexClass clazz) {
+  private List<DexClassAndMethod> findMethodsWithName(
+      DexString methodName,
+      DexClass clazz,
+      HumanRewritingFlags.Builder builder,
+      DexApplication app) {
     List<DexClassAndMethod> found = new ArrayList<>();
     clazz.forEachClassMethodMatching(definition -> definition.getName() == methodName, found::add);
+    if (found.isEmpty()
+        && methodName.toString().equals("transferTo")
+        && clazz.type.toString().equals("java.io.InputStream")) {
+      // Special hack for JDK11 java.io.InputStream#transferTo which could not be specified
+      // correctly.
+      DexItemFactory factory = app.dexItemFactory();
+      DexProto proto =
+          factory.createProto(factory.longType, factory.createType("Ljava/io/OutputStream;"));
+      DexMethod method = factory.createMethod(clazz.type, proto, methodName);
+      MethodAccessFlags flags =
+          MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC, false);
+      builder.amendLibraryMethod(method, flags);
+      DexEncodedMethod build =
+          DexEncodedMethod.builder().setMethod(method).setAccessFlags(flags).build();
+      return ImmutableList.of(DexClassAndMethod.create(clazz, build));
+    }
     assert !found.isEmpty()
         : "Should have found a method (library specifications) for "
             + clazz.toSourceString()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NaturalIntLoopRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/NaturalIntLoopRemover.java
new file mode 100644
index 0000000..9e0908a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NaturalIntLoopRemover.java
@@ -0,0 +1,395 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.Goto;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Sub;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.Sets;
+import java.util.Set;
+
+/**
+ * The NaturalIntLoopRemover detects natural loops on an integer iterator and computes the exact
+ * number of iterations if possible. If the number of iterations is known to be 1, it transforms the
+ * loop into a straight-line single iteration of the loop body.
+ *
+ * <p>This relies on the CodeRewriter to rewrite known array length upfront. Generally this can
+ * pattern match fori and for loops with any initial value and increment, but this should be
+ * extended for while loop support.
+ */
+public class NaturalIntLoopRemover {
+
+  public void run(IRCode code) {
+    boolean loopRemoved = false;
+    for (BasicBlock comparisonBlockCandidate : code.blocks) {
+      if (isComparisonBlock(comparisonBlockCandidate)) {
+        loopRemoved |= tryRemoveLoop(code, comparisonBlockCandidate.exit().asIf());
+      }
+    }
+    if (loopRemoved) {
+      code.removeAllDeadAndTrivialPhis();
+      assert code.isConsistentSSA();
+    }
+  }
+
+  private boolean isComparisonBlock(BasicBlock comparisonBlockCandidate) {
+    if (!comparisonBlockCandidate.exit().isIf()
+        || comparisonBlockCandidate.exit().asIf().isZeroTest()) {
+      return false;
+    }
+    for (Instruction instruction : comparisonBlockCandidate.getInstructions()) {
+      if (instruction.isIf()) {
+        return true;
+      }
+      if (!(instruction.isConstNumber())) {
+        return false;
+      }
+    }
+    throw new Unreachable();
+  }
+
+  private boolean tryRemoveLoop(IRCode code, If comparison) {
+    Phi loopPhi = computeLoopPhi(comparison);
+    if (loopPhi == null) {
+      return false;
+    }
+
+    NaturalIntLoopWithKnowIterations.Builder builder =
+        NaturalIntLoopWithKnowIterations.builder(comparison);
+
+    if (!analyzeLoopIterator(comparison, loopPhi, builder)) {
+      return false;
+    }
+
+    Set<BasicBlock> loopBody = computeLoopBody(builder.getBackPredecessor(), comparison.getBlock());
+    if (loopBody == null) {
+      return false;
+    }
+    if (loopBody.contains(builder.getLoopEntry())) {
+      assert false;
+      return false;
+    }
+    builder.setLoopBody(loopBody);
+
+    if (!analyzeLoopExit(loopBody, comparison, builder)) {
+      return false;
+    }
+
+    NaturalIntLoopWithKnowIterations loop = builder.build();
+
+    if (loop.has1Iteration()) {
+      loop.remove1IterationLoop(code);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Verifies the loop is well formed: the comparison on the int iterator should jump to a loop exit
+   * on one side and to the loop body on the other side.
+   */
+  private boolean analyzeLoopExit(
+      Set<BasicBlock> loopBody, If comparison, NaturalIntLoopWithKnowIterations.Builder builder) {
+    if (loopBody.contains(comparison.getTrueTarget())) {
+      if (loopBody.contains(comparison.fallthroughBlock())) {
+        return false;
+      }
+      builder.setLoop(comparison.fallthroughBlock(), comparison.getTrueTarget());
+    } else {
+      if (!loopBody.contains(comparison.fallthroughBlock())) {
+        return false;
+      }
+      builder.setLoop(comparison.getTrueTarget(), comparison.fallthroughBlock());
+    }
+    return true;
+  }
+
+  /**
+   * Analyze the int iterator so that it is initialized with a constant int value, and each
+   * iteration of the loop increment the iterator by one of the following: i + cst, cst + i or i -
+   * cst.
+   */
+  private boolean analyzeLoopIterator(
+      If comparison, Phi loopPhi, NaturalIntLoopWithKnowIterations.Builder builder) {
+    for (int i = 0; i < loopPhi.getOperands().size(); i++) {
+      Value operand = loopPhi.getOperand(i);
+      if (operand.isPhi()) {
+        return false;
+      }
+      BasicBlock predecessor = comparison.getBlock().getPredecessors().get(i);
+      if (operand.isConstNumber()) {
+        // Initial value of the int iterator.
+        if (!operand.getType().isInt() || builder.getLoopEntry() != null) {
+          return false;
+        }
+        builder.setLoopEntry(predecessor);
+        builder.setInitCounter(operand.definition.asConstNumber().getIntValue());
+      } else if (operand.definition.isAdd()) {
+        // Increment of the int iterator of type i + cst or cst + i.
+        if (builder.getBackPredecessor() != null) {
+          return false;
+        }
+        builder.setBackPredecessor(predecessor);
+        boolean metPhiOperand = false;
+        for (Value inValue : operand.definition.inValues()) {
+          if (inValue.isConstNumber() && inValue.getType().isInt()) {
+            int counterIncrement = inValue.definition.asConstNumber().getIntValue();
+            if (counterIncrement == 0 || builder.getCounterIncrement() != 0) {
+              return false;
+            }
+            builder.setCounterIncrement(counterIncrement);
+          } else if (inValue == loopPhi) {
+            if (metPhiOperand) {
+              return false;
+            }
+            metPhiOperand = true;
+          } else {
+            return false;
+          }
+        }
+      } else if (operand.definition.isSub()) {
+        // Increment of the int iterator of type i - cst.
+        if (builder.getBackPredecessor() != null) {
+          return false;
+        }
+        builder.setBackPredecessor(predecessor);
+        Sub sub = operand.definition.asSub();
+        if (sub.leftValue() != loopPhi) {
+          return false;
+        }
+        Value subValue = sub.rightValue();
+        if (subValue.isConstNumber() && subValue.getType().isInt()) {
+          assert builder.getCounterIncrement() == 0;
+          int counterIncrement = -subValue.definition.asConstNumber().getIntValue();
+          if (counterIncrement == 0) {
+            return false;
+          }
+          builder.setCounterIncrement(counterIncrement);
+        } else {
+          return false;
+        }
+      } else {
+        return false;
+      }
+    }
+    assert builder.getLoopEntry() != null;
+    assert builder.getLoopEntry().exit().isGoto();
+    assert builder.getBackPredecessor() != null;
+    assert builder.getBackPredecessor().exit().isGoto();
+    assert builder.getCounterIncrement() != 0;
+    return true;
+  }
+
+  /**
+   * Analyze the loop comparison so that it compares a loopPhi with a constant, else answers null.
+   */
+  private Phi computeLoopPhi(If comparison) {
+    Phi loopPhi = null;
+    if (comparison.rhs().isConstant() && comparison.lhs().isPhi()) {
+      loopPhi = comparison.lhs().asPhi();
+    } else if (comparison.lhs().isConstant() && comparison.rhs().isPhi()) {
+      loopPhi = comparison.rhs().asPhi();
+    }
+    if (loopPhi == null) {
+      return null;
+    }
+    if (loopPhi.getOperands().size() != 2) {
+      return null;
+    }
+    if (loopPhi.getBlock() != comparison.getBlock()) {
+      return null;
+    }
+    return loopPhi;
+  }
+
+  /**
+   * Natural int loop structure and terminology. <code>
+   *         v
+   *     Loop Entry
+   *     int i = 0;    v < < < < < < < < < < < <
+   *         v         v                       ^
+   *       Comparison Block                    ^
+   *       if (i < constant)                   ^
+   *       v               v                   ^
+   *   Loop Exit         Loop Body Entry       ^
+   *       v             i++;                  ^
+   *   Method Exit         v                   ^
+   *       v               > > > > > > > > > > ^
+   * </code>
+   */
+  static class NaturalIntLoopWithKnowIterations {
+
+    private final int initCounter;
+    private final int counterIncrement;
+    private final If comparison;
+    private final BasicBlock loopExit;
+    private final BasicBlock loopBodyEntry;
+    private final BasicBlock backPredecessor;
+    private final Set<BasicBlock> loopBody;
+
+    NaturalIntLoopWithKnowIterations(
+        int initCounter,
+        int counterIncrement,
+        If comparison,
+        BasicBlock loopExit,
+        BasicBlock loopBodyEntry,
+        BasicBlock backPredecessor,
+        Set<BasicBlock> loopBody) {
+      this.initCounter = initCounter;
+      this.counterIncrement = counterIncrement;
+      this.comparison = comparison;
+      this.loopExit = loopExit;
+      this.loopBodyEntry = loopBodyEntry;
+      this.backPredecessor = backPredecessor;
+      this.loopBody = loopBody;
+    }
+
+    static class Builder {
+
+      private int initCounter;
+      private int counterIncrement;
+      private final If comparison;
+      private BasicBlock loopExit;
+      private BasicBlock loopBodyEntry;
+      private BasicBlock loopEntry;
+      private BasicBlock backPredecessor;
+      private Set<BasicBlock> loopBody;
+
+      Builder(If comparison) {
+        this.comparison = comparison;
+      }
+
+      public void setInitCounter(int initCounter) {
+        this.initCounter = initCounter;
+      }
+
+      public int getCounterIncrement() {
+        return counterIncrement;
+      }
+
+      public void setCounterIncrement(int counterIncrement) {
+        this.counterIncrement = counterIncrement;
+      }
+
+      public BasicBlock getLoopEntry() {
+        return loopEntry;
+      }
+
+      public void setLoopEntry(BasicBlock loopEntry) {
+        this.loopEntry = loopEntry;
+      }
+
+      public BasicBlock getBackPredecessor() {
+        return backPredecessor;
+      }
+
+      public void setBackPredecessor(BasicBlock backPredecessor) {
+        this.backPredecessor = backPredecessor;
+      }
+
+      public void setLoop(BasicBlock loopExit, BasicBlock loopBodyEntry) {
+        this.loopExit = loopExit;
+        this.loopBodyEntry = loopBodyEntry;
+      }
+
+      public void setLoopBody(Set<BasicBlock> loopBody) {
+        this.loopBody = loopBody;
+      }
+
+      public NaturalIntLoopWithKnowIterations build() {
+        return new NaturalIntLoopWithKnowIterations(
+            initCounter,
+            counterIncrement,
+            comparison,
+            loopExit,
+            loopBodyEntry,
+            backPredecessor,
+            loopBody);
+      }
+    }
+
+    static Builder builder(If comparison) {
+      return new Builder(comparison);
+    }
+
+    private BasicBlock target(int phiValue) {
+      if (comparison.rhs().isConstNumber()) {
+        int comp = comparison.rhs().getDefinition().asConstNumber().getIntValue();
+        return comparison.targetFromCondition(Integer.signum(phiValue - comp));
+      }
+      int comp = comparison.lhs().getDefinition().asConstNumber().getIntValue();
+      return comparison.targetFromCondition(Integer.signum(comp - phiValue));
+    }
+
+    public boolean has1Iteration() {
+      return target(initCounter) == loopBodyEntry
+          && target(initCounter + counterIncrement) == loopExit;
+    }
+
+    private void remove1IterationLoop(IRCode code) {
+      BasicBlock comparisonBlock = comparison.getBlock();
+      updatePhis(comparisonBlock);
+      patchControlFlow(code, comparisonBlock);
+    }
+
+    private void patchControlFlow(IRCode code, BasicBlock comparisonBlock) {
+      assert loopExit.getPhis().isEmpty(); // Edges should be split.
+      comparisonBlock.replaceLastInstruction(new Goto(loopBodyEntry), code);
+      comparisonBlock.removeSuccessor(loopExit);
+
+      backPredecessor.replaceSuccessor(comparisonBlock, loopExit);
+      backPredecessor.replaceLastInstruction(new Goto(loopExit), code);
+      comparisonBlock.removePredecessor(backPredecessor, Sets.newIdentityHashSet());
+      loopExit.replacePredecessor(comparisonBlock, backPredecessor);
+    }
+
+    private void updatePhis(BasicBlock comparisonBlock) {
+      int backIndex = comparisonBlock.getPredecessors().indexOf(backPredecessor);
+      for (Phi phi : comparisonBlock.getPhis()) {
+        Value loopEntryValue = phi.getOperand(1 - backIndex);
+        Value loopExitValue = phi.getOperand(backIndex);
+        for (Instruction uniqueUser : phi.uniqueUsers()) {
+          if (loopBody.contains(uniqueUser.getBlock())) {
+            uniqueUser.replaceValue(phi, loopEntryValue);
+          } else {
+            uniqueUser.replaceValue(phi, loopExitValue);
+          }
+        }
+        for (Phi phiUser : phi.uniquePhiUsers()) {
+          if (loopBody.contains(phiUser.getBlock())) {
+            phiUser.replaceOperand(phi, loopEntryValue);
+          } else {
+            phiUser.replaceOperand(phi, loopExitValue);
+          }
+        }
+      }
+    }
+  }
+
+  private Set<BasicBlock> computeLoopBody(BasicBlock backPredecessor, BasicBlock comparisonBlock) {
+    WorkList<BasicBlock> workList = WorkList.newIdentityWorkList();
+    workList.addIfNotSeen(backPredecessor);
+    workList.markAsSeen(comparisonBlock);
+    while (!workList.isEmpty()) {
+      BasicBlock basicBlock = workList.next();
+      if (basicBlock.isEntry()) {
+        // This can happen in loops with multiple entries (Duff device, etc.).
+        // Such loops are not generated by javac so we assume they are uncommon.
+        return null;
+      }
+      for (BasicBlock predecessor : basicBlock.getPredecessors()) {
+        workList.addIfNotSeen(predecessor);
+      }
+    }
+    return workList.getSeenSet();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index e515c08..d3f9395 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
 import com.android.tools.r8.ir.analysis.value.SingleValue;
 import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.collections.BidirectionalOneToManyRepresentativeHashMap;
@@ -27,7 +28,9 @@
 import com.android.tools.r8.utils.collections.MutableBidirectionalOneToManyRepresentativeMap;
 import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
 import com.google.common.collect.ImmutableMap;
+import java.util.Collections;
 import java.util.IdentityHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -148,7 +151,7 @@
     }
 
     public void move(DexMethod from, DexMethod to, boolean fromStatic, boolean toStatic) {
-      move(from, to, fromStatic, toStatic, 0);
+      move(from, to, fromStatic, toStatic, Collections.emptyList());
     }
 
     public RewrittenPrototypeDescription move(
@@ -156,7 +159,7 @@
         DexMethod to,
         boolean fromStatic,
         boolean toStatic,
-        int numberOfExtraNullParameters) {
+        List<ExtraUnusedNullParameter> extraUnusedNullParameters) {
       assert from != to;
       newMethodSignatures.put(from, to);
       int offsetDiff = 0;
@@ -167,12 +170,14 @@
       if (fromStatic != toStatic) {
         assert toStatic;
         offsetDiff = 1;
-        builder.addArgumentInfo(
-            0,
-            RewrittenTypeInfo.builder()
-                .setOldType(from.getHolderType())
-                .setNewType(to.getParameter(0))
-                .build());
+        builder
+            .addArgumentInfo(
+                0,
+                RewrittenTypeInfo.builder()
+                    .setOldType(from.getHolderType())
+                    .setNewType(to.getParameter(0))
+                    .build())
+            .setIsConvertedToStaticMethod();
       }
       for (int i = 0; i < from.getParameters().size(); i++) {
         DexType fromType = from.getParameter(i);
@@ -192,7 +197,7 @@
                   .build();
       RewrittenPrototypeDescription prototypeChanges =
           RewrittenPrototypeDescription.createForRewrittenTypes(returnInfo, builder.build())
-              .withExtraUnusedNullParameters(numberOfExtraNullParameters);
+              .withExtraParameters(extraUnusedNullParameters);
       prototypeChangesPerMethod.put(to, prototypeChanges);
       return prototypeChanges;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index c054329..3c8bdbc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -37,6 +37,7 @@
 import com.android.tools.r8.ir.code.NewUnboxedEnumInstance;
 import com.android.tools.r8.ir.code.StaticPut;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap.EnumData;
@@ -575,11 +576,12 @@
         method.getName().toString() + (method.isNonPrivateVirtualMethod() ? "$enumunboxing$" : "");
     DexMethod newMethod = factory.createMethod(method.getHolderType(), newProto, newMethodName);
     newMethod = ensureUniqueMethod(method, newMethod);
-    int numberOfExtraNullParameters = newMethod.getArity() - method.getReference().getArity();
+    List<ExtraUnusedNullParameter> extraUnusedNullParameters =
+        ExtraUnusedNullParameter.computeExtraUnusedNullParameters(method.getReference(), newMethod);
     boolean isStatic = method.isStatic();
     RewrittenPrototypeDescription prototypeChanges =
         lensBuilder.move(
-            method.getReference(), newMethod, isStatic, isStatic, numberOfExtraNullParameters);
+            method.getReference(), newMethod, isStatic, isStatic, extraUnusedNullParameters);
     return method.toTypeSubstitutedMethod(
         newMethod,
         builder ->
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index bdda2fe..98057dd 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.ClassFileConsumer;
 import com.android.tools.r8.SourceFileEnvironment;
 import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.debuginfo.DebugRepresentation;
 import com.android.tools.r8.dex.ApplicationWriter;
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.errors.CodeSizeOverflowDiagnostic;
@@ -137,7 +138,12 @@
     if (options.proguardMapConsumer != null) {
       proguardMapId =
           runAndWriteMap(
-              inputApp, appView, namingLens, application.timing, OriginalSourceFiles.fromClasses());
+              inputApp,
+              appView,
+              namingLens,
+              application.timing,
+              OriginalSourceFiles.fromClasses(),
+              DebugRepresentation.none(options));
       marker.setPgMapId(proguardMapId.getId());
     }
     Optional<String> markerString =
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
index 433349e..f24cac2 100644
--- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -80,6 +80,9 @@
 
   public void run(ExecutorService executorService) throws ExecutionException {
     // Collect all visibility bridges to remove.
+    if (!appView.options().enableVisibilityBridgeRemoval) {
+      return;
+    }
     ConcurrentHashMap<DexProgramClass, Set<DexEncodedMethod>> visibilityBridgesToRemove =
         new ConcurrentHashMap<>();
     processItems(
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
index 958c85b..fc48b5d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
@@ -49,9 +49,6 @@
     return prototypeChanges.getOrDefault(method, RewrittenPrototypeDescription.none());
   }
 
-  public boolean isAffected(DexMethod method) {
-    return method != getPreviousMethodSignature(method) || hasPrototypeChanges(method);
-  }
 
   @Override
   protected boolean isLegitimateToHaveEmptyMappings() {
@@ -104,13 +101,12 @@
 
   @Override
   protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
-    if (!type.isStatic() && hasPrototypeChanges(newMethod)) {
-      RewrittenPrototypeDescription prototypeChanges = getPrototypeChanges(newMethod);
-      if (prototypeChanges.getArgumentInfoCollection().isArgumentRemoved(0)) {
-        return Type.STATIC;
-      }
-    }
-    return super.mapInvocationType(newMethod, originalMethod, type);
+    return hasPrototypeChanges(newMethod)
+            && getPrototypeChanges(newMethod)
+                .getArgumentInfoCollection()
+                .isConvertedToStaticMethod()
+        ? Type.STATIC
+        : super.mapInvocationType(newMethod, originalMethod, type);
   }
 
   public static class Builder {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index 24d6e46..cca53b7 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -1021,8 +1021,10 @@
           && method.getOptimizationInfo().getUnusedArguments().get(0)
           && ParameterRemovalUtils.canRemoveUnusedParametersFrom(appView, method)
           && ParameterRemovalUtils.canRemoveUnusedParameter(appView, method, 0)) {
-        parameterChangesBuilder.addArgumentInfo(
-            0, RemovedReceiverInfo.Builder.create().setType(method.getHolderType()).build());
+        parameterChangesBuilder
+            .addArgumentInfo(
+                0, RemovedReceiverInfo.Builder.create().setType(method.getHolderType()).build())
+            .setIsConvertedToStaticMethod();
       }
 
       CallSiteOptimizationInfo optimizationInfo = method.getOptimizationInfo().getArgumentInfos();
diff --git a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
index b1237ed..0213d86 100644
--- a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
@@ -15,9 +15,11 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepMethodInfo;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.MapUtils;
@@ -52,9 +54,7 @@
   }
 
   public void run(ExecutorService executorService, Timing timing) throws ExecutionException {
-    if (options.testing.enableExperimentalProtoNormalization) {
-      timing.time("Proto normalization", () -> run(executorService));
-    }
+    timing.time("Proto normalization", () -> run(executorService));
   }
 
   private void run(ExecutorService executorService) throws ExecutionException {
@@ -280,9 +280,24 @@
   }
 
   private boolean isUnoptimizable(ProgramMethod method) {
-    // TODO(b/195112263): This is incomplete.
-    return appView.getKeepInfo(method).isPinned(options)
-        || method.getDefinition().isLibraryMethodOverride().isPossiblyTrue();
+    KeepMethodInfo keepInfo = appView.getKeepInfo(method);
+    if (!keepInfo.isParameterReorderingAllowed(options)
+        || method.getDefinition().isLibraryMethodOverride().isPossiblyTrue()) {
+      return true;
+    }
+    AppInfoWithLiveness appInfo = appView.appInfo();
+    if (appInfo.isBootstrapMethod(method)) {
+      return true;
+    }
+    ObjectAllocationInfoCollection objectAllocationInfoCollection =
+        appInfo.getObjectAllocationInfoCollection();
+    if (method.getHolder().isInterface()
+        && method.getDefinition().isAbstract()
+        && objectAllocationInfoCollection.isImmediateInterfaceOfInstantiatedLambda(
+            method.getHolder())) {
+      return true;
+    }
+    return false;
   }
 
   static class GlobalReservationState {
@@ -377,6 +392,7 @@
         do {
           DexString newMethodName = dexItemFactory.createString(newMethodBaseName + "$" + index);
           newMethodSignature = newMethodSignature.withName(newMethodName);
+          index++;
         } while (newMethodSignatures.containsValue(newMethodSignature));
       }
       if (reserve) {
diff --git a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
index bfeef49..1ce0703 100644
--- a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
@@ -56,8 +56,11 @@
   }
 
   @Override
-  public DexField getRenamedFieldSignature(DexField originalField, GraphLens codeLens) {
-    return originalField;
+  public DexField getRenamedFieldSignature(DexField originalField, GraphLens applied) {
+    if (this == applied) {
+      return originalField;
+    }
+    return getPrevious().getRenamedFieldSignature(originalField);
   }
 
   @Override
@@ -65,7 +68,7 @@
     if (this == applied) {
       return originalMethod;
     }
-    return newMethodSignatures.getOrDefault(originalMethod, originalMethod);
+    return getNextMethodSignature(getPrevious().getRenamedMethodSignature(originalMethod));
   }
 
   @Override
@@ -98,14 +101,18 @@
   protected MethodLookupResult internalDescribeLookupMethod(
       MethodLookupResult previous, DexMethod context) {
     DexMethod methodSignature = previous.getReference();
-    DexMethod newMethodSignature = getRenamedMethodSignature(methodSignature);
+    DexMethod newMethodSignature = getNextMethodSignature(methodSignature);
     if (methodSignature == newMethodSignature) {
       return previous;
     }
     assert !previous.hasReboundReference();
     return MethodLookupResult.builder(this)
         .setPrototypeChanges(
-            previous.getPrototypeChanges().combine(prototypeChanges.get(newMethodSignature)))
+            previous
+                .getPrototypeChanges()
+                .combine(
+                    prototypeChanges.getOrDefault(
+                        newMethodSignature, RewrittenPrototypeDescription.none())))
         .setReference(newMethodSignature)
         .setType(previous.getType())
         .build();
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 91f926c..14a84d5 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.shaking;
 
 import static com.android.tools.r8.dex.Constants.TEMPORARY_INSTANCE_INITIALIZER_PREFIX;
+import static com.android.tools.r8.graph.DexClassAndMethod.asProgramMethodOrNull;
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 import static com.android.tools.r8.ir.code.Invoke.Type.DIRECT;
 import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
@@ -720,7 +721,7 @@
         new VerticalClassMergerTreeFixer(
                 appView, lensBuilder, verticallyMergedClasses, synthesizedBridges)
             .fixupTypeReferences();
-    KeepInfoCollection keepInfo = appView.appInfo().getKeepInfo();
+    KeepInfoCollection keepInfo = appView.getKeepInfo();
     keepInfo.mutate(
         mutator ->
             mutator.removeKeepInfoForMergedClasses(
@@ -729,6 +730,26 @@
 
     assert lens != null;
     assert verifyGraphLens(lens);
+
+    // Rewrite collections using the lens.
+    appView.rewriteWithLens(lens);
+
+    // Copy keep info to newly synthesized methods.
+    keepInfo.mutate(
+        mutator -> {
+          for (SynthesizedBridgeCode synthesizedBridge : synthesizedBridges) {
+            ProgramMethod bridge =
+                asProgramMethodOrNull(appView.definitionFor(synthesizedBridge.method));
+            ProgramMethod target =
+                asProgramMethodOrNull(appView.definitionFor(synthesizedBridge.invocationTarget));
+            if (bridge != null && target != null) {
+              mutator.joinMethod(bridge, info -> info.merge(appView.getKeepInfo(target).joiner()));
+              continue;
+            }
+            assert false;
+          }
+        });
+
     return lens;
   }
 
@@ -1137,7 +1158,10 @@
         }
 
         deferredRenamings.map(virtualMethod.getReference(), shadowedBy.getReference());
-        deferredRenamings.recordMove(virtualMethod.getReference(), resultingMethod.getReference());
+        deferredRenamings.recordMove(
+            virtualMethod.getReference(),
+            resultingMethod.getReference(),
+            resultingMethod.isStatic());
       }
 
       if (abortMerge) {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
index 166ed69..05837f0 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.NestedGraphLens;
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.utils.IterableUtils;
@@ -21,6 +22,7 @@
 import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
 import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneRepresentativeMap;
 import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import java.util.Collection;
@@ -66,6 +68,7 @@
       contextualVirtualToDirectMethodMaps;
   private Set<DexMethod> mergedMethods;
   private final Map<DexMethod, DexMethod> originalMethodSignaturesForBridges;
+  private final Map<DexMethod, RewrittenPrototypeDescription> prototypeChanges;
 
   private VerticalClassMergerGraphLens(
       AppView<?> appView,
@@ -76,13 +79,15 @@
       Map<DexType, Map<DexMethod, GraphLensLookupResultProvider>>
           contextualVirtualToDirectMethodMaps,
       BidirectionalManyToOneRepresentativeMap<DexMethod, DexMethod> newMethodSignatures,
-      Map<DexMethod, DexMethod> originalMethodSignaturesForBridges) {
+      Map<DexMethod, DexMethod> originalMethodSignaturesForBridges,
+      Map<DexMethod, RewrittenPrototypeDescription> prototypeChanges) {
     super(appView, fieldMap, methodMap, mergedClasses.getForwardMap(), newMethodSignatures);
     this.appView = appView;
     this.mergedClasses = mergedClasses;
     this.contextualVirtualToDirectMethodMaps = contextualVirtualToDirectMethodMaps;
     this.mergedMethods = mergedMethods;
     this.originalMethodSignaturesForBridges = originalMethodSignaturesForBridges;
+    this.prototypeChanges = prototypeChanges;
   }
 
   @Override
@@ -135,6 +140,13 @@
   }
 
   @Override
+  protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
+      RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
+    return prototypeChanges.combine(
+        this.prototypeChanges.getOrDefault(method, RewrittenPrototypeDescription.none()));
+  }
+
+  @Override
   public DexMethod getPreviousMethodSignature(DexMethod method) {
     return super.getPreviousMethodSignature(
         originalMethodSignaturesForBridges.getOrDefault(method, method));
@@ -174,6 +186,8 @@
         newMethodSignatures = BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap();
     private final Map<DexMethod, DexMethod> originalMethodSignaturesForBridges =
         new IdentityHashMap<>();
+    private final Map<DexMethod, RewrittenPrototypeDescription> prototypeChanges =
+        new IdentityHashMap<>();
 
     private final Map<DexProto, DexProto> cache = new IdentityHashMap<>();
 
@@ -231,6 +245,11 @@
             entry.getValue(),
             builder.getMethodSignatureAfterClassMerging(entry.getKey(), mergedClasses));
       }
+      builder.prototypeChanges.forEach(
+          (method, prototypeChangesForMethod) ->
+              newBuilder.prototypeChanges.put(
+                  builder.getMethodSignatureAfterClassMerging(method, mergedClasses),
+                  prototypeChangesForMethod));
       return newBuilder;
     }
 
@@ -248,7 +267,8 @@
           mergedMethodsBuilder.build(),
           contextualVirtualToDirectMethodMaps,
           newMethodSignatures,
-          originalMethodSignaturesForBridges);
+          originalMethodSignaturesForBridges,
+          prototypeChanges);
     }
 
     private DexField getFieldSignatureAfterClassMerging(
@@ -335,7 +355,22 @@
     }
 
     public void recordMove(DexMethod from, DexMethod to) {
+      recordMove(from, to, false);
+    }
+
+    public void recordMove(DexMethod from, DexMethod to, boolean isStaticized) {
       newMethodSignatures.put(from, to);
+      if (isStaticized) {
+        RewrittenPrototypeDescription prototypeChangesForMethod =
+            RewrittenPrototypeDescription.create(
+                ImmutableList.of(),
+                null,
+                ArgumentInfoCollection.builder()
+                    .setArgumentInfosSize(to.getParameters().size())
+                    .setIsConvertedToStaticMethod()
+                    .build());
+        prototypeChanges.put(to, prototypeChangesForMethod);
+      }
     }
 
     public void recordCreationOfBridgeMethod(DexMethod from, DexMethod to) {
@@ -385,6 +420,7 @@
               }
             }
           });
+      prototypeChanges.putAll(builder.prototypeChanges);
       originalMethodSignaturesForBridges.putAll(builder.originalMethodSignaturesForBridges);
       for (DexType context : builder.contextualVirtualToDirectMethodMaps.keySet()) {
         Map<DexMethod, GraphLensLookupResultProvider> current =
diff --git a/src/main/java/com/android/tools/r8/utils/ClassMap.java b/src/main/java/com/android/tools/r8/utils/ClassMap.java
index 506a990..915a2f7 100644
--- a/src/main/java/com/android/tools/r8/utils/ClassMap.java
+++ b/src/main/java/com/android/tools/r8/utils/ClassMap.java
@@ -163,6 +163,15 @@
     return classes.keySet();
   }
 
+  public Iterable<DexType> getAllClassProviderTypes() {
+    ClassProvider<T> theClassProvider = classProvider.get();
+    if (theClassProvider != null) {
+      return theClassProvider.collectTypes();
+    }
+    throw new CompilationError(
+        "Cannot access all types since the classProvider is no longer available");
+  }
+
   /**
    * Forces loading of all the classes satisfying the criteria specified.
    * <p>
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 3fec28a..2b8d72a 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -298,6 +298,7 @@
   public boolean readDebugSetFileEvent = false;
   public boolean disableL8AnnotationRemoval =
       System.getProperty("com.android.tools.r8.disableL8AnnotationRemoval") != null;
+  public boolean enableVisibilityBridgeRemoval = true;
 
   public int callGraphLikelySpuriousCallEdgeThreshold = 50;
 
@@ -895,7 +896,13 @@
       return;
     }
     try {
-      machineDesugaredLibrarySpecification = specification.toMachineSpecification(this, app);
+      // TODO(b/221224178): Move the timing to top level timing in D8, R8 and L8.
+      Timing timing = Timing.create("Desugared library specification conversion", this);
+      machineDesugaredLibrarySpecification =
+          specification.toMachineSpecification(this, app, timing);
+      if (printTimes) {
+        timing.report();
+      }
     } catch (IOException e) {
       reporter.error(new ExceptionDiagnostic(e, Origin.unknown()));
     }
@@ -907,8 +914,13 @@
     if (specification.isEmpty()) {
       return;
     }
+    // TODO(b/221224178): Move the timing to top level timing in D8, R8 and L8.
+    Timing timing = Timing.create("Desugared library specification conversion", this);
     machineDesugaredLibrarySpecification =
-        specification.toMachineSpecification(this, library, desugaredJDKLib);
+        specification.toMachineSpecification(this, library, timing, desugaredJDKLib);
+    if (printTimes) {
+      timing.report();
+    }
   }
 
   // Contains flags describing library desugaring.
@@ -1658,11 +1670,11 @@
     public boolean enableEnumUnboxingDebugLogs = false;
     public boolean forceRedundantConstNumberRemoval = false;
     public boolean enableExperimentalDesugaredLibraryKeepRuleGenerator = false;
-    public boolean enableExperimentalProtoNormalization = false;
     public boolean invertConditionals = false;
     public boolean placeExceptionalBlocksLast = false;
     public boolean dontCreateMarkerInD8 = false;
     public boolean forceJumboStringProcessing = false;
+    public boolean forcePcBasedEncoding = false;
     public Set<Inliner.Reason> validInliningReasons = null;
     public boolean noLocalsTableOnInput = false;
     public boolean forceNameReflectionOptimization = false;
@@ -1884,7 +1896,7 @@
   }
 
   public boolean canUseDexPc2PcAsDebugInformation() {
-    return lineNumberOptimization == LineNumberOptimization.ON;
+    return isGeneratingDex() && lineNumberOptimization == LineNumberOptimization.ON;
   }
 
   public boolean canUseNativeDexPcInsteadOfDebugInfo() {
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 50eeb68..22932f6 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfPosition;
+import com.android.tools.r8.debuginfo.DebugRepresentation.DebugRepresentationPredicate;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -334,14 +335,21 @@
       AppView<?> appView,
       NamingLens namingLens,
       Timing timing,
-      OriginalSourceFiles originalSourceFiles) {
+      OriginalSourceFiles originalSourceFiles,
+      DebugRepresentationPredicate representation) {
     assert appView.options().proguardMapConsumer != null;
     // When line number optimization is turned off the identity mapping for line numbers is
     // used. We still run the line number optimizer to collect line numbers and inline frame
     // information for the mapping file.
     timing.begin("Line number remapping");
     ClassNameMapper mapper =
-        run(appView, appView.appInfo().app(), inputApp, namingLens, originalSourceFiles);
+        run(
+            appView,
+            appView.appInfo().app(),
+            inputApp,
+            namingLens,
+            originalSourceFiles,
+            representation);
     timing.end();
     timing.begin("Write proguard map");
     ProguardMapId mapId = ProguardMapSupplier.create(mapper, appView.options()).writeProguardMap();
@@ -465,9 +473,12 @@
       DexApplication application,
       AndroidApp inputApp,
       NamingLens namingLens,
-      OriginalSourceFiles originalSourceFiles) {
+      OriginalSourceFiles originalSourceFiles,
+      DebugRepresentationPredicate representation) {
     // For finding methods in kotlin files based on SourceDebugExtensions, we use a line method map.
     // We create it here to ensure it is only reading class files once.
+    // TODO(b/220999985): Make this threaded per virtual file. Possibly pull the kotlin line mapping
+    //  onto main thread before threading.
     CfLineToMethodMapper cfLineToMethodMapper = new CfLineToMethodMapper(inputApp);
     ClassNameMapper.Builder classNameMapperBuilder = ClassNameMapper.builder();
 
@@ -560,7 +571,7 @@
           List<MappedPosition> mappedPositions;
           Code code = method.getCode();
           boolean canUseDexPc =
-              appView.options().canUseDexPc2PcAsDebugInformation() && methods.size() == 1;
+              methods.size() == 1 && representation.useDexPcEncoding(clazz, method);
           if (code != null) {
             if (code.isDexCode() && doesContainPositions(code.asDexCode())) {
               if (canUseDexPc) {
@@ -910,7 +921,7 @@
         });
   }
 
-  private static IdentityHashMap<DexString, List<DexEncodedMethod>> groupMethodsByRenamedName(
+  public static IdentityHashMap<DexString, List<DexEncodedMethod>> groupMethodsByRenamedName(
       GraphLens graphLens, NamingLens namingLens, DexProgramClass clazz) {
     IdentityHashMap<DexString, List<DexEncodedMethod>> methodsByRenamedName =
         new IdentityHashMap<>(clazz.getMethodCollection().size());
@@ -943,7 +954,7 @@
     return false;
   }
 
-  private static boolean doesContainPositions(DexCode dexCode) {
+  public static boolean doesContainPositions(DexCode dexCode) {
     DexDebugInfo debugInfo = dexCode.getDebugInfo();
     if (debugInfo == null) {
       return false;
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelperImpl.java b/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelperImpl.java
index bff70f5..387788a 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelperImpl.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelperImpl.java
@@ -196,7 +196,10 @@
         CheckCast.builder()
             .setCastType(castType)
             .setObject(operand)
-            .setFreshOutValue(code, castType.toTypeElement(appView), operand.getLocalInfo())
+            .setFreshOutValue(
+                code,
+                castType.toTypeElement(appView, operand.getType().nullability()),
+                operand.getLocalInfo())
             .setPosition(rewrittenUser)
             .build();
     if (block.hasCatchHandlers()) {
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 5b735b1..352e12b 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -2022,8 +2022,9 @@
           System.out.println("Running on 10.0.0 is disabled, see b/144966342");
           continue;
         }
-        if (vm.getVersion() == DexVm.Version.V12_0_0) {
-          System.out.println("Running on 12.0.0 is disabled, see b/197078995");
+        if (vm.getVersion() == DexVm.Version.V12_0_0
+            || vm.getVersion() == DexVm.Version.V13_MASTER) {
+          System.out.println("Running on 12.0.0 or V13_MASTER is disabled, see b/197078995");
           continue;
         }
         vms.add(new DexRuntime(vm));
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index f695dbb..ec8d314 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -89,6 +89,10 @@
               Version.V12_0_0,
               // TODO(120402963) Triage.
               ImmutableList.of("invokecustom-with-shrinking", "invokecustom2-with-shrinking"))
+          .put(
+              Version.V13_MASTER,
+              // TODO(120402963) Triage.
+              ImmutableList.of("invokecustom-with-shrinking", "invokecustom2-with-shrinking"))
           .put(Version.DEFAULT, ImmutableList.of())
           .build();
 
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index fb17345..dc4d111 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -314,6 +314,10 @@
         return compareTo(other) == 0;
       }
 
+      public boolean isEqualToOneOf(Version... versions) {
+        return Arrays.stream(versions).anyMatch(this::isEqualTo);
+      }
+
       public boolean isNewerThan(Version other) {
         return compareTo(other) > 0;
       }
@@ -1288,7 +1292,7 @@
     R8.runForTesting(command.getInputApp(), internalOptions);
     if (benchmarkResults != null) {
       long end = System.nanoTime();
-      benchmarkResults.addRuntimeRawResult(end - start);
+      benchmarkResults.addRuntimeResult(end - start);
     }
   }
 
@@ -1368,20 +1372,20 @@
       BenchmarkResults benchmarkResults)
       throws CompilationFailedException {
     AndroidAppConsumers compatSink = new AndroidAppConsumers(builder);
+    long start = 0;
+    if (benchmarkResults != null) {
+      start = System.nanoTime();
+    }
     D8Command command = builder.build();
     InternalOptions options = command.getInternalOptions();
     if (optionsConsumer != null) {
       ExceptionUtils.withD8CompilationHandler(
           options.reporter, () -> optionsConsumer.accept(options));
     }
-    long start = 0;
-    if (benchmarkResults != null) {
-      start = System.nanoTime();
-    }
     D8.runForTesting(command.getInputApp(), options);
     if (benchmarkResults != null) {
       long end = System.nanoTime();
-      benchmarkResults.addRuntimeRawResult(end - start);
+      benchmarkResults.addRuntimeResult(end - start);
     }
     return compatSink.build();
   }
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkBase.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkBase.java
index 3753d7f..c7a7e37 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkBase.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkBase.java
@@ -32,7 +32,7 @@
 
   @Test
   public void testBenchmarks() throws Exception {
-    config.run(temp);
+    config.run(new BenchmarkEnvironment(config, temp, false));
   }
 
   public static BenchmarkRunner runner(BenchmarkConfig config) {
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java
index da0c335..c741f27 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java
@@ -5,6 +5,7 @@
 
 import static java.util.Collections.emptyList;
 
+import com.android.tools.r8.benchmarks.desugaredlib.LegacyDesugaredLibraryBenchmark;
 import com.android.tools.r8.benchmarks.helloworld.HelloWorldBenchmark;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -43,6 +44,7 @@
     BenchmarkCollection collection = new BenchmarkCollection();
     // Every benchmark that should be active on golem must be setup in this method.
     HelloWorldBenchmark.configs().forEach(collection::addBenchmark);
+    LegacyDesugaredLibraryBenchmark.configs().forEach(collection::addBenchmark);
     return collection;
   }
 
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollectionPrinter.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollectionPrinter.java
index b7786f7..c1fbad4 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollectionPrinter.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollectionPrinter.java
@@ -110,7 +110,6 @@
       throws IOException {
     // Common properties that must be consistent among all the benchmark variants.
     String suite = BenchmarkConfig.getCommonSuite(benchmarkVariants).getDartName();
-    boolean hasWarmup = BenchmarkConfig.getCommonTimeWarmupRuns(benchmarkVariants);
     List<String> metrics =
         new ArrayList<>(
             ListUtils.map(
@@ -118,12 +117,12 @@
     metrics.sort(String::compareTo);
     printSemi("final name = " + quote(benchmarkName));
     printSemi("final metrics = " + StringUtils.join(", ", metrics, BraceType.SQUARE));
-    printSemi("final group = new GroupBenchmark(name + \"Group\", metrics)");
+    printSemi("final benchmark = new StandardBenchmark(name, metrics)");
     for (BenchmarkConfig benchmark : benchmarkVariants) {
       scopeBraces(
           () -> {
             printSemi("final target = " + quote(benchmark.getTarget().getGolemName()));
-            printSemi("final options = group.addTargets(noImplementation, [target])");
+            printSemi("final options = benchmark.addTargets(noImplementation, [target])");
             printSemi("options.cpus = cpus");
             printSemi("options.isScript = true");
             printSemi("options.fromRevision = " + benchmark.getFromRevision());
@@ -139,15 +138,16 @@
                                 + " --benchmark "
                                 + benchmark.getName())));
             printSemi("options.resources.add(openjdk)");
+            for (BenchmarkDependency dependency : benchmark.getDependencies()) {
+              scopeBraces(
+                  () -> {
+                    addGolemResource("dependency", dependency.getTarball());
+                    printSemi("options.resources.add(dependency)");
+                  });
+            }
           });
     }
-    printSemi("group.addBenchmark(name, metrics)");
     printSemi(suite + ".addBenchmark(name)");
-    if (hasWarmup) {
-      printSemi("final warmupName = name + \"Warmup\"");
-      printSemi("group.addBenchmark(warmupName, [Metric.RunTimeRaw])");
-      printSemi(suite + ".addBenchmark(warmupName)");
-    }
   }
 
   private void addGolemResource(String name, Path tarball) throws IOException {
@@ -173,7 +173,7 @@
   }
 
   private static Path getJdkHome() throws IOException {
-    ProcessBuilder builder = new ProcessBuilder("python", "tools/jdk.py");
+    ProcessBuilder builder = new ProcessBuilder("python3", "tools/jdk.py");
     ProcessResult result = ToolHelper.runProcess(builder, QUIET);
     if (result.exitCode != 0) {
       throw new BenchmarkConfigError("Unexpected failure to determine jdk home: " + result);
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java
index c64dbd2..6daaa7a 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java
@@ -5,10 +5,11 @@
 
 import com.android.tools.r8.errors.Unreachable;
 import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import org.junit.rules.TemporaryFolder;
 
 public class BenchmarkConfig {
 
@@ -38,10 +39,6 @@
     return getConsistentRepresentative(variants).getSuite();
   }
 
-  public static boolean getCommonTimeWarmupRuns(List<BenchmarkConfig> variants) {
-    return getConsistentRepresentative(variants).hasTimeWarmupRuns();
-  }
-
   private static BenchmarkConfig getConsistentRepresentative(List<BenchmarkConfig> variants) {
     if (variants.isEmpty()) {
       throw new BenchmarkConfigError("Unexpected attempt to check consistency of empty collection");
@@ -60,10 +57,9 @@
     private BenchmarkTarget target = null;
     private Set<BenchmarkMetric> metrics = new HashSet<>();
     private BenchmarkSuite suite = BenchmarkSuite.getDefault();
+    private Collection<BenchmarkDependency> dependencies = new ArrayList<>();
     private int fromRevision = -1;
 
-    private boolean timeWarmupRuns = false;
-
     private Builder() {}
 
     public BenchmarkConfig build() {
@@ -85,11 +81,14 @@
       if (fromRevision < 0) {
         throw new Unreachable("Benchmark must specify from which golem revision it is valid");
       }
-      if (timeWarmupRuns && !metrics.contains(BenchmarkMetric.RunTimeRaw)) {
-        throw new Unreachable("Benchmark with warmup time must measure RunTimeRaw");
-      }
       return new BenchmarkConfig(
-          name, method, target, ImmutableSet.copyOf(metrics), suite, fromRevision, timeWarmupRuns);
+          name,
+          method,
+          target,
+          ImmutableSet.copyOf(metrics),
+          suite,
+          fromRevision,
+          dependencies);
     }
 
     public Builder setName(String name) {
@@ -107,7 +106,7 @@
       return this;
     }
 
-    public Builder measureRunTimeRaw() {
+    public Builder measureRunTime() {
       metrics.add(BenchmarkMetric.RunTimeRaw);
       return this;
     }
@@ -117,6 +116,11 @@
       return this;
     }
 
+    public Builder measureWarmup() {
+      metrics.add(BenchmarkMetric.StartupTime);
+      return this;
+    }
+
     public Builder setSuite(BenchmarkSuite suite) {
       this.suite = suite;
       return this;
@@ -127,8 +131,8 @@
       return this;
     }
 
-    public Builder timeWarmupRuns() {
-      this.timeWarmupRuns = true;
+    public Builder addDependency(BenchmarkDependency dependency) {
+      dependencies.add(dependency);
       return this;
     }
   }
@@ -141,8 +145,8 @@
   private final BenchmarkMethod method;
   private final ImmutableSet<BenchmarkMetric> metrics;
   private final BenchmarkSuite suite;
+  private final Collection<BenchmarkDependency> dependencies;
   private final int fromRevision;
-  private final boolean timeWarmupRuns;
 
   private BenchmarkConfig(
       String name,
@@ -151,13 +155,13 @@
       ImmutableSet<BenchmarkMetric> metrics,
       BenchmarkSuite suite,
       int fromRevision,
-      boolean timeWarmupRuns) {
+      Collection<BenchmarkDependency> dependencies) {
     this.id = new BenchmarkIdentifier(name, target);
     this.method = benchmarkMethod;
     this.metrics = metrics;
     this.suite = suite;
     this.fromRevision = fromRevision;
-    this.timeWarmupRuns = timeWarmupRuns;
+    this.dependencies = dependencies;
   }
 
   public BenchmarkIdentifier getIdentifier() {
@@ -168,13 +172,6 @@
     return id.getName();
   }
 
-  public String getWarmupName() {
-    if (!timeWarmupRuns) {
-      throw new BenchmarkConfigError("Invalid attempt at getting warmup benchmark name");
-    }
-    return getName() + "Warmup";
-  }
-
   public BenchmarkTarget getTarget() {
     return id.getTarget();
   }
@@ -196,11 +193,15 @@
   }
 
   public boolean hasTimeWarmupRuns() {
-    return timeWarmupRuns;
+    return hasMetric(BenchmarkMetric.StartupTime);
   }
 
-  public void run(TemporaryFolder temp) throws Exception {
-    method.run(this, temp);
+  public Collection<BenchmarkDependency> getDependencies() {
+    return dependencies;
+  }
+
+  public void run(BenchmarkEnvironment environment) throws Exception {
+    method.run(environment);
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkDependency.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkDependency.java
new file mode 100644
index 0000000..f59ebb6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkDependency.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2022, 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.benchmarks;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class BenchmarkDependency {
+
+  public static BenchmarkDependency getRuntimeJarJava8() {
+    return new BenchmarkDependency("openjdk-rt-1.8", Paths.get("third_party", "openjdk"));
+  }
+
+  public static BenchmarkDependency getAndroidJar30() {
+    return new BenchmarkDependency("lib-v30", Paths.get("third_party", "android_jar"));
+  }
+
+  // Directory name of the dependency.
+  private final String directoryName;
+
+  // Location in the R8 source tree.
+  // This should never be directly exposed as its actual location will differ on golem.
+  // See `getRoot` to obtain the actual dependency root.
+  private final Path location;
+
+  public BenchmarkDependency(String directoryName, Path location) {
+    this.directoryName = directoryName;
+    this.location = location;
+  }
+
+  public Path getTarball() {
+    return location.resolve(directoryName + ".tar.gz");
+  }
+
+  public Path getSha1() {
+    return location.resolve(directoryName + ".tar.gz.sha1");
+  }
+
+  public Path getRoot(BenchmarkEnvironment environment) {
+    return environment.translateDependencyPath(directoryName, location);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkEnvironment.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkEnvironment.java
new file mode 100644
index 0000000..aeed6f3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkEnvironment.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2022, 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.benchmarks;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.rules.TemporaryFolder;
+
+public class BenchmarkEnvironment {
+
+  private final BenchmarkConfig config;
+  private final TemporaryFolder temp;
+  private final boolean isGolem;
+
+  public BenchmarkEnvironment(BenchmarkConfig config, TemporaryFolder temp, boolean isGolem) {
+    this.config = config;
+    this.temp = temp;
+    this.isGolem = isGolem;
+  }
+
+  public BenchmarkConfig getConfig() {
+    return config;
+  }
+
+  public TemporaryFolder getTemp() {
+    return temp;
+  }
+
+  public Path translateDependencyPath(String directoryName, Path location) {
+    return isGolem
+        ? Paths.get("benchmarks", config.getName() + "Group", directoryName)
+        : location.resolve(directoryName);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMainEntryRunner.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMainEntryRunner.java
index 3e5a86a..3151343 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMainEntryRunner.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMainEntryRunner.java
@@ -8,15 +8,21 @@
 public class BenchmarkMainEntryRunner {
 
   public static void main(String[] args) throws Exception {
-    if (args.length != 2) {
+    if (args.length != 3) {
       throw new RuntimeException("Invalid arguments. Expected exactly one benchmark and target");
     }
     String benchmarkName = args[0];
     String targetIdentifier = args[1];
+    String isGolemArg = args[2];
     BenchmarkIdentifier identifier = BenchmarkIdentifier.parse(benchmarkName, targetIdentifier);
     if (identifier == null) {
       throw new RuntimeException("Invalid identifier identifier: " + benchmarkName);
     }
+    boolean isGolem = isGolemArg.equals("golem");
+    if (!isGolem && !isGolemArg.equals("local")) {
+      throw new RuntimeException(
+          "Invalid value for arg 3, expected 'golem' or 'local', got '" + isGolemArg + "'");
+    }
     BenchmarkCollection collection = BenchmarkCollection.computeCollection();
     BenchmarkConfig config = collection.getBenchmark(identifier);
     if (config == null) {
@@ -24,7 +30,7 @@
     }
     TemporaryFolder temp = new TemporaryFolder();
     temp.create();
-    config.run(temp);
+    config.run(new BenchmarkEnvironment(config, temp, isGolem));
     temp.delete();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMethod.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMethod.java
index 8c7e372..ec22276 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMethod.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMethod.java
@@ -3,10 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.benchmarks;
 
-import org.junit.rules.TemporaryFolder;
-
 @FunctionalInterface
 public interface BenchmarkMethod {
 
-  void run(BenchmarkConfig config, TemporaryFolder temp) throws Exception;
+  void run(BenchmarkEnvironment environment) throws Exception;
 }
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMetric.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMetric.java
index 9483499..62bc0ba 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMetric.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMetric.java
@@ -5,7 +5,8 @@
 
 public enum BenchmarkMetric {
   RunTimeRaw,
-  CodeSize;
+  CodeSize,
+  StartupTime;
 
   public String getDartType() {
     return "Metric." + name();
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResults.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
index f436c26..51f9f3a 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
@@ -9,8 +9,8 @@
 
 public class BenchmarkResults {
 
-  private final boolean isWarmupResults;
-  private final LongList runtimeRawResults = new LongArrayList();
+  private final BenchmarkMetric runtimeMetric;
+  private final LongList runtimeResults = new LongArrayList();
   private final LongList codeSizeResults = new LongArrayList();
 
   public static BenchmarkResults create() {
@@ -22,15 +22,19 @@
   }
 
   private BenchmarkResults(boolean isWarmupResults) {
-    this.isWarmupResults = isWarmupResults;
+    this.runtimeMetric = isWarmupResults ? BenchmarkMetric.StartupTime : BenchmarkMetric.RunTimeRaw;
+  }
+
+  private boolean isWarmupResults() {
+    return runtimeMetric == BenchmarkMetric.StartupTime;
   }
 
   private String getName(BenchmarkConfig config) {
-    return isWarmupResults ? config.getWarmupName() : config.getName();
+    return config.getName();
   }
 
-  public void addRuntimeRawResult(long result) {
-    runtimeRawResults.add(result);
+  public void addRuntimeResult(long result) {
+    runtimeResults.add(result);
   }
 
   public void addCodeSizeResult(long result) {
@@ -53,7 +57,7 @@
     verifyMetric(
         BenchmarkMetric.RunTimeRaw,
         config.getMetrics().contains(BenchmarkMetric.RunTimeRaw),
-        !runtimeRawResults.isEmpty());
+        !runtimeResults.isEmpty());
     verifyMetric(
         BenchmarkMetric.CodeSize,
         config.getMetrics().contains(BenchmarkMetric.CodeSize),
@@ -61,11 +65,12 @@
   }
 
   public static String prettyTime(long nanoTime) {
-    return "" + (nanoTime / 1000000) + "ms";
+    return "" + (nanoTime / 1000000) + " ms";
   }
 
-  private void printRunTimeRaw(BenchmarkConfig config, long duration) {
-    System.out.println(getName(config) + "(RunTimeRaw): " + prettyTime(duration));
+  private void printRunTime(BenchmarkConfig config, long duration) {
+    String metric = runtimeMetric.name();
+    System.out.println(getName(config) + "(" + metric + "): " + prettyTime(duration));
   }
 
   private void printCodeSize(BenchmarkConfig config, long bytes) {
@@ -74,15 +79,15 @@
 
   public void printResults(ResultMode mode, BenchmarkConfig config) {
     verifyConfigAndResults(config);
-    if (config.hasMetric(BenchmarkMetric.RunTimeRaw)) {
-      long sum = runtimeRawResults.stream().mapToLong(l -> l).sum();
+    if (config.hasMetric(runtimeMetric)) {
+      long sum = runtimeResults.stream().mapToLong(l -> l).sum();
       if (mode == ResultMode.SUM) {
-        printRunTimeRaw(config, sum);
+        printRunTime(config, sum);
       } else if (mode == ResultMode.AVERAGE) {
-        printRunTimeRaw(config, sum / runtimeRawResults.size());
+        printRunTime(config, sum / runtimeResults.size());
       }
     }
-    if (!isWarmupResults && config.hasMetric(BenchmarkMetric.CodeSize)) {
+    if (!isWarmupResults() && config.hasMetric(BenchmarkMetric.CodeSize)) {
       long size = codeSizeResults.getLong(0);
       for (int i = 1; i < codeSizeResults.size(); i++) {
         if (size != codeSizeResults.getLong(i)) {
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkSuite.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkSuite.java
index 1068f72..cecba1d 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkSuite.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkSuite.java
@@ -6,11 +6,7 @@
 /** Enumeration of the benchmark suites on Golem. */
 public enum BenchmarkSuite {
   R8_BENCHMARKS("R8Benchmarks", "suite"),
-  D8_BENCHMARKS("D8KeyBenchmarks", "d8KeySuite"),
-  D8_INCREMENTAL_BENCHMARKS("D8IncrementalBenchmarks", "incrementalSuite"),
-  OPENSOURCE_BENCHMARKS("OpenSourceAppDumps", "dumpsSuite"),
-  MEMORY_BENCHMARKS("R8MemoryBenchmarks", "r8MemorySuite"),
-  RETRACE_BENCHMARKS("R8RetraceBenchmarks", "r8RetraceSuite");
+  OPENSOURCE_BENCHMARKS("OpenSourceAppDumps", "dumpsSuite");
 
   private final String golemName;
   private final String dartName;
diff --git a/src/test/java/com/android/tools/r8/benchmarks/desugaredlib/LegacyDesugaredLibraryBenchmark.java b/src/test/java/com/android/tools/r8/benchmarks/desugaredlib/LegacyDesugaredLibraryBenchmark.java
new file mode 100644
index 0000000..e6d1d00
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/desugaredlib/LegacyDesugaredLibraryBenchmark.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2022, 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.benchmarks.desugaredlib;
+
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.benchmarks.BenchmarkBase;
+import com.android.tools.r8.benchmarks.BenchmarkConfig;
+import com.android.tools.r8.benchmarks.BenchmarkDependency;
+import com.android.tools.r8.benchmarks.BenchmarkEnvironment;
+import com.android.tools.r8.benchmarks.BenchmarkTarget;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class LegacyDesugaredLibraryBenchmark extends BenchmarkBase {
+
+  private static final BenchmarkDependency androidJar = BenchmarkDependency.getAndroidJar30();
+  private static final BenchmarkDependency legacyConf =
+      new BenchmarkDependency("desugar_jdk_libs_legacy", Paths.get("third_party", "openjdk"));
+
+  public LegacyDesugaredLibraryBenchmark(BenchmarkConfig config, TestParameters parameters) {
+    super(config, parameters);
+  }
+
+  @Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return parametersFromConfigs(configs());
+  }
+
+  public static List<BenchmarkConfig> configs() {
+    return ImmutableList.of(
+        BenchmarkConfig.builder()
+            .setName("LegacyDesugaredLibraryConf")
+            .setTarget(BenchmarkTarget.D8)
+            .setFromRevision(12150)
+            .setMethod(LegacyDesugaredLibraryBenchmark::run)
+            .addDependency(androidJar)
+            .addDependency(legacyConf)
+            .measureRunTime()
+            .build());
+  }
+
+  public static void run(BenchmarkEnvironment environment) throws Exception {
+    runner(environment.getConfig())
+        .setWarmupIterations(1)
+        .setBenchmarkIterations(10)
+        .reportResultSum()
+        .run(
+            results ->
+                testForD8(environment.getTemp(), Backend.DEX)
+                    .setMinApi(AndroidApiLevel.B)
+                    .addLibraryFiles(androidJar.getRoot(environment).resolve("android.jar"))
+                    .apply(
+                        b ->
+                            b.getBuilder()
+                                .addDesugaredLibraryConfiguration(
+                                    StringResource.fromFile(
+                                        legacyConf
+                                            .getRoot(environment)
+                                            .resolve("desugar_jdk_libs.json"))))
+                    .benchmarkCompile(results));
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      System.out.println(Stream.of("Hello", "world!").collect(Collectors.joining(" ")));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/helloworld/HelloWorldBenchmark.java b/src/test/java/com/android/tools/r8/benchmarks/helloworld/HelloWorldBenchmark.java
index b91e5b1..a838108 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/helloworld/HelloWorldBenchmark.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/helloworld/HelloWorldBenchmark.java
@@ -3,16 +3,20 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.benchmarks.helloworld;
 
-import com.android.tools.r8.TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.benchmarks.BenchmarkBase;
 import com.android.tools.r8.benchmarks.BenchmarkConfig;
+import com.android.tools.r8.benchmarks.BenchmarkDependency;
+import com.android.tools.r8.benchmarks.BenchmarkEnvironment;
 import com.android.tools.r8.benchmarks.BenchmarkMethod;
 import com.android.tools.r8.benchmarks.BenchmarkTarget;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.function.Function;
 import org.junit.runner.RunWith;
@@ -41,22 +45,23 @@
   }
 
   // Options/parameter setup to define variants of the benchmark above.
+  // Other benchmarks may not need this kind of options. It is just to help create the variants.
   private static class Options {
     final BenchmarkTarget target;
     final Backend backend;
-    final boolean includeLibrary;
+    final BenchmarkDependency library;
     final AndroidApiLevel minApi = AndroidApiLevel.B;
 
     public Options(BenchmarkTarget target, Backend backend, boolean includeLibrary) {
       this.target = target;
       this.backend = backend;
-      this.includeLibrary = includeLibrary;
+      library = includeLibrary ? BenchmarkDependency.getRuntimeJarJava8() : null;
     }
 
     public String getName() {
       // The name include each non-target option for the variants to ensure unique benchmarks.
       String backendString = backend.isCf() ? "Cf" : "Dex";
-      String libraryString = includeLibrary ? "" : "NoLib";
+      String libraryString = library != null ? "" : "NoLib";
       return "HelloWorld" + backendString + libraryString;
     }
   }
@@ -68,65 +73,77 @@
     for (boolean includeLibrary : BooleanUtils.values()) {
       for (Backend backend : Backend.values()) {
         Options options = new Options(target, backend, includeLibrary);
-        benchmarks.add(
+        BenchmarkConfig.Builder builder =
             BenchmarkConfig.builder()
                 // The benchmark is required to have a unique combination of name and target.
                 .setName(options.getName())
                 .setTarget(target)
                 // The benchmark is required to have at least one metric.
-                .measureRunTimeRaw()
+                .measureRunTime()
                 .measureCodeSize()
                 // The benchmark is required to have a runner method which defines the actual
                 // execution.
                 .setMethod(method.apply(options))
                 // The benchmark is required to set a "golem from revision".
                 // Find this value by looking at the current revision on golem.
-                .setFromRevision(11900)
+                .setFromRevision(12150)
                 // The benchmark can optionally time the warmup. This is not needed to use a warmup
                 // in the actual run, only to include it as its own benchmark entry on golem.
-                .timeWarmupRuns()
-                .build());
+                .measureWarmup();
+        // If compiling with a library it needs to be added as a dependency.
+        if (options.library != null) {
+          builder.addDependency(options.library);
+        }
+        benchmarks.add(builder.build());
       }
     }
   }
 
   public static BenchmarkMethod benchmarkD8(Options options) {
-    return (config, temp) ->
-        runner(config)
+    return environment ->
+        runner(environment.getConfig())
             .setWarmupIterations(1)
             .setBenchmarkIterations(100)
             .reportResultSum()
             .run(
                 results ->
-                    testForD8(temp, options.backend)
+                    testForD8(environment.getTemp(), options.backend)
                         .setMinApi(options.minApi)
-                        .applyIf(!options.includeLibrary, TestBuilder::addLibraryFiles)
+                        .addLibraryFiles(getLibraryFiles(options, environment))
                         .addProgramClasses(TestClass.class)
-                        // Compile and emit RunTimeRaw measure.
+                        // Compile and measure the run time.
                         .benchmarkCompile(results)
                         // Measure the output size.
                         .benchmarkCodeSize(results));
   }
 
   public static BenchmarkMethod benchmarkR8(Options options) {
-    return (config, temp) ->
-        runner(config)
+    return environment ->
+        runner(environment.getConfig())
             .setWarmupIterations(1)
             .setBenchmarkIterations(4)
             .reportResultSum()
             .run(
                 results ->
-                    testForR8(temp, options.backend)
-                        .applyIf(!options.includeLibrary, b -> b.addLibraryFiles().addDontWarn("*"))
+                    testForR8(environment.getTemp(), options.backend)
+                        .addLibraryFiles(getLibraryFiles(options, environment))
+                        .applyIf(options.library == null, b -> b.addDontWarn("*"))
                         .setMinApi(options.minApi)
                         .addProgramClasses(TestClass.class)
                         .addKeepMainRule(TestClass.class)
-                        // Compile and emit RunTimeRaw measure.
+                        // Compile and measure the run time.
                         .benchmarkCompile(results)
                         // Measure the output size.
                         .benchmarkCodeSize(results));
   }
 
+  private static Collection<Path> getLibraryFiles(
+      Options options, BenchmarkEnvironment environment) {
+    return options.library != null
+        ? Collections.singletonList(options.library.getRoot(environment).resolve("rt.jar"))
+        : Collections.emptyList();
+  }
+
   static class TestClass {
 
     public static void main(String[] args) {
diff --git a/src/test/java/com/android/tools/r8/debuginfo/Regress216178582Test.java b/src/test/java/com/android/tools/r8/debuginfo/Regress216178582Test.java
index 3f384d4..ac4be8c 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/Regress216178582Test.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/Regress216178582Test.java
@@ -48,6 +48,7 @@
             .setMinApi(parameters.getApiLevel())
             .addKeepMainRule(TestClass.class)
             .addKeepAttributeLineNumberTable()
+            .addOptionsModification(o -> o.testing.forcePcBasedEncoding = true)
             .compile()
             .inspect(
                 inspector -> {
diff --git a/src/test/java/com/android/tools/r8/debuginfo/pc2pc/SharedPc2PcDebugInfo.java b/src/test/java/com/android/tools/r8/debuginfo/pc2pc/SharedPc2PcDebugInfo.java
new file mode 100644
index 0000000..0bbdf46
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/pc2pc/SharedPc2PcDebugInfo.java
@@ -0,0 +1,106 @@
+// Copyright (c) 2022, 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.debuginfo.pc2pc;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class SharedPc2PcDebugInfo extends TestBase {
+
+  static final List<String> METHODS = Arrays.asList("m1", "m2", "m3", "m4");
+  static final String EXPECTED = StringUtils.lines(METHODS);
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultDexRuntime().withApiLevel(AndroidApiLevel.B).build();
+  }
+
+  public SharedPc2PcDebugInfo(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(TestClass.class)
+        .addKeepClassAndMembersRules(TestClass.class)
+        .addKeepAttributeLineNumberTable()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED)
+        .inspect(
+            inspector -> {
+              ClassSubject clazz = inspector.clazz(TestClass.class);
+              DexEncodedMethod mainMethod = clazz.mainMethod().getMethod();
+              // The main method debug info is smallest using normal line increments.
+              assertTrue(mainMethod.getCode().asDexCode().getDebugInfo().isEventBasedInfo());
+              // The mX methods can share the pc encoding which is smaller than the sum of the
+              // normal encodings.
+              DexDebugInfo shared = null;
+              for (String name : METHODS) {
+                DexEncodedMethod method = clazz.uniqueMethodWithName(name).getMethod();
+                DexDebugInfo debugInfo = method.getCode().asDexCode().getDebugInfo();
+                assertTrue(debugInfo.isPcBasedInfo());
+                // The DEX parser should allocate the same shared instance to each method.
+                assertTrue(shared == null || debugInfo == shared);
+                shared = debugInfo;
+              }
+            });
+  }
+
+  static class TestClass {
+
+    public static void m1() {
+      System.out.print("m");
+      System.out.println("1");
+    }
+
+    public static void m2() {
+      System.out.print("m");
+      System.out.print("2");
+      System.out.println();
+    }
+
+    public static void m3() {
+      PrintStream out = System.out;
+      out.println("m3");
+    }
+
+    public static void m4() {
+      String m = "m";
+      PrintStream out = System.out;
+      out.print(m);
+      String s = "4";
+      PrintStream out1 = System.out;
+      out1.print(s);
+      PrintStream out2 = System.out;
+      out2.println();
+    }
+
+    public static void main(String[] args) {
+      m1();
+      m2();
+      m3();
+      m4();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
index 6d3a0d3..f79b585 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
@@ -84,6 +84,9 @@
 
   @Test
   public void testBufferedReaderD8Cf() throws Exception {
+    Assume.assumeTrue(
+        "The alternative 3 configuration is available only in JDK 11 desugared library.",
+        isJDK11DesugaredLibrary());
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     // Use D8 to desugar with Java classfile output.
     Path jar =
@@ -140,6 +143,9 @@
   @Test
   public void testBufferedReaderD8() throws Exception {
     Assume.assumeTrue(parameters.getRuntime().isDex());
+    Assume.assumeTrue(
+        "The alternative 3 configuration is available only in JDK 11 desugared library.",
+        isJDK11DesugaredLibrary());
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForD8()
         .addLibraryFiles(getLibraryFile())
@@ -166,6 +172,9 @@
   @Test
   public void testBufferedReaderR8() throws Exception {
     Assume.assumeTrue(parameters.getRuntime().isDex());
+    Assume.assumeTrue(
+        "The alternative 3 configuration is available only in JDK 11 desugared library.",
+        isJDK11DesugaredLibrary());
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForR8(parameters.getBackend())
         .addLibraryFiles(getLibraryFile())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java
index d80d01d..6ade9c9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java
@@ -214,6 +214,8 @@
                 LibraryDesugaringTestConfiguration.builder()
                     .setMinApi(apiLevel)
                     // Minimal configuration with a different identifier.
+                    // The j$.time is rewritten because empty flags are equivalent to an empty
+                    // specification, and no marker is set for empty specifications.
                     .addDesugaredLibraryConfiguration(
                         StringResource.fromString(
                             "{"
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
index 86d87d5..0c4d6f6 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -139,7 +140,8 @@
             false,
             minApi.getLevel());
     MachineDesugaredLibrarySpecification specification =
-        spec.toMachineSpecification(new InternalOptions(factory, new Reporter()), getLibraryFile());
+        spec.toMachineSpecification(
+            new InternalOptions(factory, new Reporter()), getLibraryFile(), Timing.empty());
     Set<String> wrappersInSpec =
         specification.getWrappers().keySet().stream()
             .map(DexType::toString)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
index c21b7ea..3209290 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
@@ -78,6 +78,11 @@
 
   public ObjectsTest(TestParameters parameters, boolean libraryDesugarJavaUtilObjects) {
     this.parameters = parameters;
+    if (libraryDesugarJavaUtilObjects) {
+      Assume.assumeTrue(
+          "The alternative 3 configuration is available only in JDK 11 desugared library.",
+          isJDK11DesugaredLibrary());
+    }
     this.libraryDesugarJavaUtilObjects = libraryDesugarJavaUtilObjects;
     this.androidJar =
         ToolHelper.getAndroidJar(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
index e629c47..5d9cf31 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
@@ -26,7 +26,7 @@
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
 
-  @Parameters(name = "machine: {0}, {2}, shrink: {1}")
+  @Parameters(name = "{1}, shrink: {0}")
   public static List<Object[]> data() {
     return buildParameters(
         BooleanUtils.values(),
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
index 125b4ec..f107b2a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
@@ -23,6 +23,7 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
 import java.io.IOException;
 import java.util.Map;
 import org.junit.Assume;
@@ -46,7 +47,8 @@
   public void testMultiLevel() throws IOException {
     Assume.assumeTrue(ToolHelper.isLocalDevelopment());
 
-    LegacyToHumanSpecificationConverter converter = new LegacyToHumanSpecificationConverter();
+    LegacyToHumanSpecificationConverter converter =
+        new LegacyToHumanSpecificationConverter(Timing.empty());
 
     InternalOptions options = new InternalOptions();
 
@@ -57,7 +59,11 @@
                 StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()));
 
     MultiAPILevelHumanDesugaredLibrarySpecification humanSpec1 =
-        converter.convertAllAPILevels(spec, ToolHelper.getAndroidJar(31), options);
+        converter.convertAllAPILevels(
+            spec,
+            ToolHelper.getDesugarJDKLibs(),
+            ToolHelper.getAndroidJar(getRequiredCompilationAPILevel()),
+            options);
 
     Box<String> json = new Box<>();
     MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.export(
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodOverrideEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodOverrideEnumUnboxingTest.java
index 9dcd9cb..c11a623 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodOverrideEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodOverrideEnumUnboxingTest.java
@@ -6,7 +6,7 @@
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.KeepConstantArguments;
 import com.android.tools.r8.NeverClassInline;
@@ -67,8 +67,7 @@
     MethodSubject methodOnB =
         inspector.clazz(B.class).uniqueMethodWithFinalName(methodOnA.getFinalName());
     assertThat(methodOnB, isPresent());
-    // TODO(b/171784168): Should be true.
-    assertFalse(methodOnB.streamInstructions().anyMatch(x -> x.asDexInstruction().isInvokeSuper()));
+    assertTrue(methodOnB.streamInstructions().anyMatch(x -> x.asDexInstruction().isInvokeSuper()));
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java
new file mode 100644
index 0000000..01f306a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java
@@ -0,0 +1,154 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.interfaces;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class OpenInterfaceCheckCastTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForRuntime(parameters)
+        .addProgramClasses(getProgramClasses())
+        .addProgramClassFileData(getTransformedMainClass())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClasses(getProgramClasses())
+        .addProgramClassFileData(getTransformedMainClass())
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(getProgramClasses())
+        .addProgramClassFileData(getTransformedMainClass())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        // TODO(b/214496607): I should not be merged into A in the first place, since I is open.
+        .enableNoVerticalClassMergingAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(getExpectedOutputLines(true));
+  }
+
+  private List<Class<?>> getProgramClasses() {
+    return ImmutableList.of(I.class, A.class, B.class);
+  }
+
+  private byte[] getTransformedMainClass() throws IOException {
+    return transformer(Main.class)
+        .transformTypeInsnInMethod(
+            "asI",
+            (opcode, type, visitor) -> {
+              assertEquals(opcode, Opcodes.CHECKCAST);
+              assertEquals(type, binaryName(A.class));
+              visitor.visitTypeInsn(opcode, binaryName(I.class));
+            })
+        .transformTypeInsnInMethod(
+            "getB",
+            (opcode, type, visitor) -> {
+              assertEquals(opcode, Opcodes.NEW);
+              assertEquals(type, binaryName(A.class));
+              visitor.visitTypeInsn(opcode, binaryName(B.class));
+            })
+        .transformMethodInsnInMethod(
+            "getB",
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              assertEquals(opcode, Opcodes.INVOKESPECIAL);
+              assertEquals(owner, binaryName(A.class));
+              assertEquals(name, "<init>");
+              visitor.visitMethodInsn(opcode, binaryName(B.class), name, descriptor, isInterface);
+            })
+        .transform();
+  }
+
+  private List<String> getExpectedOutputLines(boolean isR8) {
+    if (isR8) {
+      // TODO(b/214496607): R8 should not optimize the check-cast instruction since I is open.
+      return ImmutableList.of("OK", "OK");
+    }
+    if (parameters.isDexRuntime()
+        && parameters
+            .getDexRuntimeVersion()
+            .isEqualToOneOf(Version.V5_1_1, Version.V8_1_0, Version.V9_0_0, Version.DEFAULT)) {
+      return ImmutableList.of("OK", "OK");
+    }
+    return ImmutableList.of("OK", "FAIL");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      try {
+        asI(getA());
+        System.out.println("OK");
+      } catch (ClassCastException e) {
+        System.out.println("FAIL");
+      }
+      try {
+        asI(getB());
+        System.out.println("OK");
+      } catch (ClassCastException e) {
+        System.out.println("FAIL");
+      }
+    }
+
+    @NeverInline
+    static I asI(I i) {
+      return (A) i; // transformed into a cast to I.
+    }
+
+    static I getA() {
+      return new A();
+    }
+
+    static I getB() {
+      return new A(); // transformed into new B().
+    }
+  }
+
+  @NoVerticalClassMerging
+  interface I {}
+
+  static class A implements I {}
+
+  static class B {}
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java
new file mode 100644
index 0000000..98db3cc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java
@@ -0,0 +1,141 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.interfaces;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class OpenInterfaceInliningTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForRuntime(parameters)
+        .addProgramClasses(getProgramClasses())
+        .addProgramClassFileData(getTransformedMainClass())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClasses(getProgramClasses())
+        .addProgramClassFileData(getTransformedMainClass())
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(getProgramClasses())
+        .addProgramClassFileData(getTransformedMainClass())
+        .addKeepClassAndMembersRules(Main.class)
+        // TODO(b/214496607): I should not be merged into A in the first place, since I is open.
+        .enableNoVerticalClassMergingAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(getExpectedOutputLines(true));
+  }
+
+  private List<Class<?>> getProgramClasses() {
+    return ImmutableList.of(I.class, A.class, B.class);
+  }
+
+  private byte[] getTransformedMainClass() throws IOException {
+    return transformer(Main.class)
+        .transformTypeInsnInMethod(
+            "getB",
+            (opcode, type, visitor) -> {
+              assertEquals(opcode, Opcodes.NEW);
+              assertEquals(type, binaryName(A.class));
+              visitor.visitTypeInsn(opcode, binaryName(B.class));
+            })
+        .transformMethodInsnInMethod(
+            "getB",
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              assertEquals(opcode, Opcodes.INVOKESPECIAL);
+              assertEquals(owner, binaryName(A.class));
+              assertEquals(name, "<init>");
+              visitor.visitMethodInsn(opcode, binaryName(B.class), name, descriptor, isInterface);
+            })
+        .transform();
+  }
+
+  private List<String> getExpectedOutputLines(boolean isR8) {
+    if (isR8) {
+      // TODO(b/214496607): R8 should not inline the invoke instruction since I is open.
+      return ImmutableList.of("A", "A");
+    }
+    return ImmutableList.of("A", "ICCE");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      getA().m();
+      try {
+        getB().m();
+      } catch (IncompatibleClassChangeError e) {
+        System.out.println("ICCE");
+      }
+    }
+
+    static I getA() {
+      return new A();
+    }
+
+    static I getB() {
+      return new A(); // transformed into new B().
+    }
+  }
+
+  @NoVerticalClassMerging
+  interface I {
+
+    void m();
+  }
+
+  static class A implements I {
+
+    @Override
+    public void m() {
+      System.out.println("A");
+    }
+  }
+
+  static class B {
+
+    public void m() {
+      System.out.println("B");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java
new file mode 100644
index 0000000..d73550c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java
@@ -0,0 +1,137 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.interfaces;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class OpenInterfaceInstanceofTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForRuntime(parameters)
+        .addProgramClasses(getProgramClasses())
+        .addProgramClassFileData(getTransformedMainClass())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClasses(getProgramClasses())
+        .addProgramClassFileData(getTransformedMainClass())
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(getProgramClasses())
+        .addProgramClassFileData(getTransformedMainClass())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        // TODO(b/214496607): I should not be merged into A in the first place, since I is open.
+        .enableNoVerticalClassMergingAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(getExpectedOutputLines(true));
+  }
+
+  private List<Class<?>> getProgramClasses() {
+    return ImmutableList.of(I.class, A.class, B.class);
+  }
+
+  private byte[] getTransformedMainClass() throws IOException {
+    return transformer(Main.class)
+        .transformTypeInsnInMethod(
+            "getB",
+            (opcode, type, visitor) -> {
+              assertEquals(opcode, Opcodes.NEW);
+              assertEquals(type, binaryName(A.class));
+              visitor.visitTypeInsn(opcode, binaryName(B.class));
+            })
+        .transformMethodInsnInMethod(
+            "getB",
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              assertEquals(opcode, Opcodes.INVOKESPECIAL);
+              assertEquals(owner, binaryName(A.class));
+              assertEquals(name, "<init>");
+              visitor.visitMethodInsn(opcode, binaryName(B.class), name, descriptor, isInterface);
+            })
+        .transform();
+  }
+
+  private List<String> getExpectedOutputLines(boolean isR8) {
+    if (isR8) {
+      // TODO(b/214496607): R8 should not optimize the instanceof instruction since I is open.
+      return ImmutableList.of("true", "true");
+    }
+    if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V7_0_0)) {
+      return ImmutableList.of("true", "true");
+    }
+    return ImmutableList.of("true", "false");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println(isIOrNull(getA()));
+      System.out.println(isIOrNull(getB()));
+    }
+
+    @NeverInline
+    static boolean isIOrNull(I i) {
+      if (i != null) {
+        return i instanceof I;
+      }
+      return true;
+    }
+
+    static I getA() {
+      return new A();
+    }
+
+    static I getB() {
+      return new A(); // transformed into new B().
+    }
+  }
+
+  @NoVerticalClassMerging
+  interface I {}
+
+  static class A implements I {}
+
+  static class B {}
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java
new file mode 100644
index 0000000..7740192
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java
@@ -0,0 +1,127 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.interfaces;
+
+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.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class OpenUninstantiatedInterfaceInstanceofTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForRuntime(parameters)
+        .addProgramClasses(getProgramClasses())
+        .addProgramClassFileData(getTransformedMainClass())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClasses(getProgramClasses())
+        .addProgramClassFileData(getTransformedMainClass())
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(getProgramClasses())
+        .addProgramClassFileData(getTransformedMainClass())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(getExpectedOutputLines(true));
+  }
+
+  private List<Class<?>> getProgramClasses() {
+    return ImmutableList.of(I.class, A.class, B.class);
+  }
+
+  private byte[] getTransformedMainClass() throws IOException {
+    return transformer(Main.class)
+        .transformTypeInsnInMethod(
+            "getB",
+            (opcode, type, visitor) -> {
+              assertEquals(opcode, Opcodes.NEW);
+              assertEquals(type, binaryName(A.class));
+              visitor.visitTypeInsn(opcode, binaryName(B.class));
+            })
+        .transformMethodInsnInMethod(
+            "getB",
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              assertEquals(opcode, Opcodes.INVOKESPECIAL);
+              assertEquals(owner, binaryName(A.class));
+              assertEquals(name, "<init>");
+              visitor.visitMethodInsn(opcode, binaryName(B.class), name, descriptor, isInterface);
+            })
+        .transform();
+  }
+
+  private List<String> getExpectedOutputLines(boolean isR8) {
+    if (isR8) {
+      // TODO(b/214496607): R8 should not optimize the instanceof instruction since I is open.
+      return ImmutableList.of("true");
+    }
+    if (parameters.isDexRuntime()) {
+      if (parameters.getDexRuntimeVersion().isEqualTo(Version.V7_0_0)
+          || parameters.getDexRuntimeVersion().isEqualTo(Version.V13_MASTER)) {
+        return ImmutableList.of("true");
+      }
+    }
+    return ImmutableList.of("false");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      I b = getB();
+      if (b != null) {
+        System.out.println(b instanceof I);
+      }
+    }
+
+    @NeverInline
+    static I getB() {
+      return new A(); // transformed into new B().
+    }
+  }
+
+  interface I {}
+
+  static class A implements I {}
+
+  static class B {}
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1Iterations.java b/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1Iterations.java
new file mode 100644
index 0000000..dc3ca19
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1Iterations.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.loops;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class LoopWith1Iterations extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public LoopWith1Iterations(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testLoopRemoved() throws Exception {
+    testForR8(parameters.getBackend())
+        .setMinApi(parameters.getApiLevel())
+        .addProgramClasses(Main.class)
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .noMinification()
+        .compile()
+        .inspect(this::assertLoopRemoved)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(
+            "object",
+            "loop1fori",
+            "object",
+            "loop1for",
+            "object",
+            "loop1foriAbstractValue",
+            "object",
+            "loop1forAbstractValue");
+  }
+
+  private void assertLoopRemoved(CodeInspector inspector) {
+    inspector
+        .clazz(Main.class)
+        .allMethods()
+        .forEach(
+            m ->
+                org.junit.Assert.assertTrue(
+                    m.streamInstructions()
+                        .noneMatch(i -> i.isArrayLength() || i.isGoto() || i.isIf())));
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      loop1fori();
+      loop1for();
+      loop1foriAbstractValue();
+      loop1forAbstractValue();
+    }
+
+    @NeverInline
+    public static void loop1fori() {
+      Object[] objects = new Object[1];
+      for (int i = 0; i < objects.length; i++) {
+        System.out.println("object");
+      }
+      System.out.println("loop1fori");
+    }
+
+    @NeverInline
+    public static void loop1for() {
+      Object[] objects = new Object[1];
+      for (Object object : objects) {
+        System.out.println("object");
+      }
+      System.out.println("loop1for");
+    }
+
+    @NeverInline
+    public static Object[] getObjectArray1() {
+      return new Object[1];
+    }
+
+    @NeverInline
+    public static void loop1foriAbstractValue() {
+      Object[] objects = getObjectArray1();
+      for (int i = 0; i < objects.length; i++) {
+        System.out.println("object");
+      }
+      System.out.println("loop1foriAbstractValue");
+    }
+
+    @NeverInline
+    public static void loop1forAbstractValue() {
+      Object[] objects = getObjectArray1();
+      for (Object object : objects) {
+        System.out.println("object");
+      }
+      System.out.println("loop1forAbstractValue");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/UninstantiatedAnnotatedArgumentsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/UninstantiatedAnnotatedArgumentsTest.java
index 0e4a3a2..65132d2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/UninstantiatedAnnotatedArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/UninstantiatedAnnotatedArgumentsTest.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.KeepUnusedArguments;
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterReordering;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.errors.Unreachable;
@@ -68,6 +69,7 @@
         .enableNeverClassInliningAnnotations()
         .enableConstantArgumentAnnotations(keepUninstantiatedArguments)
         .enableInliningAnnotations()
+        .enableNoParameterReorderingAnnotations()
         .enableUnusedArgumentAnnotations()
         // TODO(b/123060011): Mapping not working in presence of argument removal.
         .minification(keepUninstantiatedArguments)
@@ -168,6 +170,7 @@
     @KeepConstantArguments
     @KeepUnusedArguments
     @NeverInline
+    @NoParameterReordering
     static void testRemoveStaticFromStart(
         @Uninstantiated Dead uninstantiated,
         @Instantiated String instantiated,
@@ -178,6 +181,7 @@
     @KeepConstantArguments
     @KeepUnusedArguments
     @NeverInline
+    @NoParameterReordering
     static void testRemoveStaticFromMiddle(
         @Instantiated String instantiated,
         @Uninstantiated Dead uninstantiated,
@@ -188,6 +192,7 @@
     @KeepConstantArguments
     @KeepUnusedArguments
     @NeverInline
+    @NoParameterReordering
     static void testRemoveStaticFromEnd(
         @Instantiated String instantiated,
         @Instantiated String otherInstantiated,
@@ -198,6 +203,7 @@
     @KeepConstantArguments
     @KeepUnusedArguments
     @NeverInline
+    @NoParameterReordering
     void testRemoveVirtualFromStart(
         @Uninstantiated Dead uninstantiated,
         @Instantiated String instantiated,
@@ -208,6 +214,7 @@
     @KeepConstantArguments
     @KeepUnusedArguments
     @NeverInline
+    @NoParameterReordering
     void testRemoveVirtualFromMiddle(
         @Instantiated String instantiated,
         @Uninstantiated Dead uninstantiated,
@@ -218,6 +225,7 @@
     @KeepConstantArguments
     @KeepUnusedArguments
     @NeverInline
+    @NoParameterReordering
     void testRemoveVirtualFromEnd(
         @Instantiated String instantiated,
         @Instantiated String otherInstantiated,
diff --git a/src/test/java/com/android/tools/r8/jasmin/Regress65007724.java b/src/test/java/com/android/tools/r8/jasmin/Regress65007724.java
index 1eb0586..cdc5b63 100644
--- a/src/test/java/com/android/tools/r8/jasmin/Regress65007724.java
+++ b/src/test/java/com/android/tools/r8/jasmin/Regress65007724.java
@@ -3,11 +3,29 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.jasmin;
 
-import static org.junit.Assert.assertEquals;
-
+import com.android.tools.r8.D8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
 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 Regress65007724 extends JasminTestBase {
+
+  private final TestParameters parameters;
+
+  public Regress65007724(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
   @Test
   public void testThat16BitsIndexAreAllowed() throws Exception {
     JasminBuilder builder = new JasminBuilder();
@@ -28,8 +46,16 @@
         "invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
         "return");
 
-    String expected = runOnJava(builder, clazz.name);
-    String artResult = runOnArtD8(builder, clazz.name);
-    assertEquals(expected, artResult);
+    D8TestRunResult d8TestRunResult =
+        testForD8(parameters.getBackend())
+            .setMinApi(parameters.getApiLevel())
+            .addProgramClassFileData(builder.buildClasses())
+            .run(parameters.getRuntime(), clazz.name);
+    if (parameters.getDexRuntimeVersion().isEqualTo(Version.V13_MASTER)) {
+      // See b/220821265
+      d8TestRunResult.assertFailure();
+    } else {
+      d8TestRunResult.assertSuccessWithOutput("Hello World!");
+    }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/StaticFieldClassInitMemberRebindingTest.java b/src/test/java/com/android/tools/r8/memberrebinding/StaticFieldClassInitMemberRebindingTest.java
new file mode 100644
index 0000000..c31c93d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/StaticFieldClassInitMemberRebindingTest.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2022, 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.memberrebinding;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class StaticFieldClassInitMemberRebindingTest extends TestBase {
+
+  private static final String EXPECTED = "World!";
+  private static final String R8_EXPECTED = "Hello World!";
+
+  private final String NEW_A_DESCRIPTOR = "Lfoo/A;";
+  private final String NEW_B_DESCRIPTOR = "Lfoo/B;";
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClassFileData(
+            getMainWithRewrittenDescriptors(), getAWithPackageFoo(), getBWithRewrittenDescriptors())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(
+            getMainWithRewrittenDescriptors(), getAWithPackageFoo(), getBWithRewrittenDescriptors())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .addOptionsModification(options -> options.enableVisibilityBridgeRemoval = false)
+        .run(parameters.getRuntime(), Main.class)
+        // TODO(b/220668540): R8 should not change class loading semantics.
+        .assertSuccessWithOutputLines(R8_EXPECTED);
+  }
+
+  private byte[] getAWithPackageFoo() throws Exception {
+    return transformer(A.class).setClassDescriptor(NEW_A_DESCRIPTOR).transform();
+  }
+
+  private byte[] getBWithRewrittenDescriptors() throws Exception {
+    return transformer(B.class)
+        .setClassDescriptor(NEW_B_DESCRIPTOR)
+        .setSuper(NEW_A_DESCRIPTOR)
+        .replaceClassDescriptorInMethodInstructions(descriptor(A.class), NEW_A_DESCRIPTOR)
+        .transform();
+  }
+
+  private byte[] getMainWithRewrittenDescriptors() throws Exception {
+    return transformer(Main.class)
+        .replaceClassDescriptorInMethodInstructions(descriptor(B.class), NEW_B_DESCRIPTOR)
+        .transform();
+  }
+
+  static class /* foo. */ A {
+
+    @NeverInline
+    public static void foo() {
+      System.out.println("World!");
+    }
+  }
+
+  public static class /* foo */ B extends A {
+
+    static {
+      System.out.print("Hello ");
+    }
+  }
+
+  public static class Main {
+
+    @NeverInline
+    public static void test() {
+      B.foo(); // Resolves to A.foo(), hence does not trigger B.<clinit>().
+    }
+
+    public static void main(String[] args) {
+      test();
+      if (System.currentTimeMillis() == 0) {
+        // Needed to ensure we do not remove B.<clinit>() in first round of treeshaking before
+        // running member rebinding analysis.
+        new B();
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithInstanceInitializerCollisionTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithInstanceInitializerCollisionTest.java
index 66a4cdc..838e914 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithInstanceInitializerCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithInstanceInitializerCollisionTest.java
@@ -36,8 +36,6 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
-        .addOptionsModification(
-            options -> options.testing.enableExperimentalProtoNormalization = true)
         .enableNoHorizontalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptMethodTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptMethodTest.java
index 70dbf9d..5f30d86 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptMethodTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptMethodTest.java
@@ -38,8 +38,6 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addKeepRules("-keep class " + Main.class.getTypeName() + " { void foo(...); }")
-        .addOptionsModification(
-            options -> options.testing.enableExperimentalProtoNormalization = true)
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptVirtualMethodTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptVirtualMethodTest.java
index e9655ba..3b8abab 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptVirtualMethodTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptVirtualMethodTest.java
@@ -39,8 +39,6 @@
         .addInnerClasses(getClass())
         .addKeepClassAndMembersRules(Main.class)
         .addKeepRules("-keepclassmembers class " + A.class.getTypeName() + " { void foo(...); }")
-        .addOptionsModification(
-            options -> options.testing.enableExperimentalProtoNormalization = true)
         .enableInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithLibraryOverrideTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithLibraryOverrideTest.java
index 52d4df1..31a766e 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithLibraryOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithLibraryOverrideTest.java
@@ -38,8 +38,6 @@
         .addLibraryClasses(A.class, B.class, Library.class)
         .addDefaultRuntimeLibrary(parameters)
         .addKeepMainRule(Main.class)
-        .addOptionsModification(
-            options -> options.testing.enableExperimentalProtoNormalization = true)
         .enableInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
         .noMinification()
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithParameterAnnotationsTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithParameterAnnotationsTest.java
index a9b9af9..37bfc73 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithParameterAnnotationsTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithParameterAnnotationsTest.java
@@ -44,8 +44,6 @@
         .addKeepMainRule(Main.class)
         .addKeepClassAndMembersRules(Foo.class, Bar.class)
         .addKeepRuntimeVisibleAnnotations()
-        .addOptionsModification(
-            options -> options.testing.enableExperimentalProtoNormalization = true)
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java
index babbf9f..0e63441 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java
@@ -38,8 +38,6 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
-        .addOptionsModification(
-            options -> options.testing.enableExperimentalProtoNormalization = true)
         .enableInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java
index d9190df..dcde130 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java
@@ -37,8 +37,6 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
-        .addOptionsModification(
-            options -> options.testing.enableExperimentalProtoNormalization = true)
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java b/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
index 5405ecb..00d373c 100644
--- a/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
+++ b/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
@@ -4,13 +4,14 @@
 
 package com.android.tools.r8.regress.b150400371;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
 
 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.MethodSubject;
+import com.google.common.collect.ImmutableSet;
 import it.unimi.dsi.fastutil.ints.IntArraySet;
 import it.unimi.dsi.fastutil.ints.IntSet;
 import org.junit.Test;
@@ -45,9 +46,10 @@
               MethodSubject main =
                   inspector.method(InlineInto.class.getDeclaredMethod("main", String[].class));
               if (parameters.getApiLevel().isLessThan(apiLevelWithPcAsLineNumberSupport())) {
-                // Method has 14 actual lines, the PC mapping table will have about 50.
+                // Method has 2 actual lines for inlining of foo.
+                // The pc2pc encoding is larger than normal encoding, so it is just the two lines.
                 IntSet lines = new IntArraySet(main.getLineNumberTable().getLines());
-                assertTrue(lines.size() > 20);
+                assertEquals(ImmutableSet.of(1, 2), lines);
               } else {
                 assertNull(main.getLineNumberTable());
               }
diff --git a/src/test/java/com/android/tools/r8/shaking/RemoveCallToStaticInitTest.java b/src/test/java/com/android/tools/r8/shaking/RemoveCallToStaticInitTest.java
new file mode 100644
index 0000000..0d1fb3d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/RemoveCallToStaticInitTest.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2022, 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 static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RemoveCallToStaticInitTest extends TestBase {
+
+  private static final String EXPECTED = "Hello World!";
+  private static final String R8_EXPECTED = "World!";
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(A.class, Main.class)
+        .addProgramClassFileData(getBWithBridge())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(A.class, Main.class)
+        .addProgramClassFileData(getBWithBridge())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .run(parameters.getRuntime(), Main.class)
+        // TODO(b/220667525): R8 should emit EXPECTED
+        .assertSuccessWithOutputLines(R8_EXPECTED)
+        .inspect(
+            inspector -> {
+              ClassSubject clazz = inspector.clazz(B.class);
+              assertThat(clazz, isPresent());
+              // TODO(b/220667525): Should not remove bridge due to class init.
+              assertThat(clazz.uniqueMethodWithName("foo"), not(isPresent()));
+            });
+  }
+
+  private byte[] getBWithBridge() throws Exception {
+    return transformer(B.class)
+        .setAccessFlags(B.class.getMethod("foo"), MethodAccessFlags::setBridge)
+        .transform();
+  }
+
+  public static class A {
+
+    @NeverInline
+    public static void foo() {
+      System.out.println("World!");
+    }
+  }
+
+  public static class B extends A {
+
+    static {
+      System.out.print("Hello ");
+    }
+
+    @NeverInline
+    public static /* bridge */ void foo() {
+      A.foo();
+    }
+  }
+
+  public static class Main {
+
+    @NeverInline
+    public static void test() {
+      B.foo();
+    }
+
+    public static void main(String[] args) {
+      test();
+    }
+  }
+}
diff --git a/third_party/openjdk/desugar_jdk_libs_legacy.tar.gz.sha1 b/third_party/openjdk/desugar_jdk_libs_legacy.tar.gz.sha1
new file mode 100644
index 0000000..bcf1956
--- /dev/null
+++ b/third_party/openjdk/desugar_jdk_libs_legacy.tar.gz.sha1
@@ -0,0 +1 @@
+5e0a4ead7e29d87c31f0facb2e7971caff7ac443
\ No newline at end of file
diff --git a/tools/adb.py b/tools/adb.py
index 07ca034..2e015a0 100644
--- a/tools/adb.py
+++ b/tools/adb.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/api_sample_coverage.py b/tools/api_sample_coverage.py
index 4eea8c7..2d9b0d2 100755
--- a/tools/api_sample_coverage.py
+++ b/tools/api_sample_coverage.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/apk_masseur.py b/tools/apk_masseur.py
index b1ea43e..b363bb7 100755
--- a/tools/apk_masseur.py
+++ b/tools/apk_masseur.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/archive.py b/tools/archive.py
index 24e8e0a..d3aa196 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
@@ -37,7 +37,7 @@
   # TODO(mkroghj) This would not work for r8-lib, maybe use utils.getR8Version.
   output = subprocess.check_output([
     jdk.GetJavaExecutable(), '-jar', jar_path, '--version'
-  ])
+  ]).decode('utf-8')
   return output.splitlines()[0].strip()
 
 def GetVersion():
@@ -54,11 +54,11 @@
   return subprocess.check_output(['git', 'show', '-s', '--pretty=%d', 'HEAD'])
 
 def GetGitHash():
-  return subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip()
+  return subprocess.check_output(['git', 'rev-parse', 'HEAD']).decode('utf-8').strip()
 
 def IsMain(version):
   branches = subprocess.check_output(['git', 'branch', '-r', '--contains',
-                                      'HEAD'])
+                                      'HEAD']).decode('utf-8')
   # CL runs from gerrit does not have a branch, we always treat them as main
   # commits to archive these to the hash based location
   if len(branches) == 0:
diff --git a/tools/archive_desugar_jdk_libs.py b/tools/archive_desugar_jdk_libs.py
index 91ad6b5..3a9cfd6 100755
--- a/tools/archive_desugar_jdk_libs.py
+++ b/tools/archive_desugar_jdk_libs.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
@@ -106,6 +106,8 @@
         bazel,
         '--bazelrc=/dev/null',
         'build',
+        '--sandbox_debug',
+        '--verbose_failures',
         'maven_release' + ('_jdk11' if variant == 'jdk11' else '')]
     utils.PrintCmd(cmd)
     subprocess.check_call(cmd, env=GetJavaEnv())
diff --git a/tools/as_utils.py b/tools/as_utils.py
index 4087137..3c69d10 100644
--- a/tools/as_utils.py
+++ b/tools/as_utils.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/asmifier.py b/tools/asmifier.py
index 3fec231..1a8e4ac 100755
--- a/tools/asmifier.py
+++ b/tools/asmifier.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/benchmarks/get_deps.py b/tools/benchmarks/get_deps.py
index cd28efe..716db48 100644
--- a/tools/benchmarks/get_deps.py
+++ b/tools/benchmarks/get_deps.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/build_aosp.py b/tools/build_aosp.py
index b62ec02..5475830 100755
--- a/tools/build_aosp.py
+++ b/tools/build_aosp.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/build_r8lib.py b/tools/build_r8lib.py
index 980715e..8dc0cb8 100755
--- a/tools/build_r8lib.py
+++ b/tools/build_r8lib.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/build_sample_apk.py b/tools/build_sample_apk.py
index 376c5c0..67ead8c 100755
--- a/tools/build_sample_apk.py
+++ b/tools/build_sample_apk.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/checkout_aosp.py b/tools/checkout_aosp.py
index 268b1b3..36a5333 100755
--- a/tools/checkout_aosp.py
+++ b/tools/checkout_aosp.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/compare_apk_sizes.py b/tools/compare_apk_sizes.py
index 9a56187..895633e 100755
--- a/tools/compare_apk_sizes.py
+++ b/tools/compare_apk_sizes.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/compare_cts_results.py b/tools/compare_cts_results.py
index 00901ec..0da116d 100755
--- a/tools/compare_cts_results.py
+++ b/tools/compare_cts_results.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/compatdx.py b/tools/compatdx.py
index c4cb320..d58b907 100755
--- a/tools/compatdx.py
+++ b/tools/compatdx.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/compatproguard.py b/tools/compatproguard.py
index 10542e3..7c56549 100755
--- a/tools/compatproguard.py
+++ b/tools/compatproguard.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/compiledump.py b/tools/compiledump.py
index a810cdc..a7809ac 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/create_art_tests.py b/tools/create_art_tests.py
index a64fe71..0c59ae9 100755
--- a/tools/create_art_tests.py
+++ b/tools/create_art_tests.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/create_dx_replay.py b/tools/create_dx_replay.py
index cb27a30..8dfb0a0 100755
--- a/tools/create_dx_replay.py
+++ b/tools/create_dx_replay.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
@@ -81,7 +81,7 @@
 
   # create the first lines of the replay script
   replay_script = \
-"""#!/usr/bin/env python
+"""#!/usr/bin/env python3
 import os
 import shutil
 import subprocess
diff --git a/tools/create_maven_release.py b/tools/create_maven_release.py
index 01aebe6..b233257 100755
--- a/tools/create_maven_release.py
+++ b/tools/create_maven_release.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
@@ -230,7 +230,7 @@
   dependency_lines = []
   collect = False
   for line in dependencies.splitlines():
-    if 'runtimeClasspath' in line and "'main'" in line:
+    if line and 'runtimeClasspath' in line and "'main'" in line:
       collect = True
       continue
     if collect:
diff --git a/tools/d8.py b/tools/d8.py
index 833f7f5..766e690 100755
--- a/tools/d8.py
+++ b/tools/d8.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/d8logger.py b/tools/d8logger.py
index 9fb3508..b523294 100755
--- a/tools/d8logger.py
+++ b/tools/d8logger.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/dex2oat.py b/tools/dex2oat.py
index e21dc7d..7c5a31e 100755
--- a/tools/dex2oat.py
+++ b/tools/dex2oat.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/dexfilemerger.py b/tools/dexfilemerger.py
index 7bdfc22..de4e6ae 100755
--- a/tools/dexfilemerger.py
+++ b/tools/dexfilemerger.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/dexsegments.py b/tools/dexsegments.py
index f984063..7b901de 100755
--- a/tools/dexsegments.py
+++ b/tools/dexsegments.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/dexsplitter.py b/tools/dexsplitter.py
index 415b149..a97d089 100755
--- a/tools/dexsplitter.py
+++ b/tools/dexsplitter.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/disasm.py b/tools/disasm.py
index 368a78f..e37ac4c 100755
--- a/tools/disasm.py
+++ b/tools/disasm.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/download_all_benchmark_dependencies.py b/tools/download_all_benchmark_dependencies.py
index ab29ebd..ed940a3 100755
--- a/tools/download_all_benchmark_dependencies.py
+++ b/tools/download_all_benchmark_dependencies.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/download_kotlin_dev.py b/tools/download_kotlin_dev.py
index 6360cd2..5d2696a 100755
--- a/tools/download_kotlin_dev.py
+++ b/tools/download_kotlin_dev.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/emulator_aosp.py b/tools/emulator_aosp.py
index f63c673..d5f80dc 100755
--- a/tools/emulator_aosp.py
+++ b/tools/emulator_aosp.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/extractmarker.py b/tools/extractmarker.py
index 36a9c88..b50a2fd 100755
--- a/tools/extractmarker.py
+++ b/tools/extractmarker.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/find_haning_test.py b/tools/find_haning_test.py
index 29e78aa..786fbcf 100755
--- a/tools/find_haning_test.py
+++ b/tools/find_haning_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/git_sync_cl_chain.py b/tools/git_sync_cl_chain.py
index 09da33e..7dc512e 100755
--- a/tools/git_sync_cl_chain.py
+++ b/tools/git_sync_cl_chain.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/git_utils.py b/tools/git_utils.py
index 9d9c6bd..1e3e7c5 100644
--- a/tools/git_utils.py
+++ b/tools/git_utils.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/golem.py b/tools/golem.py
index eb9bbee..6a4e399 100755
--- a/tools/golem.py
+++ b/tools/golem.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/golem_build.py b/tools/golem_build.py
index c691293..4f668d6 100755
--- a/tools/golem_build.py
+++ b/tools/golem_build.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
@@ -8,26 +8,16 @@
 import sys
 
 import gradle
-import retrace_benchmark
 import run_benchmark
 import run_on_app_dump
 
 GRADLE_ARGS = ['--no-daemon', '-Pno_internal']
 
-LEGACY_BUILD_TARGETS = [
-  'R8',
-  'D8',
-  'buildExampleJars',
-  'downloadAndroidCts',
-  'downloadDx']
-
 def lower(items):
   return [ item.lower() for item in items ]
 
 def Main():
   targets = set()
-  targets.update(lower(LEGACY_BUILD_TARGETS))
-  targets.update(lower(retrace_benchmark.GOLEM_BUILD_TARGETS))
   targets.update(lower(run_benchmark.GOLEM_BUILD_TARGETS))
   targets.update(lower(run_on_app_dump.GOLEM_BUILD_TARGETS))
   cmd = GRADLE_ARGS + [target for target in targets]
diff --git a/tools/gradle.py b/tools/gradle.py
index 63c92b5..19fa773 100755
--- a/tools/gradle.py
+++ b/tools/gradle.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
@@ -96,7 +96,7 @@
   cmd.extend(args)
   utils.PrintCmd(cmd)
   with utils.ChangedWorkingDirectory(cwd):
-    return subprocess.check_output(cmd, env=GetJavaEnv(env))
+    return subprocess.check_output(cmd, env=GetJavaEnv(env)).decode('utf-8')
 
 def RunGradleWrapperInGetOutput(args, cwd, env=None):
   return RunGradleInGetOutput('./gradlew', args, cwd, env=env)
diff --git a/tools/historic_memory_usage.py b/tools/historic_memory_usage.py
index bb08a2e..3ef2268 100755
--- a/tools/historic_memory_usage.py
+++ b/tools/historic_memory_usage.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/historic_run.py b/tools/historic_run.py
index b0f181f..eaaadd5 100755
--- a/tools/historic_run.py
+++ b/tools/historic_run.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/inspect.py b/tools/inspect.py
index 6d655da..80c1ecd 100755
--- a/tools/inspect.py
+++ b/tools/inspect.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/internal_test.py b/tools/internal_test.py
index dae6247..0d591ad 100755
--- a/tools/internal_test.py
+++ b/tools/internal_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
@@ -166,7 +166,7 @@
 
 def ensure_git_clean():
   # Ensure clean git repo.
-  diff = subprocess.check_output(['git', 'diff'])
+  diff = subprocess.check_output(['git', 'diff']).decode('utf-8')
   if len(diff) > 0:
     log('Local modifications to the git repo, exiting')
     sys.exit(1)
@@ -328,13 +328,13 @@
   if archive:
     archive_log(stdout, stderr, exitcode, timed_out, cmd)
   else:
-    print 'Execution of %s resulted in:' % cmd
-    print 'exit code: %s ' % exitcode
-    print 'timeout: %s ' % timed_out
+    print('Execution of %s resulted in:' % cmd)
+    print('exit code: %s ' % exitcode)
+    print('timeout: %s ' % timed_out)
     with open(stderr, 'r') as f:
-      print 'stderr: %s' % f.read()
+      print('stderr: %s' % f.read())
     with open(stdout, 'r') as f:
-      print 'stdout: %s' % f.read()
+      print('stdout: %s' % f.read())
 
 def execute(cmd, archive, env=None):
   if cmd == []:
diff --git a/tools/jardiff.py b/tools/jardiff.py
index 894131c..18587ab 100755
--- a/tools/jardiff.py
+++ b/tools/jardiff.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/java.py b/tools/java.py
index 3abfcdd..fb0ef13 100755
--- a/tools/java.py
+++ b/tools/java.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/javac.py b/tools/javac.py
index 0d8ac05..242fac0 100755
--- a/tools/javac.py
+++ b/tools/javac.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/jdk.py b/tools/jdk.py
index b3af7a6..23cacc8 100755
--- a/tools/jdk.py
+++ b/tools/jdk.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/keeprule_benchmark.py b/tools/keeprule_benchmark.py
index 0a7e063..4b843c6 100755
--- a/tools/keeprule_benchmark.py
+++ b/tools/keeprule_benchmark.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/maindex.py b/tools/maindex.py
index ff5329a..735ced5 100755
--- a/tools/maindex.py
+++ b/tools/maindex.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/maven_mirror.py b/tools/maven_mirror.py
index 43ca31e..8a7f4aa 100755
--- a/tools/maven_mirror.py
+++ b/tools/maven_mirror.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/minify_tool.py b/tools/minify_tool.py
index a512665..cef72e5 100755
--- a/tools/minify_tool.py
+++ b/tools/minify_tool.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/notify.py b/tools/notify.py
index b195357..34676ba 100644
--- a/tools/notify.py
+++ b/tools/notify.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/performance_try.py b/tools/performance_try.py
index 5830cea..0c09e70 100755
--- a/tools/performance_try.py
+++ b/tools/performance_try.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/printseeds.py b/tools/printseeds.py
index 4f389d5..6c0ee8c 100755
--- a/tools/printseeds.py
+++ b/tools/printseeds.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/printuses.py b/tools/printuses.py
index 17d3df1..5253796 100755
--- a/tools/printuses.py
+++ b/tools/printuses.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/proguard.py b/tools/proguard.py
index 5984622..4a3ae7d 100755
--- a/tools/proguard.py
+++ b/tools/proguard.py
@@ -1,16 +1,13 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
 
 # Run ProGuard, Google's internal version
 
-from __future__ import print_function
-
 import os
 import subprocess
 import sys
-from exceptions import ValueError
 
 import jdk
 import utils
diff --git a/tools/r8.py b/tools/r8.py
index 60c60a0..31abcf6 100755
--- a/tools/r8.py
+++ b/tools/r8.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/r8bisect.py b/tools/r8bisect.py
index 74c880e..1b35eb3 100755
--- a/tools/r8bisect.py
+++ b/tools/r8bisect.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/retrace_benchmark.py b/tools/retrace_benchmark.py
deleted file mode 100755
index 42ed792..0000000
--- a/tools/retrace_benchmark.py
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-import argparse
-import os
-import subprocess
-import sys
-import time
-
-import golem
-import jdk
-import proguard
-import utils
-
-GOLEM_BUILD_TARGETS = ['R8Lib']
-RETRACERS = ['r8', 'proguard', 'remapper']
-
-def parse_arguments(argv):
-  parser = argparse.ArgumentParser(
-                    description = 'Run r8 retrace bootstrap benchmarks.')
-  parser.add_argument('--golem',
-                    help = 'Link in third party dependencies.',
-                    default = False,
-                    action = 'store_true')
-  parser.add_argument('--ignore-java-version',
-                    help='Do not check java version',
-                    default=False,
-                    action='store_true')
-  parser.add_argument('--print-runtimeraw',
-                    metavar='BENCHMARKNAME',
-                    help='Print the line \'<BENCHMARKNAME>(RunTimeRaw):' +
-                        ' <elapsed> ms\' at the end where <elapsed> is' +
-                        ' the elapsed time in milliseconds.')
-  parser.add_argument('--retracer',
-                    help='The retracer to use',
-                    choices=RETRACERS,
-                    required=True)
-  parser.add_argument('--download-benchmarks',
-                      help='Download retrace benchmarks',
-                      default=False,
-                      action='store_true')
-  options = parser.parse_args(argv)
-  return options
-
-def download_benchmarks():
-  utils.DownloadFromGoogleCloudStorage(
-      os.path.join(utils.THIRD_PARTY, 'retrace_benchmark') + '.tar.gz.sha1')
-
-def run_retrace(options, temp):
-  if options.download_benchmarks:
-    download_benchmarks()
-  if options.retracer == 'r8':
-    retracer_args = [
-        '-cp', utils.R8LIB_JAR, 'com.android.tools.r8.retrace.Retrace']
-  elif options.retracer == 'proguard':
-    retracer_args = ['-jar', proguard.getRetraceJar()]
-  elif options.retracer == 'remapper':
-    retracer_args = ['-jar',
-                     os.path.join(
-                        utils.THIRD_PARTY,
-                        'remapper',
-                        'remapper_deploy.jar')]
-  else:
-    assert False, "Unexpected retracer " + options.retracer
-  retrace_args = [jdk.GetJavaExecutable()] + retracer_args + [
-    os.path.join(utils.THIRD_PARTY, 'retrace_benchmark', 'r8lib.jar.map'),
-    os.path.join(utils.THIRD_PARTY, 'retrace_benchmark', 'stacktrace.txt')]
-  utils.PrintCmd(retrace_args)
-  t0 = time.time()
-  subprocess.check_call(retrace_args)
-  t1 = time.time()
-  if options.print_runtimeraw:
-    print('{}(RunTimeRaw): {} ms'
-        .format(options.print_runtimeraw, 1000.0 * (t1 - t0)))
-
-
-if __name__ == '__main__':
-  options = parse_arguments(sys.argv[1:])
-  if options.golem:
-    golem.link_third_party()
-  if not options.ignore_java_version:
-    utils.check_java_version()
-  with utils.TempDir() as temp:
-    run_retrace(options, temp)
diff --git a/tools/run-d8-on-gmscore.py b/tools/run-d8-on-gmscore.py
index 4a64693..c133bce 100755
--- a/tools/run-d8-on-gmscore.py
+++ b/tools/run-d8-on-gmscore.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/run-jdwp-tests.py b/tools/run-jdwp-tests.py
index f40f2ce..28da012 100755
--- a/tools/run-jdwp-tests.py
+++ b/tools/run-jdwp-tests.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
@@ -121,7 +121,7 @@
   cmd.extend(get_debuggee_flags(version))
   cmd.extend(args)
   setup_environment(version)
-  print "Running debuggee as:", cmd
+  print("Running debuggee as: %s" % cmd)
   return subprocess.check_call(cmd)
 
 def runDebugger(version, classpath, args):
@@ -136,14 +136,14 @@
              (dalvikvm, ' '.join(get_debuggee_flags(version))))
   cmd.extend(args)
   setup_environment(version)
-  print "Running debugger as:", cmd
+  print("Running debugger as: " % cmd)
   return subprocess.check_call(cmd)
 
 def usage():
-  print "Usage: %s [--debuggee] [--version=<version>] [--classpath=<classpath>] <args>" % (sys.argv[0])
-  print "where <version> is one of:", ', '.join(VERSIONS)
-  print "  and <classpath> is optional classpath (default: %s)" % JDWP_TESTS_HOSTDEX
-  print "  and <args> will be passed on as arguments to the art runtime."
+  print("Usage: %s [--debuggee] [--version=<version>] [--classpath=<classpath>] <args>" % (sys.argv[0]))
+  print("where <version> is one of:", ', '.join(VERSIONS))
+  print("  and <classpath> is optional classpath (default: %s)" % JDWP_TESTS_HOSTDEX)
+  print("  and <args> will be passed on as arguments to the art runtime.")
 
 def main():
   version = 'default'
@@ -161,7 +161,7 @@
     else:
       args.append(arg)
   if version not in VERSIONS:
-    print "Invalid version", version
+    print("Invalid version %s" % version)
     usage()
     return 1
   if not debuggee and len(args) == 0:
diff --git a/tools/run-r8-on-gmscore.py b/tools/run-r8-on-gmscore.py
index ee8b813..280941e 100755
--- a/tools/run-r8-on-gmscore.py
+++ b/tools/run-r8-on-gmscore.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/run_benchmark.py b/tools/run_benchmark.py
index d5d4fc0..507442e 100755
--- a/tools/run_benchmark.py
+++ b/tools/run_benchmark.py
@@ -99,6 +99,7 @@
     'com.android.tools.r8.benchmarks.BenchmarkMainEntryRunner',
     options.benchmark,
     options.target,
+    'golem' if options.golem else 'local',
     ])
   return subprocess.check_call(cmd)
 
diff --git a/tools/run_bootstrap_benchmark.py b/tools/run_bootstrap_benchmark.py
deleted file mode 100755
index bc07347..0000000
--- a/tools/run_bootstrap_benchmark.py
+++ /dev/null
@@ -1,98 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2018, 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.
-
-import argparse
-import os
-import sys
-
-import golem
-import minify_tool
-import toolhelper
-import utils
-
-MEMORY_XMX_LIMIT_BENCHMARK = 270
-
-def parse_arguments(argv):
-  parser = argparse.ArgumentParser(
-      description = 'Run r8 bootstrap benchmarks.')
-  parser.add_argument('--golem',
-      help = 'Link in third party dependencies.',
-      default = False,
-      action = 'store_true')
-  parser.add_argument('--limit-memory-runtime-test',
-      help = 'Run in a specific memory limit.',
-      default = False,
-      action = 'store_true')
-  return parser.parse_args(argv)
-
-
-def dex(input, output):
-  return_code = toolhelper.run(
-      'd8', [
-        input,
-        '--output', output,
-        '--lib', utils.RT_JAR,
-        '--min-api', '10000',
-        '--no-desugaring',
-      ],
-      debug=False,
-      build=False)
-  if return_code != 0:
-    sys.exit(return_code)
-
-if __name__ == '__main__':
-  options = parse_arguments(sys.argv[1:])
-  if options.golem:
-    golem.link_third_party()
-  with utils.TempDir() as temp:
-    memory_file = os.path.join(temp, 'memory.dump')
-    r8_output = os.path.join(temp, 'r8.zip')
-    d8_r8_output = os.path.join(temp, 'd8r8.zip')
-    d8_pg_output = os.path.join(temp, 'd8pg.zip')
-
-    run_memory_test = options.limit_memory_runtime_test
-
-    java_args = (['-Xmx%sM' % MEMORY_XMX_LIMIT_BENCHMARK]
-                 if run_memory_test else [])
-
-    benchmark_name = "MemoryR8Pinned" if run_memory_test else "BootstrapR8"
-
-    return_code = minify_tool.minify_tool(
-      input_jar=utils.PINNED_R8_JAR,
-      output_jar=r8_output,
-      debug=False,
-      build=False,
-      track_memory_file=memory_file,
-      benchmark_name=benchmark_name,
-      java_args=java_args)
-
-    if return_code != 0:
-      sys.exit(return_code)
-
-    if run_memory_test:
-      # We are not tracking code-size, so return early.
-      sys.exit(0)
-
-    dex(r8_output, d8_r8_output)
-    print "BootstrapR8(CodeSize):", utils.uncompressed_size(r8_output)
-    print "BootstrapR8Dex(CodeSize):", utils.uncompressed_size(d8_r8_output)
-
-    dex(utils.PINNED_PGR8_JAR, d8_pg_output)
-    print "BootstrapR8PG(CodeSize):", utils.uncompressed_size(
-        utils.PINNED_PGR8_JAR)
-    print "BootstrapR8PGDex(CodeSize):", utils.uncompressed_size(d8_pg_output)
-
-    r8_notreeshaking_output = os.path.join(temp, 'r8-notreeshaking.zip')
-    return_code = minify_tool.minify_tool(
-      input_jar=utils.PINNED_R8_JAR,
-      output_jar=r8_notreeshaking_output,
-      debug=False,
-      build=False,
-      benchmark_name="BootstrapR8NoTreeShaking",
-      additional_args=["--no-tree-shaking"])
-    if return_code != 0:
-      sys.exit(return_code)
-
-  sys.exit(0)
diff --git a/tools/run_kotlin_benchmarks.py b/tools/run_kotlin_benchmarks.py
index 166c3e9..ce0f1ad 100755
--- a/tools/run_kotlin_benchmarks.py
+++ b/tools/run_kotlin_benchmarks.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/sanitize_libraries.py b/tools/sanitize_libraries.py
index 8ca54a7..bc61d50 100755
--- a/tools/sanitize_libraries.py
+++ b/tools/sanitize_libraries.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/test.py b/tools/test.py
index 56f30e7..ec4bb00 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
@@ -219,6 +219,7 @@
 
   if utils.is_bot():
     gradle.RunGradle(['--no-daemon', 'clean'])
+    print('Running with python ' + str(sys.version_info))
 
   desugar_jdk_json_dir = None
   if options.desugared_library_configuration:
diff --git a/tools/test_android_cts.py b/tools/test_android_cts.py
index fa814bc..f98d724 100755
--- a/tools/test_android_cts.py
+++ b/tools/test_android_cts.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/test_framework.py b/tools/test_framework.py
deleted file mode 100755
index 99e0719..0000000
--- a/tools/test_framework.py
+++ /dev/null
@@ -1,128 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-# Run D8 or DX on 'third_party/framework/framework_<version>.jar'.
-# Report Golem-compatible CodeSize and RunTimeRaw values:
-#
-#     <NAME>-Total(CodeSize): <size>
-#     <NAME>-Total(RunTimeRaw>: <time> ms
-#
-# and also detailed segment sizes for each dex segment:
-#
-#    <NAME>-Code(CodeSize): <size>
-#    <NAME>-AnnotationSets(CodeSize): <size>
-#    ...
-#
-# Uses the DexSegment Java tool (Gradle target).
-
-from __future__ import print_function
-from glob import glob
-import argparse
-import golem
-import jdk
-import os
-import re
-import subprocess
-import sys
-import time
-
-import utils
-
-DX_JAR = os.path.join(utils.REPO_ROOT, 'tools', 'linux', 'dx', 'framework',
-    'dx.jar')
-FRAMEWORK_JAR = os.path.join('third_party', 'framework',
-    'framework_14082017_desugared.jar')
-MIN_SDK_VERSION = '24'
-
-def parse_arguments():
-  parser = argparse.ArgumentParser(
-      description = 'Run D8 or DX'
-          ' third_party/framework/framework*.jar.'
-          ' Report Golem-compatible CodeSize and RunTimeRaw values.')
-  parser.add_argument('--tool',
-      choices = ['dx', 'd8', 'd8-release'],
-      required = True,
-      help = 'Compiler tool to use.')
-  parser.add_argument('--golem',
-      help = 'Running on golem, link in third_party resources.',
-      default = False,
-      action = 'store_true')
-  parser.add_argument('--name',
-      required = True,
-      help = 'Results will be printed using the specified benchmark name (e.g.'
-          ' <NAME>-<segment>(CodeSize): <bytes>), the full size is reported'
-          ' with <NAME>-Total(CodeSize)')
-  parser.add_argument('--print-memoryuse',
-      help = 'Prints the line \'<NAME>-Total(MemoryUse):' 
-          ' <mem>\' at the end where <mem> is the peak'
-          ' peak resident set size (VmHWM) in bytes.',
-      default = False,
-      action = 'store_true')
-  parser.add_argument('--output',
-      help = 'Output directory to keep the generated files')
-  return parser.parse_args()
-
-def Main():
-  args = parse_arguments()
-  if args.golem:
-    golem.link_third_party()
-  utils.check_java_version()
-  output_dir = args.output
-  with utils.TempDir() as temp_dir:
-
-    if not output_dir:
-      output_dir = temp_dir
-
-    xmx = None
-    if args.tool == 'dx':
-      tool_file = DX_JAR
-      tool_args = ['--dex', '--output=' + output_dir, '--multi-dex',
-                   '--min-sdk-version=' + MIN_SDK_VERSION]
-      xmx = '-Xmx1600m'
-    else:
-      tool_file = utils.D8_JAR
-      tool_args = ['--output', output_dir, '--min-api', MIN_SDK_VERSION]
-      if args.tool == 'd8-release':
-        tool_args.append('--release')
-      xmx = '-Xmx600m'
-
-    cmd = []
-
-    track_memory_file = None
-    if args.print_memoryuse:
-      track_memory_file = os.path.join(output_dir, utils.MEMORY_USE_TMP_FILE)
-      cmd.extend(['tools/track_memory.sh', track_memory_file])
-
-    if tool_file.endswith('.jar'):
-      assert xmx is not None
-      cmd.extend([jdk.GetJavaExecutable(), xmx, '-jar'])
-
-    cmd.extend([tool_file] + tool_args + [FRAMEWORK_JAR])
-
-    utils.PrintCmd(cmd)
-
-    t0 = time.time()
-    subprocess.check_call(cmd)
-    dt = time.time() - t0
-
-    if args.print_memoryuse:
-      print('{}-Total(MemoryUse): {}'
-          .format(args.name, utils.grep_memoryuse(track_memory_file)))
-
-    dex_files = [f for f in glob(os.path.join(output_dir, '*.dex'))]
-    code_size = 0
-    for dex_file in dex_files:
-      code_size += os.path.getsize(dex_file)
-
-    print('{}-Total(RunTimeRaw): {} ms'
-      .format(args.name, 1000.0 * dt))
-
-    print('{}-Total(CodeSize): {}'
-      .format(args.name, code_size))
-
-    utils.print_dexsegments(args.name, dex_files)
-
-if __name__ == '__main__':
-  sys.exit(Main())
diff --git a/tools/test_gradle_benchmarks.py b/tools/test_gradle_benchmarks.py
index b7c53b8..d9c00e6 100755
--- a/tools/test_gradle_benchmarks.py
+++ b/tools/test_gradle_benchmarks.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/test_helloexample.py b/tools/test_helloexample.py
deleted file mode 100755
index f81e64c..0000000
--- a/tools/test_helloexample.py
+++ /dev/null
@@ -1,254 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-# Run R8 on a simple Hello World program
-# Report Golem-compatible RunTimeRaw values:
-#
-#     <NAME>-Total(RunTimeRaw): <time> ms
-#
-# where <NAME> is Hello{,Dex}{,Large}{,NoOpt}
-
-import argparse
-import os
-import subprocess
-import sys
-import time
-import zipfile
-
-import golem
-import jdk
-import proguard
-import utils
-
-HELLO_JAR = os.path.join(utils.BUILD, 'test', 'examples', 'hello.jar')
-
-EXTRA_INPUTS = [
-  os.path.join(utils.THIRD_PARTY, 'sample_libraries', lib) for lib in [
-    'animal-sniffer-annotations-1.17.jar',
-    'annotations-13.0.jar',
-    'checker-compat-qual-2.5.2.jar',
-    'collections-28.0.0.jar',
-    'common-1.1.1.jar',
-    'commons-collections4-4.3.jar',
-    'commons-compress-1.18.jar',
-    'commons-lang3-3.8.1.jar',
-    'commons-math3-3.6.1.jar',
-    'constraint-layout-solver-1.1.3.jar',
-    'converter-gson-2.5.0.jar',
-    'dagger-2.22.1.jar',
-    'error_prone_annotations-2.2.0.jar',
-    'failureaccess-1.0.1.jar',
-    'gson-2.8.2.jar',
-    'guava-27.1-android.jar',
-    'j2objc-annotations-1.1.jar',
-    'javax.inject-1.jar',
-    'jsr305-3.0.2.jar',
-    'kotlin-stdlib-1.3.21.jar',
-    'kotlin-stdlib-common-1.3.21.jar',
-    'kotlin-stdlib-jdk7-1.3.21.jar',
-    'listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar',
-    'okhttp-3.14.0.jar',
-    'okio-1.17.2.jar',
-    'play-services-ads-17.2.0-javadoc.jar',
-    'play-services-ads-base-17.2.0-javadoc.jar',
-    'play-services-ads-lite-17.2.0-javadoc.jar',
-    'play-services-analytics-16.0.8-javadoc.jar',
-    'play-services-analytics-impl-16.0.8-javadoc.jar',
-    'play-services-base-16.1.0-javadoc.jar',
-    'play-services-basement-16.2.0-javadoc.jar',
-    'play-services-cast-16.1.2-javadoc.jar',
-    'play-services-drive-16.1.0-javadoc.jar',
-    'play-services-fitness-16.0.1-javadoc.jar',
-    'play-services-games-17.0.0-javadoc.jar',
-    'play-services-gass-17.2.0-javadoc.jar',
-    'play-services-gcm-16.1.0-javadoc.jar',
-    'play-services-iid-16.0.1-javadoc.jar',
-    'play-services-measurement-16.4.0-javadoc.jar',
-    'play-services-measurement-api-16.4.0-javadoc.jar',
-    'play-services-measurement-base-16.4.0-javadoc.jar',
-    'play-services-measurement-impl-16.4.0-javadoc.jar',
-    'play-services-measurement-sdk-16.4.0-javadoc.jar',
-    'play-services-measurement-sdk-api-16.4.0-javadoc.jar',
-    'play-services-tagmanager-v4-impl-16.0.8-javadoc.jar',
-    'play-services-vision-17.0.2-javadoc.jar',
-    'play-services-vision-common-17.0.2-javadoc.jar',
-    'protobuf-lite-3.0.1.jar',
-    'reactive-streams-1.0.2.jar',
-    'retrofit-2.5.0.jar',
-    'rxjava-2.2.8.jar',
-    'support-annotations-28.0.0.jar',
-  ]
-]
-
-EXTRA_KEEP_RULES = ['-dontwarn java.lang.ClassValue']
-
-def parse_arguments():
-  parser = argparse.ArgumentParser(
-      description = 'Compile a hello world example program')
-  parser.add_argument('--tool',
-                      choices = ['d8', 'r8'] + proguard.getVersions(),
-                      required = True,
-                      help = 'Compiler tool to use.')
-  parser.add_argument('--output-mode',
-                      choices = ['dex', 'cf'],
-                      required = True,
-                      help = 'Output mode to compile to.')
-  parser.add_argument('--golem',
-                      help = 'Running on golem, link in third_party resources.',
-                      default = False,
-                      action = 'store_true')
-  parser.add_argument('--large',
-                      help = 'Add many additional program inputs.',
-                      default = False,
-                      action = 'store_true')
-  parser.add_argument('--noopt',
-                      help = 'Disable most optimizations/processing.',
-                      default = False,
-                      action = 'store_true')
-  parser.add_argument('--print-memoryuse',
-                      help = 'Prints the line \'<NAME>-Total(MemoryUse):'
-                             ' <mem>\' at the end where <mem> is the peak'
-                             ' peak resident set size (VmHWM) in bytes.',
-                      default = False,
-                      action = 'store_true')
-  parser.add_argument('--output',
-                      help = 'Output directory to keep the generated files')
-  return parser.parse_args()
-
-def GetConfRules(extra, noopt):
-  rules = ['-keep class hello.Hello { void main(java.lang.String[]); }']
-  if len(extra) > 0:
-    rules.extend(EXTRA_KEEP_RULES)
-  if noopt:
-    rules.extend([
-      '-dontoptimize',
-      '-dontshrink',
-      '-dontobfuscate',
-      '-keepattributes *',
-    ])
-  return rules
-
-def GetCompilerPrefix(tool, mode, output, input, lib, extra, noopt):
-  return [
-    jdk.GetJavaExecutable(),
-    '-jar', utils.R8_JAR if tool == 'r8' else utils.D8_JAR,
-    '--output', output,
-    '--lib', lib,
-    '--debug' if noopt else '--release',
-    input,
-  ] + ([] if mode == 'cf' else ['--min-api', '21']) + extra
-
-def Compile(tool, output_mode, lib, extra, output_dir, noopt, temp_dir):
-  output = os.path.join(output_dir, 'out.zip')
-  if tool == 'd8':
-    if output_mode != 'dex':
-      raise ValueError('Invalid output mode for D8')
-    classpath = []
-    for cp_entry in extra:
-      classpath.extend(['--classpath', cp_entry])
-    return [
-      GetCompilerPrefix(
-        tool, output_mode, output, HELLO_JAR, lib, classpath, noopt)
-    ]
-  # The compilation is either R8 or PG.
-  # Write keep rules to a temporary file.
-  rules = GetConfRules(extra, noopt)
-  rules_file = os.path.join(temp_dir, 'rules.conf')
-  open(rules_file, 'w').write('\n'.join(rules))
-  if tool == 'r8':
-    cmd = GetCompilerPrefix(
-        tool, output_mode, output, HELLO_JAR, lib, extra, noopt)
-    cmd.extend(['--pg-conf', rules_file])
-    if output_mode == 'cf':
-      cmd.append('--classfile')
-    return [cmd]
-  if proguard.isValidVersion(tool):
-    # Build PG invokation with additional rules to silence warnings.
-    pg_out = output if output_mode == 'cf' \
-      else os.path.join(output_dir, 'pgout.zip')
-    cmds = [proguard.getCmd([
-      '-injars', ':'.join([HELLO_JAR] + extra),
-      '-libraryjars', lib,
-      '-outjars', pg_out,
-      '-dontwarn **',
-      '@' + rules_file
-    ], version=tool)]
-    if output_mode == 'dex':
-      cmds.append(
-          GetCompilerPrefix('d8', 'dex', output, pg_out, lib, [], noopt))
-    return cmds
-  raise ValueError('Unknown tool: ' + tool)
-
-def ProcessInput(input, tmp_dir):
-  if not input.endswith('.aar'):
-    return input
-  out_dir = os.path.join(tmp_dir, input)
-  os.makedirs(out_dir)
-  zip = zipfile.ZipFile(input, 'r')
-  zip.extractall(out_dir)
-  zip.close()
-  return os.path.join(out_dir, 'classes.jar')
-
-def Main():
-  args = parse_arguments()
-  if args.golem:
-    golem.link_third_party()
-  utils.check_java_version()
-
-  with utils.TempDir() as temp_dir:
-    cmd_prefix = []
-    output_dir = args.output if args.output else temp_dir
-    temp_dir = os.path.join(args.output, 'tmp') if args.output else temp_dir
-
-    track_memory_file = None
-    if args.print_memoryuse:
-      track_memory_file = os.path.join(output_dir, utils.MEMORY_USE_TMP_FILE)
-      cmd_prefix.extend(['tools/track_memory.sh', track_memory_file])
-
-    name = 'CompileHelloExample'
-
-    tool = args.tool
-    output_mode = args.output_mode
-    lib = None
-    if output_mode == 'dex':
-      name += 'Dex'
-      lib = utils.get_android_jar(28)
-    else:
-      lib = utils.RT_JAR
-
-    extra = []
-    if args.large:
-      name += 'Large'
-      extra = EXTRA_INPUTS
-
-    if args.noopt:
-      name += 'NoOpt'
-
-    cmds = Compile(
-      tool,
-      output_mode,
-      lib,
-      extra,
-      output_dir,
-      args.noopt,
-      temp_dir,
-    )
-
-    t0 = time.time()
-    for cmd in cmds:
-      fullcmd = cmd_prefix + cmd
-      utils.PrintCmd(fullcmd)
-      subprocess.check_output(fullcmd)
-    dt = time.time() - t0
-
-    if args.print_memoryuse:
-      print('{}(MemoryUse): {}'
-            .format(name, utils.grep_memoryuse(track_memory_file)))
-
-    print('{}(RunTimeRaw): {} ms'
-          .format(name, 1000.0 * dt))
-
-if __name__ == '__main__':
-  sys.exit(Main())
diff --git a/tools/test_r8cfsegments.py b/tools/test_r8cfsegments.py
index 426718a..be8e49c 100755
--- a/tools/test_r8cfsegments.py
+++ b/tools/test_r8cfsegments.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/trigger.py b/tools/trigger.py
index 33b44a7..2b9816a 100755
--- a/tools/trigger.py
+++ b/tools/trigger.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/update_prebuilds_in_android.py b/tools/update_prebuilds_in_android.py
index 3f30964..6f8da19 100755
--- a/tools/update_prebuilds_in_android.py
+++ b/tools/update_prebuilds_in_android.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/upload_to_x20.py b/tools/upload_to_x20.py
index 2d08362..3a1ef77 100755
--- a/tools/upload_to_x20.py
+++ b/tools/upload_to_x20.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/tools/utils.py b/tools/utils.py
index eb7849b..d616707 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -142,7 +142,7 @@
       'show',
       tag_or_hash,
       '-s',
-      '--format=oneline']).splitlines()[-1].split()
+      '--format=oneline']).decode('utf-8').splitlines()[-1].split()
   # The info should be on the following form [hash,"Version",version]
   if len(info) == 3 and len(info[0]) == 40 and info[1] == "Version":
     return info[2]
@@ -310,7 +310,7 @@
 
 def is_main():
   remotes = subprocess.check_output(['git', 'branch', '-r', '--contains',
-                                     'HEAD'])
+                                     'HEAD']).decode('utf-8')
   return 'origin/main' in remotes
 
 def get_HEAD_sha1():
@@ -359,13 +359,13 @@
 def ls_files_on_cloud_storage(destination):
   cmd = [get_gsutil(), 'ls', destination]
   PrintCmd(cmd)
-  return subprocess.check_output(cmd)
+  return subprocess.check_output(cmd).decode('utf-8')
 
 def cat_file_on_cloud_storage(destination, ignore_errors=False):
   cmd = [get_gsutil(), 'cat', destination]
   PrintCmd(cmd)
   try:
-    return subprocess.check_output(cmd)
+    return subprocess.check_output(cmd).decode('utf-8').strip()
   except subprocess.CalledProcessError as e:
     if ignore_errors:
       return ''
@@ -535,7 +535,7 @@
          'com.android.tools.r8.cf_segments.MeasureLib',
          cfFile]
   PrintCmd(cmd)
-  output = subprocess.check_output(cmd)
+  output = subprocess.check_output(cmd).decode('utf-8')
 
   matches = DEX_SEGMENTS_RESULT_PATTERN.findall(output)
 
@@ -567,8 +567,8 @@
 # Ensure that we are not benchmarking with a google jvm.
 def check_java_version():
   cmd= [jdk.GetJavaExecutable(), '-version']
-  output = subprocess.check_output(cmd, stderr = subprocess.STDOUT)
-  m = re.search('openjdk version "([^"]*)"', output.decode('utf-8'))
+  output = subprocess.check_output(cmd, stderr = subprocess.STDOUT).decode('utf-8')
+  m = re.search('openjdk version "([^"]*)"', output)
   if m is None:
     raise Exception("Can't check java version: no version string in output"
         " of 'java -version': '{}'".format(output))
@@ -604,7 +604,7 @@
 def getR8Version(path):
   cmd = [jdk.GetJavaExecutable(), '-cp', path, 'com.android.tools.r8.R8',
         '--version']
-  output = subprocess.check_output(cmd, stderr = subprocess.STDOUT)
+  output = subprocess.check_output(cmd, stderr = subprocess.STDOUT).decode('utf-8')
   # output is of the form 'R8 <version> (with additional info)'
   # so we split on '('; clean up tailing spaces; and strip off 'R8 '.
   return output.split('(')[0].strip()[3:]
diff --git a/tools/utils_aosp.py b/tools/utils_aosp.py
index 3e2a715..216e43d 100644
--- a/tools/utils_aosp.py
+++ b/tools/utils_aosp.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.