Extend simple inlining constraint to const number arguments

Fixes: 176067541
Change-Id: I07cd7aa31d8133b88517b5f1ef40b8efc3f01bf7
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/AlwaysSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/AlwaysSimpleInliningConstraint.java
index a164607..93cdb16 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/AlwaysSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/AlwaysSimpleInliningConstraint.java
@@ -36,7 +36,8 @@
   }
 
   @Override
-  public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+  public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+      IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
     return this;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanFalseSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanFalseSimpleInliningConstraint.java
index 351d51d..2c88a60 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanFalseSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanFalseSimpleInliningConstraint.java
@@ -41,7 +41,8 @@
   }
 
   @Override
-  public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+  public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+      IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
     return this;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanTrueSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanTrueSimpleInliningConstraint.java
index bf3fde3..46c29b0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanTrueSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanTrueSimpleInliningConstraint.java
@@ -41,7 +41,8 @@
   }
 
   @Override
-  public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+  public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+      IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
     return this;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/EqualToNumberSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/EqualToNumberSimpleInliningConstraint.java
new file mode 100644
index 0000000..a321670
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/EqualToNumberSimpleInliningConstraint.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2021, 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.ir.analysis.inlining;
+
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import it.unimi.dsi.fastutil.ints.IntList;
+
+public class EqualToNumberSimpleInliningConstraint extends SimpleInliningArgumentConstraint {
+
+  private final long rawValue;
+
+  private EqualToNumberSimpleInliningConstraint(int argumentIndex, long rawValue) {
+    super(argumentIndex);
+    this.rawValue = rawValue;
+  }
+
+  static EqualToNumberSimpleInliningConstraint create(
+      int argumentIndex, long rawValue, SimpleInliningConstraintFactory witness) {
+    assert witness != null;
+    return new EqualToNumberSimpleInliningConstraint(argumentIndex, rawValue);
+  }
+
+  @Override
+  public boolean isSatisfied(InvokeMethod invoke) {
+    Value argumentRoot = getArgument(invoke).getAliasedValue();
+    return argumentRoot.isDefinedByInstructionSatisfying(Instruction::isConstNumber)
+        && argumentRoot.getDefinition().asConstNumber().getRawValue() == rawValue;
+  }
+
+  @Override
+  public SimpleInliningConstraint fixupAfterRemovingThisParameter(
+      SimpleInliningConstraintFactory factory) {
+    assert getArgumentIndex() > 0;
+    return factory.createNumberConstraint(getArgumentIndex() - 1, rawValue);
+  }
+
+  @Override
+  public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+      IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NeverSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NeverSimpleInliningConstraint.java
index 4568e5f..809dbb9 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NeverSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NeverSimpleInliningConstraint.java
@@ -35,7 +35,8 @@
   }
 
   @Override
-  public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+  public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+      IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
     return this;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotEqualToNumberSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotEqualToNumberSimpleInliningConstraint.java
new file mode 100644
index 0000000..4739231
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotEqualToNumberSimpleInliningConstraint.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2021, 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.ir.analysis.inlining;
+
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import it.unimi.dsi.fastutil.ints.IntList;
+
+public class NotEqualToNumberSimpleInliningConstraint extends SimpleInliningArgumentConstraint {
+
+  private final long rawValue;
+
+  private NotEqualToNumberSimpleInliningConstraint(int argumentIndex, long rawValue) {
+    super(argumentIndex);
+    this.rawValue = rawValue;
+  }
+
+  static NotEqualToNumberSimpleInliningConstraint create(
+      int argumentIndex, long rawValue, SimpleInliningConstraintFactory witness) {
+    assert witness != null;
+    return new NotEqualToNumberSimpleInliningConstraint(argumentIndex, rawValue);
+  }
+
+  @Override
+  public boolean isSatisfied(InvokeMethod invoke) {
+    Value argumentRoot = getArgument(invoke).getAliasedValue();
+    return argumentRoot.isDefinedByInstructionSatisfying(Instruction::isConstNumber)
+        && argumentRoot.getDefinition().asConstNumber().getRawValue() != rawValue;
+  }
+
+  @Override
+  public SimpleInliningConstraint fixupAfterRemovingThisParameter(
+      SimpleInliningConstraintFactory factory) {
+    assert getArgumentIndex() > 0;
+    return factory.createNotNumberConstraint(getArgumentIndex() - 1, rawValue);
+  }
+
+  @Override
+  public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+      IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotNullSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotNullSimpleInliningConstraint.java
index 0834cba..00288f8 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotNullSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotNullSimpleInliningConstraint.java
@@ -41,10 +41,10 @@
   }
 
   @Override
-  public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+  public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+      IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
     if (unboxedArgumentIndices.contains(getArgumentIndex())) {
-      // TODO(b/176067541): Could be refined to an argument-equals-int constraint.
-      return NeverSimpleInliningConstraint.getInstance();
+      return factory.createNotNumberConstraint(getArgumentIndex(), 0);
     }
     return this;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
index 19607ee..5a0d3cf 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
@@ -45,10 +45,10 @@
   }
 
   @Override
-  public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+  public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+      IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
     if (unboxedArgumentIndices.contains(getArgumentIndex())) {
-      // TODO(b/176067541): Could be refined to an argument-equals-int constraint.
-      return NeverSimpleInliningConstraint.getInstance();
+      return factory.createNumberConstraint(getArgumentIndex(), 0);
     }
     return this;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java
index df108ac..82c3f7d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java
@@ -110,5 +110,5 @@
       SimpleInliningConstraintFactory factory);
 
   public abstract SimpleInliningConstraint rewrittenWithUnboxedArguments(
-      IntList unboxedArgumentIndices);
+      IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java
index 1a7e997..6d79b82 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Sets;
+import java.util.OptionalLong;
 import java.util.Set;
 
 /**
@@ -105,43 +106,46 @@
     switch (instruction.opcode()) {
       case IF:
         If ifInstruction = instruction.asIf();
-        if (ifInstruction.isZeroTest()) {
-          Value lhs = ifInstruction.lhs().getAliasedValue();
-          if (lhs.isArgument() && !lhs.isThis()) {
-            int argumentIndex = lhs.getDefinition().asArgument().getIndex();
-            DexType argumentType = method.getDefinition().getArgumentType(argumentIndex);
-            int currentDepth = instructionDepth;
-
-            // Compute the constraint for which paths through the true target are guaranteed to exit
-            // early.
-            SimpleInliningConstraint trueTargetConstraint =
-                computeConstraintFromIfZeroTest(
-                        argumentIndex, argumentType, ifInstruction.getType())
-                    // Only recurse into the true target if the constraint from the if-instruction
-                    // is not 'never'.
-                    .lazyMeet(
-                        () ->
-                            analyzeInstructionsInBlock(
-                                ifInstruction.getTrueTarget(), currentDepth));
-
-            // Compute the constraint for which paths through the false target are guaranteed to
-            // exit early.
-            SimpleInliningConstraint fallthroughTargetConstraint =
-                computeConstraintFromIfZeroTest(
-                        argumentIndex, argumentType, ifInstruction.getType().inverted())
-                    // Only recurse into the false target if the constraint from the if-instruction
-                    // is not 'never'.
-                    .lazyMeet(
-                        () ->
-                            analyzeInstructionsInBlock(
-                                ifInstruction.fallthroughBlock(), currentDepth));
-
-            // Paths going through this basic block are guaranteed to exit early if the true target
-            // is guaranteed to exit early or the false target is.
-            return trueTargetConstraint.join(fallthroughTargetConstraint);
-          }
+        Value singleArgumentOperand = getSingleArgumentOperand(ifInstruction);
+        if (singleArgumentOperand == null || singleArgumentOperand.isThis()) {
+          break;
         }
-        break;
+
+        Value otherOperand =
+            ifInstruction.isZeroTest()
+                ? null
+                : ifInstruction.getOperand(
+                    1 - ifInstruction.inValues().indexOf(singleArgumentOperand));
+
+        int argumentIndex =
+            singleArgumentOperand.getAliasedValue().getDefinition().asArgument().getIndex();
+        DexType argumentType = method.getDefinition().getArgumentType(argumentIndex);
+        int currentDepth = instructionDepth;
+
+        // Compute the constraint for which paths through the true target are guaranteed to exit
+        // early.
+        SimpleInliningConstraint trueTargetConstraint =
+            computeConstraintFromIfTest(
+                    argumentIndex, argumentType, otherOperand, ifInstruction.getType())
+                // Only recurse into the true target if the constraint from the if-instruction
+                // is not 'never'.
+                .lazyMeet(
+                    () -> analyzeInstructionsInBlock(ifInstruction.getTrueTarget(), currentDepth));
+
+        // Compute the constraint for which paths through the false target are guaranteed to
+        // exit early.
+        SimpleInliningConstraint fallthroughTargetConstraint =
+            computeConstraintFromIfTest(
+                    argumentIndex, argumentType, otherOperand, ifInstruction.getType().inverted())
+                // Only recurse into the false target if the constraint from the if-instruction
+                // is not 'never'.
+                .lazyMeet(
+                    () ->
+                        analyzeInstructionsInBlock(ifInstruction.fallthroughBlock(), currentDepth));
+
+        // Paths going through this basic block are guaranteed to exit early if the true target
+        // is guaranteed to exit early or the false target is.
+        return trueTargetConstraint.join(fallthroughTargetConstraint);
 
       case GOTO:
         return analyzeInstructionsInBlock(instruction.asGoto().getTarget(), instructionDepth);
@@ -162,24 +166,39 @@
     return NeverSimpleInliningConstraint.getInstance();
   }
 
-  private SimpleInliningConstraint computeConstraintFromIfZeroTest(
-      int argumentIndex, DexType argumentType, If.Type type) {
+  private SimpleInliningConstraint computeConstraintFromIfTest(
+      int argumentIndex, DexType argumentType, Value otherOperand, If.Type type) {
+    boolean isZeroTest = otherOperand == null;
     switch (type) {
       case EQ:
-        if (argumentType.isReferenceType()) {
-          return factory.createNullConstraint(argumentIndex);
-        }
-        if (argumentType.isBooleanType()) {
-          return factory.createBooleanFalseConstraint(argumentIndex);
+        if (isZeroTest) {
+          if (argumentType.isReferenceType()) {
+            return factory.createNullConstraint(argumentIndex);
+          }
+          if (argumentType.isBooleanType()) {
+            return factory.createBooleanFalseConstraint(argumentIndex);
+          }
+        } else if (argumentType.isPrimitiveType()) {
+          OptionalLong rawValue = getRawNumberValue(otherOperand);
+          if (rawValue.isPresent()) {
+            return factory.createNumberConstraint(argumentIndex, rawValue.getAsLong());
+          }
         }
         return NeverSimpleInliningConstraint.getInstance();
 
       case NE:
-        if (argumentType.isReferenceType()) {
-          return factory.createNotNullConstraint(argumentIndex);
-        }
-        if (argumentType.isBooleanType()) {
-          return factory.createBooleanTrueConstraint(argumentIndex);
+        if (isZeroTest) {
+          if (argumentType.isReferenceType()) {
+            return factory.createNotNullConstraint(argumentIndex);
+          }
+          if (argumentType.isBooleanType()) {
+            return factory.createBooleanTrueConstraint(argumentIndex);
+          }
+        } else if (argumentType.isPrimitiveType()) {
+          OptionalLong rawValue = getRawNumberValue(otherOperand);
+          if (rawValue.isPresent()) {
+            return factory.createNotNumberConstraint(argumentIndex, rawValue.getAsLong());
+          }
         }
         return NeverSimpleInliningConstraint.getInstance();
 
@@ -187,4 +206,33 @@
         return NeverSimpleInliningConstraint.getInstance();
     }
   }
+
+  private OptionalLong getRawNumberValue(Value value) {
+    Value root = value.getAliasedValue();
+    if (root.isDefinedByInstructionSatisfying(Instruction::isConstNumber)) {
+      return OptionalLong.of(root.getDefinition().asConstNumber().getRawValue());
+    }
+    return OptionalLong.empty();
+  }
+
+  private Value getSingleArgumentOperand(If ifInstruction) {
+    Value singleArgumentOperand = null;
+
+    Value lhs = ifInstruction.lhs();
+    if (lhs.getAliasedValue().isArgument()) {
+      singleArgumentOperand = lhs;
+    }
+
+    if (!ifInstruction.isZeroTest()) {
+      Value rhs = ifInstruction.rhs();
+      if (rhs.getAliasedValue().isArgument()) {
+        if (singleArgumentOperand != null) {
+          return null;
+        }
+        singleArgumentOperand = rhs;
+      }
+    }
+
+    return singleArgumentOperand;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintConjunction.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintConjunction.java
index c993b12..ae2e07b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintConjunction.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintConjunction.java
@@ -77,11 +77,12 @@
   }
 
   @Override
-  public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+  public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+      IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
     List<SimpleInliningConstraint> rewrittenConstraints =
         ListUtils.mapOrElse(
             constraints,
-            constraint -> constraint.rewrittenWithUnboxedArguments(unboxedArgumentIndices),
+            constraint -> constraint.rewrittenWithUnboxedArguments(unboxedArgumentIndices, factory),
             null);
     return rewrittenConstraints != null
         ? new SimpleInliningConstraintConjunction(rewrittenConstraints)
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintDisjunction.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintDisjunction.java
index a6b418d..42bcaf6 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintDisjunction.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintDisjunction.java
@@ -77,11 +77,12 @@
   }
 
   @Override
-  public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+  public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+      IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
     List<SimpleInliningConstraint> rewrittenConstraints =
         ListUtils.mapOrElse(
             constraints,
-            constraint -> constraint.rewrittenWithUnboxedArguments(unboxedArgumentIndices),
+            constraint -> constraint.rewrittenWithUnboxedArguments(unboxedArgumentIndices, factory),
             null);
     return rewrittenConstraints != null
         ? new SimpleInliningConstraintDisjunction(rewrittenConstraints)
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintFactory.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintFactory.java
index 99603e8..ed3cd55 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintFactory.java
@@ -77,6 +77,16 @@
         () -> NullSimpleInliningConstraint.create(argumentIndex, this));
   }
 
+  public NotEqualToNumberSimpleInliningConstraint createNotNumberConstraint(
+      int argumentIndex, long rawValue) {
+    return NotEqualToNumberSimpleInliningConstraint.create(argumentIndex, rawValue, this);
+  }
+
+  public EqualToNumberSimpleInliningConstraint createNumberConstraint(
+      int argumentIndex, long rawValue) {
+    return EqualToNumberSimpleInliningConstraint.create(argumentIndex, rawValue, this);
+  }
+
   private <T extends SimpleInliningArgumentConstraint> T createArgumentConstraint(
       int argumentIndex, T[] lowConstraints, Map<Integer, T> highConstraints, Supplier<T> fn) {
     return argumentIndex < lowConstraints.length
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 3ac5e73b..5517fb8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -98,6 +98,10 @@
     return position == null ? "???" : position.toString();
   }
 
+  public Value getOperand(int index) {
+    return inValues().get(index);
+  }
+
   public List<Value> inValues() {
     return inValues;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index fe45c93..b11c440 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -411,7 +411,8 @@
     return method
         .getOptimizationInfo()
         .getSimpleInliningConstraint()
-        .rewrittenWithUnboxedArguments(unboxedArgumentIndices);
+        .rewrittenWithUnboxedArguments(
+            unboxedArgumentIndices, appView.simpleInliningConstraintFactory());
   }
 
   private DexMethod ensureUniqueMethod(DexEncodedMethod encodedMethod, DexMethod newMethod) {