No longer force IR for code that only requires instruction desugaring

Fixed missing rewritings and stack height adjustment which where
hidden by running the code through IR.

Bug: 147485959
Change-Id: I26c288b3ebab1a0a7b497bca15b3604df0ea222b
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index 926577f..dc284ed 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -233,7 +233,8 @@
       if (typeMethods != null) {
         DexClass holder = appInfo.definitionForHolder(method);
         DexEncodedMethod definition = method.lookupOnClass(holder);
-        assert definition != null : "Could not find method " + method.toString();
+        assert definition != null
+            : "Could not find method " + method.toString() + " in " + context.getTypeName();
         if (!allowObfuscation) {
           noObfuscationTypes.add(method.holder);
         }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
index 98bc101..e55bf9a 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
@@ -30,6 +30,7 @@
 import java.util.ArrayDeque;
 import java.util.Deque;
 import java.util.Objects;
+import java.util.SortedMap;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -342,10 +343,26 @@
     this.stack = stack;
   }
 
+  // This is used from tests. As fastutils are repackaged and minified the method above is
+  // not available from tests which use fastutils in their original namespace.
+  public CfFrame(SortedMap<Integer, FrameType> locals, Deque<FrameType> stack) {
+    this(
+        locals instanceof Int2ReferenceAVLTreeMap
+            ? (Int2ReferenceAVLTreeMap<FrameType>) locals
+            : new Int2ReferenceAVLTreeMap<>(locals),
+        stack);
+  }
+
   public Int2ReferenceSortedMap<FrameType> getLocals() {
     return locals;
   }
 
+  // This is used from tests. As fastutils are repackaged and minified the method above is
+  // not available from tests which use fastutils in their original namespace.
+  public SortedMap<Integer, FrameType> getLocalsAsSortedMap() {
+    return locals;
+  }
+
   public Deque<FrameType> getStack() {
     return stack;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index acd8b41..8abedbe 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -2299,7 +2299,7 @@
 
   private DexType createStaticallyKnownType(Class<?> clazz) {
     return createStaticallyKnownType(
-        createString(DescriptorUtils.javaTypeToDescriptor(clazz.getName())));
+        createString(DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName())));
   }
 
   private DexType createStaticallyKnownType(DexString descriptor) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index a241a05..2cbebcd 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -88,6 +88,7 @@
 import com.android.tools.r8.ir.optimize.string.StringOptimizer;
 import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.ir.synthetic.SynthesizedCode;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.naming.IdentifierNameStringMarker;
 import com.android.tools.r8.position.MethodPosition;
@@ -540,9 +541,6 @@
       return;
     }
     checkPrefixMerging(method);
-    if (!needsIRConversion(definition.getCode(), method)) {
-      return;
-    }
     if (options.isGeneratingClassFiles()
         || !(options.passthroughDexCode && definition.getCode().isDexCode())) {
       // We do not process in call graph order, so anything could be a leaf.
@@ -560,7 +558,10 @@
     }
   }
 
-  private boolean needsIRConversion(Code code, ProgramMethod method) {
+  private boolean needsIRConversion(ProgramMethod method) {
+    if (appView.enableWholeProgramOptimizations()) {
+      return true;
+    }
     if (options.testing.forceIRForCfToCfDesugar) {
       return true;
     }
@@ -570,23 +571,23 @@
     if (!options.cfToCfDesugar) {
       return true;
     }
-    if (instructionDesugaring.needsDesugaring(method)) {
-      return true;
-    }
     if (desugaredLibraryAPIConverter != null
         && desugaredLibraryAPIConverter.shouldRegisterCallback(method)) {
       return true;
     }
-
-    NeedsIRDesugarUseRegistry useRegistry =
-        new NeedsIRDesugarUseRegistry(
-            appView,
-            desugaredLibraryRetargeter,
-            interfaceMethodRewriter,
-            desugaredLibraryAPIConverter);
-    method.registerCodeReferences(useRegistry);
-
-    return useRegistry.needsDesugaring();
+    if (method.getDefinition().getCode() instanceof SynthesizedCode) {
+      // SynthesizedCode needs IR to generate the code.
+      return true;
+    } else {
+      NeedsIRDesugarUseRegistry useRegistry =
+          new NeedsIRDesugarUseRegistry(
+              appView,
+              desugaredLibraryRetargeter,
+              interfaceMethodRewriter,
+              desugaredLibraryAPIConverter);
+      method.registerCodeReferences(useRegistry);
+      return useRegistry.needsDesugaring();
+    }
   }
 
   private void checkPrefixMerging(ProgramMethod method) {
@@ -1119,10 +1120,12 @@
     if (options.testing.hookInIrConversion != null) {
       options.testing.hookInIrConversion.run();
     }
-    if (options.skipIR) {
+
+    if (!needsIRConversion(method) || options.skipIR) {
       feedback.markProcessed(method.getDefinition(), ConstraintWithTarget.NEVER);
       return Timing.empty();
     }
+
     IRCode code = method.buildIR(appView);
     if (code == null) {
       feedback.markProcessed(method.getDefinition(), ConstraintWithTarget.NEVER);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/RecordCfMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/RecordCfMethods.java
index 85a7982..47bc98b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/RecordCfMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/RecordCfMethods.java
@@ -44,8 +44,6 @@
 public final class RecordCfMethods {
 
   public static void registerSynthesizedCodeReferences(DexItemFactory factory) {
-    factory.createSynthesizedType(
-        "Lcom/android/tools/r8/desugar/records/RecordMethods$RecordStub;");
     factory.createSynthesizedType("Ljava/lang/Record;");
     factory.createSynthesizedType("Ljava/util/Arrays;");
     factory.createSynthesizedType("[Ljava/lang/Object;");
@@ -121,9 +119,7 @@
                 new Int2ReferenceAVLTreeMap<>(
                     new int[] {0, 1},
                     new FrameType[] {
-                      FrameType.initialized(
-                          options.itemFactory.createType(
-                              "Lcom/android/tools/r8/desugar/records/RecordMethods$RecordStub;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Record;")),
                       FrameType.initialized(options.itemFactory.objectType)
                     }),
                 new ArrayDeque<>(Arrays.asList())),
@@ -133,9 +129,7 @@
                 new Int2ReferenceAVLTreeMap<>(
                     new int[] {0, 1},
                     new FrameType[] {
-                      FrameType.initialized(
-                          options.itemFactory.createType(
-                              "Lcom/android/tools/r8/desugar/records/RecordMethods$RecordStub;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Record;")),
                       FrameType.initialized(options.itemFactory.objectType)
                     }),
                 new ArrayDeque<>(
@@ -240,9 +234,7 @@
                 new Int2ReferenceAVLTreeMap<>(
                     new int[] {0, 1, 2},
                     new FrameType[] {
-                      FrameType.initialized(
-                          options.itemFactory.createType(
-                              "Lcom/android/tools/r8/desugar/records/RecordMethods$RecordStub;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Record;")),
                       FrameType.initialized(options.itemFactory.stringType),
                       FrameType.initialized(options.itemFactory.stringType)
                     }),
@@ -263,9 +255,7 @@
                 new Int2ReferenceAVLTreeMap<>(
                     new int[] {0, 1, 2},
                     new FrameType[] {
-                      FrameType.initialized(
-                          options.itemFactory.createType(
-                              "Lcom/android/tools/r8/desugar/records/RecordMethods$RecordStub;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Record;")),
                       FrameType.initialized(options.itemFactory.stringType),
                       FrameType.initialized(options.itemFactory.stringType)
                     }),
@@ -325,9 +315,7 @@
                 new Int2ReferenceAVLTreeMap<>(
                     new int[] {0, 1, 2, 3, 4, 5, 6},
                     new FrameType[] {
-                      FrameType.initialized(
-                          options.itemFactory.createType(
-                              "Lcom/android/tools/r8/desugar/records/RecordMethods$RecordStub;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Record;")),
                       FrameType.initialized(options.itemFactory.stringType),
                       FrameType.initialized(options.itemFactory.stringType),
                       FrameType.initialized(options.itemFactory.createType("[Ljava/lang/String;")),
@@ -398,9 +386,7 @@
                 new Int2ReferenceAVLTreeMap<>(
                     new int[] {0, 1, 2, 3, 4, 5, 6},
                     new FrameType[] {
-                      FrameType.initialized(
-                          options.itemFactory.createType(
-                              "Lcom/android/tools/r8/desugar/records/RecordMethods$RecordStub;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Record;")),
                       FrameType.initialized(options.itemFactory.stringType),
                       FrameType.initialized(options.itemFactory.stringType),
                       FrameType.initialized(options.itemFactory.createType("[Ljava/lang/String;")),
@@ -416,9 +402,7 @@
                 new Int2ReferenceAVLTreeMap<>(
                     new int[] {0, 1, 2, 3, 4, 5},
                     new FrameType[] {
-                      FrameType.initialized(
-                          options.itemFactory.createType(
-                              "Lcom/android/tools/r8/desugar/records/RecordMethods$RecordStub;")),
+                      FrameType.initialized(options.itemFactory.createType("Ljava/lang/Record;")),
                       FrameType.initialized(options.itemFactory.stringType),
                       FrameType.initialized(options.itemFactory.stringType),
                       FrameType.initialized(options.itemFactory.createType("[Ljava/lang/String;")),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java
index b9a8880..1b5e6aa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java
@@ -130,7 +130,11 @@
     assert !instruction.isInitClass();
     if (instruction.isInvokeDynamic() && needsDesugaring(instruction.asInvokeDynamic(), context)) {
       return desugarInvokeDynamicOnRecord(
-          instruction.asInvokeDynamic(), context, eventConsumer, methodProcessingContext);
+          instruction.asInvokeDynamic(),
+          localStackAllocator,
+          context,
+          eventConsumer,
+          methodProcessingContext);
     }
     if (instruction.isInvoke()) {
       CfInvoke cfInvoke = instruction.asInvoke();
@@ -146,6 +150,7 @@
 
   public List<CfInstruction> desugarInvokeDynamicOnRecord(
       CfInvokeDynamic invokeDynamic,
+      LocalStackAllocator localStackAllocator,
       ProgramMethod context,
       CfInstructionDesugaringEventConsumer eventConsumer,
       MethodProcessingContext methodProcessingContext) {
@@ -166,7 +171,13 @@
           ClassNameComputationInfo.ClassNameMapping.SIMPLE_NAME.map(
               recordValueType.getValue().toDescriptorString(), context.getHolder(), factory);
       return desugarInvokeRecordToString(
-          recordClass, fieldNames, fields, simpleName, eventConsumer, methodProcessingContext);
+          recordClass,
+          fieldNames,
+          fields,
+          simpleName,
+          localStackAllocator,
+          eventConsumer,
+          methodProcessingContext);
     }
     if (callSite.methodName == factory.hashCodeMethodName) {
       return desugarInvokeRecordHashCode(
@@ -273,12 +284,14 @@
       DexString fieldNames,
       DexField[] fields,
       DexString simpleName,
+      LocalStackAllocator localStackAllocator,
       CfInstructionDesugaringEventConsumer eventConsumer,
       MethodProcessingContext methodProcessingContext) {
     ensureGetFieldsAsObjects(recordClass, fields, eventConsumer);
     ArrayList<CfInstruction> instructions = new ArrayList<>();
     instructions.add(new CfConstString(simpleName));
     instructions.add(new CfConstString(fieldNames));
+    localStackAllocator.allocateLocalStack(2);
     ProgramMethod programMethod =
         synthesizeRecordHelper(
             recordToStringHelperProto,
diff --git a/src/test/java/com/android/tools/r8/desugar/records/GenerateRecordMethods.java b/src/test/java/com/android/tools/r8/desugar/records/GenerateRecordMethods.java
index 53d6201..e3078a2 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/GenerateRecordMethods.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/GenerateRecordMethods.java
@@ -8,22 +8,30 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.cf.code.CfTypeInstruction;
 import com.android.tools.r8.cfmethodgeneration.MethodGenerationBase;
+import com.android.tools.r8.desugar.records.RecordMethods.RecordStub;
 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.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.RecordRewriter;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.FileUtils;
 import com.google.common.collect.ImmutableList;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.Deque;
 import java.util.List;
+import java.util.SortedMap;
 import java.util.stream.Collectors;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -34,6 +42,8 @@
 public class GenerateRecordMethods extends MethodGenerationBase {
   private final DexType GENERATED_TYPE =
       factory.createType("Lcom/android/tools/r8/ir/desugar/RecordCfMethods;");
+  private final DexType RECORD_STUB_TYPE =
+      factory.createType(DescriptorUtils.javaTypeToDescriptor(RecordStub.class.getTypeName()));
   private final List<Class<?>> METHOD_TEMPLATE_CLASSES = ImmutableList.of(RecordMethods.class);
 
   protected final TestParameters parameters;
@@ -64,32 +74,30 @@
 
   @Override
   protected CfCode getCode(String holderName, String methodName, CfCode code) {
-    DexType recordStubType =
-        factory.createType("Lcom/android/tools/r8/desugar/records/RecordMethods$RecordStub;");
     code.setInstructions(
         code.getInstructions().stream()
-            .map(instruction -> rewriteRecordStub(factory, instruction, recordStubType))
+            .map(instruction -> rewriteRecordStub(instruction))
             .collect(Collectors.toList()));
     return code;
   }
 
-  private CfInstruction rewriteRecordStub(
-      DexItemFactory factory, CfInstruction instruction, DexType recordStubType) {
+  private CfInstruction rewriteRecordStub(CfInstruction instruction) {
     if (instruction.isTypeInstruction()) {
       CfTypeInstruction typeInstruction = instruction.asTypeInstruction();
-      return typeInstruction.withType(
-          rewriteType(factory, recordStubType, typeInstruction.getType()));
+      return typeInstruction.withType(rewriteType(typeInstruction.getType()));
     }
     if (instruction.isInvoke()) {
       CfInvoke cfInvoke = instruction.asInvoke();
       DexMethod method = cfInvoke.getMethod();
       DexMethod newMethod =
-          factory.createMethod(
-              rewriteType(factory, recordStubType, method.holder),
-              method.proto,
-              rewriteName(method.name));
+          factory.createMethod(rewriteType(method.holder), method.proto, rewriteName(method.name));
       return new CfInvoke(cfInvoke.getOpcode(), newMethod, cfInvoke.isInterface());
     }
+    if (instruction.isFrame()) {
+      CfFrame cfFrame = instruction.asFrame();
+      return new CfFrame(
+          rewriteLocals(cfFrame.getLocalsAsSortedMap()), rewriteStack(cfFrame.getStack()));
+    }
     return instruction;
   }
 
@@ -99,8 +107,40 @@
         : name.toString();
   }
 
-  private DexType rewriteType(DexItemFactory factory, DexType recordStubType, DexType type) {
-    return type == recordStubType ? factory.recordType : type;
+  private DexType rewriteType(DexType type) {
+    DexType baseType = type.isArrayType() ? type.toBaseType(factory) : type;
+    if (baseType != RECORD_STUB_TYPE) {
+      return type;
+    }
+    return type.isArrayType()
+        ? type.replaceBaseType(factory.recordType, factory)
+        : factory.recordType;
+  }
+
+  private FrameType rewriteFrameType(FrameType frameType) {
+    if (frameType.isInitialized() && frameType.getInitializedType().isReferenceType()) {
+      DexType newType = rewriteType(frameType.getInitializedType());
+      if (newType == frameType.getInitializedType()) {
+        return frameType;
+      }
+      return FrameType.initialized(newType);
+    } else {
+      assert !frameType.isUninitializedNew();
+      assert !frameType.isUninitializedThis();
+      return frameType;
+    }
+  }
+
+  private SortedMap<Integer, FrameType> rewriteLocals(SortedMap<Integer, FrameType> locals) {
+    Int2ReferenceSortedMap<FrameType> newLocals = new Int2ReferenceAVLTreeMap<>();
+    locals.forEach((index, local) -> newLocals.put((int) index, rewriteFrameType(local)));
+    return newLocals;
+  }
+
+  private Deque<FrameType> rewriteStack(Deque<FrameType> stack) {
+    ArrayDeque<FrameType> newStack = new ArrayDeque<>();
+    stack.forEach(frameType -> newStack.add(rewriteFrameType(frameType)));
+    return newStack;
   }
 
   @Test