https://gcc.gnu.org/g:19378ecd3851564bb168dd7779cb52c79193cb2d
commit r17-4697-g19378ecd3851564bb168dd7779cb52c79193cb2d Author: wangjue <[email protected]> Date: Sun Sep 27 11:34:07 2026 -0600 [PATCH v2] vect: Look through promotions for SAT_TRUNC 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. 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. Diff: --- .../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 +++++ .../riscv/rvv/autovec/sat-trunc-promote-run-1.c | 33 +++++++++ gcc/tree-vect-patterns.cc | 83 +++++++++++++++++----- 5 files changed, 119 insertions(+), 18 deletions(-) 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 5941b3b75436..c06f1423140f 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 57cca4504ba2..bc9a55e8b07c 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 000000000000..298a76461a4f --- /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 000000000000..04a7c09f07ec --- /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 5fcc37f053c7..06159fa6ab8f 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)
