> -----Original Message-----
> From: wangjue <[email protected]>
> Sent: 10 September 2026 09:14
> To: [email protected]
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; juewang
> <[email protected]>
> Subject: [PATCH v2] vect: Look through promotions for SAT_TRUNC
> 
> From: juewang <[email protected]>
> 
> Use a single-use unpromoted input when MAX_EXPR and SAT_TRUNC are
> supported for the narrower type, avoiding a redundant widen-and-narrow
> sequence.
> 
> The test cases were generated by an LLM.
> 
> Assisted-by: GPT-6
> 
> Regtested on rv64gcv, rv32gcv, x86_64 and aarch64 with no regressions.
> 
> Changes since v1:
> 
> v2: Update the test cases for the upstream canonicalization changes.
> v1: Add vect_sat_trunc_clip_input_vectype to check target support.
>     Use the appropriate gimple_build_assign overload for MAX_EXPR.
> 

Vect parts are OK.  You need a RISCV maintainer for the tests.

Thanks,
Tamar

> gcc/ChangeLog:
> 
>       * tree-vect-patterns.cc (vect_sat_trunc_clip_input_vectype): New.
>       (vect_recog_sat_trunc_pattern): Look through promotions and check
>       target support for MAX_EXPR.
> 
> gcc/testsuite/ChangeLog:
> 
>       * gcc.target/riscv/rvv/autovec/pr120378-3.c: 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-3.c |  2 +-
>  .../gcc.target/riscv/rvv/autovec/pr120378-4.c |  2 +-
>  .../riscv/rvv/autovec/sat-trunc-promote-1.c   | 17 ++++
>  .../rvv/autovec/sat-trunc-promote-run-1.c     | 33 ++++++++
>  gcc/tree-vect-patterns.cc                     | 83 +++++++++++++++----
>  5 files changed, 119 insertions(+), 18 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-3.c
> b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-3.c
> index 5941b3b7543..c06f1423140 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 -fdump-tree-optimized" }
> */
> +/* { 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 57cca4504ba..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 -fdump-tree-optimized" }
> */
> +/* { 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..298a76461a4
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/sat-trunc-promote-1.c
> @@ -0,0 +1,17 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv64gcv -mabi=lp64d -O3 -mrvv-max-lmul=m1 -
> fno-vect-cost-model -fdump-tree-optimized" } */
> +
> +#include <stdint-gcc.h>
> +
> +__attribute__ ((noipa)) void
> +clip_scale_u8 (uint8_t *__restrict dst, const int8_t *__restrict src, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      int x = src[i] * 3;
> +      dst[i] = (unsigned) x > 255u ? (int) (-(unsigned) x) >> 31 : x;
> +    }
> +}
> +
> +/* { 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..04a7c09f07e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/sat-trunc-promote-run-1.c
> @@ -0,0 +1,33 @@
> +/* { dg-do run { target { riscv_v && rv64 } } } */
> +/* { dg-options "-march=rv64gcv -mabi=lp64d -O3 -mrvv-max-lmul=m1 -
> fno-vect-cost-model" } */
> +
> +#include "sat-trunc-promote-1.c"
> +
> +#define N 256
> +
> +int
> +main (void)
> +{
> +  int8_t input[N];
> +  uint8_t output[N];
> +
> +  for (int i = 0; i < N; ++i)
> +    input[i] = i + INT8_MIN;
> +
> +  /* Exercise every input value, empty loops and partial vectors.  */
> +  for (int n = 0; n <= N; ++n)
> +    {
> +      clip_scale_u8 (output, input, n);
> +
> +#pragma GCC novector
> +      for (int i = 0; i < n; ++i)
> +     {
> +       int x = input[i] * 3;
> +       uint8_t expected = x < 0 ? 0 : x > UINT8_MAX ? UINT8_MAX : x;
> +       if (output[i] != expected)
> +         __builtin_abort ();
> +     }
> +    }
> +
> +  return 0;
> +}
> diff --git a/gcc/tree-vect-patterns.cc b/gcc/tree-vect-patterns.cc
> index 5fcc37f053c..06159fa6ab8 100644
> --- a/gcc/tree-vect-patterns.cc
> +++ b/gcc/tree-vect-patterns.cc
> @@ -4864,6 +4864,24 @@ vect_recog_sat_sub_pattern (vec_info *vinfo,
> stmt_vec_info stmt_vinfo,
>    return NULL;
>  }
> 
> +/* Return the vector type for ITYPE if the target supports the MAX_EXPR
> +   and SAT_TRUNC sequence.  */
> +
> +static tree
> +vect_sat_trunc_clip_input_vectype (vec_info *vinfo, tree v_otype,
> +                                tree itype)
> +{
> +  tree v_itype;
> +  if (!vect_supportable_direct_optab_p
> +     (vinfo, itype, MAX_EXPR, itype, &v_itype)
> +      || !direct_internal_fn_supported_p
> +        (IFN_SAT_TRUNC, tree_pair (v_otype, v_itype),
> +         OPTIMIZE_FOR_BOTH))
> +    return NULL_TREE;
> +
> +  return v_itype;
> +}
> +
>  /*
>   * Try to detect saturation truncation pattern (SAT_TRUNC), aka below gimple:
>   *   overflow_5 = x_4(D) > 4294967295;
> @@ -4892,31 +4910,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;
> +      tree v_itype = NULL_TREE;
> 
> -      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
> +             = vect_sat_trunc_clip_input_vectype (vinfo, v_otype,
> +                                                    unprom.type);
> +           if (v_unprom_type != NULL_TREE)
> +             {
> +               input = unprom.op;
> +               itype = unprom.type;
> +               v_itype = v_unprom_type;
> +             }
> +         }
> 
> -       gcall *call = gimple_build_call_internal (fn, 1, temp);
> -       tree out_ssa = vect_recog_temp_ssa_var (otype, NULL);
> +       if (v_itype == NULL_TREE)
> +         v_itype
> +           = vect_sat_trunc_clip_input_vectype (vinfo, v_otype, itype);
> 
> -       gimple_call_set_lhs (call, out_ssa);
> -       gimple_call_set_nothrow (call, /* nothrow_p */ false);
> -       gimple_set_location (call, gimple_location (last_stmt));
> +       if (v_itype != NULL_TREE)
> +         {
> +           tree temp = vect_recog_temp_ssa_var (itype, NULL);
> +           gimple *max_stmt
> +             = gimple_build_assign (temp, MAX_EXPR,
> +                                    build_zero_cst (itype), input);
> +           append_pattern_def_seq (vinfo, stmt_vinfo, max_stmt, v_itype);
> 
> -       *type_out = v_otype;
> +           gcall *call = gimple_build_call_internal (fn, 1, temp);
> +           tree out_ssa = vect_recog_temp_ssa_var (otype, NULL);
> 
> -       return call;
> -     }
> +           gimple_call_set_lhs (call, out_ssa);
> +           gimple_call_set_nothrow (call, false);
> +           gimple_set_location (call, gimple_location (last_stmt));
> 
> +           *type_out = v_otype;
> +
> +           return call;
> +         }
> +     }
>      }
> 
>    if ((gimple_unsigned_integer_sat_trunc (lhs, ops, NULL)
> --
> 2.34.1

Reply via email to