On Thu, Aug 27, 2026 at 10:10 AM wangjue <[email protected]> wrote:
>
> From: juewang <[email protected]>
>
> Use a single-use unpromoted input when the target can narrow it
> directly to the output type.  This avoids widening the input only to
> narrow it again.
>
> Add RISC-V tests for x264-style promoted inputs, saturation boundary
> values, and one-to-three-level unsigned narrowing.  Disable vector
> costing where the tests require vectorization independent of the
> default tune.

Can you show the former original GIMPLE and matched patterns and the
resulting optimized pattern sequence?

> gcc/ChangeLog:
>
>         * tree-vect-patterns.cc (vect_recog_sat_trunc_pattern): Look through
>         promotions.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.target/riscv/rvv/autovec/pr120378-2.c: Dump optimized GIMPLE.
>         * gcc.target/riscv/rvv/autovec/pr120378-3.c: Likewise.  Disable the
>         vector cost model.
>         * gcc.target/riscv/rvv/autovec/pr120378-4.c: Likewise.
>         * gcc.target/riscv/rvv/autovec/sat-trunc-promote-1.c: New test.
>         * gcc.target/riscv/rvv/autovec/sat-trunc-promote-run-1.c: New test.
> ---
>  .../gcc.target/riscv/rvv/autovec/pr120378-2.c |  2 +-
>  .../gcc.target/riscv/rvv/autovec/pr120378-3.c |  2 +-
>  .../gcc.target/riscv/rvv/autovec/pr120378-4.c |  2 +-
>  .../riscv/rvv/autovec/sat-trunc-promote-1.c   | 25 +++++++
>  .../rvv/autovec/sat-trunc-promote-run-1.c     | 45 +++++++++++++
>  gcc/tree-vect-patterns.cc                     | 65 ++++++++++++++-----
>  6 files changed, 122 insertions(+), 19 deletions(-)
>  create mode 100644 
> gcc/testsuite/gcc.target/riscv/rvv/autovec/sat-trunc-promote-1.c
>  create mode 100644 
> gcc/testsuite/gcc.target/riscv/rvv/autovec/sat-trunc-promote-run-1.c
>
> diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-2.c 
> b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-2.c
> index 9a880e0f67b..2208e954242 100644
> --- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-2.c
> +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-2.c
> @@ -1,5 +1,5 @@
>  /* { dg-do compile } */
> -/* { dg-options "-march=rv64gcv -mabi=lp64d -O3" } */
> +/* { dg-options "-march=rv64gcv -mabi=lp64d -O3 -fdump-tree-optimized" } */
>
>  #include <stdint.h>
>
> diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-3.c 
> b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-3.c
> index d6b2ddd3f8d..a71f8bfa19a 100644
> --- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-3.c
> +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-3.c
> @@ -1,5 +1,5 @@
>  /* { dg-do compile } */
> -/* { dg-options "-march=rv64gcv -mabi=lp64d -O3" } */
> +/* { dg-options "-march=rv64gcv -mabi=lp64d -O3 -fno-vect-cost-model 
> -fdump-tree-optimized" } */
>
>  #include <stdint.h>
>
> diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-4.c 
> b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-4.c
> index 4657e52f883..bc9a55e8b07 100644
> --- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-4.c
> +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-4.c
> @@ -1,5 +1,5 @@
>  /* { dg-do compile } */
> -/* { dg-options "-march=rv64gcv -mabi=lp64d -O3" } */
> +/* { dg-options "-march=rv64gcv -mabi=lp64d -O3 -fno-vect-cost-model 
> -fdump-tree-optimized" } */
>
>  #include <stdint.h>
>
> diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/sat-trunc-promote-1.c 
> b/gcc/testsuite/gcc.target/riscv/rvv/autovec/sat-trunc-promote-1.c
> new file mode 100644
> index 00000000000..00768de993c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/sat-trunc-promote-1.c
> @@ -0,0 +1,25 @@
> +/* { dg-do compile { target { rv64 } } } */
> +/* { dg-options "-march=rv64gcv -mabi=lp64d -O3 -mrvv-max-lmul=m1 
> -fno-vect-cost-model -fdump-tree-optimized" } */
> +
> +#include <stdint-gcc.h>
> +
> +static inline uint8_t
> +clip_uint8 (int x)
> +{
> +  return x & ~255 ? (-x) >> 31 : x;
> +}
> +
> +__attribute__ ((noipa)) void
> +hpel_clip (uint8_t *__restrict dst, const uint8_t *__restrict src, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      int value = (src[i - 2] + src[i + 3]
> +                  - 5 * (src[i - 1] + src[i + 2])
> +                  + 20 * (src[i] + src[i + 1]));
> +      dst[i] = clip_uint8 ((value + 16) >> 5);
> +    }
> +}
> +
> +/* { dg-final { scan-tree-dump-times {\.SAT_TRUNC } 1 "optimized" } } */
> +/* { dg-final { scan-assembler-times {\tvnclipu\.wi} 1 } } */
> diff --git 
> a/gcc/testsuite/gcc.target/riscv/rvv/autovec/sat-trunc-promote-run-1.c 
> b/gcc/testsuite/gcc.target/riscv/rvv/autovec/sat-trunc-promote-run-1.c
> new file mode 100644
> index 00000000000..6249defff38
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/sat-trunc-promote-run-1.c
> @@ -0,0 +1,45 @@
> +/* { dg-do run { target { riscv_v && rv64 } } } */
> +/* { dg-options "-march=rv64gcv -mabi=lp64d -O3 -mrvv-max-lmul=m1 
> -fno-vect-cost-model" } */
> +
> +#include <stdint-gcc.h>
> +
> +#define N 128
> +
> +static inline uint8_t
> +clip_uint8 (int x)
> +{
> +  return x & ~255 ? (-x) >> 31 : x;
> +}
> +
> +__attribute__ ((noipa)) static void
> +clip_loop (uint8_t *__restrict dst, const int16_t *__restrict src, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      int x = (src[i] >> 8) * 256 + (src[i] & 255);
> +      dst[i] = clip_uint8 (x);
> +    }
> +}
> +
> +int
> +main (void)
> +{
> +  static const int16_t values[] = {
> +    INT16_MIN, -1, 0, 255, 256, INT16_MAX
> +  };
> +  static const uint8_t expected[] = { 0, 0, 0, 255, 255, 255 };
> +  int16_t input[N];
> +  uint8_t output[N];
> +
> +  for (int i = 0; i < N; ++i)
> +    input[i] = values[i % 6];
> +
> +  clip_loop (output, input, N);
> +
> +#pragma GCC novector
> +  for (int i = 0; i < N; ++i)
> +    if (output[i] != expected[i % 6])
> +      __builtin_abort ();
> +
> +  return 0;
> +}
> diff --git a/gcc/tree-vect-patterns.cc b/gcc/tree-vect-patterns.cc
> index 406f81b95c8..c7c5f80c4e9 100644
> --- a/gcc/tree-vect-patterns.cc
> +++ b/gcc/tree-vect-patterns.cc
> @@ -4843,31 +4843,64 @@ vect_recog_sat_trunc_pattern (vec_info *vinfo, 
> stmt_vec_info stmt_vinfo,
>    if ((gimple_unsigned_integer_narrow_clip (lhs, ops, NULL))
>         && type_has_mode_precision_p (otype))
>      {
> -      tree itype = TREE_TYPE (ops[0]);
> -      tree v_itype = get_vectype_for_scalar_type (vinfo, itype);
> +      tree input = ops[0];
> +      tree itype = TREE_TYPE (input);
>        tree v_otype = get_vectype_for_scalar_type (vinfo, otype);
>        internal_fn fn = IFN_SAT_TRUNC;
>
> -      if (v_itype != NULL_TREE && v_otype != NULL_TREE
> -       && direct_internal_fn_supported_p (fn, tree_pair (v_otype, v_itype),
> -                                          OPTIMIZE_FOR_BOTH))
> +      if (v_otype != NULL_TREE)
>         {
> -         tree temp = vect_recog_temp_ssa_var (itype, NULL);
> -         gimple * max_stmt = gimple_build_assign (temp, build2 (MAX_EXPR, 
> itype, build_zero_cst(itype), ops[0]));
> -         append_pattern_def_seq (vinfo, stmt_vinfo, max_stmt, v_itype);
> +         /* Avoid widening an input only to narrow it again.  A signed
> +            promotion does not affect MAX (X, 0), so use the unpromoted
> +            value when the target can narrow it directly to the output
> +            type.  */
> +         vect_unpromoted_value unprom;
> +         bool single_use_p = true;
> +         if (vect_look_through_possible_promotion (vinfo, input, &unprom,
> +                                                     &single_use_p)
> +             && single_use_p
> +             && INTEGRAL_TYPE_P (unprom.type)
> +             && TYPE_SIGN (unprom.type) == TYPE_SIGN (itype)
> +             && type_has_mode_precision_p (unprom.type)
> +             && TYPE_PRECISION (otype) < TYPE_PRECISION (unprom.type)
> +             && TYPE_PRECISION (unprom.type) < TYPE_PRECISION (itype))
> +           {
> +             tree v_unprom_type
> +               = get_vectype_for_scalar_type (vinfo, unprom.type);
> +             if (v_unprom_type != NULL_TREE
> +                 && direct_internal_fn_supported_p
> +                      (fn, tree_pair (v_otype, v_unprom_type),
> +                       OPTIMIZE_FOR_BOTH))
> +               {
> +                 input = unprom.op;
> +                 itype = unprom.type;
> +               }
> +           }
>
> -         gcall *call = gimple_build_call_internal (fn, 1, temp);
> -         tree out_ssa = vect_recog_temp_ssa_var (otype, NULL);
> +         tree v_itype = get_vectype_for_scalar_type (vinfo, itype);
> +         if (v_itype != NULL_TREE
> +             && direct_internal_fn_supported_p
> +                  (fn, tree_pair (v_otype, v_itype), OPTIMIZE_FOR_BOTH))
> +           {
> +             tree temp = vect_recog_temp_ssa_var (itype, NULL);
> +             gimple *max_stmt
> +               = gimple_build_assign (temp,
> +                                      build2 (MAX_EXPR, itype,
> +                                              build_zero_cst (itype), 
> input));

Please do not build a GENERIC MAX_EXPR, instead use the appropriate
gimple_build_assign overload (this also suggests this was LLM generated).

Also I fail to see a check that the generated MAX_EXPR is supported by
the target?

> +             append_pattern_def_seq (vinfo, stmt_vinfo, max_stmt, v_itype);
>
> -         gimple_call_set_lhs (call, out_ssa);
> -         gimple_call_set_nothrow (call, /* nothrow_p */ false);
> -         gimple_set_location (call, gimple_location (last_stmt));
> +             gcall *call = gimple_build_call_internal (fn, 1, temp);
> +             tree out_ssa = vect_recog_temp_ssa_var (otype, NULL);
>
> -         *type_out = v_otype;
> +             gimple_call_set_lhs (call, out_ssa);
> +             gimple_call_set_nothrow (call, false);
> +             gimple_set_location (call, gimple_location (last_stmt));
>
> -         return call;
> -       }
> +             *type_out = v_otype;
>
> +             return call;
> +           }
> +       }
>      }
>
>    if ((gimple_unsigned_integer_sat_trunc (lhs, ops, NULL)
> --
> 2.34.1
>

Reply via email to