Fix Swap issues with desugared library conversions

- Remove swap when unboxing and use a temp
- Don't swap when last but one parameter needs to
  be converted and last parameter is wide.

Bug: b/266741933
Change-Id: I9b1aa35446540c1c2f382d41543c1173fbb62a52
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
index 2d72b93..757b75f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
@@ -88,6 +88,7 @@
       return rewriteLibraryInvoke(
           instruction.asInvoke(),
           methodProcessingContext,
+          freshLocalProvider,
           localStackAllocator,
           eventConsumer,
           context);
@@ -237,6 +238,7 @@
   private Collection<CfInstruction> rewriteLibraryInvoke(
       CfInvoke invoke,
       MethodProcessingContext methodProcessingContext,
+      FreshLocalProvider freshLocalProvider,
       LocalStackAllocator localStackAllocator,
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context) {
@@ -257,7 +259,12 @@
     return wrapperSynthesizor
         .getConversionCfProvider()
         .generateInlinedAPIConversion(
-            invoke, methodProcessingContext, localStackAllocator, eventConsumer, context);
+            invoke,
+            methodProcessingContext,
+            freshLocalProvider,
+            localStackAllocator,
+            eventConsumer,
+            context);
   }
 
   // If the option is set, we try to outline API conversions as much as possible to reduce the
@@ -265,6 +272,9 @@
   // of soft verification failures. We cannot outline API conversions through super invokes, to
   // instance initializers and to non public methods.
   private boolean shouldOutlineAPIConversion(CfInvoke invoke, ProgramMethod context) {
+    if (appView.options().testing.forceInlineAPIConversions) {
+      return false;
+    }
     if (invoke.isInvokeSuper(context.getHolderType())) {
       return false;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryConversionCfProvider.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryConversionCfProvider.java
index 94bd06b..7907037 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryConversionCfProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryConversionCfProvider.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.cf.code.CfReturn;
 import com.android.tools.r8.cf.code.CfStackInstruction;
 import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
+import com.android.tools.r8.cf.code.CfStore;
 import com.android.tools.r8.contexts.CompilationContext.MainThreadContext;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.contexts.CompilationContext.UniqueContext;
@@ -36,6 +37,7 @@
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryAPICallbackSynthesizorEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryAPIConverterEventConsumer;
@@ -265,6 +267,7 @@
   public Collection<CfInstruction> generateInlinedAPIConversion(
       CfInvoke invoke,
       MethodProcessingContext methodProcessingContext,
+      FreshLocalProvider freshLocalProvider,
       LocalStackAllocator localStackAllocator,
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context) {
@@ -285,26 +288,34 @@
             context,
             methodProcessingContext::createUniqueContext);
 
-    // If only the last 2 parameters require conversion, we do everything inlined.
-    // If other parameters require conversion, we outline the parameter conversion but keep the API
-    // call inlined.
-    // The returned value is always converted inlined.
-    boolean requireOutlinedParameterConversion = false;
-    for (int i = 0; i < parameterConversions.length - 2; i++) {
-      requireOutlinedParameterConversion |= parameterConversions[i] != null;
-    }
-
+    int parameterSize = invokedMethod.getParameters().size();
     ArrayList<CfInstruction> cfInstructions = new ArrayList<>();
-    if (requireOutlinedParameterConversion) {
-      addOutlineParameterConversionInstructions(
-          parameterConversions,
-          cfInstructions,
-          methodProcessingContext,
-          invokedMethod,
-          localStackAllocator,
-          eventConsumer);
-    } else {
-      addInlineParameterConversionInstructions(parameterConversions, cfInstructions);
+    if (parameterSize != 0) {
+      // If only the last 2 parameters require conversion, we do everything inlined.
+      // If other parameters require conversion, we outline the parameter conversion but keep the
+      // API
+      // call inlined. The returned value is always converted inlined.
+      boolean requireOutlinedParameterConversion = false;
+      for (int i = 0; i < parameterConversions.length - 2; i++) {
+        requireOutlinedParameterConversion |= parameterConversions[i] != null;
+      }
+      // We cannot use the swap instruction if the last parameter is wide.
+      requireOutlinedParameterConversion |=
+          invokedMethod.getParameters().get(parameterSize - 1).isWideType();
+
+      if (requireOutlinedParameterConversion) {
+        addOutlineParameterConversionInstructions(
+            parameterConversions,
+            cfInstructions,
+            methodProcessingContext,
+            invokedMethod,
+            freshLocalProvider,
+            localStackAllocator,
+            eventConsumer);
+      } else {
+        addInlineParameterConversionInstructions(
+            parameterConversions, cfInstructions, invokedMethod);
+      }
     }
 
     DexMethod convertedMethod =
@@ -325,6 +336,7 @@
       ArrayList<CfInstruction> cfInstructions,
       MethodProcessingContext methodProcessingContext,
       DexMethod invokedMethod,
+      FreshLocalProvider freshLocalProvider,
       LocalStackAllocator localStackAllocator,
       CfInstructionDesugaringEventConsumer eventConsumer) {
     localStackAllocator.allocateLocalStack(4);
@@ -353,8 +365,10 @@
     eventConsumer.acceptAPIConversion(parameterConversion);
     cfInstructions.add(
         new CfInvoke(Opcodes.INVOKESTATIC, parameterConversion.getReference(), false));
+    int arrayLocal = freshLocalProvider.getFreshLocal(ValueType.OBJECT.requiredRegisters());
+    cfInstructions.add(new CfStore(ValueType.OBJECT, arrayLocal));
     for (int i = 0; i < parameterConversions.length; i++) {
-      cfInstructions.add(new CfStackInstruction(Opcode.Dup));
+      cfInstructions.add(new CfLoad(ValueType.OBJECT, arrayLocal));
       cfInstructions.add(new CfConstNumber(i, ValueType.INT));
       DexType parameterType =
           parameterConversions[i] != null
@@ -368,9 +382,7 @@
       } else {
         cfInstructions.add(new CfCheckCast(parameterType));
       }
-      cfInstructions.add(new CfStackInstruction(Opcode.Swap));
     }
-    cfInstructions.add(new CfStackInstruction(Opcode.Pop));
   }
 
   private CfCode computeParameterConversionCfCode(
@@ -399,15 +411,13 @@
       stackIndex++;
     }
     cfInstructions.add(new CfReturn(ValueType.OBJECT));
-    return new CfCode(
-        holder,
-        invokedMethod.getParameters().size() + 4,
-        invokedMethod.getParameters().size(),
-        cfInstructions);
+    return new CfCode(holder, stackIndex + 4, stackIndex, cfInstructions);
   }
 
   private void addInlineParameterConversionInstructions(
-      DexMethod[] parameterConversions, ArrayList<CfInstruction> cfInstructions) {
+      DexMethod[] parameterConversions,
+      ArrayList<CfInstruction> cfInstructions,
+      DexMethod invokedMethod) {
     if (parameterConversions.length > 0
         && parameterConversions[parameterConversions.length - 1] != null) {
       cfInstructions.add(
@@ -416,6 +426,10 @@
     }
     if (parameterConversions.length > 1
         && parameterConversions[parameterConversions.length - 2] != null) {
+      assert !invokedMethod
+          .getParameters()
+          .get(invokedMethod.getParameters().size() - 1)
+          .isWideType();
       cfInstructions.add(new CfStackInstruction(Opcode.Swap));
       cfInstructions.add(
           new CfInvoke(
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 ca9c1e7..bc9d193 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1917,6 +1917,7 @@
     public boolean neverReuseCfLocalRegisters = false;
     public boolean roundtripThroughLIR = false;
     public boolean checkReceiverAlwaysNullInCallSiteOptimization = true;
+    public boolean forceInlineAPIConversions = false;
     private boolean hasReadCheckDeterminism = false;
     private DeterminismChecker determinismChecker = null;
     public boolean usePcEncodingInCfForTesting = false;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
index 3091b22..4659eba 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
 
-import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.SPECIFICATIONS_WITH_CF2CF;
 import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11;
 
 import com.android.tools.r8.TestParameters;
@@ -13,6 +13,7 @@
 import com.android.tools.r8.desugar.desugaredlibrary.test.CustomLibrarySpecification;
 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.time.MonthDay;
 import java.util.List;
@@ -30,25 +31,29 @@
   private final TestParameters parameters;
   private final LibraryDesugaringSpecification libraryDesugaringSpecification;
   private final CompilationSpecification compilationSpecification;
+  private final boolean forceInlineAPIConversions;
 
   private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.O;
-  private static final String EXPECTED_RESULT = StringUtils.lines("--01-16");
+  private static final String EXPECTED_RESULT = StringUtils.lines("--01-16", "--01-02");
 
   @Parameters(name = "{0}, spec: {1}, {2}")
   public static List<Object[]> data() {
     return buildParameters(
         getConversionParametersUpToExcluding(MIN_SUPPORTED),
         getJdk8Jdk11(),
-        DEFAULT_SPECIFICATIONS);
+        SPECIFICATIONS_WITH_CF2CF,
+        BooleanUtils.values());
   }
 
   public BasicLongDoubleConversionTest(
       TestParameters parameters,
       LibraryDesugaringSpecification libraryDesugaringSpecification,
-      CompilationSpecification compilationSpecification) {
+      CompilationSpecification compilationSpecification,
+      boolean forceInlineAPIConversions) {
     this.parameters = parameters;
     this.libraryDesugaringSpecification = libraryDesugaringSpecification;
     this.compilationSpecification = compilationSpecification;
+    this.forceInlineAPIConversions = forceInlineAPIConversions;
   }
 
   @Test
@@ -59,7 +64,10 @@
             new CustomLibrarySpecification(CustomLibClass.class, MIN_SUPPORTED))
         .addKeepMainRule(Executor.class)
         .addOptionsModification(options -> options.testing.trackDesugaredAPIConversions = true)
+        .addOptionsModification(
+            options -> options.testing.forceInlineAPIConversions = forceInlineAPIConversions)
         .allowDiagnosticWarningMessages()
+        .compile()
         .run(parameters.getRuntime(), Executor.class)
         .assertSuccessWithOutput(EXPECTED_RESULT);
   }
@@ -69,6 +77,7 @@
     public static void main(String[] args) {
       System.out.println(
           CustomLibClass.mix(3L, 4L, MonthDay.of(1, 2), 5.0, 6.0, MonthDay.of(10, 20)));
+      System.out.println(CustomLibClass.convertThenWide(3L, 4L, MonthDay.of(1, 2), 5.0));
     }
   }
 
@@ -81,5 +90,11 @@
         long l1, long l2, MonthDay monthDay1, double d1, double d2, MonthDay monthDay2) {
       return monthDay1.withDayOfMonth((int) (monthDay2.getDayOfMonth() + l1 - d1 + l2 - d2));
     }
+
+    // This tests the case where only the last but one parameter requires a conversion and the last
+    // parameter is wide.
+    public static MonthDay convertThenWide(long l1, long l2, MonthDay monthDay1, double d1) {
+      return monthDay1.withDayOfMonth((int) (l1 - d1 + l2));
+    }
   }
 }