Add LirToLirDesugaredLibraryLibRewriter

This add support for running the DesugaredLibraryLibRewriter cf-to-cf and lir-to-lir.

Bug: b/391572031
Bug: b/404474726
Change-Id: Iaae2ce2763ce8d87dbc16518d357dc9ea4a2b2e6
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index d20e039..b66ceee 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.disabledesugarer.DesugaredLibraryDisableDesugarer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.AutoCloseableRetargeter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.CfToCfDesugaredLibraryLibRewriter;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryLibRewriter;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeter;
 import com.android.tools.r8.ir.desugar.icce.AlwaysThrowingInstructionDesugaring;
@@ -106,7 +107,8 @@
     }
     this.nestBasedAccessDesugaring = NestBasedAccessDesugaring.create(appView);
     BackportedMethodRewriter backportedMethodRewriter = new BackportedMethodRewriter(appView);
-    DesugaredLibraryLibRewriter desugaredLibRewriter = DesugaredLibraryLibRewriter.create(appView);
+    CfToCfDesugaredLibraryLibRewriter desugaredLibRewriter =
+        DesugaredLibraryLibRewriter.createCfToCf(appView);
     if (desugaredLibRewriter != null) {
       desugarings.add(desugaredLibRewriter);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaring.java
index 16099e7..d59d1cb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaring.java
@@ -20,6 +20,8 @@
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.LirToLirDesugaredLibraryApiConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryLibRewriter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.LirToLirDesugaredLibraryLibRewriter;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
 import com.android.tools.r8.ir.optimize.DeadCodeRemover;
@@ -93,6 +95,8 @@
     CfInstructionDesugaringEventConsumer eventConsumer =
         CfInstructionDesugaringEventConsumer.createForR8LirToLirLibraryDesugaring(
             appView, profileCollectionAdditions);
+    LirToLirDesugaredLibraryLibRewriter desugaredLibraryLibRewriter =
+        DesugaredLibraryLibRewriter.createLirToLir(appView, eventConsumer);
     // TODO(b/391572031): Implement lir-to-lir interface method rewriting.
     InterfaceMethodRewriter interfaceMethodRewriter = null;
     LirToLirDesugaredLibraryApiConverter desugaredLibraryAPIConverter =
@@ -112,6 +116,7 @@
                       new R8LibraryDesugaringGraphLens(
                           appView,
                           desugaredLibraryAPIConverter,
+                          desugaredLibraryLibRewriter,
                           interfaceMethodRewriter,
                           eventConsumer,
                           method,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaringGraphLens.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaringGraphLens.java
index 2125d91..867641b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaringGraphLens.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaringGraphLens.java
@@ -33,6 +33,7 @@
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryConversionCfProvider;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.LirToLirDesugaredLibraryApiConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.LirToLirDesugaredLibraryLibRewriter;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
 import com.android.tools.r8.ir.optimize.CustomLensCodeRewriter;
 import java.util.ArrayList;
@@ -43,6 +44,7 @@
 public class R8LibraryDesugaringGraphLens extends DefaultNonIdentityGraphLens {
 
   private final LirToLirDesugaredLibraryApiConverter desugaredLibraryAPIConverter;
+  private final LirToLirDesugaredLibraryLibRewriter desugaredLibraryLibRewriter;
 
   @SuppressWarnings("UnusedVariable")
   private final InterfaceMethodRewriter interfaceMethodRewriter;
@@ -59,12 +61,14 @@
   public R8LibraryDesugaringGraphLens(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       LirToLirDesugaredLibraryApiConverter desugaredLibraryAPIConverter,
+      LirToLirDesugaredLibraryLibRewriter desugaredLibraryLibRewriter,
       InterfaceMethodRewriter interfaceMethodRewriter,
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod method,
       MethodProcessingContext methodProcessingContext) {
     super(appView);
     this.desugaredLibraryAPIConverter = desugaredLibraryAPIConverter;
+    this.desugaredLibraryLibRewriter = desugaredLibraryLibRewriter;
     this.interfaceMethodRewriter = interfaceMethodRewriter;
     this.eventConsumer = eventConsumer;
     this.method = method;
@@ -93,6 +97,14 @@
     // TODO(b/391572031): Implement invoke desugaring.
     assert previous.getPrototypeChanges().isEmpty();
 
+    if (desugaredLibraryLibRewriter != null) {
+      MethodLookupResult result =
+          desugaredLibraryLibRewriter.lookupMethod(previous, method, methodProcessingContext, this);
+      if (result != previous) {
+        return result;
+      }
+    }
+
     if (desugaredLibraryAPIConverter != null) {
       return desugaredLibraryAPIConverter.lookupMethod(
           previous, method, methodProcessingContext, this);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/CfToCfDesugaredLibraryLibRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/CfToCfDesugaredLibraryLibRewriter.java
new file mode 100644
index 0000000..aadda62
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/CfToCfDesugaredLibraryLibRewriter.java
@@ -0,0 +1,66 @@
+// 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.desugar.desugaredlibrary.retargeter;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfOpcodeUtils;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.DesugarDescription;
+import java.util.Collections;
+import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.IntConsumer;
+import org.objectweb.asm.Opcodes;
+
+public class CfToCfDesugaredLibraryLibRewriter extends DesugaredLibraryLibRewriter
+    implements CfInstructionDesugaring {
+
+  CfToCfDesugaredLibraryLibRewriter(
+      AppView<?> appView,
+      Map<DexMethod, BiFunction<DexItemFactory, DexMethod, CfCode>> rewritings) {
+    super(appView, rewritings);
+  }
+
+  @Override
+  public void acceptRelevantAsmOpcodes(IntConsumer consumer) {
+    CfOpcodeUtils.acceptCfInvokeOpcodes(consumer);
+  }
+
+  @Override
+  public DesugarDescription compute(CfInstruction instruction, ProgramMethod context) {
+    if (!isApplicableToContext(context) || !instruction.isInvoke()) {
+      return DesugarDescription.nothing();
+    }
+    DexMethod invokedMethod = instruction.asInvoke().getMethod();
+    if (!rewritings.containsKey(invokedMethod)) {
+      return DesugarDescription.nothing();
+    }
+    return DesugarDescription.builder()
+        .setDesugarRewrite(
+            (position,
+                freshLocalProvider,
+                localStackAllocator,
+                desugaringInfo,
+                eventConsumer,
+                localContext,
+                methodProcessingContext,
+                desugarings,
+                dexItemFactory) -> {
+              DexMethod newInvokeTarget =
+                  getRetargetMethod(
+                      invokedMethod, eventConsumer, localContext, methodProcessingContext);
+              assert appView.definitionForHolder(newInvokeTarget, context) != null;
+              assert !appView.definitionForHolder(newInvokeTarget, context).isInterface();
+              return Collections.singletonList(
+                  new CfInvoke(Opcodes.INVOKESTATIC, newInvokeTarget, false));
+            })
+        .build();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryLibRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryLibRewriter.java
index 58d5363..30a7efa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryLibRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryLibRewriter.java
@@ -4,60 +4,70 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter;
 
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.cf.code.CfOpcodeUtils;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
-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.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
-import com.android.tools.r8.ir.desugar.DesugarDescription;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.LibraryDesugaringOptions;
+import com.android.tools.r8.synthesis.SyntheticItems;
 import com.google.common.collect.ImmutableMap;
-import java.util.Collections;
 import java.util.Map;
 import java.util.function.BiFunction;
-import java.util.function.IntConsumer;
-import org.objectweb.asm.Opcodes;
 
 /**
  * This holds specific rewritings when using desugared library and specific libraries such as
  * androidx.
  */
-public class DesugaredLibraryLibRewriter implements CfInstructionDesugaring {
+public abstract class DesugaredLibraryLibRewriter {
 
-  private final AppView<?> appView;
-  private final Map<DexMethod, BiFunction<DexItemFactory, DexMethod, CfCode>> rewritings;
+  final AppView<?> appView;
+  final Map<DexMethod, BiFunction<DexItemFactory, DexMethod, CfCode>> rewritings;
 
-  private DesugaredLibraryLibRewriter(
+  DesugaredLibraryLibRewriter(
       AppView<?> appView,
       Map<DexMethod, BiFunction<DexItemFactory, DexMethod, CfCode>> rewritings) {
     this.appView = appView;
     this.rewritings = rewritings;
   }
 
-  public static DesugaredLibraryLibRewriter create(AppView<?> appView) {
-    if (appView
-        .options()
-        .getLibraryDesugaringOptions()
-        .getMachineDesugaredLibrarySpecification()
-        .getRewriteType()
-        .isEmpty()) {
+  public static CfToCfDesugaredLibraryLibRewriter createCfToCf(AppView<?> appView) {
+    LibraryDesugaringOptions libraryDesugaringOptions =
+        appView.options().getLibraryDesugaringOptions();
+    if (libraryDesugaringOptions.isLirToLirLibraryDesugaringEnabled()
+        || libraryDesugaringOptions
+            .getMachineDesugaredLibrarySpecification()
+            .getRewriteType()
+            .isEmpty()) {
       return null;
     }
     Map<DexMethod, BiFunction<DexItemFactory, DexMethod, CfCode>> rewritings = computeMap(appView);
     if (rewritings.isEmpty()) {
       return null;
     }
-    return new DesugaredLibraryLibRewriter(appView, rewritings);
+    return new CfToCfDesugaredLibraryLibRewriter(appView, rewritings);
+  }
+
+  public static LirToLirDesugaredLibraryLibRewriter createLirToLir(
+      AppView<?> appView, CfInstructionDesugaringEventConsumer eventConsumer) {
+    LibraryDesugaringOptions libraryDesugaringOptions =
+        appView.options().getLibraryDesugaringOptions();
+    if (libraryDesugaringOptions.isCfToCfLibraryDesugaringEnabled()
+        || libraryDesugaringOptions
+            .getMachineDesugaredLibrarySpecification()
+            .getRewriteType()
+            .isEmpty()) {
+      return null;
+    }
+    Map<DexMethod, BiFunction<DexItemFactory, DexMethod, CfCode>> rewritings = computeMap(appView);
+    if (rewritings.isEmpty()) {
+      return null;
+    }
+    return new LirToLirDesugaredLibraryLibRewriter(appView, eventConsumer, rewritings);
   }
 
   public static Map<DexMethod, BiFunction<DexItemFactory, DexMethod, CfCode>> computeMap(
@@ -67,17 +77,14 @@
     if (!appView.appInfo().hasDefinitionForWithoutExistenceAssert(navType)) {
       return ImmutableMap.of();
     }
-    ImmutableMap.Builder<DexMethod, BiFunction<DexItemFactory, DexMethod, CfCode>> builder =
-        ImmutableMap.builder();
-    DexType navTypeCompanion = factory.createType("Landroidx/navigation/NavType$Companion;");
-    DexProto fromProto = factory.createProto(navType, factory.stringType, factory.stringType);
-    DexString name = factory.createString("fromArgType");
-    DexMethod from = factory.createMethod(navTypeCompanion, fromProto, name);
-    DexClassAndMethod dexClassAndMethod = appView.definitionFor(from);
-    if (dexClassAndMethod == null) {
+    DexMethod from =
+        factory.createMethod(
+            factory.createType("Landroidx/navigation/NavType$Companion;"),
+            factory.createProto(navType, factory.stringType, factory.stringType),
+            "fromArgType");
+    if (!appView.appInfo().hasDefinitionFor(from)) {
       appView
-          .options()
-          .reporter
+          .reporter()
           .warning(
               "The class "
                   + navType
@@ -91,56 +98,25 @@
     }
     BiFunction<DexItemFactory, DexMethod, CfCode> cfCodeProvider =
         DesugaredLibraryCfMethods::DesugaredLibraryBridge_fromArgType;
-    builder.put(from, cfCodeProvider);
-    return builder.build();
+    return ImmutableMap.of(from, cfCodeProvider);
   }
 
-  @Override
-  public void acceptRelevantAsmOpcodes(IntConsumer consumer) {
-    CfOpcodeUtils.acceptCfInvokeOpcodes(consumer);
+  boolean isApplicableToContext(ProgramMethod context) {
+    SyntheticItems syntheticItems = appView.getSyntheticItems();
+    return !syntheticItems.isSyntheticOfKind(
+        context.getHolderType(), kinds -> kinds.DESUGARED_LIBRARY_BRIDGE);
   }
 
-  @Override
-  public DesugarDescription compute(CfInstruction instruction, ProgramMethod context) {
-    if (appView
-        .getSyntheticItems()
-        .isSyntheticOfKind(context.getHolderType(), kinds -> kinds.DESUGARED_LIBRARY_BRIDGE)) {
-      return DesugarDescription.nothing();
-    }
-    if (instruction.isInvoke() && rewritings.containsKey(instruction.asInvoke().getMethod())) {
-      return DesugarDescription.builder()
-          .setDesugarRewrite(
-              (position,
-                  freshLocalProvider,
-                  localStackAllocator,
-                  desugaringInfo,
-                  eventConsumer,
-                  localContext,
-                  methodProcessingContext,
-                  desugarings,
-                  dexItemFactory) -> {
-                DexMethod newInvokeTarget =
-                    ensureBridge(
-                        instruction.asInvoke().getMethod(),
-                        eventConsumer,
-                        methodProcessingContext,
-                        localContext);
-                assert appView.definitionFor(newInvokeTarget.getHolderType()) != null;
-                assert !appView.definitionFor(newInvokeTarget.getHolderType()).isInterface();
-                return Collections.singletonList(
-                    new CfInvoke(Opcodes.INVOKESTATIC, newInvokeTarget, false));
-              })
-          .build();
-    }
-    return DesugarDescription.nothing();
-  }
-
-  private DexMethod ensureBridge(
+  DexMethod getRetargetMethod(
       DexMethod source,
       CfInstructionDesugaringEventConsumer eventConsumer,
-      MethodProcessingContext methodProcessingContext,
-      ProgramMethod localContext) {
+      ProgramMethod context,
+      MethodProcessingContext methodProcessingContext) {
+    assert isApplicableToContext(context);
     BiFunction<DexItemFactory, DexMethod, CfCode> target = rewritings.get(source);
+    if (target == null) {
+      return null;
+    }
     ProgramMethod newMethod =
         appView
             .getSyntheticItems()
@@ -154,7 +130,7 @@
                         .setProto(appView.dexItemFactory().prependHolderToProto(source))
                         .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
                         .setCode(methodSig -> target.apply(appView.dexItemFactory(), methodSig)));
-    eventConsumer.acceptDesugaredLibraryBridge(newMethod, localContext);
+    eventConsumer.acceptDesugaredLibraryBridge(newMethod, context);
     return newMethod.getReference();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/LirToLirDesugaredLibraryLibRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/LirToLirDesugaredLibraryLibRewriter.java
new file mode 100644
index 0000000..fd40977
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/LirToLirDesugaredLibraryLibRewriter.java
@@ -0,0 +1,51 @@
+// 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.desugar.desugaredlibrary.retargeter;
+
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.lens.MethodLookupResult;
+import com.android.tools.r8.ir.code.InvokeType;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.R8LibraryDesugaringGraphLens;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+public class LirToLirDesugaredLibraryLibRewriter extends DesugaredLibraryLibRewriter {
+
+  private final CfInstructionDesugaringEventConsumer eventConsumer;
+
+  LirToLirDesugaredLibraryLibRewriter(
+      AppView<?> appView,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      Map<DexMethod, BiFunction<DexItemFactory, DexMethod, CfCode>> rewritings) {
+    super(appView, rewritings);
+    this.eventConsumer = eventConsumer;
+  }
+
+  public MethodLookupResult lookupMethod(
+      MethodLookupResult previous,
+      ProgramMethod context,
+      MethodProcessingContext methodProcessingContext,
+      R8LibraryDesugaringGraphLens lens) {
+    if (isApplicableToContext(context)) {
+      DexMethod retargetMethod =
+          getRetargetMethod(
+              previous.getReference(), eventConsumer, context, methodProcessingContext);
+      if (retargetMethod != null) {
+        return MethodLookupResult.builder(lens, lens.getPrevious())
+            .setReference(retargetMethod)
+            .setReboundReference(retargetMethod)
+            .setIsInterface(false)
+            .setType(InvokeType.STATIC)
+            .build();
+      }
+    }
+    return previous;
+  }
+}