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" } } */

Reply via email to