On Tue, Jul 21, 2026 at 10:56 AM Naveen
<[email protected]> wrote:
>
> 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.
>
> 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 | 146 +++++++++++++++++++
> 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 +++---
> 4 files changed, 210 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 017b1362b7e..1795963b0c6 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -6822,6 +6822,152 @@ 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 for
> + negative C and X < C to X <= C - 1 for positive C. */
> +(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
> + {
> + bool ok = true;
> +
> + if (!known_eq (VECTOR_CST_NELTS (@1), VECTOR_CST_NELTS (@2)))
> + ok = false;
> + else
> + {
> + unsigned HOST_WIDE_INT nelts;
> + if (VECTOR_CST_NELTS (@1).is_constant (&nelts))
> + {
> + for (unsigned HOST_WIDE_INT i = 0; i < nelts; ++i)
> + {
> + tree cmp_cst = vector_cst_elt (@1, i);
> + tree res_cst = vector_cst_elt (@2, i);
> + if (TREE_CODE (cmp_cst) != INTEGER_CST
> + || TREE_CODE (res_cst) != INTEGER_CST
> + || tree_int_cst_sgn (res_cst)
> + != (minmax == MAX_EXPR ? -1 : 1)
> + || !wi::eq_p (wi::to_wide (cmp_cst),
> + wi::to_wide (res_cst)
> + + (minmax == MAX_EXPR ? 1 : -1)))
> + {
> + ok = false;
> + break;
> + }
> + }
> + }
> + else
> + {
> + /* For variable-length vectors, handle matching encodings and
> + check the encoded elements. */
I think this path should work for the constant number of elements case as well.
But can we split this out to a tree.{cc,h} helper? Maybe
tree uniform_vector_difference_p (const_tree, const_tree)?
Thanks,
Richard.
> + if (VECTOR_CST_LOG2_NPATTERNS (@1)
> + != VECTOR_CST_LOG2_NPATTERNS (@2)
> + || VECTOR_CST_NELTS_PER_PATTERN (@1)
> + != VECTOR_CST_NELTS_PER_PATTERN (@2)
> + || vector_cst_encoded_nelts (@1)
> + != vector_cst_encoded_nelts (@2))
> + ok = false;
> + else
> + {
> + unsigned int encoded_nelts = vector_cst_encoded_nelts (@1);
> + for (unsigned int i = 0; i < encoded_nelts; ++i)
> + {
> + tree cmp_cst = VECTOR_CST_ENCODED_ELT (@1, i);
> + tree res_cst = VECTOR_CST_ENCODED_ELT (@2, i);
> + if (TREE_CODE (cmp_cst) != INTEGER_CST
> + || TREE_CODE (res_cst) != INTEGER_CST
> + || tree_int_cst_sgn (res_cst)
> + != (minmax == MAX_EXPR ? -1 : 1)
> + || !wi::eq_p (wi::to_wide (cmp_cst),
> + wi::to_wide (res_cst)
> + + (minmax == MAX_EXPR ? 1 : -1)))
> + {
> + ok = false;
> + break;
> + }
> + }
> + }
> + }
> + }
> + }
> + (if (VECTOR_INTEGER_TYPE_P (type)
> + && target_supports_op_p (type, MINMAX, optab_vector)
> + && ok)
> + (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
> + {
> + bool ok = true;
> +
> + if (!known_eq (VECTOR_CST_NELTS (@1), VECTOR_CST_NELTS (@2)))
> + ok = false;
> + else
> + {
> + unsigned HOST_WIDE_INT nelts;
> + if (VECTOR_CST_NELTS (@1).is_constant (&nelts))
> + {
> + for (unsigned HOST_WIDE_INT i = 0; i < nelts; ++i)
> + {
> + tree cmp_cst = vector_cst_elt (@1, i);
> + tree res_cst = vector_cst_elt (@2, i);
> + if (TREE_CODE (cmp_cst) != INTEGER_CST
> + || TREE_CODE (res_cst) != INTEGER_CST
> + || tree_int_cst_sgn (res_cst)
> + != (minmax == MAX_EXPR ? -1 : 1)
> + || !wi::eq_p (wi::to_wide (cmp_cst),
> + wi::to_wide (res_cst)
> + + (minmax == MAX_EXPR ? 1 : -1)))
> + {
> + ok = false;
> + break;
> + }
> + }
> + }
> + else
> + {
> + /* For variable-length vectors, handle matching encodings and
> + check the encoded elements. */
> + if (VECTOR_CST_LOG2_NPATTERNS (@1)
> + != VECTOR_CST_LOG2_NPATTERNS (@2)
> + || VECTOR_CST_NELTS_PER_PATTERN (@1)
> + != VECTOR_CST_NELTS_PER_PATTERN (@2)
> + || vector_cst_encoded_nelts (@1)
> + != vector_cst_encoded_nelts (@2))
> + ok = false;
> + else
> + {
> + unsigned int encoded_nelts = vector_cst_encoded_nelts (@1);
> + for (unsigned int i = 0; i < encoded_nelts; ++i)
> + {
> + tree cmp_cst = VECTOR_CST_ENCODED_ELT (@1, i);
> + tree res_cst = VECTOR_CST_ENCODED_ELT (@2, i);
> + if (TREE_CODE (cmp_cst) != INTEGER_CST
> + || TREE_CODE (res_cst) != INTEGER_CST
> + || tree_int_cst_sgn (res_cst)
> + != (minmax == MAX_EXPR ? -1 : 1)
> + || !wi::eq_p (wi::to_wide (cmp_cst),
> + wi::to_wide (res_cst)
> + + (minmax == MAX_EXPR ? 1 : -1)))
> + {
> + ok = false;
> + break;
> + }
> + }
> + }
> + }
> + }
> + }
> + (if (VECTOR_INTEGER_TYPE_P (type)
> + && target_supports_op_p (type, MINMAX, optab_vector)
> + && ok)
> + (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 } } */
> --
> 2.34.1
>