NoParameterReordering annotation to disable parameter normalization

Change-Id: If0d64c620ad2020d29ad080f9fffc184dfbdc99d
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
index c82c666..cd46a71 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -28,6 +28,7 @@
   private final boolean allowConstantArgumentOptimization;
   private final boolean allowInlining;
   private final boolean allowMethodStaticizing;
+  private final boolean allowParameterReordering;
   private final boolean allowParameterTypeStrengthening;
   private final boolean allowReturnTypeStrengthening;
   private final boolean allowUnusedArgumentOptimization;
@@ -39,6 +40,7 @@
     this.allowConstantArgumentOptimization = builder.isConstantArgumentOptimizationAllowed();
     this.allowInlining = builder.isInliningAllowed();
     this.allowMethodStaticizing = builder.isMethodStaticizingAllowed();
+    this.allowParameterReordering = builder.isParameterReorderingAllowed();
     this.allowParameterTypeStrengthening = builder.isParameterTypeStrengtheningAllowed();
     this.allowReturnTypeStrengthening = builder.isReturnTypeStrengtheningAllowed();
     this.allowUnusedArgumentOptimization = builder.isUnusedArgumentOptimizationAllowed();
@@ -97,6 +99,16 @@
     return allowMethodStaticizing;
   }
 
+  public boolean isParameterReorderingAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration)
+        && isShrinkingAllowed(configuration)
+        && internalIsParameterReorderingAllowed();
+  }
+
+  boolean internalIsParameterReorderingAllowed() {
+    return allowParameterReordering;
+  }
+
   public boolean isParameterTypeStrengtheningAllowed(GlobalKeepInfoConfiguration configuration) {
     return isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
@@ -158,6 +170,7 @@
     private boolean allowConstantArgumentOptimization;
     private boolean allowInlining;
     private boolean allowMethodStaticizing;
+    private boolean allowParameterReordering;
     private boolean allowParameterTypeStrengthening;
     private boolean allowReturnTypeStrengthening;
     private boolean allowUnusedArgumentOptimization;
@@ -173,6 +186,7 @@
       allowConstantArgumentOptimization = original.internalIsConstantArgumentOptimizationAllowed();
       allowInlining = original.internalIsInliningAllowed();
       allowMethodStaticizing = original.internalIsMethodStaticizingAllowed();
+      allowParameterReordering = original.internalIsParameterReorderingAllowed();
       allowParameterTypeStrengthening = original.internalIsParameterTypeStrengtheningAllowed();
       allowReturnTypeStrengthening = original.internalIsReturnTypeStrengtheningAllowed();
       allowUnusedArgumentOptimization = original.internalIsUnusedArgumentOptimizationAllowed();
@@ -256,6 +270,25 @@
       return setAllowMethodStaticizing(false);
     }
 
+    // Parameter reordering.
+
+    public boolean isParameterReorderingAllowed() {
+      return allowParameterReordering;
+    }
+
+    public Builder setAllowParameterReordering(boolean allowParameterReordering) {
+      this.allowParameterReordering = allowParameterReordering;
+      return self();
+    }
+
+    public Builder allowParameterReordering() {
+      return setAllowParameterReordering(true);
+    }
+
+    public Builder disallowParameterReordering() {
+      return setAllowParameterReordering(false);
+    }
+
     // Parameter type strengthening.
 
     public boolean isParameterTypeStrengtheningAllowed() {
@@ -361,6 +394,7 @@
               == other.internalIsConstantArgumentOptimizationAllowed()
           && isInliningAllowed() == other.internalIsInliningAllowed()
           && isMethodStaticizingAllowed() == other.internalIsMethodStaticizingAllowed()
+          && isParameterReorderingAllowed() == other.internalIsParameterReorderingAllowed()
           && isParameterTypeStrengtheningAllowed()
               == other.internalIsParameterTypeStrengtheningAllowed()
           && isReturnTypeStrengtheningAllowed() == other.internalIsReturnTypeStrengtheningAllowed()
@@ -382,6 +416,7 @@
           .disallowConstantArgumentOptimization()
           .disallowInlining()
           .disallowMethodStaticizing()
+          .disallowParameterReordering()
           .disallowParameterTypeStrengthening()
           .disallowReturnTypeStrengthening()
           .disallowUnusedArgumentOptimization()
@@ -395,6 +430,7 @@
           .allowConstantArgumentOptimization()
           .allowInlining()
           .allowMethodStaticizing()
+          .allowParameterReordering()
           .allowParameterTypeStrengthening()
           .allowReturnTypeStrengthening()
           .allowUnusedArgumentOptimization()
@@ -428,6 +464,11 @@
       return self();
     }
 
+    public Joiner disallowParameterReordering() {
+      builder.disallowParameterReordering();
+      return self();
+    }
+
     public Joiner disallowParameterTypeStrengthening() {
       builder.disallowParameterTypeStrengthening();
       return self();
@@ -464,6 +505,8 @@
           .applyIf(!joiner.builder.isInliningAllowed(), Joiner::disallowInlining)
           .applyIf(!joiner.builder.isMethodStaticizingAllowed(), Joiner::disallowMethodStaticizing)
           .applyIf(
+              !joiner.builder.isParameterReorderingAllowed(), Joiner::disallowParameterReordering)
+          .applyIf(
               !joiner.builder.isParameterTypeStrengtheningAllowed(),
               Joiner::disallowParameterTypeStrengthening)
           .applyIf(
diff --git a/src/main/java/com/android/tools/r8/shaking/NoParameterReorderingRule.java b/src/main/java/com/android/tools/r8/shaking/NoParameterReorderingRule.java
new file mode 100644
index 0000000..c8d0e6c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/NoParameterReorderingRule.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2022, 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.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class NoParameterReorderingRule extends NoOptimizationBaseRule<NoParameterReorderingRule> {
+
+  public static final String RULE_NAME = "noparameterreordering";
+
+  public static class Builder
+      extends NoOptimizationBaseRule.Builder<NoParameterReorderingRule, Builder> {
+
+    Builder() {
+      super();
+    }
+
+    @Override
+    public NoParameterReorderingRule.Builder self() {
+      return this;
+    }
+
+    @Override
+    public NoParameterReorderingRule build() {
+      return new NoParameterReorderingRule(
+          origin,
+          getPosition(),
+          source,
+          buildClassAnnotations(),
+          classAccessFlags,
+          negatedClassAccessFlags,
+          classTypeNegated,
+          classType,
+          classNames,
+          buildInheritanceAnnotations(),
+          inheritanceClassName,
+          inheritanceIsExtends,
+          memberRules);
+    }
+  }
+
+  NoParameterReorderingRule(
+      Origin origin,
+      Position position,
+      String source,
+      List<ProguardTypeMatcher> classAnnotations,
+      ProguardAccessFlags classAccessFlags,
+      ProguardAccessFlags negatedClassAccessFlags,
+      boolean classTypeNegated,
+      ProguardClassType classType,
+      ProguardClassNameList classNames,
+      List<ProguardTypeMatcher> inheritanceAnnotations,
+      ProguardTypeMatcher inheritanceClassName,
+      boolean inheritanceIsExtends,
+      List<ProguardMemberRule> memberRules) {
+    super(
+        origin,
+        position,
+        source,
+        classAnnotations,
+        classAccessFlags,
+        negatedClassAccessFlags,
+        classTypeNegated,
+        classType,
+        classNames,
+        inheritanceAnnotations,
+        inheritanceClassName,
+        inheritanceIsExtends,
+        memberRules);
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  @Override
+  String typeString() {
+    return RULE_NAME;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 0f2ad2c..4ed94e5 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -514,6 +514,12 @@
           configurationBuilder.addRule(rule);
           return true;
         }
+        if (acceptString(NoParameterReorderingRule.RULE_NAME)) {
+          ProguardConfigurationRule rule =
+              parseNoOptimizationRule(optionStart, NoParameterReorderingRule.builder());
+          configurationBuilder.addRule(rule);
+          return true;
+        }
         if (acceptString(NoParameterTypeStrengtheningRule.RULE_NAME)) {
           ProguardConfigurationRule rule =
               parseNoOptimizationRule(optionStart, NoParameterTypeStrengtheningRule.builder());
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 89b55dd..95ec8c3 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -267,6 +267,7 @@
           || rule instanceof KeepConstantArgumentRule
           || rule instanceof KeepUnusedReturnValueRule
           || rule instanceof NoMethodStaticizingRule
+          || rule instanceof NoParameterReorderingRule
           || rule instanceof NoParameterTypeStrengtheningRule
           || rule instanceof NoReturnTypeStrengtheningRule
           || rule instanceof KeepUnusedArgumentRule
@@ -1255,6 +1256,13 @@
             .asMethodJoiner()
             .disallowMethodStaticizing();
         context.markAsUsed();
+      } else if (context instanceof NoParameterReorderingRule) {
+        assert item.isProgramMethod();
+        dependentMinimumKeepInfo
+            .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+            .asMethodJoiner()
+            .disallowParameterReordering();
+        context.markAsUsed();
       } else if (context instanceof NoParameterTypeStrengtheningRule) {
         assert item.isProgramMethod();
         dependentMinimumKeepInfo
diff --git a/src/test/java/com/android/tools/r8/NoParameterReordering.java b/src/test/java/com/android/tools/r8/NoParameterReordering.java
new file mode 100644
index 0000000..0a82d0f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NoParameterReordering.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2022, 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD})
+public @interface NoParameterReordering {}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index e1be80b..ab32b4f 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.shaking.NoFieldTypeStrengtheningRule;
 import com.android.tools.r8.shaking.NoHorizontalClassMergingRule;
 import com.android.tools.r8.shaking.NoMethodStaticizingRule;
+import com.android.tools.r8.shaking.NoParameterReorderingRule;
 import com.android.tools.r8.shaking.NoParameterTypeStrengtheningRule;
 import com.android.tools.r8.shaking.NoReturnTypeStrengtheningRule;
 import com.android.tools.r8.shaking.NoUnusedInterfaceRemovalRule;
@@ -517,6 +518,12 @@
             NoMethodStaticizingRule.RULE_NAME, NoMethodStaticizing.class);
   }
 
+  public T enableNoParameterReorderingAnnotations() {
+    return addNoParameterReorderingAnnotation()
+        .addInternalMatchAnnotationOnMethodRule(
+            NoParameterReorderingRule.RULE_NAME, NoParameterReordering.class);
+  }
+
   public T enableNoParameterTypeStrengtheningAnnotations() {
     return addNoParameterTypeStrengtheningAnnotation()
         .addInternalMatchAnnotationOnMethodRule(
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index e88f882..5445d03 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -474,6 +474,10 @@
     return addTestingAnnotation(NoMethodStaticizing.class);
   }
 
+  public final T addNoParameterReorderingAnnotation() {
+    return addTestingAnnotation(NoParameterReordering.class);
+  }
+
   public final T addNoParameterTypeStrengtheningAnnotation() {
     return addTestingAnnotation(NoParameterTypeStrengthening.class);
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
index f42443d..b406af6 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
@@ -70,7 +70,10 @@
     testForR8(backend)
         .applyIf(
             backend.isCf(),
-            builder -> builder.addProgramFiles(getProgramFiles(test)),
+            builder ->
+                builder
+                    .addProgramFiles(getProgramFiles(test))
+                    .enableNoParameterTypeStrengtheningAnnotations(),
             builder -> builder.addProgramDexFileData(getProgramDexFileData(test)))
         .addKeepRuleFiles(Paths.get(EXAMPLES_DIR, test, "keep-rules.txt"))
         .addIgnoreWarnings()