Merge commit 'f0e2cd9064daa176dd9a6a3bf831c0e8310475f2' into dev-release

Change-Id: I4def325a506af616debaacd7574eb3c48d21631b
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index 360bb74..92666b6 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -7,8 +7,38 @@
   "common_flags": [
     {
       "api_level_below_or_equal": 10000,
+      "amend_library_field": [
+        "public static java.time.LocalDate java.time.LocalDate#EPOCH"
+      ],
       "amend_library_method": [
-        "public java.lang.Object[] java.util.Collection#toArray(java.util.function.IntFunction)"
+        "public int java.time.Duration#toHoursPart()",
+        "public int java.time.Duration#toMillisPart()",
+        "public int java.time.Duration#toMinutesPart()",
+        "public int java.time.Duration#toNanosPart()",
+        "public int java.time.Duration#toSecondsPart()",
+        "public java.lang.Object[] java.util.Collection#toArray(java.util.function.IntFunction)",
+        "public java.time.Duration java.time.Duration#truncatedTo(java.time.temporal.TemporalUnit)",
+        "public java.time.temporal.ChronoUnit java.util.concurrent.TimeUnit#toChronoUnit()",
+        "public java.util.List java.util.stream.Stream#toList()",
+        "public java.util.stream.Stream java.time.LocalDate#datesUntil(java.time.LocalDate)",
+        "public java.util.stream.Stream java.time.LocalDate#datesUntil(java.time.LocalDate, java.time.Period)",
+        "public long java.time.LocalDate#toEpochSecond(java.time.LocalTime, java.time.ZoneOffset)",
+        "public long java.time.OffsetTime#toEpochSecond(java.time.LocalDate)",
+        "public long java.time.Duration#dividedBy(java.time.Duration)",
+        "public long java.time.Duration#toDaysPart()",
+        "public long java.time.Duration#toSeconds()",
+        "public long java.time.LocalTime#toEpochSecond(java.time.LocalDate, java.time.ZoneOffset)",
+        "public long java.util.concurrent.TimeUnit#convert(java.time.Duration)",
+        "public static java.time.Clock java.time.Clock#tickMillis(java.time.ZoneId)",
+        "public static java.time.LocalDate java.time.LocalDate#ofInstant(java.time.Instant, java.time.ZoneId)",
+        "public static java.time.LocalTime java.time.LocalTime#ofInstant(java.time.Instant, java.time.ZoneId)",
+        "public static java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit#of(java.time.temporal.ChronoUnit)",
+        "public static java.util.stream.Collector java.util.stream.Collectors#filtering(java.util.function.Predicate, java.util.stream.Collector)",
+        "public static java.util.stream.Collector java.util.stream.Collectors#flatMapping(java.util.function.Function, java.util.stream.Collector)",
+        "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableList()",
+        "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function)",
+        "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function, java.util.function.BinaryOperator)",
+        "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableSet()"
       ]
     },
     {
@@ -29,18 +59,7 @@
         "long java.time.LocalDate#toEpochSecond(java.time.LocalTime, java.time.ZoneOffset)": "java.time.DesugarLocalDate",
         "java.time.Clock java.time.Clock#tickMillis(java.time.ZoneId)": "java.time.DesugarClock",
         "long java.time.OffsetTime#toEpochSecond(java.time.LocalDate)": "java.time.DesugarOffsetTime"
-      },
-      "amend_library_field": [
-        "public static java.time.LocalDate java.time.LocalDate#EPOCH"
-      ],
-      "amend_library_method": [
-        "public java.util.stream.Stream java.time.LocalDate#datesUntil(java.time.LocalDate)",
-        "public java.util.stream.Stream java.time.LocalDate#datesUntil(java.time.LocalDate, java.time.Period)",
-        "public static java.time.LocalDate java.time.LocalDate#ofInstant(java.time.Instant, java.time.ZoneId)",
-        "public long java.time.LocalDate#toEpochSecond(java.time.LocalTime, java.time.ZoneOffset)",
-        "public static java.time.Clock java.time.Clock#tickMillis(java.time.ZoneId)",
-        "public long java.time.OffsetTime#toEpochSecond(java.time.LocalDate)"
-      ]
+      }
     },
     {
       "api_level_below_or_equal": 10000,
@@ -67,12 +86,7 @@
         "java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit#of(java.time.temporal.ChronoUnit)": "java.util.concurrent.DesugarTimeUnit",
         "java.time.temporal.ChronoUnit java.util.concurrent.TimeUnit#toChronoUnit()": "java.util.concurrent.DesugarTimeUnit",
         "long java.util.concurrent.TimeUnit#convert(java.time.Duration)": "java.util.concurrent.DesugarTimeUnit"
-      },
-      "amend_library_method": [
-        "public static java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit#of(java.time.temporal.ChronoUnit)",
-        "public java.time.temporal.ChronoUnit java.util.concurrent.TimeUnit#toChronoUnit()",
-        "public long java.util.concurrent.TimeUnit#convert(java.time.Duration)"
-      ]
+      }
     },
     {
       "api_level_below_or_equal": 29,
@@ -86,6 +100,18 @@
       ]
     },
     {
+      "api_level_below_or_equal": 33,
+      "api_level_greater_or_equal": 24,
+      "emulate_interface": {
+        "java.util.stream.Stream": {
+          "rewrittenType": "j$.util.stream.Stream",
+          "emulatedMethods": [
+            "public java.util.List java.util.stream.Stream#toList()"
+          ]
+        }
+      }
+    },
+    {
       "api_level_below_or_equal": 32,
       "api_level_greater_or_equal": 24,
       "rewrite_prefix": {
@@ -106,15 +132,7 @@
         "java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function)": "java.util.stream.DesugarCollectors",
         "java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function, java.util.function.BinaryOperator)": "java.util.stream.DesugarCollectors",
         "java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableSet()": "java.util.stream.DesugarCollectors"
-      },
-      "amend_library_method": [
-        "public static java.util.stream.Collector java.util.stream.Collectors#filtering(java.util.function.Predicate, java.util.stream.Collector)",
-        "public static java.util.stream.Collector java.util.stream.Collectors#flatMapping(java.util.function.Function, java.util.stream.Collector)",
-        "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableList()",
-        "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function)",
-        "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function, java.util.function.BinaryOperator)",
-        "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableSet()"
-      ]
+      }
     },
     {
       "api_level_below_or_equal": 30,
@@ -135,20 +153,7 @@
         "java.time.Duration java.time.Duration#truncatedTo(java.time.temporal.TemporalUnit)": "java.time.DesugarDuration",
         "java.time.LocalTime java.time.LocalTime#ofInstant(java.time.Instant, java.time.ZoneId)": "java.time.DesugarLocalTime",
         "long java.time.LocalTime#toEpochSecond(java.time.LocalDate, java.time.ZoneOffset)": "java.time.DesugarLocalTime"
-      },
-      "amend_library_method": [
-        "public long java.time.Duration#dividedBy(java.time.Duration)",
-        "public long java.time.Duration#toDaysPart()",
-        "public int java.time.Duration#toHoursPart()",
-        "public int java.time.Duration#toMillisPart()",
-        "public int java.time.Duration#toMinutesPart()",
-        "public int java.time.Duration#toNanosPart()",
-        "public long java.time.Duration#toSeconds()",
-        "public int java.time.Duration#toSecondsPart()",
-        "public java.time.Duration java.time.Duration#truncatedTo(java.time.temporal.TemporalUnit)",
-        "public static java.time.LocalTime java.time.LocalTime#ofInstant(java.time.Instant, java.time.ZoneId)",
-        "public long java.time.LocalTime#toEpochSecond(java.time.LocalDate, java.time.ZoneOffset)"
-      ]
+      }
     },
     {
       "api_level_below_or_equal": 25,
@@ -368,6 +373,7 @@
     {
       "api_level_below_or_equal": 10000,
       "amend_library_method": [
+        "public java.time.chrono.IsoEra java.time.LocalDate#getEra()",
         "public static java.util.stream.DoubleStream java.util.stream.DoubleStream#iterate(double, java.util.function.DoublePredicate, java.util.function.DoubleUnaryOperator)",
         "public static java.util.stream.IntStream java.util.stream.IntStream#iterate(int, java.util.function.IntPredicate, java.util.function.IntUnaryOperator)",
         "public static java.util.stream.LongStream java.util.stream.LongStream#iterate(long, java.util.function.LongPredicate, java.util.function.LongUnaryOperator)",
@@ -379,10 +385,7 @@
       "api_level_greater_or_equal": 26,
       "covariant_retarget_method": {
         "java.time.chrono.IsoEra java.time.LocalDate#getEra()": "java.time.chrono.Era"
-      },
-      "amend_library_method": [
-        "public java.time.chrono.IsoEra java.time.LocalDate#getEra()"
-      ]
+      }
     },
     {
       "api_level_below_or_equal": 25,
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
index b75ec43..7214afc 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -7,10 +7,46 @@
   "common_flags": [
     {
       "api_level_below_or_equal": 10000,
+      "amend_library_field": [
+        "public static java.time.LocalDate java.time.LocalDate#EPOCH"
+      ],
       "amend_library_method": [
+        "public int java.time.Duration#toHoursPart()",
+        "public int java.time.Duration#toMillisPart()",
+        "public int java.time.Duration#toMinutesPart()",
+        "public int java.time.Duration#toNanosPart()",
+        "public int java.time.Duration#toSecondsPart()",
         "public java.lang.Object[] java.util.Collection#toArray(java.util.function.IntFunction)",
+        "public java.time.Duration java.time.Duration#truncatedTo(java.time.temporal.TemporalUnit)",
+        "public java.time.temporal.ChronoUnit java.util.concurrent.TimeUnit#toChronoUnit()",
+        "public java.util.List java.util.stream.Stream#toList()",
+        "public java.util.stream.Stream java.time.LocalDate#datesUntil(java.time.LocalDate)",
+        "public java.util.stream.Stream java.time.LocalDate#datesUntil(java.time.LocalDate, java.time.Period)",
+        "public long java.io.InputStream#transferTo(java.io.OutputStream)",
+        "public long java.io.ByteArrayInputStream#transferTo(java.io.OutputStream)",
+        "public long java.time.LocalDate#toEpochSecond(java.time.LocalTime, java.time.ZoneOffset)",
+        "public long java.time.OffsetTime#toEpochSecond(java.time.LocalDate)",
+        "public long java.time.Duration#dividedBy(java.time.Duration)",
+        "public long java.time.Duration#toDaysPart()",
+        "public long java.time.Duration#toSeconds()",
+        "public long java.time.LocalTime#toEpochSecond(java.time.LocalDate, java.time.ZoneOffset)",
+        "public long java.util.concurrent.TimeUnit#convert(java.time.Duration)",
+        "public static java.lang.String java.nio.file.Files#readString(java.nio.file.Path)",
+        "public static java.lang.String java.nio.file.Files#readString(java.nio.file.Path, java.nio.charset.Charset)",
+        "public static java.nio.file.Path java.nio.file.Files#writeString(java.nio.file.Path, java.lang.CharSequence, java.nio.file.OpenOption[])",
+        "public static java.nio.file.Path java.nio.file.Files#writeString(java.nio.file.Path, java.lang.CharSequence, java.nio.charset.Charset, java.nio.file.OpenOption[])",
         "public static java.nio.file.Path java.nio.file.Path#of(java.lang.String, java.lang.String[])",
-        "public static java.nio.file.Path java.nio.file.Path#of(java.net.URI)"
+        "public static java.nio.file.Path java.nio.file.Path#of(java.net.URI)",
+        "public static java.time.Clock java.time.Clock#tickMillis(java.time.ZoneId)",
+        "public static java.time.LocalDate java.time.LocalDate#ofInstant(java.time.Instant, java.time.ZoneId)",
+        "public static java.time.LocalTime java.time.LocalTime#ofInstant(java.time.Instant, java.time.ZoneId)",
+        "public static java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit#of(java.time.temporal.ChronoUnit)",
+        "public static java.util.stream.Collector java.util.stream.Collectors#filtering(java.util.function.Predicate, java.util.stream.Collector)",
+        "public static java.util.stream.Collector java.util.stream.Collectors#flatMapping(java.util.function.Function, java.util.stream.Collector)",
+        "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableList()",
+        "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function)",
+        "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function, java.util.function.BinaryOperator)",
+        "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableSet()"
       ]
     },
     {
@@ -36,22 +72,7 @@
         "java.lang.String java.nio.file.Files#readString(java.nio.file.Path, java.nio.charset.Charset)": "java.nio.file.DesugarFiles",
         "java.nio.file.Path java.nio.file.Files#writeString(java.nio.file.Path, java.lang.CharSequence, java.nio.file.OpenOption[])": "java.nio.file.DesugarFiles",
         "java.nio.file.Path java.nio.file.Files#writeString(java.nio.file.Path, java.lang.CharSequence, java.nio.charset.Charset, java.nio.file.OpenOption[])": "java.nio.file.DesugarFiles"
-      },
-      "amend_library_field": [
-        "public static java.time.LocalDate java.time.LocalDate#EPOCH"
-      ],
-      "amend_library_method": [
-        "public java.util.stream.Stream java.time.LocalDate#datesUntil(java.time.LocalDate)",
-        "public java.util.stream.Stream java.time.LocalDate#datesUntil(java.time.LocalDate, java.time.Period)",
-        "public static java.time.LocalDate java.time.LocalDate#ofInstant(java.time.Instant, java.time.ZoneId)",
-        "public long java.time.LocalDate#toEpochSecond(java.time.LocalTime, java.time.ZoneOffset)",
-        "public static java.time.Clock java.time.Clock#tickMillis(java.time.ZoneId)",
-        "public long java.time.OffsetTime#toEpochSecond(java.time.LocalDate)",
-        "public static java.lang.String java.nio.file.Files#readString(java.nio.file.Path)",
-        "public static java.lang.String java.nio.file.Files#readString(java.nio.file.Path, java.nio.charset.Charset)",
-        "public static java.nio.file.Path java.nio.file.Files#writeString(java.nio.file.Path, java.lang.CharSequence, java.nio.file.OpenOption[])",
-        "public static java.nio.file.Path java.nio.file.Files#writeString(java.nio.file.Path, java.lang.CharSequence, java.nio.charset.Charset, java.nio.file.OpenOption[])"
-      ]
+      }
     },
     {
       "api_level_below_or_equal": 10000,
@@ -85,14 +106,7 @@
       "retarget_method_with_emulated_dispatch": {
         "long java.io.InputStream#transferTo(java.io.OutputStream)": "java.io.DesugarInputStream",
         "long java.io.ByteArrayInputStream#transferTo(java.io.OutputStream)": "long java.io.DesugarInputStream#transferTo(java.io.InputStream, java.io.OutputStream)"
-      },
-      "amend_library_method": [
-        "public static java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit#of(java.time.temporal.ChronoUnit)",
-        "public java.time.temporal.ChronoUnit java.util.concurrent.TimeUnit#toChronoUnit()",
-        "public long java.util.concurrent.TimeUnit#convert(java.time.Duration)",
-        "public long java.io.InputStream#transferTo(java.io.OutputStream)",
-        "public long java.io.ByteArrayInputStream#transferTo(java.io.OutputStream)"
-      ]
+      }
     },
     {
       "api_level_below_or_equal": 29,
@@ -106,6 +120,18 @@
       ]
     },
     {
+      "api_level_below_or_equal": 33,
+      "api_level_greater_or_equal": 24,
+      "emulate_interface": {
+        "java.util.stream.Stream": {
+          "rewrittenType": "j$.util.stream.Stream",
+          "emulatedMethods": [
+            "public java.util.List java.util.stream.Stream#toList()"
+          ]
+        }
+      }
+    },
+    {
       "api_level_below_or_equal": 32,
       "api_level_greater_or_equal": 24,
       "rewrite_prefix": {
@@ -126,15 +152,7 @@
         "java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function)": "java.util.stream.DesugarCollectors",
         "java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function, java.util.function.BinaryOperator)": "java.util.stream.DesugarCollectors",
         "java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableSet()": "java.util.stream.DesugarCollectors"
-      },
-      "amend_library_method": [
-        "public static java.util.stream.Collector java.util.stream.Collectors#filtering(java.util.function.Predicate, java.util.stream.Collector)",
-        "public static java.util.stream.Collector java.util.stream.Collectors#flatMapping(java.util.function.Function, java.util.stream.Collector)",
-        "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableList()",
-        "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function)",
-        "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function, java.util.function.BinaryOperator)",
-        "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableSet()"
-      ]
+      }
     },
     {
       "api_level_below_or_equal": 30,
@@ -155,20 +173,7 @@
         "java.time.Duration java.time.Duration#truncatedTo(java.time.temporal.TemporalUnit)": "java.time.DesugarDuration",
         "java.time.LocalTime java.time.LocalTime#ofInstant(java.time.Instant, java.time.ZoneId)": "java.time.DesugarLocalTime",
         "long java.time.LocalTime#toEpochSecond(java.time.LocalDate, java.time.ZoneOffset)": "java.time.DesugarLocalTime"
-      },
-      "amend_library_method": [
-        "public long java.time.Duration#dividedBy(java.time.Duration)",
-        "public long java.time.Duration#toDaysPart()",
-        "public int java.time.Duration#toHoursPart()",
-        "public int java.time.Duration#toMillisPart()",
-        "public int java.time.Duration#toMinutesPart()",
-        "public int java.time.Duration#toNanosPart()",
-        "public long java.time.Duration#toSeconds()",
-        "public int java.time.Duration#toSecondsPart()",
-        "public java.time.Duration java.time.Duration#truncatedTo(java.time.temporal.TemporalUnit)",
-        "public static java.time.LocalTime java.time.LocalTime#ofInstant(java.time.Instant, java.time.ZoneId)",
-        "public long java.time.LocalTime#toEpochSecond(java.time.LocalDate, java.time.ZoneOffset)"
-      ]
+      }
     },
     {
       "api_level_below_or_equal": 25,
@@ -527,6 +532,7 @@
     {
       "api_level_below_or_equal": 10000,
       "amend_library_method": [
+        "public java.time.chrono.IsoEra java.time.LocalDate#getEra()",
         "public static java.util.stream.DoubleStream java.util.stream.DoubleStream#iterate(double, java.util.function.DoublePredicate, java.util.function.DoubleUnaryOperator)",
         "public static java.util.stream.IntStream java.util.stream.IntStream#iterate(int, java.util.function.IntPredicate, java.util.function.IntUnaryOperator)",
         "public static java.util.stream.LongStream java.util.stream.LongStream#iterate(long, java.util.function.LongPredicate, java.util.function.LongUnaryOperator)",
@@ -546,10 +552,7 @@
       "api_level_greater_or_equal": 26,
       "covariant_retarget_method": {
         "java.time.chrono.IsoEra java.time.LocalDate#getEra()": "java.time.chrono.Era"
-      },
-      "amend_library_method": [
-        "public java.time.chrono.IsoEra java.time.LocalDate#getEra()"
-      ]
+      }
     },
     {
       "api_level_below_or_equal": 25,
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index ee368b2..087c305 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -524,7 +524,6 @@
       assert ArtProfileCompletenessChecker.verify(appView);
 
       LirConverter.rewriteLirWithLens(appView, timing, executorService);
-      appView.clearCodeRewritings(executorService, timing);
 
       VerticalClassMerger.createForInitialClassMerging(appViewWithLiveness, timing)
           .runIfNecessary(executorService, timing);
@@ -757,13 +756,11 @@
       }
 
       GenericSignatureContextBuilder genericContextBuilderBeforeFinalMerging = null;
-      if (appView.hasCfByteCodePassThroughMethods()) {
-        LirConverter.rewriteLirWithLens(appView, timing, executorService);
-        appView.clearCodeRewritings(executorService, timing);
-      } else {
-        // Rewrite LIR with lens to allow building IR from LIR in class mergers.
-        LirConverter.rewriteLirWithLens(appView, timing, executorService);
-        appView.clearCodeRewritings(executorService, timing);
+
+      // Rewrite LIR with lens to allow building IR from LIR in class mergers.
+      LirConverter.rewriteLirWithLens(appView, timing, executorService);
+
+      if (!appView.hasCfByteCodePassThroughMethods()) {
         assert appView.dexItemFactory().verifyNoCachedTypeElements();
 
         if (appView.hasLiveness()) {
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 612ecec1e..2d24e07 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -183,11 +183,10 @@
       KeepInfoCollection keepInfo = appView.getKeepInfo();
       keepInfo.mutate(mutator -> mutator.removeKeepInfoForMergedClasses(prunedItems));
       appView.rewriteWithLens(horizontalClassMergerGraphLens, executorService, timing);
-      LirConverter.rewriteLirWithLens(appViewWithClassHierarchy, timing, executorService);
       new IdentifierMinifier(appViewWithClassHierarchy)
           .rewriteDexItemBasedConstStringInStaticFields(executorService);
+      LirConverter.rewriteLirWithLens(appViewWithClassHierarchy, timing, executorService);
       appView.rebuildAppInfo(newApplication);
-      appView.clearCodeRewritings(executorService, timing);
     } else {
       SyntheticItems syntheticItems = appView.appInfo().getSyntheticItems();
       assert !syntheticItems.hasPendingSyntheticClasses();
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
index 865d83b..5afa942 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -44,6 +44,10 @@
     this.type = type;
   }
 
+  public static Builder builder() {
+    return new Builder();
+  }
+
   @Override
   public int opcode() {
     return Opcodes.ARRAY_GET;
@@ -299,4 +303,40 @@
   public void buildLir(LirBuilder<Value, ?> builder) {
     builder.addArrayGet(getMemberType(), array(), index());
   }
+
+  public static class Builder extends BuilderBase<Builder, ArrayGet> {
+
+    private Value arrayValue;
+    private Value indexValue;
+    private MemberType memberType;
+
+    public Builder setArrayValue(Value arrayValue) {
+      this.arrayValue = arrayValue;
+      return this;
+    }
+
+    public Builder setIndexValue(Value indexValue) {
+      this.indexValue = indexValue;
+      return this;
+    }
+
+    public Builder setMemberType(MemberType memberType) {
+      this.memberType = memberType;
+      return this;
+    }
+
+    @Override
+    public ArrayGet build() {
+      if (memberType == null && outValue.getType().isReferenceType()) {
+        memberType = MemberType.OBJECT;
+      }
+      assert memberType != null;
+      return amend(new ArrayGet(memberType, outValue, arrayValue, indexValue));
+    }
+
+    @Override
+    public Builder self() {
+      return this;
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 126f23a..9b4fffb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -45,6 +45,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 public abstract class Instruction
@@ -1764,6 +1765,13 @@
     protected Value outValue;
     protected Position position;
 
+    public final B applyIf(boolean condition, Consumer<B> consumer) {
+      if (condition) {
+        consumer.accept(self());
+      }
+      return self();
+    }
+
     public abstract I build();
 
     public abstract B self();
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index b60c42e..c448a2d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -52,6 +52,11 @@
   }
 
   @Override
+  public Builder newBuilder() {
+    return builder();
+  }
+
+  @Override
   public int opcode() {
     return Opcodes.INVOKE_DIRECT;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index f677699..e4c26ae 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -32,6 +32,15 @@
     super(target, result, arguments);
   }
 
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  @Override
+  public Builder newBuilder() {
+    return builder();
+  }
+
   @Override
   public boolean getInterfaceBit() {
     return true;
@@ -147,4 +156,17 @@
   public void buildLir(LirBuilder<Value, ?> builder) {
     builder.addInvokeInterface(getInvokedMethod(), arguments());
   }
+
+  public static class Builder extends InvokeMethod.Builder<Builder, InvokeInterface> {
+
+    @Override
+    public InvokeInterface build() {
+      return amend(new InvokeInterface(method, outValue, arguments));
+    }
+
+    @Override
+    public Builder self() {
+      return this;
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index e15b67c..c40827c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -77,6 +77,8 @@
     }
   }
 
+  public abstract Builder<? extends Builder, ? extends InvokeMethod> newBuilder();
+
   public Value getFirstNonReceiverArgument() {
     return getArgument(getFirstNonReceiverArgumentIndex());
   }
@@ -346,11 +348,12 @@
     return false;
   }
 
-  abstract static class Builder<B extends Builder<B, I>, I extends InvokeMethod>
+  public abstract static class Builder<B extends Builder<B, I>, I extends InvokeMethod>
       extends BuilderBase<B, I> {
 
     protected DexMethod method;
     protected List<Value> arguments = Collections.emptyList();
+    protected boolean isInterface = false;
 
     public B setArguments(Value... arguments) {
       return setArguments(Arrays.asList(arguments));
@@ -367,6 +370,11 @@
           factory, TypeElement.fromDexType(method.getReturnType(), maybeNull(), appView));
     }
 
+    public B setIsInterface(boolean isInterface) {
+      this.isInterface = isInterface;
+      return self();
+    }
+
     public B setSingleArgument(Value argument) {
       return setArguments(ImmutableList.of(argument));
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index 9ef005e..601448c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -37,6 +37,11 @@
   }
 
   @Override
+  public Builder<? extends Builder, ? extends InvokeMethod> newBuilder() {
+    throw new Unreachable();
+  }
+
+  @Override
   public boolean getInterfaceBit() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 8abaa2c..16f0c04 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
-
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.dex.code.DexInstruction;
 import com.android.tools.r8.dex.code.DexInvokeStatic;
@@ -48,6 +47,11 @@
   }
 
   @Override
+  public Builder newBuilder() {
+    return builder();
+  }
+
+  @Override
   public boolean getInterfaceBit() {
     return isInterface;
   }
@@ -229,7 +233,7 @@
       assert method != null;
       assert method.getArity() == arguments.size();
       assert outValue == null || !method.getReturnType().isVoidType();
-      return amend(new InvokeStatic(method, outValue, arguments));
+      return amend(new InvokeStatic(method, outValue, arguments, isInterface));
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index d1d9ad4..b5520a0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -34,6 +34,15 @@
     this.isInterface = isInterface;
   }
 
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  @Override
+  public Builder newBuilder() {
+    return builder();
+  }
+
   @Override
   public boolean getInterfaceBit() {
     return isInterface;
@@ -132,4 +141,17 @@
   public void buildLir(LirBuilder<Value, ?> builder) {
     builder.addInvokeSuper(getInvokedMethod(), arguments(), isInterface);
   }
+
+  public static class Builder extends InvokeMethod.Builder<Builder, InvokeSuper> {
+
+    @Override
+    public InvokeSuper build() {
+      return amend(new InvokeSuper(method, outValue, arguments, isInterface));
+    }
+
+    @Override
+    public Builder self() {
+      return this;
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 6d97ebe..b1cf3bd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -37,6 +37,11 @@
   }
 
   @Override
+  public Builder newBuilder() {
+    return builder();
+  }
+
+  @Override
   public boolean getInterfaceBit() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
index 60d1cd4..f17b896 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
@@ -35,6 +35,7 @@
 import com.android.tools.r8.utils.ObjectUtils;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.ThrowingAction;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.verticalclassmerging.IncompleteVerticalClassMergerBridgeCode;
 import java.util.concurrent.ExecutionException;
@@ -85,6 +86,19 @@
       Timing timing,
       ExecutorService executorService)
       throws ExecutionException {
+    rewriteLirWithLens(
+        appView,
+        timing,
+        executorService,
+        () -> appView.clearCodeRewritings(executorService, timing));
+  }
+
+  public static <T extends Throwable> void rewriteLirWithLens(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      Timing timing,
+      ExecutorService executorService,
+      ThrowingAction<T> onChangedAction)
+      throws ExecutionException, T {
     assert appView.testing().canUseLir(appView);
     assert appView.testing().isSupportedLirPhase();
     assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
@@ -108,6 +122,8 @@
     timing.begin("LIR->LIR@" + graphLens.getClass().getName());
     rewriteLirWithUnappliedLens(appView, executorService);
     timing.end();
+
+    onChangedAction.execute();
   }
 
   private static void rewriteLirWithUnappliedLens(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 04fb94e..7595d4e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -334,10 +334,7 @@
         initializeAndroidQMethodProviders(factory);
       }
       if (options.getMinApiLevel().isLessThan(AndroidApiLevel.R)) {
-        if (options.testing.alwaysBackportListSetMapMethods
-            || typeIsPresentWithoutBackportsFrom(factory.javaUtilSetType, AndroidApiLevel.R)) {
-          initializeAndroidRSetListMapMethodProviders(factory);
-        }
+        initializeAndroidRSetListMapMethodProviders(factory);
         if (typeIsAbsentOrPresentWithoutBackportsFrom(factory.objectsType, AndroidApiLevel.R)) {
           initializeAndroidRObjectsMethodProviders(factory);
           if (typeIsPresent(factory.supplierType)) {
@@ -347,10 +344,7 @@
       }
       if (options.getMinApiLevel().isLessThan(AndroidApiLevel.S)) {
         initializeAndroidSMethodProviders(factory);
-        if (options.testing.alwaysBackportListSetMapMethods
-            || typeIsPresentWithoutBackportsFrom(factory.javaUtilSetType, AndroidApiLevel.S)) {
-          initializeAndroidSSetListMapMethodProviders(factory);
-        }
+        initializeAndroidSSetListMapMethodProviders(factory);
       }
       if (options.getMinApiLevel().isLessThan(AndroidApiLevel.Sv2)) {
         initializeAndroidSv2MethodProviders(factory);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/ClassOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/ClassOptimizer.java
new file mode 100644
index 0000000..b166b9e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/ClassOptimizer.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2023, 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.library;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.optimize.AffectedValues;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.Set;
+
+public class ClassOptimizer extends StatelessLibraryMethodModelCollection {
+
+  private final InternalOptions options;
+  private final DexItemFactory dexItemFactory;
+  private final DexMethod getConstructor;
+  private final DexMethod getDeclaredConstructor;
+  private final DexMethod getMethod;
+  private final DexMethod getDeclaredMethod;
+
+  ClassOptimizer(AppView<?> appView) {
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+    this.options = appView.options();
+    this.dexItemFactory = dexItemFactory;
+    getConstructor = dexItemFactory.classMethods.getConstructor;
+    getDeclaredConstructor = dexItemFactory.classMethods.getDeclaredConstructor;
+    getMethod = dexItemFactory.classMethods.getMethod;
+    getDeclaredMethod = dexItemFactory.classMethods.getDeclaredMethod;
+  }
+
+  @Override
+  public DexType getType() {
+    return dexItemFactory.classType;
+  }
+
+  @Override
+  public InstructionListIterator optimize(
+      IRCode code,
+      BasicBlockIterator blockIterator,
+      InstructionListIterator instructionIterator,
+      InvokeMethod invoke,
+      DexClassAndMethod singleTarget,
+      AffectedValues affectedValues,
+      Set<BasicBlock> blocksToRemove) {
+    DexMethod singleTargetReference = singleTarget.getReference();
+    if (singleTargetReference.isIdenticalTo(getConstructor)
+        || singleTargetReference.isIdenticalTo(getDeclaredConstructor)
+        || singleTargetReference.isIdenticalTo(getMethod)
+        || singleTargetReference.isIdenticalTo(getDeclaredMethod)) {
+      EmptyVarargsUtil.replaceWithNullIfEmptyArray(
+          invoke, invoke.arguments().size() - 1, code, instructionIterator, options);
+      assert instructionIterator.peekPrevious() == invoke;
+    }
+    return instructionIterator;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/ConstructorOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/ConstructorOptimizer.java
new file mode 100644
index 0000000..f0d66a9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/ConstructorOptimizer.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2023, 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.library;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.optimize.AffectedValues;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.Set;
+
+public class ConstructorOptimizer extends StatelessLibraryMethodModelCollection {
+
+  private final InternalOptions options;
+  private final DexItemFactory dexItemFactory;
+  private final DexMethod newInstance;
+
+  ConstructorOptimizer(AppView<?> appView) {
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+    this.options = appView.options();
+    this.dexItemFactory = dexItemFactory;
+    newInstance = dexItemFactory.constructorMethods.newInstance;
+  }
+
+  @Override
+  public DexType getType() {
+    return dexItemFactory.constructorType;
+  }
+
+  @Override
+  public InstructionListIterator optimize(
+      IRCode code,
+      BasicBlockIterator blockIterator,
+      InstructionListIterator instructionIterator,
+      InvokeMethod invoke,
+      DexClassAndMethod singleTarget,
+      AffectedValues affectedValues,
+      Set<BasicBlock> blocksToRemove) {
+    DexMethod singleTargetReference = singleTarget.getReference();
+    if (singleTargetReference.isIdenticalTo(newInstance)) {
+      EmptyVarargsUtil.replaceWithNullIfEmptyArray(invoke, 1, code, instructionIterator, options);
+      assert instructionIterator.peekPrevious() == invoke;
+    }
+    return instructionIterator;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsUtil.java b/src/main/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsUtil.java
new file mode 100644
index 0000000..61bc097
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsUtil.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2023, 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.library;
+
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.InternalOptions;
+
+public class EmptyVarargsUtil {
+  public static void replaceWithNullIfEmptyArray(
+      InvokeMethod invoke,
+      int argumentIndex,
+      IRCode code,
+      InstructionListIterator instructionIterator,
+      InternalOptions options) {
+    Value argument = invoke.getArgument(argumentIndex);
+    if (argument.isDefinedByInstructionSatisfying(
+        i -> i.isNewArrayEmpty() && i.asNewArrayEmpty().sizeIfConst() == 0)) {
+      instructionIterator.previous();
+      Value replacement = instructionIterator.insertConstNullInstruction(code, options);
+      invoke.replaceValue(argumentIndex, replacement);
+      instructionIterator.next();
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
index cadab0f..788cbf6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
@@ -46,9 +46,12 @@
     this.appView = appView;
     timing.begin("Register optimizers");
     PrimitiveMethodOptimizer.forEachPrimitiveOptimizer(appView, this::register);
+    register(new ClassOptimizer(appView));
     register(new CollectionsOptimizer(appView));
+    register(new ConstructorOptimizer(appView));
     register(new ListOptimizer(appView));
     register(new MapOptimizer(appView));
+    register(new MethodOptimizer(appView));
     register(new ObjectMethodOptimizer(appView));
     register(new ObjectsMethodOptimizer(appView));
     register(new SetOptimizer(appView));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/MethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/MethodOptimizer.java
new file mode 100644
index 0000000..bf87ca7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/MethodOptimizer.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2023, 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.library;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.optimize.AffectedValues;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.Set;
+
+public class MethodOptimizer extends StatelessLibraryMethodModelCollection {
+
+  private final InternalOptions options;
+  private final DexItemFactory dexItemFactory;
+  private final DexMethod invoke;
+
+  MethodOptimizer(AppView<?> appView) {
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+    this.options = appView.options();
+    this.dexItemFactory = dexItemFactory;
+    invoke = dexItemFactory.methodMethods.invoke;
+  }
+
+  @Override
+  public DexType getType() {
+    return dexItemFactory.methodType;
+  }
+
+  @Override
+  public InstructionListIterator optimize(
+      IRCode code,
+      BasicBlockIterator blockIterator,
+      InstructionListIterator instructionIterator,
+      InvokeMethod invoke,
+      DexClassAndMethod singleTarget,
+      AffectedValues affectedValues,
+      Set<BasicBlock> blocksToRemove) {
+    DexMethod singleTargetReference = singleTarget.getReference();
+    if (singleTargetReference.isIdenticalTo(this.invoke)) {
+      EmptyVarargsUtil.replaceWithNullIfEmptyArray(invoke, 2, code, instructionIterator, options);
+      assert instructionIterator.peekPrevious() == invoke;
+    }
+    return instructionIterator;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
index bd2fdb3..3a14982 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
@@ -330,6 +330,7 @@
       throws ExecutionException {
     // To simplify the analysis, we currently bail out for non-final classes.
     // TODO(b/296030319): Handle non-final classes.
+    Set<DexType> serviceImplementations = appView.appServices().computeAllServiceImplementations();
     MapUtils.removeIf(
         nonFinalInstanceFields,
         (holderType, fields) -> {
@@ -337,7 +338,8 @@
           DexProgramClass holder = fields.iterator().next().getHolder();
           // If the class is kept it could be instantiated directly, in which case all default field
           // values could be live.
-          if (appView.getKeepInfo(holder).isPinned(appView.options())) {
+          if (appView.getKeepInfo(holder).isPinned(appView.options())
+              || serviceImplementations.contains(holder.getType())) {
             fields.forEach(liveDefaultValueConsumer);
             return true;
           }
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 9d63f56..1b06eb4 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
@@ -158,7 +158,6 @@
       updateEnclosingMethodAttributes(executorService, protoNormalizerGraphLens);
       appView.rewriteWithLens(protoNormalizerGraphLens, executorService, timing);
       LirConverter.rewriteLirWithLens(appView, timing, executorService);
-      appView.clearCodeRewritings(executorService, timing);
     }
     appView.notifyOptimizationFinishedForTesting();
     timing.end();
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index fbcab01..4a358ee 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -161,7 +161,6 @@
 import com.android.tools.r8.utils.IteratorUtils;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.OptionalBool;
-import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -4131,10 +4130,6 @@
     private final Map<DexProgramClass, Set<DexClass>> injectedInterfaces =
         new ConcurrentHashMap<>();
 
-    // Subset of live methods that need have keep requirements.
-    private final List<Pair<ProgramMethod, Consumer<KeepMethodInfo.Joiner>>>
-        liveMethodsWithKeepActions = new ArrayList<>();
-
     SyntheticAdditions(ProcessorContext processorContext) {
       this.processorContext = processorContext;
     }
@@ -4150,7 +4145,6 @@
               && liveMethods.isEmpty()
               && syntheticClasspathClasses.isEmpty()
               && injectedInterfaces.isEmpty();
-      assert !empty || liveMethodsWithKeepActions.isEmpty();
       return empty;
     }
 
@@ -4191,13 +4185,9 @@
 
       // All synthetic additions are initial tree shaking only. No need to track keep reasons.
       KeepReasonWitness fakeReason = enqueuer.graphReporter.fakeReportShouldNotBeUsed();
-
       for (ProgramMethod desugaredMethod : desugaredMethods) {
         enqueuer.worklist.enqueueTraceCodeAction(desugaredMethod);
       }
-
-      liveMethodsWithKeepActions.forEach(
-          item -> enqueuer.keepInfo.joinMethod(item.getFirst(), item.getSecond()));
       for (ProgramMethod liveMethod : liveMethods.values()) {
         assert !enqueuer.targetedMethods.contains(liveMethod.getDefinition());
         enqueuer.markMethodAsTargeted(liveMethod, fakeReason);
@@ -4209,7 +4199,6 @@
             enqueuer.objectAllocationInfoCollection.injectInterfaces(
                 enqueuer.appInfo(), clazz, itfs);
           });
-
       minimumSyntheticKeepInfo.forEach(
           (method, minimumKeepInfoForMethod) -> {
             enqueuer.getKeepInfo().registerCompilerSynthesizedMethod(method);
diff --git a/src/main/java/com/android/tools/r8/synthesis/CallOtherMergeableSyntheticMethodUseRegistry.java b/src/main/java/com/android/tools/r8/synthesis/CallOtherMergeableSyntheticMethodUseRegistry.java
new file mode 100644
index 0000000..080fd8e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/CallOtherMergeableSyntheticMethodUseRegistry.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2025, 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.synthesis;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DefaultUseRegistryWithResult;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.google.common.collect.Iterables;
+
+public class CallOtherMergeableSyntheticMethodUseRegistry
+    extends DefaultUseRegistryWithResult<Boolean, ProgramMethod> {
+
+  public CallOtherMergeableSyntheticMethodUseRegistry(AppView<?> appView, ProgramMethod context) {
+    super(appView, context, false);
+  }
+
+  @Override
+  public void registerInvokeStatic(DexMethod method) {
+    if (appView.getSyntheticItems().isSynthetic(method.getHolderType())
+        && isSyntheticMethodAllowingGlobalMerging(method)) {
+      setResult(true);
+    }
+  }
+
+  private boolean isSyntheticMethodAllowingGlobalMerging(DexMethod method) {
+    return Iterables.all(
+        appView.getSyntheticItems().getSyntheticKinds(method.getHolderType()),
+        kind ->
+            kind.isSyntheticMethodKind() && kind.asSyntheticMethodKind().isAllowGlobalMerging());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 5a83444..0b9f17c 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.graph.fixup.TreeFixerBase;
 import com.android.tools.r8.graph.lens.GraphLens;
@@ -586,7 +587,14 @@
     potentialEquivalences.forEach(
         members -> {
           T t = members.get(0);
-          if (t.getKind().mayCallOtherSyntheticMethods(appView.getSyntheticItems().getNaming())) {
+          boolean mayCallOtherSyntheticMethods = false;
+          if (t.isMethodDefinition()) {
+            ProgramMethod method = t.asMethodDefinition().getMethod();
+            mayCallOtherSyntheticMethods =
+                method.registerCodeReferencesWithResult(
+                    new CallOtherMergeableSyntheticMethodUseRegistry(appView, method));
+          }
+          if (mayCallOtherSyntheticMethods) {
             indirectPotentialEquivalences.add(members);
           } else {
             directPotentialEquivalences.add(members);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index ebc0b1d..1ef8edc 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -259,11 +259,6 @@
 
     public abstract boolean isSingleSyntheticMethod();
 
-    @SuppressWarnings("ReferenceEquality")
-    public boolean mayCallOtherSyntheticMethods(SyntheticNaming naming) {
-      return this == naming.AUTOCLOSEABLE_DISPATCHER;
-    }
-
     public abstract boolean isFixedSuffixSynthetic();
 
     public abstract boolean isGlobal();
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 f26afb8..8bf530b 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2174,8 +2174,6 @@
       return currentPhase == LirPhase.POST;
     }
 
-    // If false, use the desugared library implementation when desugared library is enabled.
-    public boolean alwaysBackportListSetMapMethods = true;
     public boolean neverReuseCfLocalRegisters = false;
     public boolean checkReceiverAlwaysNullInCallSiteOptimization = true;
     public boolean forceInlineAPIConversions = false;
diff --git a/src/main/java/com/android/tools/r8/utils/ThrowingAction.java b/src/main/java/com/android/tools/r8/utils/ThrowingAction.java
index d5b4fa8..a044eeb 100644
--- a/src/main/java/com/android/tools/r8/utils/ThrowingAction.java
+++ b/src/main/java/com/android/tools/r8/utils/ThrowingAction.java
@@ -5,6 +5,11 @@
 package com.android.tools.r8.utils;
 
 @FunctionalInterface
-public interface ThrowingAction<E extends Throwable> {
-  void execute() throws E;
+public interface ThrowingAction<T extends Throwable> {
+
+  void execute() throws T;
+
+  static ThrowingAction<RuntimeException> empty() {
+    return () -> {};
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
index acb8b0d..86a3bba 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
@@ -29,6 +29,7 @@
 import com.android.tools.r8.shaking.KeepInfoCollection;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.ThrowingAction;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.Timing.TimingMerger;
 import com.google.common.collect.Streams;
@@ -250,7 +251,7 @@
 
   private void rewriteCodeWithLens(ExecutorService executorService, Timing timing)
       throws ExecutionException {
-    LirConverter.rewriteLirWithLens(appView, timing, executorService);
+    LirConverter.rewriteLirWithLens(appView, timing, executorService, ThrowingAction.empty());
     new IdentifierMinifier(appView).rewriteDexItemBasedConstStringInStaticFields(executorService);
   }
 
diff --git a/src/test/examplesJava17/desugaredlibrary/StreamToListTest.java b/src/test/examplesJava17/desugaredlibrary/StreamToListTest.java
new file mode 100644
index 0000000..e9e4786
--- /dev/null
+++ b/src/test/examplesJava17/desugaredlibrary/StreamToListTest.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2025, 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 desugaredlibrary;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.SPECIFICATIONS_WITH_CF2CF;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.stream.Stream;
+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 StreamToListTest extends DesugaredLibraryTestBase {
+
+  private final TestParameters parameters;
+  private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+  private final CompilationSpecification compilationSpecification;
+
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("a - c");
+
+  @Parameters(name = "{0}, spec: {1}, {2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+        ImmutableList.of(JDK11, JDK11_PATH),
+        SPECIFICATIONS_WITH_CF2CF);
+  }
+
+  public StreamToListTest(
+      TestParameters parameters,
+      LibraryDesugaringSpecification libraryDesugaringSpecification,
+      CompilationSpecification compilationSpecification) {
+    this.parameters = parameters;
+    this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+    this.compilationSpecification = compilationSpecification;
+  }
+
+  @Test
+  public void test() throws Throwable {
+    testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+        .addInnerClassesAndStrippedOuter(getClass())
+        .addKeepMainRule(Main.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  public static class Main {
+    public static void main(String[] args) {
+      List<String> strings = Stream.of("a", "b", "c").toList();
+      System.out.println(strings.get(0) + " - " + strings.get(2));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/GetMajorAndMinorSdkVersionBackportOutlineInBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/GetMajorAndMinorSdkVersionBackportOutlineInBackportTest.java
index 4e39653..337c252 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/GetMajorAndMinorSdkVersionBackportOutlineInBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/GetMajorAndMinorSdkVersionBackportOutlineInBackportTest.java
@@ -137,10 +137,7 @@
       assertEquals(
           1,
           countInvokeStaticToMethod(apiOutline1.uniqueMethod(), getGetMajorSdkVersion(inspector)));
-      // TODO(b/382242341):There are 4 backports, as even though they are identical two by two, they
-      //  are not currently shared, as synthetic sharing does not see equivalence of the backports
-      //  calling the API outline synthetics, which are shared.
-      for (int i = 2; i < 6; i++) {
+      for (int i = 2; i < 3; i++) {
         ClassSubject backport =
             inspector.clazz(
                 SyntheticItemsTestUtils.syntheticBackportWithForwardingClass(TestClass.class, i));
@@ -174,8 +171,6 @@
     } else {
       ClassSubject apiOutline0 =
           inspector.clazz(SyntheticItemsTestUtils.syntheticApiOutlineClass(TestClass.class, 0));
-      // TODO(b/382242341): There are 4 methods merged into the outline synthetic. Two calling
-      //  getMajorSdkVersion and two calling getMinorSdkVersion.
       assertEquals(4, apiOutline0.allMethods().size());
       MethodSubject main = inspector.clazz(TestClass.class).mainMethod();
       assertEquals(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CollectionOfTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CollectionOfTest.java
index e58ce54..5792e22 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CollectionOfTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CollectionOfTest.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
 import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -55,34 +54,21 @@
     this.compilationSpecification = compilationSpecification;
   }
 
-  private String getExpectedOutput(boolean desugaredLib, boolean alwaysBackportListSetMapMethods) {
+  private String getExpectedOutput() {
     if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.R)) {
       return EXPECTED_OUTPUT_CORRECT;
     }
-    if (alwaysBackportListSetMapMethods) {
-      return EXPECTED_OUTPUT_BACKPORT;
-    }
-    if (desugaredLib && libraryDesugaringSpecification != JDK8) {
-      if (parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
-        return EXPECTED_OUTPUT_CORRECT;
-      }
-      // TODO(b/243679691): This should also be correct, but is not because we use backports in
-      //  partial desugaring.
-      return EXPECTED_OUTPUT_BACKPORT;
-    }
     return EXPECTED_OUTPUT_BACKPORT;
   }
 
   @Test
   public void testCollectionOf() throws Throwable {
-    for (Boolean value : BooleanUtils.values()) {
-      testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
-          .addProgramFiles(INPUT_JAR)
-          .addKeepMainRule(MAIN_CLASS)
-          .addOptionsModification(opt -> opt.testing.alwaysBackportListSetMapMethods = value)
-          .run(parameters.getRuntime(), MAIN_CLASS)
-          .assertSuccessWithOutput(getExpectedOutput(true, value));
-    }
+    testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+        .addProgramFiles(INPUT_JAR)
+        .addKeepMainRule(MAIN_CLASS)
+        .compile()
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .assertSuccessWithOutput(getExpectedOutput());
   }
 
   @Test
@@ -94,6 +80,6 @@
         .addProgramFiles(INPUT_JAR)
         .setMinApi(parameters)
         .run(parameters.getRuntime(), MAIN_CLASS)
-        .assertSuccessWithOutput(getExpectedOutput(false, true));
+        .assertSuccessWithOutput(getExpectedOutput());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryInvokeAllResolveTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryInvokeAllResolveTest.java
index a6e298f..8893a42 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryInvokeAllResolveTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryInvokeAllResolveTest.java
@@ -68,8 +68,9 @@
               + " java.util.stream.LongStream.takeWhile(java.util.function.LongPredicate)",
           "java.util.stream.DoubleStream"
               + " java.util.stream.DoubleStream.dropWhile(java.util.function.DoublePredicate)",
+          // Stream.toList() and FileStore.getBlockSize() were added in 33 which confuses the
+          // required library (30).
           "java.util.List java.util.stream.Stream.toList()",
-          // FileStore.getBlockSize() was added in 33 which confuses the required library (30).
           "long java.nio.file.FileStore.getBlockSize()",
           // The call is present but unreachable above 26.
           "java.nio.channels.FileChannel"
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
index 2c948b2..50cb8a9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
@@ -77,8 +77,7 @@
       ImmutableSet.of(
           "java.util.stream.Stream java.util.stream.Stream.takeWhile(java.util.function.Predicate)",
           "java.util.stream.Stream"
-              + " java.util.stream.Stream.dropWhile(java.util.function.Predicate)",
-          "java.util.List java.util.stream.Stream.toList()");
+              + " java.util.stream.Stream.dropWhile(java.util.function.Predicate)");
   private static final Set<String> FAILURES_NUMBER_STREAM =
       ImmutableSet.of(
           "java.util.stream.IntStream"
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
index 2faea77..ca43f85 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
@@ -94,14 +94,6 @@
                 parameters, libraryDesugaringSpecification, compilationSpecification)
             .addProgramFiles(Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR + "stream.jar"))
             .addKeepMainRule(TEST_CLASS)
-            .applyIf(
-                compilationSpecification.isProgramShrink(),
-                b ->
-                    b.addOptionsModification(
-                        options -> {
-                          // TODO(b/140233505): Allow devirtualization once fixed.
-                          options.enableDevirtualization = false;
-                        }))
             .compile()
             .inspect(this::checkRewrittenInvokes)
             .inspectKeepRules(
@@ -146,11 +138,25 @@
         classSubject
             .uniqueMethodWithOriginalName("main")
             .streamInstructions()
-            .filter(instr -> instr.isInvokeInterface() || instr.isInvokeStatic())
+            .filter(
+                instr ->
+                    instr.isInvokeInterface() || instr.isInvokeStatic() || instr.isInvokeVirtual())
+            .filter(
+                instr -> {
+                  String holder = instr.getMethod().getHolderType().getTypeName();
+                  return !holder.equals("java.lang.Class")
+                      && !holder.equals("java.lang.Object")
+                      && !holder.equals("java.io.PrintStream");
+                })
             .collect(Collectors.toList());
+    assertEquals(22, invokes.size());
     assertInvokeStaticMatching(invokes, 0, "Set$-EL;spliterator");
     assertInvokeStaticMatching(invokes, 1, "Collection$-EL;stream");
-    assertInvokeInterfaceMatching(invokes, 2, "Set;iterator");
+    if (compilationSpecification.isProgramShrink()) {
+      assertInvokeVirtualMatching(invokes, 2, "HashSet;iterator");
+    } else {
+      assertInvokeInterfaceMatching(invokes, 2, "Set;iterator");
+    }
     assertInvokeStaticMatching(invokes, 3, "Collection$-EL;stream");
     assertInvokeStaticMatching(invokes, 4, "Set$-EL;spliterator");
     assertInvokeInterfaceMatching(invokes, 8, "Iterator;remove");
@@ -165,7 +171,6 @@
     assertInvokeStaticMatching(invokes, 19, "Comparator$-CC;comparingInt");
     assertInvokeStaticMatching(invokes, 20, "List$-EL;sort");
     assertInvokeStaticMatching(invokes, 21, "Collection$-EL;stream");
-    assertEquals(22, invokes.size());
   }
 
   private void assertInvokeInterfaceMatching(List<InstructionSubject> invokes, int i, String s) {
@@ -178,6 +183,11 @@
     assertTrue(invokes.get(i).toString().contains(s));
   }
 
+  private void assertInvokeVirtualMatching(List<InstructionSubject> invokes, int i, String s) {
+    assertTrue(invokes.get(i).isInvokeVirtual());
+    assertTrue(invokes.get(i).toString().contains(s));
+  }
+
   private void assertGeneratedKeepRulesAreCorrect(String keepRules) {
 
     String prefix = libraryDesugaringSpecification.functionPrefix(parameters);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsDominanceTest.java b/src/test/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsDominanceTest.java
new file mode 100644
index 0000000..24ba024
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsDominanceTest.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2023, 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.library;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.util.Arrays;
+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 EmptyVarargsDominanceTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("[]");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) throws NoSuchMethodException {
+      Class<?>[] emptyClassArray = new Class[0];
+      if (System.currentTimeMillis() == 0) {
+        System.out.println(Main.class.getConstructor(emptyClassArray));
+      } else {
+        System.out.println(Arrays.toString(emptyClassArray));
+      }
+    }
+
+    public Main() {}
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsTest.java
index ea72d77..2384387 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsTest.java
@@ -53,9 +53,9 @@
 
               MethodSubject testMethod =
                   mainClassSubject.uniqueMethodWithOriginalName("testDeclared");
-              assertTrue(testMethod.streamInstructions().anyMatch(InstructionSubject::isNewArray));
+              assertTrue(testMethod.streamInstructions().noneMatch(InstructionSubject::isNewArray));
               testMethod = mainClassSubject.uniqueMethodWithOriginalName("testNonDeclared");
-              assertTrue(testMethod.streamInstructions().anyMatch(InstructionSubject::isNewArray));
+              assertTrue(testMethod.streamInstructions().noneMatch(InstructionSubject::isNewArray));
             })
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("hi", "hi");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/DefaultFieldValueJoinerWithServiceLoaderTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/DefaultFieldValueJoinerWithServiceLoaderTest.java
new file mode 100644
index 0000000..1efc4cb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/DefaultFieldValueJoinerWithServiceLoaderTest.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2025, 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.membervaluepropagation;
+
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.Lists;
+import java.util.List;
+import java.util.ServiceLoader;
+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 DefaultFieldValueJoinerWithServiceLoaderTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    List<String> serviceImplementations = Lists.newArrayList();
+    serviceImplementations.add(B.class.getTypeName());
+    serviceImplementations.add(C.class.getTypeName());
+
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addDataResources(
+            DataEntryResource.fromBytes(
+                StringUtils.lines(serviceImplementations).getBytes(),
+                "META-INF/services/" + A.class.getTypeName(),
+                Origin.unknown()))
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("0", "1", "0", "2");
+  }
+
+  public interface A {
+
+    void init();
+
+    void print();
+  }
+
+  public static class B implements A {
+
+    int f;
+
+    @Override
+    public void init() {
+      f = 1;
+    }
+
+    @Override
+    public void print() {
+      System.out.println(f);
+    }
+  }
+
+  public static class C implements A {
+
+    int g;
+
+    @Override
+    public void init() {
+      g = 2;
+    }
+
+    @Override
+    public void print() {
+      System.out.println(g);
+    }
+  }
+
+  public static class Main {
+    public static void main(String[] args) {
+      for (A a : ServiceLoader.load(A.class)) {
+        a.print();
+        a.init();
+        a.print();
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsOnMethodWithArgumentReturnTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsOnMethodWithArgumentReturnTest.java
new file mode 100644
index 0000000..9fb503f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsOnMethodWithArgumentReturnTest.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2025, 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.assumenosideeffects;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+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.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.PrintStream;
+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 AssumeNoSideEffectsOnMethodWithArgumentReturnTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepClassAndMembersRules(Main.class)
+        .addKeepRules(
+            "-assumenosideeffects class " + Preconditions.class.getTypeName() + " {",
+            "  static java.lang.Object checkNotNull(java.lang.Object);",
+            "}")
+        .setMinApi(parameters)
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject preconditionsClass = inspector.clazz(Preconditions.class);
+              assertThat(preconditionsClass, isPresentIf(parameters.isCfRuntime()));
+
+              MethodSubject checkNotNullMethod =
+                  preconditionsClass.uniqueMethodWithOriginalName("checkNotNull");
+              assertThat(checkNotNullMethod, isPresentIf(parameters.isCfRuntime()));
+
+              MethodSubject testMethodSubject =
+                  inspector.clazz(Main.class).uniqueMethodWithOriginalName("test");
+              assertThat(testMethodSubject, isPresent());
+              if (parameters.isCfRuntime()) {
+                // Calls are not eliminated due to not running the move-result optimization when
+                // compiling to CF.
+                assertThat(testMethodSubject, invokesMethod(checkNotNullMethod));
+              } else {
+                assertTrue(
+                    testMethodSubject
+                        .streamInstructions()
+                        .filter(InstructionSubject::isInvokeMethod)
+                        .map(InstructionSubject::getMethod)
+                        .allMatch(
+                            method ->
+                                method.toSourceString().contains(PrintStream.class.getTypeName())));
+              }
+            })
+        .run(parameters.getRuntime(), Main.class, "Hello", ",", " ", "world", "!")
+        .assertSuccessWithOutputLines("Hello, world!");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      test(args[0], args[1], args[2], args[3], args[4]);
+    }
+
+    static void test(Object p1, Object p2, Object p3, Object p4, Object p5) {
+      Object nonNullP1 = Preconditions.checkNotNull(p1);
+      System.out.print(nonNullP1);
+      Object nonNullP2 = Preconditions.checkNotNull(p2);
+      System.out.print(nonNullP2);
+      Object nonNullP3 = Preconditions.checkNotNull(p3);
+      System.out.print(nonNullP3);
+      Object nonNullP4 = Preconditions.checkNotNull(p4);
+      System.out.print(nonNullP4);
+      Object nonNullP5 = Preconditions.checkNotNull(p5);
+      System.out.println(nonNullP5);
+    }
+  }
+
+  static class Preconditions {
+
+    static <T> T checkNotNull(T t) {
+      if (t == null) {
+        throw new RuntimeException("Expected non-null object");
+      }
+      return t;
+    }
+  }
+}
diff --git a/src/test/testbase/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestCompileResult.java b/src/test/testbase/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestCompileResult.java
index 023803d..0f11fc1 100644
--- a/src/test/testbase/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestCompileResult.java
+++ b/src/test/testbase/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestCompileResult.java
@@ -61,6 +61,11 @@
     this.runnableCompiledResult = computeRunnableCompiledResult();
   }
 
+  public DesugaredLibraryTestCompileResult<T> disassemble() throws IOException, ExecutionException {
+    compileResult.disassemble();
+    return this;
+  }
+
   public <E extends Throwable> DesugaredLibraryTestCompileResult<T> inspectCustomLib(
       ThrowingConsumer<CodeInspector, E> consumer) throws IOException, E {
     assert customLibCompile != null;