Add @KeepUnusedReturnValue testing annotation

Change-Id: I7f5d6dd476c1e3b44a79159348f1aaa69581a283
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
index f8d617b..470e442 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -32,6 +32,7 @@
 import com.android.tools.r8.optimize.argumentpropagation.propagation.VirtualDispatchMethodArgumentPropagator;
 import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
@@ -50,6 +51,7 @@
 
   private final AppView<AppInfoWithLiveness> appView;
   private final MethodStateCollectionByReference methodStates;
+  private final InternalOptions options;
 
   private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
   private final List<Set<DexProgramClass>> stronglyConnectedProgramComponents;
@@ -66,6 +68,7 @@
     this.appView = appView;
     this.immediateSubtypingInfo = immediateSubtypingInfo;
     this.methodStates = methodStates;
+    this.options = appView.options();
     this.stronglyConnectedProgramComponents = stronglyConnectedProgramComponents;
     this.interfaceDispatchOutsideProgram = interfaceDispatchOutsideProgram;
   }
@@ -158,7 +161,7 @@
     }
 
     // Do not optimize @KeepConstantArgument methods.
-    if (appView.appInfo().isKeepConstantArgumentsMethod(method)) {
+    if (!appView.getKeepInfo(method).isConstantArgumentOptimizationAllowed(options)) {
       methodState = MethodState.unknown();
     }
 
@@ -215,7 +218,7 @@
   private MethodState getMethodStateAfterUninstantiatedParameterRemoval(
       ProgramMethod method, MethodState methodState) {
     assert methodState.isMonomorphic() || methodState.isUnknown();
-    if (appView.appInfo().isKeepConstantArgumentsMethod(method)) {
+    if (!appView.getKeepInfo(method).isConstantArgumentOptimizationAllowed(options)) {
       return methodState;
     }
 
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
index 72129db..34d8967 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
@@ -19,7 +19,7 @@
     if (!keepInfo.isParameterRemovalAllowed(options)) {
       return false;
     }
-    if (appView.appInfo().isKeepUnusedArgumentsMethod(method)) {
+    if (!appView.getKeepInfo(method).isUnusedArgumentOptimizationAllowed(options)) {
       return false;
     }
     return method.getDefinition().isLibraryMethodOverride().isFalse()
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 78c8797..e156407 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -153,10 +153,6 @@
   private final Set<DexMethod> neverInlineDueToSingleCaller;
   /** Items for which to print inlining decisions for (testing only). */
   private final Set<DexMethod> whyAreYouNotInlining;
-  /** All methods that may not have any parameters with a constant value removed. */
-  private final Set<DexMethod> keepConstantArguments;
-  /** All methods that may not have any unused arguments removed. */
-  private final Set<DexMethod> keepUnusedArguments;
   /** All methods that must be reprocessed (testing only). */
   private final Set<DexMethod> reprocess;
   /** All methods that must not be reprocessed (testing only). */
@@ -228,8 +224,6 @@
       Set<DexMethod> alwaysInline,
       Set<DexMethod> neverInlineDueToSingleCaller,
       Set<DexMethod> whyAreYouNotInlining,
-      Set<DexMethod> keepConstantArguments,
-      Set<DexMethod> keepUnusedArguments,
       Set<DexMethod> reprocess,
       Set<DexMethod> neverReprocess,
       PredicateSet<DexType> alwaysClassInline,
@@ -265,8 +259,6 @@
     this.alwaysInline = alwaysInline;
     this.neverInlineDueToSingleCaller = neverInlineDueToSingleCaller;
     this.whyAreYouNotInlining = whyAreYouNotInlining;
-    this.keepConstantArguments = keepConstantArguments;
-    this.keepUnusedArguments = keepUnusedArguments;
     this.reprocess = reprocess;
     this.neverReprocess = neverReprocess;
     this.alwaysClassInline = alwaysClassInline;
@@ -310,8 +302,6 @@
         previous.alwaysInline,
         previous.neverInlineDueToSingleCaller,
         previous.whyAreYouNotInlining,
-        previous.keepConstantArguments,
-        previous.keepUnusedArguments,
         previous.reprocess,
         previous.neverReprocess,
         previous.alwaysClassInline,
@@ -360,8 +350,6 @@
         pruneMethods(previous.alwaysInline, prunedItems, executorService, futures),
         pruneMethods(previous.neverInlineDueToSingleCaller, prunedItems, executorService, futures),
         pruneMethods(previous.whyAreYouNotInlining, prunedItems, executorService, futures),
-        pruneMethods(previous.keepConstantArguments, prunedItems, executorService, futures),
-        pruneMethods(previous.keepUnusedArguments, prunedItems, executorService, futures),
         pruneMethods(previous.reprocess, prunedItems, executorService, futures),
         pruneMethods(previous.neverReprocess, prunedItems, executorService, futures),
         previous.alwaysClassInline,
@@ -567,8 +555,6 @@
         alwaysInline,
         neverInlineDueToSingleCaller,
         whyAreYouNotInlining,
-        keepConstantArguments,
-        keepUnusedArguments,
         reprocess,
         neverReprocess,
         alwaysClassInline,
@@ -649,8 +635,6 @@
     this.alwaysInline = previous.alwaysInline;
     this.neverInlineDueToSingleCaller = previous.neverInlineDueToSingleCaller;
     this.whyAreYouNotInlining = previous.whyAreYouNotInlining;
-    this.keepConstantArguments = previous.keepConstantArguments;
-    this.keepUnusedArguments = previous.keepUnusedArguments;
     this.reprocess = previous.reprocess;
     this.neverReprocess = previous.neverReprocess;
     this.alwaysClassInline = previous.alwaysClassInline;
@@ -814,22 +798,6 @@
     return whyAreYouNotInlining.isEmpty();
   }
 
-  public boolean isKeepConstantArgumentsMethod(ProgramMethod method) {
-    return isKeepConstantArgumentsMethod(method.getReference());
-  }
-
-  public boolean isKeepConstantArgumentsMethod(DexMethod method) {
-    return keepConstantArguments.contains(method);
-  }
-
-  public boolean isKeepUnusedArgumentsMethod(ProgramMethod method) {
-    return isKeepUnusedArgumentsMethod(method.getReference());
-  }
-
-  public boolean isKeepUnusedArgumentsMethod(DexMethod method) {
-    return keepUnusedArguments.contains(method);
-  }
-
   public boolean isNeverReprocessMethod(ProgramMethod method) {
     return neverReprocess.contains(method.getReference())
         || method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite();
@@ -1285,8 +1253,6 @@
         lens.rewriteReferences(alwaysInline),
         lens.rewriteReferences(neverInlineDueToSingleCaller),
         lens.rewriteReferences(whyAreYouNotInlining),
-        lens.rewriteReferences(keepConstantArguments),
-        lens.rewriteReferences(keepUnusedArguments),
         lens.rewriteReferences(reprocess),
         lens.rewriteReferences(neverReprocess),
         alwaysClassInline.rewriteItems(lens::lookupType),
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 658cfbf..46732db 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3806,8 +3806,6 @@
             amendWithCompanionMethods(rootSet.alwaysInline),
             amendWithCompanionMethods(rootSet.neverInlineDueToSingleCaller),
             amendWithCompanionMethods(rootSet.whyAreYouNotInlining),
-            amendWithCompanionMethods(rootSet.keepConstantArguments),
-            amendWithCompanionMethods(rootSet.keepUnusedArguments),
             amendWithCompanionMethods(rootSet.reprocess),
             amendWithCompanionMethods(rootSet.neverReprocess),
             rootSet.alwaysClassInline,
diff --git a/src/main/java/com/android/tools/r8/shaking/ConstantArgumentRule.java b/src/main/java/com/android/tools/r8/shaking/KeepConstantArgumentRule.java
similarity index 83%
rename from src/main/java/com/android/tools/r8/shaking/ConstantArgumentRule.java
rename to src/main/java/com/android/tools/r8/shaking/KeepConstantArgumentRule.java
index bc33778..f234a09 100644
--- a/src/main/java/com/android/tools/r8/shaking/ConstantArgumentRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepConstantArgumentRule.java
@@ -7,10 +7,12 @@
 import com.android.tools.r8.position.Position;
 import java.util.List;
 
-public class ConstantArgumentRule extends ProguardConfigurationRule {
+public class KeepConstantArgumentRule extends NoOptimizationBaseRule<KeepConstantArgumentRule> {
+
+  public static final String RULE_NAME = "keepconstantarguments";
 
   public static class Builder
-      extends ProguardConfigurationRule.Builder<ConstantArgumentRule, Builder> {
+      extends NoOptimizationBaseRule.Builder<KeepConstantArgumentRule, Builder> {
 
     private Builder() {
       super();
@@ -22,8 +24,8 @@
     }
 
     @Override
-    public ConstantArgumentRule build() {
-      return new ConstantArgumentRule(
+    public KeepConstantArgumentRule build() {
+      return new KeepConstantArgumentRule(
           origin,
           getPosition(),
           source,
@@ -40,7 +42,7 @@
     }
   }
 
-  private ConstantArgumentRule(
+  private KeepConstantArgumentRule(
       Origin origin,
       Position position,
       String source,
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
index 9b83643..bcf9467 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -83,12 +83,6 @@
     return checkDiscarded;
   }
 
-  public boolean isParameterRemovalAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration)
-        && isShrinkingAllowed(configuration)
-        && !isCheckDiscardedEnabled(configuration);
-  }
-
   /**
    * True if an item must be present in the output.
    *
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 b43c44d..c82c666 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -25,18 +25,24 @@
   }
 
   private final boolean allowClassInlining;
+  private final boolean allowConstantArgumentOptimization;
   private final boolean allowInlining;
   private final boolean allowMethodStaticizing;
   private final boolean allowParameterTypeStrengthening;
   private final boolean allowReturnTypeStrengthening;
+  private final boolean allowUnusedArgumentOptimization;
+  private final boolean allowUnusedReturnValueOptimization;
 
   private KeepMethodInfo(Builder builder) {
     super(builder);
     this.allowClassInlining = builder.isClassInliningAllowed();
+    this.allowConstantArgumentOptimization = builder.isConstantArgumentOptimizationAllowed();
     this.allowInlining = builder.isInliningAllowed();
     this.allowMethodStaticizing = builder.isMethodStaticizingAllowed();
     this.allowParameterTypeStrengthening = builder.isParameterTypeStrengtheningAllowed();
     this.allowReturnTypeStrengthening = builder.isReturnTypeStrengtheningAllowed();
+    this.allowUnusedArgumentOptimization = builder.isUnusedArgumentOptimizationAllowed();
+    this.allowUnusedReturnValueOptimization = builder.isUnusedReturnValueOptimizationAllowed();
   }
 
   // This builder is not private as there are known instances where it is safe to modify keep info
@@ -50,6 +56,12 @@
     return isParameterRemovalAllowed(configuration);
   }
 
+  public boolean isParameterRemovalAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration)
+        && isShrinkingAllowed(configuration)
+        && !isCheckDiscardedEnabled(configuration);
+  }
+
   public boolean isClassInliningAllowed(GlobalKeepInfoConfiguration configuration) {
     return isOptimizationAllowed(configuration) && internalIsClassInliningAllowed();
   }
@@ -58,6 +70,14 @@
     return allowClassInlining;
   }
 
+  public boolean isConstantArgumentOptimizationAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration) && internalIsConstantArgumentOptimizationAllowed();
+  }
+
+  boolean internalIsConstantArgumentOptimizationAllowed() {
+    return allowConstantArgumentOptimization;
+  }
+
   public boolean isInliningAllowed(GlobalKeepInfoConfiguration configuration) {
     return isOptimizationAllowed(configuration) && internalIsInliningAllowed();
   }
@@ -97,6 +117,26 @@
     return allowReturnTypeStrengthening;
   }
 
+  public boolean isUnusedArgumentOptimizationAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration)
+        && isShrinkingAllowed(configuration)
+        && internalIsUnusedArgumentOptimizationAllowed();
+  }
+
+  boolean internalIsUnusedArgumentOptimizationAllowed() {
+    return allowUnusedArgumentOptimization;
+  }
+
+  public boolean isUnusedReturnValueOptimizationAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration)
+        && isShrinkingAllowed(configuration)
+        && internalIsUnusedReturnValueOptimizationAllowed();
+  }
+
+  boolean internalIsUnusedReturnValueOptimizationAllowed() {
+    return allowUnusedReturnValueOptimization;
+  }
+
   public Joiner joiner() {
     assert !isTop();
     return new Joiner(this);
@@ -115,10 +155,13 @@
   public static class Builder extends KeepInfo.Builder<Builder, KeepMethodInfo> {
 
     private boolean allowClassInlining;
+    private boolean allowConstantArgumentOptimization;
     private boolean allowInlining;
     private boolean allowMethodStaticizing;
     private boolean allowParameterTypeStrengthening;
     private boolean allowReturnTypeStrengthening;
+    private boolean allowUnusedArgumentOptimization;
+    private boolean allowUnusedReturnValueOptimization;
 
     private Builder() {
       super();
@@ -127,12 +170,18 @@
     private Builder(KeepMethodInfo original) {
       super(original);
       allowClassInlining = original.internalIsClassInliningAllowed();
+      allowConstantArgumentOptimization = original.internalIsConstantArgumentOptimizationAllowed();
       allowInlining = original.internalIsInliningAllowed();
       allowMethodStaticizing = original.internalIsMethodStaticizingAllowed();
       allowParameterTypeStrengthening = original.internalIsParameterTypeStrengtheningAllowed();
       allowReturnTypeStrengthening = original.internalIsReturnTypeStrengtheningAllowed();
+      allowUnusedArgumentOptimization = original.internalIsUnusedArgumentOptimizationAllowed();
+      allowUnusedReturnValueOptimization =
+          original.internalIsUnusedReturnValueOptimizationAllowed();
     }
 
+    // Class inlining.
+
     public boolean isClassInliningAllowed() {
       return allowClassInlining;
     }
@@ -150,6 +199,27 @@
       return setAllowClassInlining(false);
     }
 
+    // Constant argument optimization.
+
+    public boolean isConstantArgumentOptimizationAllowed() {
+      return allowConstantArgumentOptimization;
+    }
+
+    public Builder setAllowConstantArgumentOptimization(boolean allowConstantArgumentOptimization) {
+      this.allowConstantArgumentOptimization = allowConstantArgumentOptimization;
+      return self();
+    }
+
+    public Builder allowConstantArgumentOptimization() {
+      return setAllowConstantArgumentOptimization(true);
+    }
+
+    public Builder disallowConstantArgumentOptimization() {
+      return setAllowConstantArgumentOptimization(false);
+    }
+
+    // Inlining.
+
     public boolean isInliningAllowed() {
       return allowInlining;
     }
@@ -167,6 +237,8 @@
       return setAllowInlining(false);
     }
 
+    // Method staticizing.
+
     public boolean isMethodStaticizingAllowed() {
       return allowMethodStaticizing;
     }
@@ -184,6 +256,8 @@
       return setAllowMethodStaticizing(false);
     }
 
+    // Parameter type strengthening.
+
     public boolean isParameterTypeStrengtheningAllowed() {
       return allowParameterTypeStrengthening;
     }
@@ -201,6 +275,8 @@
       return setAllowParameterTypeStrengthening(false);
     }
 
+    // Return type strengthening.
+
     public boolean isReturnTypeStrengtheningAllowed() {
       return allowReturnTypeStrengthening;
     }
@@ -218,6 +294,45 @@
       return setAllowReturnTypeStrengthening(false);
     }
 
+    // Unused argument optimization.
+
+    public boolean isUnusedArgumentOptimizationAllowed() {
+      return allowUnusedArgumentOptimization;
+    }
+
+    public Builder setAllowUnusedArgumentOptimization(boolean allowUnusedArgumentOptimization) {
+      this.allowUnusedArgumentOptimization = allowUnusedArgumentOptimization;
+      return self();
+    }
+
+    public Builder allowUnusedArgumentOptimization() {
+      return setAllowUnusedArgumentOptimization(true);
+    }
+
+    public Builder disallowUnusedArgumentOptimization() {
+      return setAllowUnusedArgumentOptimization(false);
+    }
+
+    // Unused return value optimization.
+
+    public boolean isUnusedReturnValueOptimizationAllowed() {
+      return allowUnusedReturnValueOptimization;
+    }
+
+    public Builder setAllowUnusedReturnValueOptimization(
+        boolean allowUnusedReturnValueOptimization) {
+      this.allowUnusedReturnValueOptimization = allowUnusedReturnValueOptimization;
+      return self();
+    }
+
+    public Builder allowUnusedReturnValueOptimization() {
+      return setAllowUnusedReturnValueOptimization(true);
+    }
+
+    public Builder disallowUnusedReturnValueOptimization() {
+      return setAllowUnusedReturnValueOptimization(false);
+    }
+
     @Override
     public Builder self() {
       return this;
@@ -242,11 +357,17 @@
     boolean internalIsEqualTo(KeepMethodInfo other) {
       return super.internalIsEqualTo(other)
           && isClassInliningAllowed() == other.internalIsClassInliningAllowed()
+          && isConstantArgumentOptimizationAllowed()
+              == other.internalIsConstantArgumentOptimizationAllowed()
           && isInliningAllowed() == other.internalIsInliningAllowed()
           && isMethodStaticizingAllowed() == other.internalIsMethodStaticizingAllowed()
           && isParameterTypeStrengtheningAllowed()
               == other.internalIsParameterTypeStrengtheningAllowed()
-          && isReturnTypeStrengtheningAllowed() == other.internalIsReturnTypeStrengtheningAllowed();
+          && isReturnTypeStrengtheningAllowed() == other.internalIsReturnTypeStrengtheningAllowed()
+          && isUnusedArgumentOptimizationAllowed()
+              == other.internalIsUnusedArgumentOptimizationAllowed()
+          && isUnusedReturnValueOptimizationAllowed()
+              == other.internalIsUnusedReturnValueOptimizationAllowed();
     }
 
     @Override
@@ -258,20 +379,26 @@
     public Builder makeTop() {
       return super.makeTop()
           .disallowClassInlining()
+          .disallowConstantArgumentOptimization()
           .disallowInlining()
           .disallowMethodStaticizing()
           .disallowParameterTypeStrengthening()
-          .disallowReturnTypeStrengthening();
+          .disallowReturnTypeStrengthening()
+          .disallowUnusedArgumentOptimization()
+          .disallowUnusedReturnValueOptimization();
     }
 
     @Override
     public Builder makeBottom() {
       return super.makeBottom()
           .allowClassInlining()
+          .allowConstantArgumentOptimization()
           .allowInlining()
           .allowMethodStaticizing()
           .allowParameterTypeStrengthening()
-          .allowReturnTypeStrengthening();
+          .allowReturnTypeStrengthening()
+          .allowUnusedArgumentOptimization()
+          .allowUnusedReturnValueOptimization();
     }
   }
 
@@ -286,6 +413,11 @@
       return self();
     }
 
+    public Joiner disallowConstantArgumentOptimization() {
+      builder.disallowConstantArgumentOptimization();
+      return self();
+    }
+
     public Joiner disallowInlining() {
       builder.disallowInlining();
       return self();
@@ -306,6 +438,16 @@
       return self();
     }
 
+    public Joiner disallowUnusedArgumentOptimization() {
+      builder.disallowUnusedArgumentOptimization();
+      return self();
+    }
+
+    public Joiner disallowUnusedReturnValueOptimization() {
+      builder.disallowUnusedReturnValueOptimization();
+      return self();
+    }
+
     @Override
     public Joiner asMethodJoiner() {
       return this;
@@ -316,6 +458,9 @@
       // Should be extended to merge the fields of this class in case any are added.
       return super.merge(joiner)
           .applyIf(!joiner.builder.isClassInliningAllowed(), Joiner::disallowClassInlining)
+          .applyIf(
+              !joiner.builder.isConstantArgumentOptimizationAllowed(),
+              Joiner::disallowConstantArgumentOptimization)
           .applyIf(!joiner.builder.isInliningAllowed(), Joiner::disallowInlining)
           .applyIf(!joiner.builder.isMethodStaticizingAllowed(), Joiner::disallowMethodStaticizing)
           .applyIf(
@@ -323,7 +468,13 @@
               Joiner::disallowParameterTypeStrengthening)
           .applyIf(
               !joiner.builder.isReturnTypeStrengtheningAllowed(),
-              Joiner::disallowReturnTypeStrengthening);
+              Joiner::disallowReturnTypeStrengthening)
+          .applyIf(
+              !joiner.builder.isUnusedArgumentOptimizationAllowed(),
+              Joiner::disallowUnusedArgumentOptimization)
+          .applyIf(
+              !joiner.builder.isUnusedReturnValueOptimizationAllowed(),
+              Joiner::disallowUnusedReturnValueOptimization);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/UnusedArgumentRule.java b/src/main/java/com/android/tools/r8/shaking/KeepUnusedArgumentRule.java
similarity index 83%
rename from src/main/java/com/android/tools/r8/shaking/UnusedArgumentRule.java
rename to src/main/java/com/android/tools/r8/shaking/KeepUnusedArgumentRule.java
index dd537bc..18ba2ff 100644
--- a/src/main/java/com/android/tools/r8/shaking/UnusedArgumentRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepUnusedArgumentRule.java
@@ -7,10 +7,12 @@
 import com.android.tools.r8.position.Position;
 import java.util.List;
 
-public class UnusedArgumentRule extends ProguardConfigurationRule {
+public class KeepUnusedArgumentRule extends NoOptimizationBaseRule<KeepUnusedArgumentRule> {
+
+  public static final String RULE_NAME = "keepunusedarguments";
 
   public static class Builder
-      extends ProguardConfigurationRule.Builder<UnusedArgumentRule, Builder> {
+      extends NoOptimizationBaseRule.Builder<KeepUnusedArgumentRule, Builder> {
 
     private Builder() {
       super();
@@ -22,8 +24,8 @@
     }
 
     @Override
-    public UnusedArgumentRule build() {
-      return new UnusedArgumentRule(
+    public KeepUnusedArgumentRule build() {
+      return new KeepUnusedArgumentRule(
           origin,
           getPosition(),
           source,
@@ -40,7 +42,7 @@
     }
   }
 
-  private UnusedArgumentRule(
+  private KeepUnusedArgumentRule(
       Origin origin,
       Position position,
       String source,
@@ -76,6 +78,6 @@
 
   @Override
   String typeString() {
-    return "keepunusedarguments";
+    return RULE_NAME;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/ConstantArgumentRule.java b/src/main/java/com/android/tools/r8/shaking/KeepUnusedReturnValueRule.java
similarity index 76%
copy from src/main/java/com/android/tools/r8/shaking/ConstantArgumentRule.java
copy to src/main/java/com/android/tools/r8/shaking/KeepUnusedReturnValueRule.java
index bc33778..2543285 100644
--- a/src/main/java/com/android/tools/r8/shaking/ConstantArgumentRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepUnusedReturnValueRule.java
@@ -1,29 +1,32 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// 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 ConstantArgumentRule extends ProguardConfigurationRule {
+public class KeepUnusedReturnValueRule extends NoOptimizationBaseRule<KeepUnusedReturnValueRule> {
+
+  public static final String RULE_NAME = "keepunusedreturnvalue";
 
   public static class Builder
-      extends ProguardConfigurationRule.Builder<ConstantArgumentRule, Builder> {
+      extends NoOptimizationBaseRule.Builder<KeepUnusedReturnValueRule, Builder> {
 
-    private Builder() {
+    Builder() {
       super();
     }
 
     @Override
-    public Builder self() {
+    public KeepUnusedReturnValueRule.Builder self() {
       return this;
     }
 
     @Override
-    public ConstantArgumentRule build() {
-      return new ConstantArgumentRule(
+    public KeepUnusedReturnValueRule build() {
+      return new KeepUnusedReturnValueRule(
           origin,
           getPosition(),
           source,
@@ -40,7 +43,7 @@
     }
   }
 
-  private ConstantArgumentRule(
+  KeepUnusedReturnValueRule(
       Origin origin,
       Position position,
       String source,
@@ -76,6 +79,6 @@
 
   @Override
   String typeString() {
-    return "keepconstantarguments";
+    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 b1fdef2..0f2ad2c 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -450,13 +450,21 @@
           configurationBuilder.addRule(rule);
           return true;
         }
-        if (acceptString("keepconstantarguments")) {
-          ConstantArgumentRule rule = parseConstantArgumentRule(optionStart);
+        if (acceptString(KeepConstantArgumentRule.RULE_NAME)) {
+          KeepConstantArgumentRule rule =
+              parseNoOptimizationRule(optionStart, KeepConstantArgumentRule.builder());
           configurationBuilder.addRule(rule);
           return true;
         }
-        if (acceptString("keepunusedarguments")) {
-          UnusedArgumentRule rule = parseUnusedArgumentRule(optionStart);
+        if (acceptString(KeepUnusedArgumentRule.RULE_NAME)) {
+          KeepUnusedArgumentRule rule =
+              parseNoOptimizationRule(optionStart, KeepUnusedArgumentRule.builder());
+          configurationBuilder.addRule(rule);
+          return true;
+        }
+        if (acceptString(KeepUnusedReturnValueRule.RULE_NAME)) {
+          KeepUnusedReturnValueRule rule =
+              parseNoOptimizationRule(optionStart, KeepUnusedReturnValueRule.builder());
           configurationBuilder.addRule(rule);
           return true;
         }
@@ -918,10 +926,10 @@
           "Expecting '-keep' option after '-if' option.", origin, getPosition(optionStart)));
     }
 
-    private ConstantArgumentRule parseConstantArgumentRule(Position start)
+    private KeepConstantArgumentRule parseConstantArgumentRule(Position start)
         throws ProguardRuleParserException {
-      ConstantArgumentRule.Builder keepRuleBuilder =
-          ConstantArgumentRule.builder().setOrigin(origin).setStart(start);
+      KeepConstantArgumentRule.Builder keepRuleBuilder =
+          KeepConstantArgumentRule.builder().setOrigin(origin).setStart(start);
       parseClassSpec(keepRuleBuilder, false);
       Position end = getPosition();
       keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
@@ -929,10 +937,10 @@
       return keepRuleBuilder.build();
     }
 
-    private UnusedArgumentRule parseUnusedArgumentRule(Position start)
+    private KeepUnusedArgumentRule parseUnusedArgumentRule(Position start)
         throws ProguardRuleParserException {
-      UnusedArgumentRule.Builder keepRuleBuilder =
-          UnusedArgumentRule.builder().setOrigin(origin).setStart(start);
+      KeepUnusedArgumentRule.Builder keepRuleBuilder =
+          KeepUnusedArgumentRule.builder().setOrigin(origin).setStart(start);
       parseClassSpec(keepRuleBuilder, false);
       Position end = getPosition();
       keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
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 2f7d3f4..2832262 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -111,12 +111,9 @@
         new DependentMinimumKeepInfoCollection();
     private final LinkedHashMap<DexReference, DexReference> reasonAsked = new LinkedHashMap<>();
     private final Set<DexMethod> alwaysInline = Sets.newIdentityHashSet();
-    private final Set<DexMethod> neverInline = Sets.newIdentityHashSet();
     private final Set<DexMethod> neverInlineDueToSingleCaller = Sets.newIdentityHashSet();
     private final Set<DexMethod> bypassClinitforInlining = Sets.newIdentityHashSet();
     private final Set<DexMethod> whyAreYouNotInlining = Sets.newIdentityHashSet();
-    private final Set<DexMethod> keepParametersWithConstantValue = Sets.newIdentityHashSet();
-    private final Set<DexMethod> keepUnusedArguments = Sets.newIdentityHashSet();
     private final Set<DexMethod> reprocess = Sets.newIdentityHashSet();
     private final Set<DexMethod> neverReprocess = Sets.newIdentityHashSet();
     private final PredicateSet<DexType> alwaysClassInline = new PredicateSet<>();
@@ -267,11 +264,12 @@
       } else if (rule instanceof NoFieldTypeStrengtheningRule) {
         markMatchingFields(clazz, memberKeepRules, rule, null, ifRule);
       } else if (rule instanceof InlineRule
-          || rule instanceof ConstantArgumentRule
+          || rule instanceof KeepConstantArgumentRule
+          || rule instanceof KeepUnusedReturnValueRule
           || rule instanceof NoMethodStaticizingRule
           || rule instanceof NoParameterTypeStrengtheningRule
           || rule instanceof NoReturnTypeStrengtheningRule
-          || rule instanceof UnusedArgumentRule
+          || rule instanceof KeepUnusedArgumentRule
           || rule instanceof ReprocessMethodRule
           || rule instanceof WhyAreYouNotInliningRule) {
         markMatchingMethods(clazz, memberKeepRules, rule, null, ifRule);
@@ -364,18 +362,13 @@
             alwaysInline,
             bypassClinitforInlining);
       }
-      assert Sets.intersection(neverInline, alwaysInline).isEmpty()
-          : "A method cannot be marked as both -neverinline and -alwaysinline.";
       return new RootSet(
           dependentMinimumKeepInfo,
           ImmutableList.copyOf(reasonAsked.values()),
           alwaysInline,
-          neverInline,
           neverInlineDueToSingleCaller,
           bypassClinitforInlining,
           whyAreYouNotInlining,
-          keepParametersWithConstantValue,
-          keepUnusedArguments,
           reprocess,
           neverReprocess,
           alwaysClassInline,
@@ -456,7 +449,6 @@
 
     ConsequentRootSet buildConsequentRootSet() {
       return new ConsequentRootSet(
-          neverInline,
           neverInlineDueToSingleCaller,
           neverClassInline,
           dependentMinimumKeepInfo,
@@ -1301,11 +1293,6 @@
         }
       } else if (context instanceof ProguardIdentifierNameStringRule) {
         evaluateIdentifierNameStringRule(item, context, ifRule);
-      } else if (context instanceof ConstantArgumentRule) {
-        if (item.isMethod()) {
-          keepParametersWithConstantValue.add(item.asMethod().getReference());
-          context.markAsUsed();
-        }
       } else if (context instanceof ReprocessClassInitializerRule) {
         DexProgramClass clazz = item.asProgramClass();
         if (clazz != null && clazz.hasClassInitializer()) {
@@ -1336,11 +1323,27 @@
           }
           context.markAsUsed();
         }
-      } else if (context instanceof UnusedArgumentRule) {
-        if (item.isMethod()) {
-          keepUnusedArguments.add(item.asMethod().getReference());
-          context.markAsUsed();
-        }
+      } else if (context instanceof KeepConstantArgumentRule) {
+        assert item.isProgramMethod();
+        dependentMinimumKeepInfo
+            .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+            .asMethodJoiner()
+            .disallowConstantArgumentOptimization();
+        context.markAsUsed();
+      } else if (context instanceof KeepUnusedArgumentRule) {
+        assert item.isProgramMethod();
+        dependentMinimumKeepInfo
+            .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+            .asMethodJoiner()
+            .disallowUnusedArgumentOptimization();
+        context.markAsUsed();
+      } else if (context instanceof KeepUnusedReturnValueRule) {
+        assert item.isProgramMethod();
+        dependentMinimumKeepInfo
+            .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+            .asMethodJoiner()
+            .disallowUnusedReturnValueOptimization();
+        context.markAsUsed();
       } else {
         throw new Unreachable();
       }
@@ -1673,8 +1676,6 @@
     public final Set<DexMethod> alwaysInline;
     public final Set<DexMethod> bypassClinitForInlining;
     public final Set<DexMethod> whyAreYouNotInlining;
-    public final Set<DexMethod> keepConstantArguments;
-    public final Set<DexMethod> keepUnusedArguments;
     public final Set<DexMethod> reprocess;
     public final Set<DexMethod> neverReprocess;
     public final PredicateSet<DexType> alwaysClassInline;
@@ -1692,12 +1693,9 @@
         DependentMinimumKeepInfoCollection dependentMinimumKeepInfo,
         ImmutableList<DexReference> reasonAsked,
         Set<DexMethod> alwaysInline,
-        Set<DexMethod> neverInline,
         Set<DexMethod> neverInlineDueToSingleCaller,
         Set<DexMethod> bypassClinitForInlining,
         Set<DexMethod> whyAreYouNotInlining,
-        Set<DexMethod> keepConstantArguments,
-        Set<DexMethod> keepUnusedArguments,
         Set<DexMethod> reprocess,
         Set<DexMethod> neverReprocess,
         PredicateSet<DexType> alwaysClassInline,
@@ -1725,8 +1723,6 @@
       this.alwaysInline = alwaysInline;
       this.bypassClinitForInlining = bypassClinitForInlining;
       this.whyAreYouNotInlining = whyAreYouNotInlining;
-      this.keepConstantArguments = keepConstantArguments;
-      this.keepUnusedArguments = keepUnusedArguments;
       this.reprocess = reprocess;
       this.neverReprocess = neverReprocess;
       this.alwaysClassInline = alwaysClassInline;
@@ -2044,7 +2040,6 @@
   public static class ConsequentRootSet extends RootSetBase {
 
     ConsequentRootSet(
-        Set<DexMethod> neverInline,
         Set<DexMethod> neverInlineDueToSingleCaller,
         Set<DexType> neverClassInline,
         DependentMinimumKeepInfoCollection dependentMinimumKeepInfo,
@@ -2110,9 +2105,6 @@
           Collections.emptySet(),
           Collections.emptySet(),
           Collections.emptySet(),
-          Collections.emptySet(),
-          Collections.emptySet(),
-          Collections.emptySet(),
           PredicateSet.empty(),
           Collections.emptySet(),
           Collections.emptySet(),
diff --git a/src/test/java/com/android/tools/r8/KeepUnusedReturnValue.java b/src/test/java/com/android/tools/r8/KeepUnusedReturnValue.java
new file mode 100644
index 0000000..f797cf9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/KeepUnusedReturnValue.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 KeepUnusedReturnValue {}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index ce82006..e1be80b 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.CollectingGraphConsumer;
+import com.android.tools.r8.shaking.KeepUnusedReturnValueRule;
 import com.android.tools.r8.shaking.NoFieldTypeStrengtheningRule;
 import com.android.tools.r8.shaking.NoHorizontalClassMergingRule;
 import com.android.tools.r8.shaking.NoMethodStaticizingRule;
@@ -498,6 +499,12 @@
     return addOptionsModification(options -> options.testing.allowInliningOfSynthetics = false);
   }
 
+  public T enableKeepUnusedReturnValueAnnotations() {
+    return addKeepUnusedReturnValueAnnotation()
+        .addInternalMatchAnnotationOnMethodRule(
+            KeepUnusedReturnValueRule.RULE_NAME, KeepUnusedReturnValue.class);
+  }
+
   public T enableNoFieldTypeStrengtheningAnnotations() {
     return addNoFieldTypeStrengtheningAnnotation()
         .addInternalMatchAnnotationOnFieldRule(
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 0c3b2ae..e88f882 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -438,6 +438,10 @@
     return addTestingAnnotation(Keep.class);
   }
 
+  public final T addKeepUnusedReturnValueAnnotation() {
+    return addTestingAnnotation(KeepUnusedReturnValue.class);
+  }
+
   public final T addMemberValuePropagationAnnotations() {
     return addTestingAnnotation(NeverPropagateValue.class);
   }