Version 1.4.39

Cherry pick: Support for argument removal in dex frontend
CL: https://r8-review.googlesource.com/c/r8/+/33386

Bug: 123317382
Change-Id: I36d6c54c6cf38c316015e628760d6e51b98b276f
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index f8b366e..eb8dbbf 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.4.38";
+  public static final String LABEL = "1.4.39";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 884df4f..bac74f6 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -79,6 +79,7 @@
 
         private int argumentIndex = -1;
         private boolean isAlwaysNull = false;
+        private DexType type = null;
 
         public Builder setArgumentIndex(int argumentIndex) {
           this.argumentIndex = argumentIndex;
@@ -90,18 +91,26 @@
           return this;
         }
 
+        public Builder setType(DexType type) {
+          this.type = type;
+          return this;
+        }
+
         public RemovedArgumentInfo build() {
           assert argumentIndex >= 0;
-          return new RemovedArgumentInfo(argumentIndex, isAlwaysNull);
+          assert type != null;
+          return new RemovedArgumentInfo(argumentIndex, isAlwaysNull, type);
         }
       }
 
       private final int argumentIndex;
       private final boolean isAlwaysNull;
+      private final DexType type;
 
-      private RemovedArgumentInfo(int argumentIndex, boolean isAlwaysNull) {
+      private RemovedArgumentInfo(int argumentIndex, boolean isAlwaysNull, DexType type) {
         this.argumentIndex = argumentIndex;
         this.isAlwaysNull = isAlwaysNull;
+        this.type = type;
       }
 
       public static Builder builder() {
@@ -112,6 +121,10 @@
         return argumentIndex;
       }
 
+      public DexType getType() {
+        return type;
+      }
+
       public boolean isAlwaysNull() {
         return isAlwaysNull;
       }
@@ -122,7 +135,7 @@
 
       public RemovedArgumentInfo withArgumentIndex(int argumentIndex) {
         return this.argumentIndex != argumentIndex
-            ? new RemovedArgumentInfo(argumentIndex, isAlwaysNull)
+            ? new RemovedArgumentInfo(argumentIndex, isAlwaysNull, type)
             : this;
       }
     }
@@ -788,8 +801,8 @@
    * <p>Subclasses can override the lookup methods.
    *
    * <p>For method mapping where invocation type can change just override {@link
-   * #mapInvocationType(DexMethod, DexMethod, DexMethod, Type)} if the default name mapping applies,
-   * and only invocation type might need to change.
+   * #mapInvocationType(DexMethod, DexMethod, Type)} if the default name mapping applies, and only
+   * invocation type might need to change.
    */
   public static class NestedGraphLense extends GraphLense {
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index 6acd9b1..9d92099 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -38,9 +38,9 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentInfo;
+import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentsInfo;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.CanonicalPositions;
 import com.android.tools.r8.ir.code.CatchHandlers;
@@ -49,14 +49,14 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
 
 public class DexSourceCode implements SourceCode {
 
   private final DexCode code;
-  private final MethodAccessFlags accessFlags;
-  private final DexProto proto;
+  private final DexEncodedMethod method;
 
   // Mapping from instruction offset to instruction index in the DexCode instruction array.
   private final Map<Integer, Integer> offsetToInstructionIndex = new HashMap<>();
@@ -72,8 +72,6 @@
   private Position currentPosition = null;
   private final CanonicalPositions canonicalPositions;
 
-  private final List<TypeLatticeElement> argumentTypes;
-
   private List<DexDebugEntry> debugEntries = null;
   // In case of inlining the position of the invoke in the caller.
   private final DexMethod originalMethod;
@@ -85,11 +83,8 @@
       Position callerPosition,
       AppInfo appInfo) {
     this.code = code;
-    this.proto = method.method.proto;
-    this.accessFlags = method.accessFlags;
+    this.method = method;
     this.originalMethod = originalMethod;
-
-    argumentTypes = computeArgumentTypes(appInfo);
     DexDebugInfo info = code.getDebugInfo();
     if (info != null) {
       debugEntries = info.computeEntries(originalMethod);
@@ -148,16 +143,46 @@
     if (code.incomingRegisterSize == 0) {
       return;
     }
+
+    RemovedArgumentsInfo removedArgumentsInfo = builder.prototypeChanges.getRemovedArgumentsInfo();
+    ListIterator<RemovedArgumentInfo> removedArgumentIterator = removedArgumentsInfo.iterator();
+    RemovedArgumentInfo nextRemovedArgument =
+        removedArgumentIterator.hasNext() ? removedArgumentIterator.next() : null;
+
     // Fill in the Argument instructions (incomingRegisterSize last registers) in the argument
     // block.
+    int argumentIndex = 0;
+
     int register = code.registerSize - code.incomingRegisterSize;
-    if (!accessFlags.isStatic()) {
+    if (!method.isStatic()) {
       builder.addThisArgument(register);
+      ++argumentIndex;
       ++register;
     }
-    for (TypeLatticeElement typeLattice : argumentTypes) {
-      builder.addNonThisArgument(register, typeLattice);
-      register += typeLattice.requiredRegisters();
+
+    int numberOfArguments =
+        method.method.proto.parameters.values.length
+            + removedArgumentsInfo.numberOfRemovedArguments()
+            + (method.isStatic() ? 0 : 1);
+
+    for (int usedArgumentIndex = 0; argumentIndex < numberOfArguments; ++argumentIndex) {
+      TypeLatticeElement type;
+      if (nextRemovedArgument != null && nextRemovedArgument.getArgumentIndex() == argumentIndex) {
+        type =
+            TypeLatticeElement.fromDexType(
+                nextRemovedArgument.getType(), true, builder.getAppInfo());
+        builder.addConstantOrUnusedArgument(register);
+        nextRemovedArgument =
+            removedArgumentIterator.hasNext() ? removedArgumentIterator.next() : null;
+      } else {
+        type =
+            TypeLatticeElement.fromDexType(
+                method.method.proto.parameters.values[usedArgumentIndex++],
+                true,
+                builder.getAppInfo());
+        builder.addNonThisArgument(register, type);
+      }
+      register += type.requiredRegisters();
     }
     builder.flushArgumentInstructions();
   }
@@ -309,14 +334,6 @@
         arrayFilledDataPayloadResolver.getData(payloadOffset));
   }
 
-  private List<TypeLatticeElement> computeArgumentTypes(AppInfo appInfo) {
-    List<TypeLatticeElement> types = new ArrayList<>(proto.parameters.size());
-    for (DexType type : proto.parameters.values) {
-      types.add(TypeLatticeElement.fromDexType(type, true, appInfo));
-    }
-    return types;
-  }
-
   private boolean isInvoke(Instruction dex) {
     return dex instanceof InvokeDirect
         || dex instanceof InvokeDirectRange
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 4c9b15e..a49b6ac 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -392,7 +392,7 @@
   private DexEncodedMethod context;
   private final AppInfo appInfo;
   private final Origin origin;
-  private RewrittenPrototypeDescription prototypeChanges;
+  final RewrittenPrototypeDescription prototypeChanges;
   private ListIterator<RemovedArgumentInfo> removedArgumentsIterator;
   private int argumentCount = 0;
 
@@ -889,15 +889,8 @@
       DebugLocalInfo local = getOutgoingLocal(register);
       Value value = writeRegister(register, typeLattice, ThrowingInfo.NO_THROW, local);
       addInstruction(new Argument(value));
-    } else if (removedArgumentInfo.isAlwaysNull()) {
-      if (pendingArgumentInstructions == null) {
-        pendingArgumentInstructions = new ArrayList<>();
-      }
-      DebugLocalInfo local = getOutgoingLocal(register);
-      Value value = writeRegister(register, TypeLatticeElement.NULL, ThrowingInfo.NO_THROW, local);
-      pendingArgumentInstructions.add(new ConstNumber(value, 0));
     } else {
-      assert removedArgumentInfo.isNeverUsed();
+      handleConstantOrUnusedArgument(register, removedArgumentInfo);
     }
   }
 
@@ -913,6 +906,25 @@
     }
   }
 
+  public void addConstantOrUnusedArgument(int register) {
+    handleConstantOrUnusedArgument(register, getRemovedArgumentInfo());
+  }
+
+  private void handleConstantOrUnusedArgument(
+      int register, RemovedArgumentInfo removedArgumentInfo) {
+    assert removedArgumentInfo != null;
+    if (removedArgumentInfo.isAlwaysNull()) {
+      if (pendingArgumentInstructions == null) {
+        pendingArgumentInstructions = new ArrayList<>();
+      }
+      DebugLocalInfo local = getOutgoingLocal(register);
+      Value value = writeRegister(register, TypeLatticeElement.NULL, ThrowingInfo.NO_THROW, local);
+      pendingArgumentInstructions.add(new ConstNumber(value, 0));
+    } else {
+      assert removedArgumentInfo.isNeverUsed();
+    }
+  }
+
   public void flushArgumentInstructions() {
     if (pendingArgumentInstructions != null) {
       pendingArgumentInstructions.forEach(this::addInstruction);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index 11e0e59..6bd668c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -297,7 +297,11 @@
           removedArgumentsInfo = new ArrayList<>();
         }
         removedArgumentsInfo.add(
-            RemovedArgumentInfo.builder().setArgumentIndex(i + offset).setIsAlwaysNull().build());
+            RemovedArgumentInfo.builder()
+                .setArgumentIndex(i + offset)
+                .setIsAlwaysNull()
+                .setType(type)
+                .build());
       }
     }
     return removedArgumentsInfo != null
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
index 9d2bb32..f0aa91a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
@@ -208,8 +208,8 @@
       return null;
     }
     assert method.getCode().getOwner() == method;
-    int argumentCount =
-        method.method.proto.parameters.size() + (method.accessFlags.isStatic() ? 0 : 1);
+    int offset = method.accessFlags.isStatic() ? 0 : 1;
+    int argumentCount = method.method.proto.parameters.size() + offset;
     // TODO(65810338): Implement for virtual methods as well.
     if (method.accessFlags.isPrivate() || method.accessFlags.isStatic()) {
       CollectUsedArguments collector = new CollectUsedArguments();
@@ -222,9 +222,13 @@
       BitSet used = collector.getUsedArguments();
       if (used.cardinality() < argumentCount) {
         List<RemovedArgumentInfo> unused = new ArrayList<>();
-        for (int i = 0; i < argumentCount; i++) {
-          if (!used.get(i)) {
-            unused.add(RemovedArgumentInfo.builder().setArgumentIndex(i).build());
+        for (int argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) {
+          if (!used.get(argumentIndex)) {
+            unused.add(
+                RemovedArgumentInfo.builder()
+                    .setArgumentIndex(argumentIndex)
+                    .setType(method.method.proto.parameters.values[argumentIndex - offset])
+                    .build());
           }
         }
         return new RemovedArgumentsInfo(unused);