Recognize MIN-MAX.

Rationale:
Because more contextual information is better.

Bug: b/74026074

Test: test-art-host,target
Change-Id: If884d64d800823f32d7ca6217c28fef25b86af9e
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 3483770..2b6f905 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -854,11 +854,29 @@
                                     HInstruction* cursor) {
   DataType::Type type = x->GetType();
   DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64);
-  HAbs* abs = new (allocator) HAbs(type, x, x->GetDexPc());
+  HAbs* abs = new (allocator) HAbs(type, x, cursor->GetDexPc());
   cursor->GetBlock()->InsertInstructionBefore(abs, cursor);
   return abs;
 }
 
+// Constructs a new MIN/MAX(x, y) node in the HIR.
+static HInstruction* NewIntegralMinMax(ArenaAllocator* allocator,
+                                       HInstruction* x,
+                                       HInstruction* y,
+                                       HInstruction* cursor,
+                                       bool is_min) {
+  DataType::Type type = x->GetType();
+  DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64);
+  HBinaryOperation* minmax = nullptr;
+  if (is_min) {
+    minmax = new (allocator) HMin(type, x, y, cursor->GetDexPc());
+  } else {
+    minmax = new (allocator) HMax(type, x, y, cursor->GetDexPc());
+  }
+  cursor->GetBlock()->InsertInstructionBefore(minmax, cursor);
+  return minmax;
+}
+
 // Returns true if operands a and b consists of widening type conversions
 // (either explicit or implicit) to the given to_type.
 static bool AreLowerPrecisionArgs(DataType::Type to_type, HInstruction* a, HInstruction* b) {
@@ -924,8 +942,15 @@
     // Test if both values are same-typed int or long.
     if (t_type == f_type &&
         (t_type == DataType::Type::kInt32 || t_type == DataType::Type::kInt64)) {
-      // Try to replace typical integral ABS constructs.
-      if (true_value->IsNeg()) {
+      // Try to replace typical integral MIN/MAX/ABS constructs.
+      if ((cmp == kCondLT || cmp == kCondLE || cmp == kCondGT || cmp == kCondGE) &&
+          ((a == true_value && b == false_value) ||
+           (b == true_value && a == false_value))) {
+        // Found a < b ? a : b (MIN) or a < b ? b : a (MAX)
+        //    or a > b ? a : b (MAX) or a > b ? b : a (MIN).
+        bool is_min = (cmp == kCondLT || cmp == kCondLE) == (a == true_value);
+        replace_with = NewIntegralMinMax(GetGraph()->GetAllocator(), a, b, select, is_min);
+      } else if (true_value->IsNeg()) {
         HInstruction* negated = true_value->InputAt(0);
         if ((cmp == kCondLT || cmp == kCondLE) &&
             (a == negated && a == false_value && IsInt64Value(b, 0))) {
diff --git a/test/679-checker-minmax/expected.txt b/test/679-checker-minmax/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/679-checker-minmax/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/679-checker-minmax/info.txt b/test/679-checker-minmax/info.txt
new file mode 100644
index 0000000..4f7b9f5
--- /dev/null
+++ b/test/679-checker-minmax/info.txt
@@ -0,0 +1 @@
+Functional tests on detecting min/max.
diff --git a/test/679-checker-minmax/src/Main.java b/test/679-checker-minmax/src/Main.java
new file mode 100644
index 0000000..d016de6
--- /dev/null
+++ b/test/679-checker-minmax/src/Main.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Functional tests for detecting min/max.
+ */
+public class Main {
+
+  /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Cnd:z\d+>> GreaterThanOrEqual [<<Op1:i\d+>>,<<Op2:i\d+>>]
+  /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>]
+  /// CHECK-DAG:              Return [<<Sel>>]
+  //
+  /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Min:i\d+>> Min
+  /// CHECK-DAG:              Return [<<Min>>]
+  //
+  /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-NOT:              Select
+  public static int min1(int a, int b) {
+    return a < b ? a : b;
+  }
+
+  /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Cnd:z\d+>> GreaterThan [<<Op1:i\d+>>,<<Op2:i\d+>>]
+  /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>]
+  /// CHECK-DAG:              Return [<<Sel>>]
+  //
+  /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Min:i\d+>> Min
+  /// CHECK-DAG:              Return [<<Min>>]
+  //
+  /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-NOT:              Select
+  public static int min2(int a, int b) {
+    return a <= b ? a : b;
+  }
+
+  /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Cnd:z\d+>> LessThanOrEqual [<<Op1:i\d+>>,<<Op2:i\d+>>]
+  /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op2>>,<<Op1>>,<<Cnd>>]
+  /// CHECK-DAG:              Return [<<Sel>>]
+  //
+  /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Min:i\d+>> Min
+  /// CHECK-DAG:              Return [<<Min>>]
+  //
+  /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-NOT:              Select
+  public static int min3(int a, int b) {
+    return a > b ? b : a;
+  }
+
+  /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Op1:i\d+>>,<<Op2:i\d+>>]
+  /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op2>>,<<Op1>>,<<Cnd>>]
+  /// CHECK-DAG:              Return [<<Sel>>]
+  //
+  /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Min:i\d+>> Min
+  /// CHECK-DAG:              Return [<<Min>>]
+  //
+  /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-NOT:              Select
+  public static int min4(int a, int b) {
+    return a >= b ? b : a;
+  }
+
+  /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Cnd:z\d+>> GreaterThanOrEqual [<<Op1:i\d+>>,<<Op2:i\d+>>]
+  /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op2>>,<<Op1>>,<<Cnd>>]
+  /// CHECK-DAG:              Return [<<Sel>>]
+  //
+  /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Max:i\d+>> Max
+  /// CHECK-DAG:              Return [<<Max>>]
+  //
+  /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-NOT:              Select
+  public static int max1(int a, int b) {
+    return a < b ? b : a;
+  }
+
+  /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Cnd:z\d+>> GreaterThan [<<Op1:i\d+>>,<<Op2:i\d+>>]
+  /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op2>>,<<Op1>>,<<Cnd>>]
+  /// CHECK-DAG:              Return [<<Sel>>]
+  //
+  /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Max:i\d+>> Max
+  /// CHECK-DAG:              Return [<<Max>>]
+  //
+  /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-NOT:              Select
+  public static int max2(int a, int b) {
+    return a <= b ? b : a;
+  }
+
+  /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Cnd:z\d+>> LessThanOrEqual [<<Op1:i\d+>>,<<Op2:i\d+>>]
+  /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>]
+  /// CHECK-DAG:              Return [<<Sel>>]
+  //
+  /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Max:i\d+>> Max
+  /// CHECK-DAG:              Return [<<Max>>]
+  //
+  /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-NOT:              Select
+  public static int max3(int a, int b) {
+    return a > b ? a : b;
+  }
+
+  /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_inlining (before)
+  /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Op1:i\d+>>,<<Op2:i\d+>>]
+  /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>]
+  /// CHECK-DAG:              Return [<<Sel>>]
+  //
+  /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-DAG: <<Max:i\d+>> Max
+  /// CHECK-DAG:              Return [<<Max>>]
+  //
+  /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_inlining (after)
+  /// CHECK-NOT:              Select
+  public static int max4(int a, int b) {
+    return a >= b ? a : b;
+  }
+
+  public static void main(String[] args) {
+    expectEquals(10, min1(10, 20));
+    expectEquals(10, min2(10, 20));
+    expectEquals(10, min3(10, 20));
+    expectEquals(10, min4(10, 20));
+    expectEquals(20, max1(10, 20));
+    expectEquals(20, max2(10, 20));
+    expectEquals(20, max3(10, 20));
+    expectEquals(20, max4(10, 20));
+    System.out.println("passed");
+  }
+
+  private static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+}