[gcc r17-3277] tree-optimization: Fold canonicalized vector min/max conditionals [PR98602]
"Naveen H.S via Gcc-cvs" <[email protected]>
| Newsgroups | gmane.comp.gcc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://gcc.gnu.org/g:729eb68a0021bd37acff6a07a505b89687b974b9 commit r17-3277-g729eb68a0021bd37acff6a07a505b89687b974b9 Author: Naveen <[email protected]> Date: Thu Aug 13 22:00:54 2026 -0700 tree-optimization: Fold canonicalized vector min/max conditionals [PR98602] 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]> Diff: --- 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(-) diff --git a/gcc/match.pd b/gcc/match.pd index 5fc89426880d..f43d2e79363b 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -7015,6 +7015,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 000000000000..6a4a421747ab --- /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 caf9d7cd9bb4..6328d5a1b381 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 9c84690cd1b5..172fa6a9da5e 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 c8aa42b3e10e..94cd6fcfb04a 100644 --- a/gcc/tree.cc +++ b/gcc/tree.cc @@ -10910,6 +10910,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 1ccbf848d9b1..61a0639bbff1 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -5411,6 +5411,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. */