Extend unused argument removal to private methods

Bug: 65810338
Change-Id: I4be35f2719afa97234985fb1948726ffa4bca7ab
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 e06cdbef..573ffb5 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
@@ -104,14 +104,22 @@
     private final MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
     private final Set<Wrapper<DexMethod>> usedSignatures = new HashSet<>();
 
-    private DexProto protoWithRemovedArguments(DexMethod method, RemovedArgumentsInfo unused) {
-      DexType[] parameters =
-          new DexType[method.proto.parameters.size() - unused.numberOfRemovedArguments()];
-      if (parameters.length > 0) {
+    private DexProto protoWithRemovedArguments(
+        DexEncodedMethod encodedMethod, RemovedArgumentsInfo unused) {
+      DexMethod method = encodedMethod.method;
+
+      int firstArgumentIndex = encodedMethod.isStatic() ? 0 : 1;
+      int numberOfParameters = method.proto.parameters.size() - unused.numberOfRemovedArguments();
+      if (!encodedMethod.isStatic() && unused.isArgumentRemoved(0)) {
+        numberOfParameters++;
+      }
+
+      DexType[] parameters = new DexType[numberOfParameters];
+      if (numberOfParameters > 0) {
         int newIndex = 0;
-        for (int j = 0; j < method.proto.parameters.size(); j++) {
-          if (!unused.isArgumentRemoved(j)) {
-            parameters[newIndex++] = method.proto.parameters.values[j];
+        for (int oldIndex = 0; oldIndex < method.proto.parameters.size(); oldIndex++) {
+          if (!unused.isArgumentRemoved(oldIndex + firstArgumentIndex)) {
+            parameters[newIndex++] = method.proto.parameters.values[oldIndex];
           }
         }
         assert newIndex == parameters.length;
@@ -128,19 +136,26 @@
     }
 
     DexEncodedMethod removeArguments(DexEncodedMethod method, RemovedArgumentsInfo unused) {
+      if (unused == null) {
+        return null;
+      }
+
       boolean removed = usedSignatures.remove(equivalence.wrap(method.method));
       assert removed;
-      DexProto newProto = protoWithRemovedArguments(method.method, unused);
+      DexProto newProto = protoWithRemovedArguments(method, unused);
       DexMethod newSignature;
       int count = 0;
       DexString newName = null;
       do {
-        newName =
-            newName == null
-                ? method.method.name
-                : appView
-                    .dexItemFactory()
-                    .createString(method.method.name.toSourceString() + count);
+        if (newName == null) {
+          newName = method.method.name;
+        } else if (method.method.name != appView.dexItemFactory().initMethodName) {
+          newName =
+              appView.dexItemFactory().createString(method.method.name.toSourceString() + count);
+        } else {
+          // Constructors must be named `<init>`.
+          return null;
+        }
         newSignature =
             appView.dexItemFactory().createMethod(method.method.holder, newProto, newName);
         count++;
@@ -158,8 +173,8 @@
     for (int i = 0; i < clazz.directMethods().length; i++) {
       DexEncodedMethod method = clazz.directMethods()[i];
       RemovedArgumentsInfo unused = collectUnusedArguments(method);
-      if (unused != null) {
-        DexEncodedMethod newMethod = signatures.removeArguments(method, unused);
+      DexEncodedMethod newMethod = signatures.removeArguments(method, unused);
+      if (newMethod != null) {
         clazz.directMethods()[i] = newMethod;
         methodMapping.put(method.method, newMethod.method);
         removedArguments.put(newMethod.method, unused);
@@ -178,21 +193,25 @@
     assert method.getCode().getOwner() == method;
     int argumentCount =
         method.method.proto.parameters.size() + (method.accessFlags.isStatic() ? 0 : 1);
-    // TODO(65810338): Implement for private and virtual as well.
-    if (!method.accessFlags.isStatic()) {
-      return null;
-    }
-    CollectUsedArguments collector = new CollectUsedArguments();
-    method.getCode().registerArgumentReferences(collector);
-    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());
-        }
+    // TODO(65810338): Implement for virtual methods as well.
+    if (method.accessFlags.isPrivate() || method.accessFlags.isStatic()) {
+      CollectUsedArguments collector = new CollectUsedArguments();
+      if (!method.accessFlags.isStatic()) {
+        // TODO(65810338): The receiver cannot be removed without transforming the method to being
+        // static.
+        collector.register(0);
       }
-      return new RemovedArgumentsInfo(unused);
+      method.getCode().registerArgumentReferences(collector);
+      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());
+          }
+        }
+        return new RemovedArgumentsInfo(unused);
+      }
     }
     return null;
   }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsObjectTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsObjectTest.java
index 10bf5df..2f7e437 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsObjectTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsObjectTest.java
@@ -6,6 +6,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.google.common.collect.ImmutableList;
@@ -40,6 +41,7 @@
     }
   }
 
+  @NeverClassInline
   static class TestClass {
 
     @NeverInline
@@ -57,10 +59,29 @@
       return a;
     }
 
+    @NeverInline
+    private Object b(Object a) {
+      return a;
+    }
+
+    @NeverInline
+    private Object b(Object a, Object b) {
+      return a;
+    }
+
+    @NeverInline
+    private Object b(Object a, Object b, Object c) {
+      return a;
+    }
+
     public static void main(String[] args) {
+      TestClass instance = new TestClass();
       System.out.print(a(new TestObject("1")));
       System.out.print(a(new TestObject("2"), new TestObject("3")));
       System.out.print(a(new TestObject("4"), new TestObject("5"), new TestObject("6")));
+      System.out.print(instance.b(new TestObject("1")));
+      System.out.print(instance.b(new TestObject("2"), new TestObject("3")));
+      System.out.print(instance.b(new TestObject("4"), new TestObject("5"), new TestObject("6")));
     }
   }
 
@@ -76,18 +97,18 @@
 
   @Override
   public String getExpectedResult() {
-    return "124";
+    return "124124";
   }
 
   @Override
   public void inspectTestClass(ClassSubject clazz) {
-    assertEquals(4, clazz.allMethods().size());
+    assertEquals(8, clazz.allMethods().size());
     clazz.forAllMethods(
-        method -> {
-          Assert.assertTrue(
-              method.getFinalName().equals("main")
-                  || (method.getFinalSignature().parameters.length == 1
-                      && method.getFinalSignature().parameters[0].equals("java.lang.Object")));
-        });
+        method ->
+            Assert.assertTrue(
+                method.getFinalName().equals("main")
+                    || method.getFinalName().equals("<init>")
+                    || (method.getFinalSignature().parameters.length == 1
+                        && method.getFinalSignature().parameters[0].equals("java.lang.Object"))));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsTestBase.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsTestBase.java
index d20de91..0a5d8e9 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsTestBase.java
@@ -55,8 +55,9 @@
         .addProgramClasses(getTestClass())
         .addProgramClasses(getAdditionalClasses())
         .addKeepMainRule(getTestClass())
-        .addKeepRules(minification ? "" : "-dontobfuscate")
+        .minification(minification)
         .enableInliningAnnotations()
+        .enableClassInliningAnnotations()
         .compile()
         .run(getTestClass())
         .inspect(this::inspect)