> On 1 Sep 2026, at 09:05, Richard Biener <[email protected]> wrote: > > On Tue, Sep 1, 2026 at 7:53 AM <[email protected]> wrote: >> >> From: Kyrylo Tkachov <[email protected]> >> >> The PR120378 RISC-V tests use this source form: >> >> (NT) ((UT) x > NT_MAX ? (-x) >> (PREC - 1) : x) >> >> For every defined execution, it is an unsigned saturating truncation of >> MAX (x, 0). The signed negation is undefined at the signed minimum >> under the default overflow rules. >> >> Loop if-conversion changes the selected negation to unsigned arithmetic. >> This loses the distinction between the signed source and the explicit >> unsigned-negation form from PR126981, which must not become a saturating >> truncation. >> >> Canonicalize the signed form before gimplification, while its overflow >> semantics are still present: >> >> (NT) MIN ((UT) MAX (x, 0), NT_MAX) > > What's the reason to do this only for GENERIC? Once you split > the expression and use some temporaries the canonicalization > will no longer work on GENERIC but would work fine during early > GIMPLE optimization, before if-conversion wrecks it.
I guess I was trying to be conservative for the test cases in particular, but doing it unconditionally works as well. I’ve sent out an updated version. Thanks, Kyrill > >> Require an unsigned comparison, a signed right shift, undefined and >> unsanitized negation overflow, exact constants, no side effects, and >> enabled optimization. This keeps the original form with -O0, -fwrapv, >> -ftrapv, sanitization, volatile operands, and explicit unsigned negation. >> >> The tests cover these boundaries and restore the RISC-V saturation >> patterns. They also enable three optimized dumps that the existing >> tests scan. Update pr103771.c because the same transformation now >> happens before phiopt. >> >> Bootstrapped and tested on aarch64-none-linux-gnu. >> Ok for trunk? >> Thanks, >> Kyrill >> >> gcc/ChangeLog: >> >> PR tree-optimization/127166 >> * match.pd: Canonicalize conditional signed narrow clips before >> gimplification. >> >> gcc/testsuite/ChangeLog: >> >> PR tree-optimization/127166 >> * gcc.dg/tree-ssa/pr103771.c: Check the earlier canonicalization. >> * gcc.dg/tree-ssa/pr127166.c: New test. >> * gcc.dg/tree-ssa/pr127166-2.c: New test. >> * gcc.dg/tree-ssa/pr127166-3.c: New test. >> * gcc.dg/vect/pr127166.c: New test. >> * gcc.target/riscv/rvv/autovec/pr120378-1.c: Update expected dump >> counts. >> * gcc.target/riscv/rvv/autovec/pr120378-2.c: Request an optimized >> dump. >> * gcc.target/riscv/rvv/autovec/pr120378-3.c: Likewise. Update >> expected dump counts. >> * gcc.target/riscv/rvv/autovec/pr120378-4.c: Request an optimized >> dump. >> >> Signed-off-by: Kyrylo Tkachov <[email protected]> >> --- >> gcc/match.pd | 40 +++++++++++++++ >> gcc/testsuite/gcc.dg/tree-ssa/pr103771.c | 5 +- >> gcc/testsuite/gcc.dg/tree-ssa/pr127166-2.c | 13 +++++ >> gcc/testsuite/gcc.dg/tree-ssa/pr127166-3.c | 15 ++++++ >> gcc/testsuite/gcc.dg/tree-ssa/pr127166.c | 30 +++++++++++ >> gcc/testsuite/gcc.dg/vect/pr127166.c | 50 +++++++++++++++++++ >> .../gcc.target/riscv/rvv/autovec/pr120378-1.c | 4 +- >> .../gcc.target/riscv/rvv/autovec/pr120378-2.c | 2 +- >> .../gcc.target/riscv/rvv/autovec/pr120378-3.c | 6 +-- >> .../gcc.target/riscv/rvv/autovec/pr120378-4.c | 2 +- >> 10 files changed, 158 insertions(+), 9 deletions(-) >> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr127166-2.c >> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr127166-3.c >> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr127166.c >> create mode 100644 gcc/testsuite/gcc.dg/vect/pr127166.c >> >> diff --git a/gcc/match.pd b/gcc/match.pd >> index 5777c833896..263bb93159b 100644 >> --- a/gcc/match.pd >> +++ b/gcc/match.pd >> @@ -13372,6 +13372,46 @@ and, >> (BUILT_IN_CONSTANT_P (nop_convert@1 @0)) >> (BUILT_IN_CONSTANT_P @0)) >> >> +#if GENERIC >> +/* Canonicalize a conditional signed narrow clip while the signed negation >> + has its original overflow semantics: >> + >> + (NT) ((UT) X > NT_MAX ? (-X) >> (PREC - 1) : X) >> + -> (NT) MIN ((UT) MAX (X, 0), NT_MAX). */ >> +(if (INTEGRAL_TYPE_P (type) && TYPE_UNSIGNED (type)) >> + (simplify >> + (cond (gt (nop_convert@4 @0) INTEGER_CST@1) >> + (convert (rshift@5 >> + (nop_convert? (negate@3 (nop_convert? @0))) >> + INTEGER_CST@2)) >> + (convert @0)) >> + (if (optimize >> + && !TREE_SIDE_EFFECTS (@0) >> + && !TYPE_UNSIGNED (TREE_TYPE (@0)) >> + && TYPE_UNSIGNED (TREE_TYPE (@4)) >> + && !TYPE_UNSIGNED (TREE_TYPE (@5)) >> + && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@3)) >> + && !TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@3))) >> + (with >> + { >> + tree utype = TREE_TYPE (@4); >> + unsigned itype_precision = TYPE_PRECISION (TREE_TYPE (@0)); >> + unsigned otype_precision = TYPE_PRECISION (type); >> + wide_int trunc_max = wi::mask (otype_precision, false, >> itype_precision); >> + wide_int int_cst_1 = wi::to_wide (@1, itype_precision); >> + wide_int int_cst_2 = wi::to_wide (@2, itype_precision); >> + wide_int shift_amount = wi::uhwi (itype_precision - 1, >> + itype_precision); >> + } >> + (if (otype_precision < itype_precision >> + && wi::eq_p (trunc_max, int_cst_1) >> + && wi::eq_p (int_cst_2, shift_amount)) >> + (convert (min:utype >> + (convert:utype >> + (max @0 { build_zero_cst (TREE_TYPE (@0)); })) >> + @1))))))) >> +#endif >> + >> #if GIMPLE >> >> /* Include the saturation alu match patterns. */ >> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr103771.c >> b/gcc/testsuite/gcc.dg/tree-ssa/pr103771.c >> index 8061e2df79e..b3dde923281 100644 >> --- a/gcc/testsuite/gcc.dg/tree-ssa/pr103771.c >> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr103771.c >> @@ -1,6 +1,7 @@ >> /* { dg-do compile } */ >> -/* { dg-options "-O3 -fdump-tree-phiopt1-details" } */ >> -/* { dg-final { scan-tree-dump-times "changed to factor operation out from >> COND_EXPR." 1 "phiopt1" } } */ >> +/* { dg-options "-O3 -fdump-tree-gimple" } */ >> +/* { dg-final { scan-tree-dump-times "MAX_EXPR" 1 "gimple" } } */ >> +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "gimple" } } */ >> >> typedef unsigned char uint8_t; >> >> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr127166-2.c >> b/gcc/testsuite/gcc.dg/tree-ssa/pr127166-2.c >> new file mode 100644 >> index 00000000000..9f48c5b79aa >> --- /dev/null >> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr127166-2.c >> @@ -0,0 +1,13 @@ >> +/* PR tree-optimization/127166 */ >> +/* { dg-do compile } */ >> +/* { dg-require-effective-target int32 } */ >> +/* { dg-options "-O0 -fdump-tree-gimple" } */ >> + >> +unsigned short >> +f (int x) >> +{ >> + return ((unsigned int) x > 65535u ? (-x) >> 31 : x); >> +} >> + >> +/* Keep the source control flow when optimization is disabled. */ >> +/* { dg-final { scan-tree-dump-not "MAX_EXPR|MIN_EXPR" "gimple" } } */ >> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr127166-3.c >> b/gcc/testsuite/gcc.dg/tree-ssa/pr127166-3.c >> new file mode 100644 >> index 00000000000..266b0aac75a >> --- /dev/null >> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr127166-3.c >> @@ -0,0 +1,15 @@ >> +/* PR tree-optimization/127166 */ >> +/* { dg-do compile } */ >> +/* { dg-require-effective-target int32 } */ >> +/* { dg-options "-O2 -fsanitize=signed-integer-overflow" } */ >> +/* { dg-additional-options "-fdump-tree-optimized" } */ >> + >> +unsigned short >> +f (int x) >> +{ >> + return ((unsigned int) x > 65535u ? (-x) >> 31 : x); >> +} >> + >> +/* Keep the conditional signed negation so that it is checked at runtime. >> */ >> +/* { dg-final { scan-tree-dump-not "MAX_EXPR|MIN_EXPR" "optimized" } } */ >> +/* { dg-final { scan-tree-dump {\.UBSAN_CHECK_SUB} "optimized" } } */ >> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr127166.c >> b/gcc/testsuite/gcc.dg/tree-ssa/pr127166.c >> new file mode 100644 >> index 00000000000..9649fc2d565 >> --- /dev/null >> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr127166.c >> @@ -0,0 +1,30 @@ >> +/* PR tree-optimization/127166 */ >> +/* { dg-do compile } */ >> +/* { dg-require-effective-target int32 } */ >> +/* { dg-options "-O2 -fdump-tree-gimple" } */ >> + >> +unsigned short >> +signed_clip (int x) >> +{ >> + return ((unsigned int) x > 65535u ? (-x) >> 31 : x); >> +} >> + >> +unsigned short >> +unsigned_clip (int x) >> +{ >> + return ((unsigned int) x > 65535u >> + ? (int) (-(unsigned int) x) >> 31 : x); >> +} >> + >> +volatile int v; >> + >> +unsigned short >> +volatile_clip (void) >> +{ >> + return ((unsigned int) v > 65535u ? (-v) >> 31 : v); >> +} >> + >> +/* Only the side-effect-free conditional signed negation can become a narrow >> + clip. */ >> +/* { dg-final { scan-tree-dump-times "MAX_EXPR" 1 "gimple" } } */ >> +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "gimple" } } */ >> diff --git a/gcc/testsuite/gcc.dg/vect/pr127166.c >> b/gcc/testsuite/gcc.dg/vect/pr127166.c >> new file mode 100644 >> index 00000000000..5bc59ad3769 >> --- /dev/null >> +++ b/gcc/testsuite/gcc.dg/vect/pr127166.c >> @@ -0,0 +1,50 @@ >> +/* PR tree-optimization/127166 */ >> +/* { dg-additional-options "-fwrapv" } */ >> +/* { 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]; >> + out[i] = ((unsigned int) x > 65535u ? (-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 & 3) == 0 ? (-__INT_MAX__ - 1) : i * 12345 - 30000; >> + >> + clip (out, in, N); >> + >> +#pragma GCC novector >> + for (int i = 0; i < N; ++i) >> + { >> + int x = in[i]; >> + unsigned short ref; >> + >> + if (x == (-__INT_MAX__ - 1) || x > 65535) >> + ref = 65535; >> + else if (x < 0) >> + ref = 0; >> + else >> + ref = x; >> + if (out[i] != ref) >> + abort (); >> + } >> + >> + return 0; >> +} >> diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-1.c >> b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-1.c >> index 54f25e0ceaa..f5a9acec858 100644 >> --- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-1.c >> +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-1.c >> @@ -16,6 +16,6 @@ clip_loop (uint8_t *res, int *x, int w) >> res[i] = clip_uint8 (x[i]); >> } >> >> -/* { dg-final { scan-tree-dump-times ".SAT_TRUNC " 1 "optimized" } } */ >> -/* { dg-final { scan-tree-dump-times "MAX_EXPR " 1 "optimized" } } */ >> +/* { dg-final { scan-tree-dump-times ".SAT_TRUNC " 2 "optimized" } } */ >> +/* { dg-final { scan-tree-dump-times "MAX_EXPR " 2 "optimized" } } */ >> /* { dg-final { scan-assembler-times {vnclipu\.wi} 2 } } */ >> diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-2.c >> b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-2.c >> index 9a880e0f67b..2208e954242 100644 >> --- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-2.c >> +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr120378-2.c >> @@ -1,5 +1,5 @@ >> /* { dg-do compile } */ >> -/* { dg-options "-march=rv64gcv -mabi=lp64d -O3" } */ >> +/* { dg-options "-march=rv64gcv -mabi=lp64d -O3 -fdump-tree-optimized" } */ >> >> #include <stdint.h> >> >> 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 d6b2ddd3f8d..5941b3b7543 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" } */ >> +/* { dg-options "-march=rv64gcv -mabi=lp64d -O3 -fdump-tree-optimized" } */ >> >> #include <stdint.h> >> >> @@ -16,6 +16,6 @@ clip_loop (uint8_t *res, int64_t *x, int w) >> res[i] = clip_uint8 (x[i]); >> } >> >> -/* { dg-final { scan-tree-dump-times ".SAT_TRUNC " 1 "optimized" } } */ >> -/* { dg-final { scan-tree-dump-times "MAX_EXPR " 1 "optimized" } } */ >> +/* { dg-final { scan-tree-dump-times ".SAT_TRUNC " 2 "optimized" } } */ >> +/* { dg-final { scan-tree-dump-times "MAX_EXPR " 2 "optimized" } } */ >> /* { dg-final { scan-assembler-times {vnclipu\.wi} 3 } } */ >> 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 4657e52f883..57cca4504ba 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" } */ >> +/* { dg-options "-march=rv64gcv -mabi=lp64d -O3 -fdump-tree-optimized" } */ >> >> #include <stdint.h> >> >> -- >> 2.50.1 (Apple Git-155)
