The existing VEC_COND_EXPR min/max patterns require the comparison and result
constants to be equal so they do not recognize this canonicalized form.
Add patterns for the GE/LT forms produced from negative signed maximum
expressions and for the LE/GT forms produced from positive signed or unsigned
minimum expressions. Handle both normal and reversed conditional arms.
The off-by-one check is performed elementwise so the transform is not
restricted to uniform vector constants.

This also handles non-uniform vector constants such as:
X >= { -99, -98 } ? X : { -100, -99 }
which can be folded to:
MAX_EXPR <X, { -100, -99 }>
when each comparison element is exactly one greater than the corresponding
result element. Variable-length vector constants are handled when the two
constants have matching VECTOR_CST encodings.

gcc/ChangeLog:
        PR tree-optimization/98602
        * match.pd (vec_cond (cmp @0 VECTOR_CST@1) @0 VECTOR_CST@2):
        New simplification.
        (vec_cond (cmp @0 VECTOR_CST@1) VECTOR_CST@2 @0): Likewise.
        * tree.cc (record_uniform_integer_difference): New helper.
        (uniform_vector_difference_p): New function.
        * tree.h (uniform_vector_difference_p): Declare.

gcc/testsuite/ChangeLog:
        PR tree-optimization/98602
        * g++.dg/tree-ssa/pr98602.C: New test.
        * g++.target/aarch64/sve/max_1.C: Remove fixed XFAILs.
        * g++.target/aarch64/sve/min_1.C: Likewise.

Signed-off-by: Naveen <[email protected]>
---
 gcc/match.pd                                 | 36 +++++++++
 gcc/testsuite/g++.dg/tree-ssa/pr98602.C      | 37 +++++++++
 gcc/testsuite/g++.target/aarch64/sve/max_1.C | 10 +--
 gcc/testsuite/g++.target/aarch64/sve/min_1.C | 44 +++++------
 gcc/tree.cc                                  | 83 ++++++++++++++++++++
 gcc/tree.h                                   |  5 ++
 6 files changed, 188 insertions(+), 27 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/tree-ssa/pr98602.C

diff --git a/gcc/match.pd b/gcc/match.pd
index 723780b8c38..4b966f4ae0c 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -6822,6 +6822,42 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        && target_supports_op_p (type, MINMAX, optab_vector))
     (minmax @0 @1))))
 
+/* Comparison canonicalization changes X > C to X >= C + 1 and
+   X < C to X <= C - 1.  */
+(for cmp (ge le)
+     minmax (max min)
+     MINMAX (MAX_EXPR MIN_EXPR)
+ (simplify
+  (vec_cond (cmp @0 VECTOR_CST@1) @0 VECTOR_CST@2)
+  (with
+   {
+     tree diff = (minmax == MAX_EXPR
+                 ? uniform_vector_difference_p (@1, @2)
+                 : uniform_vector_difference_p (@2, @1));
+   }
+   (if (VECTOR_INTEGER_TYPE_P (type)
+       && target_supports_op_p (type, MINMAX, optab_vector)
+       && diff
+       && integer_onep (diff))
+    (minmax @0 @2)))))
+
+(for cmp (lt gt)
+     minmax (max min)
+     MINMAX (MAX_EXPR MIN_EXPR)
+ (simplify
+  (vec_cond (cmp @0 VECTOR_CST@1) VECTOR_CST@2 @0)
+  (with
+   {
+     tree diff = (minmax == MAX_EXPR
+                 ? uniform_vector_difference_p (@1, @2)
+                 : uniform_vector_difference_p (@2, @1));
+   }
+   (if (VECTOR_INTEGER_TYPE_P (type)
+       && target_supports_op_p (type, MINMAX, optab_vector)
+       && diff
+       && integer_onep (diff))
+    (minmax @0 @2)))))
+
 /* Try to optimize x < 0 ? -1 : 0 into (signed) x >> 31
    and x < 0 ? 1 : 0 into (unsigned) x >> 31.  */
 (simplify
diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr98602.C 
b/gcc/testsuite/g++.dg/tree-ssa/pr98602.C
new file mode 100644
index 00000000000..6a4a421747a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-ssa/pr98602.C
@@ -0,0 +1,37 @@
+/* { dg-do compile { target aarch64*-*-* } } */
+/* { dg-options "-O1 -fdump-tree-forwprop1-raw -Wno-psabi" } */
+
+typedef int v4si __attribute__ ((vector_size (16)));
+typedef unsigned int v4ui __attribute__ ((vector_size (16)));
+
+v4si
+smax_nonuniform (v4si x)
+{
+  return x >= (v4si) { -99, -98, -97, -96 }
+        ? x : (v4si) { -100, -99, -98, -97 };
+}
+
+v4si
+smax_nonuniform_rev (v4si x)
+{
+  return x < (v4si) { -99, -98, -97, -96 }
+        ? (v4si) { -100, -99, -98, -97 } : x;
+}
+
+v4si
+smin_nonuniform (v4si x)
+{
+  return x <= (v4si) { 99, 100, 101, 102 }
+        ? x : (v4si) { 100, 101, 102, 103 };
+}
+
+v4ui
+umin_nonuniform_rev (v4ui x)
+{
+  return x > (v4ui) { 1, 2, 3, 4 }
+        ? (v4ui) { 2, 3, 4, 5 } : x;
+}
+
+/* { dg-final { scan-tree-dump-times "max_expr, " 2 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "min_expr, " 2 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-not "vec_cond_expr, " "forwprop1" } } */
diff --git a/gcc/testsuite/g++.target/aarch64/sve/max_1.C 
b/gcc/testsuite/g++.target/aarch64/sve/max_1.C
index caf9d7cd9bb..6328d5a1b38 100644
--- a/gcc/testsuite/g++.target/aarch64/sve/max_1.C
+++ b/gcc/testsuite/g++.target/aarch64/sve/max_1.C
@@ -39,11 +39,11 @@ TEST_TYPE (uint32_t, 128, 7, 255)
 /* { dg-final { scan-assembler-times {\tumax\tz[0-9]+\.h, p[0-7]/m, 
z[0-9]+\.h, z[0-9]+\.h\n} 2 } } */
 /* { dg-final { scan-assembler-times {\tumax\tz[0-9]+\.s, p[0-7]/m, 
z[0-9]+\.s, z[0-9]+\.s\n} 1 } } */
 
-/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.b, z[0-9]+\.b, #-100\n} 
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.b, z[0-9]+\.b, #-110\n} 
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.b, z[0-9]+\.b, #-120\n} 
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.h, z[0-9]+\.h, #-128\n} 
2 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.s, z[0-9]+\.s, #-128\n} 
1 { xfail *-*-* } } } */
+/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.b, z[0-9]+\.b, #-100\n} 
1 } } */
+/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.b, z[0-9]+\.b, #-110\n} 
1 } } */
+/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.b, z[0-9]+\.b, #-120\n} 
1 } } */
+/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.h, z[0-9]+\.h, #-128\n} 
2 } } */
+/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.s, z[0-9]+\.s, #-128\n} 
1 } } */
 
 /* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.b, z[0-9]+\.b, #50\n} 3 
} } */
 /* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.h, z[0-9]+\.h, #50\n} 2 
} } */
diff --git a/gcc/testsuite/g++.target/aarch64/sve/min_1.C 
b/gcc/testsuite/g++.target/aarch64/sve/min_1.C
index 9c84690cd1b..172fa6a9da5 100644
--- a/gcc/testsuite/g++.target/aarch64/sve/min_1.C
+++ b/gcc/testsuite/g++.target/aarch64/sve/min_1.C
@@ -45,29 +45,29 @@ TEST_TYPE (uint32_t, 128, 7, 255)
 /* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.h, z[0-9]+\.h, #-128\n} 
2 } } */
 /* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.s, z[0-9]+\.s, #-128\n} 
1 } } */
 
-/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.b, z[0-9]+\.b, #50\n} 3 
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.h, z[0-9]+\.h, #50\n} 2 
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.s, z[0-9]+\.s, #50\n} 1 
{ xfail *-*-* } } } */
+/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.b, z[0-9]+\.b, #50\n} 3 
} } */
+/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.h, z[0-9]+\.h, #50\n} 2 
} } */
+/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.s, z[0-9]+\.s, #50\n} 1 
} } */
 
-/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.b, z[0-9]+\.b, #100\n} 
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.b, z[0-9]+\.b, #110\n} 
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.b, z[0-9]+\.b, #120\n} 
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.h, z[0-9]+\.h, #127\n} 
2 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.s, z[0-9]+\.s, #127\n} 
1 { xfail *-*-* } } } */
+/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.b, z[0-9]+\.b, #100\n} 
1 } } */
+/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.b, z[0-9]+\.b, #110\n} 
1 } } */
+/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.b, z[0-9]+\.b, #120\n} 
1 } } */
+/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.h, z[0-9]+\.h, #127\n} 
2 } } */
+/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.s, z[0-9]+\.s, #127\n} 
1 } } */
 
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #2\n} 1 
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #3\n} 1 
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.h, z[0-9]+\.h, #4\n} 1 
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #5\n} 1 
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.h, z[0-9]+\.h, #6\n} 1 
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.s, z[0-9]+\.s, #7\n} 1 
{ xfail *-*-* } } } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #2\n} 1 
} } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #3\n} 1 
} } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.h, z[0-9]+\.h, #4\n} 1 
} } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #5\n} 1 
} } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.h, z[0-9]+\.h, #6\n} 1 
} } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.s, z[0-9]+\.s, #7\n} 1 
} } */
 
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #50\n} 3 
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.h, z[0-9]+\.h, #50\n} 2 
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.s, z[0-9]+\.s, #50\n} 1 
{ xfail *-*-* } } } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #50\n} 3 
} } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.h, z[0-9]+\.h, #50\n} 2 
} } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.s, z[0-9]+\.s, #50\n} 1 
} } */
 
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #250\n} 
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #251\n} 
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #253\n} 
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.h, z[0-9]+\.h, #255\n} 
2 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.s, z[0-9]+\.s, #255\n} 
1 { xfail *-*-* } } } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #250\n} 
1 } } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #251\n} 
1 } } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #253\n} 
1 } } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.h, z[0-9]+\.h, #255\n} 
2 } } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.s, z[0-9]+\.s, #255\n} 
1 } } */
diff --git a/gcc/tree.cc b/gcc/tree.cc
index 977bdaa0845..b7e02cd84fd 100644
--- a/gcc/tree.cc
+++ b/gcc/tree.cc
@@ -10903,6 +10903,89 @@ uniform_integer_cst_p (tree t)
   return NULL_TREE;
 }
 
+/* Return true if ELT1 and ELT2 are INTEGER_CSTs and if ELT1 - ELT2 is
+   consistent with the uniform difference recorded in DIFF.  */
+
+static bool
+record_uniform_integer_difference (const_tree elt1, const_tree elt2,
+                                  widest_int *diff, bool *diff_p)
+{
+  STRIP_ANY_LOCATION_WRAPPER (elt1);
+  STRIP_ANY_LOCATION_WRAPPER (elt2);
+
+  if (TREE_CODE (elt1) != INTEGER_CST
+      || TREE_CODE (elt2) != INTEGER_CST)
+    return false;
+
+  widest_int elt_diff = wi::to_widest (elt1) - wi::to_widest (elt2);
+  if (!*diff_p)
+    {
+      *diff = elt_diff;
+      *diff_p = true;
+      return true;
+    }
+
+  return *diff == elt_diff;
+}
+
+/* Return the uniform difference between two INTEGER_CSTs or corresponding
+   elements of two VECTOR_CSTs or NULL_TREE if no such difference exists.  */
+
+tree
+uniform_vector_difference_p (const_tree t1, const_tree t2)
+{
+  STRIP_ANY_LOCATION_WRAPPER (t1);
+  STRIP_ANY_LOCATION_WRAPPER (t2);
+
+  if (TREE_CODE (t1) == INTEGER_CST
+      && TREE_CODE (t2) == INTEGER_CST)
+    {
+      widest_int diff = wi::to_widest (t1) - wi::to_widest (t2);
+      if (!wi::fits_shwi_p (diff))
+       return NULL_TREE;
+      return build_int_cst (long_long_integer_type_node, diff.to_shwi ());
+    }
+
+  if (TREE_CODE (t1) != VECTOR_CST
+      || TREE_CODE (t2) != VECTOR_CST
+      || !known_eq (VECTOR_CST_NELTS (t1), VECTOR_CST_NELTS (t2)))
+    return NULL_TREE;
+
+  widest_int diff;
+  bool diff_p = false;
+
+  if (VECTOR_CST_LOG2_NPATTERNS (t1) == VECTOR_CST_LOG2_NPATTERNS (t2)
+      && (VECTOR_CST_NELTS_PER_PATTERN (t1)
+         == VECTOR_CST_NELTS_PER_PATTERN (t2)))
+    {
+      unsigned int encoded_nelts = vector_cst_encoded_nelts (t1);
+      gcc_assert (encoded_nelts == vector_cst_encoded_nelts (t2));
+
+      for (unsigned int i = 0; i < encoded_nelts; ++i)
+       if (!record_uniform_integer_difference (VECTOR_CST_ENCODED_ELT (t1, i),
+                                               VECTOR_CST_ENCODED_ELT (t2, i),
+                                               &diff, &diff_p))
+         return NULL_TREE;
+    }
+  else
+    {
+      unsigned HOST_WIDE_INT nelts;
+      if (!VECTOR_CST_NELTS (t1).is_constant (&nelts))
+       return NULL_TREE;
+
+      for (unsigned HOST_WIDE_INT i = 0; i < nelts; ++i)
+       if (!record_uniform_integer_difference (vector_cst_elt (t1, i),
+                                               vector_cst_elt (t2, i),
+                                               &diff, &diff_p))
+         return NULL_TREE;
+    }
+
+  if (!diff_p || !wi::fits_shwi_p (diff))
+    return NULL_TREE;
+
+  return build_int_cst (long_long_integer_type_node, diff.to_shwi ());
+}
+
 /* Checks to see if T is a constant or a constant vector and if each element E
    adheres to ~E + 1 == pow2 then return ~E otherwise NULL_TREE.  */
 
diff --git a/gcc/tree.h b/gcc/tree.h
index d038d2afd33..e31699e2d99 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5390,6 +5390,11 @@ extern tree ssa_uniform_vector_p (tree);
 
 extern tree uniform_integer_cst_p (tree);
 
+/* Return the uniform difference between two INTEGER_CSTs or corresponding
+   elements of two VECTOR_CSTs or NULL_TREE if no such difference exists.  */
+
+extern tree uniform_vector_difference_p (const_tree, const_tree);
+
 extern int single_nonzero_element (const_tree);
 
 /* Given a CONSTRUCTOR CTOR, return the element values as a vector.  */
-- 
2.34.1

Reply via email to