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.
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