https://gcc.gnu.org/g:097cd0ceee61aa4b5100931341e9c8642f060c0a
commit r17-3844-g097cd0ceee61aa4b5100931341e9c8642f060c0a Author: Kyrylo Tkachov <[email protected]> Date: Mon Aug 31 22:19:25 2026 +0200 tree-optimization: Tighten unsigned narrow clip matching The unsigned_integer_narrow_clip pattern assumes that its comparison is unsigned and its right shift is signed. nop_convert? does not require an unsigned conversion. A signed comparison can preserve negative inputs. An unsigned right shift produces zero or one instead of the all-ones clip value. Capture both expressions and check their result types before applying the transform. The tests cover each rejected form. The unsigned-shift test constrains the input range so that the existing INT_MIN guard does not reject it first. Bootstrapped and tested on aarch64-none-linux-gnu. gcc/ChangeLog: * match-sat-alu.pd (unsigned_integer_narrow_clip): Require an unsigned comparison and a signed right shift. gcc/testsuite/ChangeLog: * gcc.dg/vect/pr127166-2.c: New test. * gcc.target/riscv/rvv/autovec/pr127166.c: New test. Signed-off-by: Kyrylo Tkachov <[email protected]> Diff: --- gcc/match-sat-alu.pd | 13 ++++-- gcc/testsuite/gcc.dg/vect/pr127166-2.c | 44 ++++++++++++++++++ .../gcc.target/riscv/rvv/autovec/pr127166.c | 53 ++++++++++++++++++++++ 3 files changed, 105 insertions(+), 5 deletions(-) diff --git a/gcc/match-sat-alu.pd b/gcc/match-sat-alu.pd index 7bbe2bbb26ef..22855726d90a 100644 --- a/gcc/match-sat-alu.pd +++ b/gcc/match-sat-alu.pd @@ -207,16 +207,19 @@ along with GCC; see the file COPYING3. If not see /* SAT_U_TRUNC<NT>(MAX (0, X)) = (UT)X & ~(NT)(-1) ? (-X) >> TYPE_PRECISION(X) - 1 : X - The gimple representation uses X > ~(NT)(-1) instead of - using & so match on gt instead of bit_and. + The gimple representation uses an unsigned X > ~(NT)(-1) instead of + using & so match on gt instead of bit_and. The right shift must be + signed so that it produces -1 for values above NT_MAX. The two sides only agree when X is not the minimum of its type. At that value the negation is its own inverse, so the shift yields -1 and the expression gives NT_MAX where MAX (0, X) gives 0. */ - (convert (cond^ (gt (nop_convert? @0) INTEGER_CST@1) - (rshift:s (nop_convert? (negate (nop_convert? @0))) INTEGER_CST@2) + (convert (cond^ (gt (nop_convert?@3 @0) INTEGER_CST@1) + (rshift:s@4 (nop_convert? (negate (nop_convert? @0))) INTEGER_CST@2) @0)) - (if (! TYPE_UNSIGNED (TREE_TYPE (@0))) + (if (! TYPE_UNSIGNED (TREE_TYPE (@0)) + && TYPE_UNSIGNED (TREE_TYPE (@3)) + && ! TYPE_UNSIGNED (TREE_TYPE (@4))) (with { unsigned itype_precision = TYPE_PRECISION (TREE_TYPE (@0)); diff --git a/gcc/testsuite/gcc.dg/vect/pr127166-2.c b/gcc/testsuite/gcc.dg/vect/pr127166-2.c new file mode 100644 index 000000000000..9e48c44d06e4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/vect/pr127166-2.c @@ -0,0 +1,44 @@ +/* PR tree-optimization/127166 */ +/* { dg-require-effective-target int32 } */ +/* { dg-require-effective-target vect_int } */ + +#include "tree-vect.h" + +#define N 64 + +__attribute__ ((noipa)) void +clip (unsigned short *__restrict out, const int *__restrict in, int n) +{ + for (int i = 0; i < n; ++i) + { + int x = in[i] & __INT_MAX__; + out[i] = ((unsigned int) x > 65535u + ? (unsigned int) (-x) >> 31 : x); + } +} + +int +main (void) +{ + int in[N]; + unsigned short out[N]; + + check_vect (); + + for (int i = 0; i < N; ++i) + in[i] = (i & 1) ? -1 : 70000 + i; + + clip (out, in, N); + +#pragma GCC novector + for (int i = 0; i < N; ++i) + { + if (out[i] != 1) + abort (); + } + + return 0; +} + +/* The unsigned right shift does not produce the all-ones clip value. */ +/* { dg-final { scan-tree-dump-not "\\.SAT_TRUNC" "vect" } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr127166.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr127166.c new file mode 100644 index 000000000000..30a43ad64ed5 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr127166.c @@ -0,0 +1,53 @@ +/* PR tree-optimization/127166 */ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d -O2 -fgimple" } */ +/* { dg-additional-options "-fdump-tree-vect-details" } */ + +void __GIMPLE (ssa, startwith ("fix_loops")) +clip_signed_compare (unsigned short *out, const int *in) +{ + int i; + int raw; + int x; + int neg; + int shifted; + int selected; + unsigned short narrowed; + _Bool cmp; + const int *pin; + const int *next_pin; + unsigned short *pout; + unsigned short *next_pout; + +__BB(2): + goto __BB3; + +__BB(3): + i_1 = __PHI (__BB2: 0, __BB4: i_2); + pin_3 = __PHI (__BB2: in_4(D), __BB4: next_pin_5); + pout_6 = __PHI (__BB2: out_7(D), __BB4: next_pout_8); + raw_15 = __MEM <const int> (pin_3); + x_9 = raw_15 | 1; + cmp_10 = x_9 > 65535; + neg_11 = -x_9; + shifted_12 = neg_11 >> 31; + selected_13 = cmp_10 ? shifted_12 : x_9; + narrowed_14 = (unsigned short) selected_13; + __MEM <unsigned short> (pout_6) = narrowed_14; + next_pin_5 = pin_3 + 4ul; + next_pout_8 = pout_6 + 2ul; + i_2 = i_1 + 1; + if (i_2 != 64) + goto __BB4; + else + goto __BB5; + +__BB(4): + goto __BB3; + +__BB(5): + return; +} + +/* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect" } } */ +/* { dg-final { scan-tree-dump-not "\\.SAT_TRUNC" "vect" } } */
