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