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
>

Reply via email to