Enabled nested min-max SIMDization for narrower operands.

Bug: b/74026074

Test: test-art-host,target
Change-Id: Ic6ee31be6192fb2b3bae3be8986da261a744be07
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 71e24de..b41b659 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -153,6 +153,18 @@
         return false;
     }
   }
+  // A MIN-MAX on narrower operands qualifies as well
+  // (returning the operator itself).
+  if (instruction->IsMin() || instruction->IsMax()) {
+    HBinaryOperation* min_max = instruction->AsBinaryOperation();
+    DCHECK(min_max->GetType() == DataType::Type::kInt32 ||
+           min_max->GetType() == DataType::Type::kInt64);
+    if (IsSignExtensionAndGet(min_max->InputAt(0), type, operand) &&
+        IsSignExtensionAndGet(min_max->InputAt(1), type, operand)) {
+      *operand = min_max;
+      return true;
+    }
+  }
   return false;
 }
 
@@ -216,6 +228,18 @@
         return false;
     }
   }
+  // A MIN-MAX on narrower operands qualifies as well
+  // (returning the operator itself).
+  if (instruction->IsMin() || instruction->IsMax()) {
+    HBinaryOperation* min_max = instruction->AsBinaryOperation();
+    DCHECK(min_max->GetType() == DataType::Type::kInt32 ||
+           min_max->GetType() == DataType::Type::kInt64);
+    if (IsZeroExtensionAndGet(min_max->InputAt(0), type, operand) &&
+        IsZeroExtensionAndGet(min_max->InputAt(1), type, operand)) {
+      *operand = min_max;
+      return true;
+    }
+  }
   return false;
 }
 
diff --git a/test/651-checker-simd-minmax/src/ByteSimdMinMax.java b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java
index b995494..8dacd5d 100644
--- a/test/651-checker-simd-minmax/src/ByteSimdMinMax.java
+++ b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java
@@ -129,7 +129,7 @@
   /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
-  /// CHECK-START-{ARM64,MIPS64}: void ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (after)
+  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (after)
   /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                      loop:none
   /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>]        loop:none
   /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>  outer_loop:none
@@ -142,6 +142,38 @@
     }
   }
 
+  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinMax(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<I11:i\d+>>  IntConstant -11                      loop:none
+  /// CHECK-DAG: <<I23:i\d+>>  IntConstant 23                       loop:none
+  /// CHECK-DAG: <<Rpl1:d\d+>> VecReplicateScalar [<<I23>>]         loop:none
+  /// CHECK-DAG: <<Rpl2:d\d+>> VecReplicateScalar [<<I11>>]         loop:none
+  /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>  outer_loop:none
+  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get>>,<<Rpl1>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Min>>,<<Rpl2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>       outer_loop:none
+  private static void doitMinMax(byte[] x, byte[] y) {
+    int n = Math.min(x.length, y.length);
+    for (int i = 0; i < n; i++) {
+      x[i] = (byte) Math.max(-11, Math.min(y[i], 23));
+    }
+  }
+
+  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinMaxUnsigned(byte[], byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<I11:i\d+>>  IntConstant 11                       loop:none
+  /// CHECK-DAG: <<I23:i\d+>>  IntConstant 23                       loop:none
+  /// CHECK-DAG: <<Rpl1:d\d+>> VecReplicateScalar [<<I23>>]         loop:none
+  /// CHECK-DAG: <<Rpl2:d\d+>> VecReplicateScalar [<<I11>>]         loop:none
+  /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>  outer_loop:none
+  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get>>,<<Rpl1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Min>>,<<Rpl2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>       outer_loop:none
+  private static void doitMinMaxUnsigned(byte[] x, byte[] y) {
+    int n = Math.min(x.length, y.length);
+    for (int i = 0; i < n; i++) {
+      x[i] = (byte) Math.max(11, Math.min(y[i] & 0xff, 23));
+    }
+  }
+
   public static void main() {
     // Initialize cross-values for all possible values.
     int total = 256 * 256;
@@ -184,6 +216,18 @@
       byte expected = (byte) Math.min(y[i], 100);
       expectEquals(expected, x[i]);
     }
+    doitMinMax(x, y);
+    for (int i = 0; i < total; i++) {
+      int s = y[i];
+      byte expected = (byte) (s < -11 ? -11 : (s > 23 ? 23 : s));
+      expectEquals(expected, x[i]);
+    }
+    doitMinMaxUnsigned(x, y);
+    for (int i = 0; i < total; i++) {
+      int u = y[i] & 0xff;
+      byte expected = (byte) (u < 11 ? 11 : (u > 23 ? 23 : u));
+      expectEquals(expected, x[i]);
+    }
 
     System.out.println("ByteSimdMinMax passed");
   }
diff --git a/test/651-checker-simd-minmax/src/ShortSimdMinMax.java b/test/651-checker-simd-minmax/src/ShortSimdMinMax.java
index aae7891..9075d80 100644
--- a/test/651-checker-simd-minmax/src/ShortSimdMinMax.java
+++ b/test/651-checker-simd-minmax/src/ShortSimdMinMax.java
@@ -129,7 +129,7 @@
   /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
   //
-  /// CHECK-START-{ARM64,MIPS64}: void ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (after)
+  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (after)
   /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                      loop:none
   /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>]        loop:none
   /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>   outer_loop:none
@@ -142,6 +142,38 @@
     }
   }
 
+  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinMax(short[], short[]) loop_optimization (after)
+  /// CHECK-DAG: <<I11:i\d+>>  IntConstant -1111                    loop:none
+  /// CHECK-DAG: <<I23:i\d+>>  IntConstant 2323                     loop:none
+  /// CHECK-DAG: <<Rpl1:d\d+>> VecReplicateScalar [<<I23>>]         loop:none
+  /// CHECK-DAG: <<Rpl2:d\d+>> VecReplicateScalar [<<I11>>]         loop:none
+  /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>  outer_loop:none
+  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get>>,<<Rpl1>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Min>>,<<Rpl2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>       outer_loop:none
+  private static void doitMinMax(short[] x, short[] y) {
+    int n = Math.min(x.length, y.length);
+    for (int i = 0; i < n; i++) {
+      x[i] = (short) Math.max(-1111, Math.min(y[i], 2323));
+    }
+  }
+
+  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinMaxUnsigned(short[], short[]) loop_optimization (after)
+  /// CHECK-DAG: <<I11:i\d+>>  IntConstant 1111                     loop:none
+  /// CHECK-DAG: <<I23:i\d+>>  IntConstant 2323                     loop:none
+  /// CHECK-DAG: <<Rpl1:d\d+>> VecReplicateScalar [<<I23>>]         loop:none
+  /// CHECK-DAG: <<Rpl2:d\d+>> VecReplicateScalar [<<I11>>]         loop:none
+  /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>  outer_loop:none
+  /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get>>,<<Rpl1>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Min>>,<<Rpl2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>>       outer_loop:none
+  private static void doitMinMaxUnsigned(short[] x, short[] y) {
+    int n = Math.min(x.length, y.length);
+    for (int i = 0; i < n; i++) {
+      x[i] = (short) Math.max(1111, Math.min(y[i] & 0xffff, 2323));
+    }
+  }
+
   public static void main() {
     short[] interesting = {
       (short) 0x0000, (short) 0x0001, (short) 0x007f,
@@ -198,6 +230,18 @@
       short expected = (short) Math.min(y[i], 100);
       expectEquals(expected, x[i]);
     }
+    doitMinMax(x, y);
+    for (int i = 0; i < total; i++) {
+      int s = y[i];
+      short expected = (short) (s < -1111 ? -1111 : (s > 2323 ? 2323 : s));
+      expectEquals(expected, x[i]);
+    }
+    doitMinMaxUnsigned(x, y);
+    for (int i = 0; i < total; i++) {
+      int u = y[i] & 0xffff;
+      short expected = (short) (u < 1111 ? 1111 : (u > 2323 ? 2323 : u));
+      expectEquals(expected, x[i]);
+    }
 
     System.out.println("ShortSimdMinMax passed");
   }
diff --git a/test/679-checker-minmax/src/Main.java b/test/679-checker-minmax/src/Main.java
index 4f0261c..e330a53 100644
--- a/test/679-checker-minmax/src/Main.java
+++ b/test/679-checker-minmax/src/Main.java
@@ -20,6 +20,82 @@
 public class Main {
 
   //
+  // Direct intrinsics.
+  //
+
+  /// CHECK-START: int Main.minI(int) instruction_simplifier (before)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+  /// CHECK-DAG: <<Con:i\d+>> IntConstant 20
+  /// CHECK-DAG: <<Min:i\d+>> InvokeStaticOrDirect [<<Par>>,<<Con>>] intrinsic:MathMinIntInt
+  /// CHECK-DAG:              Return [<<Min>>]
+  //
+  /// CHECK-START: int Main.minI(int) instruction_simplifier (after)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+  /// CHECK-DAG: <<Con:i\d+>> IntConstant 20
+  /// CHECK-DAG: <<Min:i\d+>> Min [<<Par>>,<<Con>>]
+  /// CHECK-DAG:              Return [<<Min>>]
+  //
+  /// CHECK-START: int Main.minI(int) instruction_simplifier (after)
+  /// CHECK-NOT:              InvokeStaticOrDirect
+  public static int minI(int a) {
+    return Math.min(a, 20);
+  }
+
+  /// CHECK-START: long Main.minL(long) instruction_simplifier (before)
+  /// CHECK-DAG: <<Par:j\d+>> ParameterValue
+  /// CHECK-DAG: <<Con:j\d+>> LongConstant 20
+  /// CHECK-DAG: <<Min:j\d+>> InvokeStaticOrDirect [<<Par>>,<<Con>>] intrinsic:MathMinLongLong
+  /// CHECK-DAG:              Return [<<Min>>]
+  //
+  /// CHECK-START: long Main.minL(long) instruction_simplifier (after)
+  /// CHECK-DAG: <<Par:j\d+>> ParameterValue
+  /// CHECK-DAG: <<Con:j\d+>> LongConstant 20
+  /// CHECK-DAG: <<Min:j\d+>> Min [<<Par>>,<<Con>>]
+  /// CHECK-DAG:              Return [<<Min>>]
+  //
+  /// CHECK-START: long Main.minL(long) instruction_simplifier (after)
+  /// CHECK-NOT:              InvokeStaticOrDirect
+  public static long minL(long a) {
+    return Math.min(a, 20L);
+  }
+
+  /// CHECK-START: int Main.maxI(int) instruction_simplifier (before)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+  /// CHECK-DAG: <<Con:i\d+>> IntConstant 20
+  /// CHECK-DAG: <<Max:i\d+>> InvokeStaticOrDirect [<<Par>>,<<Con>>] intrinsic:MathMaxIntInt
+  /// CHECK-DAG:              Return [<<Max>>]
+  //
+  /// CHECK-START: int Main.maxI(int) instruction_simplifier (after)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+  /// CHECK-DAG: <<Con:i\d+>> IntConstant 20
+  /// CHECK-DAG: <<Max:i\d+>> Max [<<Par>>,<<Con>>]
+  /// CHECK-DAG:              Return [<<Max>>]
+  //
+  /// CHECK-START: int Main.maxI(int) instruction_simplifier (after)
+  /// CHECK-NOT:              InvokeStaticOrDirect
+  public static int maxI(int a) {
+    return Math.max(a, 20);
+  }
+
+  /// CHECK-START: long Main.maxL(long) instruction_simplifier (before)
+  /// CHECK-DAG: <<Par:j\d+>> ParameterValue
+  /// CHECK-DAG: <<Con:j\d+>> LongConstant 20
+  /// CHECK-DAG: <<Max:j\d+>> InvokeStaticOrDirect [<<Par>>,<<Con>>] intrinsic:MathMaxLongLong
+  /// CHECK-DAG:              Return [<<Max>>]
+  //
+  /// CHECK-START: long Main.maxL(long) instruction_simplifier (after)
+  /// CHECK-DAG: <<Par:j\d+>> ParameterValue
+  /// CHECK-DAG: <<Con:j\d+>> LongConstant 20
+  /// CHECK-DAG: <<Max:j\d+>> Max [<<Par>>,<<Con>>]
+  /// CHECK-DAG:              Return [<<Max>>]
+  //
+  /// CHECK-START: long Main.maxL(long) instruction_simplifier (after)
+  /// CHECK-NOT:              InvokeStaticOrDirect
+  public static long maxL(long a) {
+    return Math.max(a, 20L);
+  }
+
+  //
   // Different types.
   //
 
@@ -343,6 +419,14 @@
 
   public static void main(String[] args) {
     // Types.
+    expectEquals(10, minI(10));
+    expectEquals(20, minI(25));
+    expectEquals(10L, minL(10L));
+    expectEquals(20L, minL(25L));
+    expectEquals(20, maxI(10));
+    expectEquals(25, maxI(25));
+    expectEquals(20L, maxL(10L));
+    expectEquals(25L, maxL(25L));
     expectEquals(10, min1(10, 20));
     expectEquals(10, min2(10, 20));
     expectEquals(10, min3(10, 20));