On Wed, 13 May 2026, Raphael Moreira Zinsly wrote:
> This is a follow up of
> https://gcc.gnu.org/pipermail/gcc-patches/2025-June/687919.html
> I had it in my queue but due to other priorities it got postponed
>
> -- >8 --
>
> This patch allows us to use the "splat the sign bit" idiom to
> efficiently select between 0 and 2^n-1.
> With that we can avoid branches and we got a shorter sequence than
> using conditional-zero instructions.
>
> The PLUS 2^n-1 sequence appears particularly for signed division
> by a power of two.
>
> gcc/ChangeLog:
> * match.pd
> (A < 0 ? ARG1 OP 2^n-1 : ARG1): New pattern.
> (A < 0 ? ARG0 : ARG0 OP 2^n-1): New pattern.
>
> gcc/testsuite/ChangeLog:
> * gcc.dg/tree-ssa/phi-opt-49.c: New test.
> * gcc.target/sh/pr59533-1.c: Fix expected output.
> ---
> gcc/match.pd | 15 +++++++++
> gcc/testsuite/gcc.dg/tree-ssa/phi-opt-49.c | 36 ++++++++++++++++++++++
> gcc/testsuite/gcc.target/sh/pr59533-1.c | 12 ++++----
> 3 files changed, 57 insertions(+), 6 deletions(-)
> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phi-opt-49.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index b037b1a2876..f32f53d910e 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -12099,3 +12099,18 @@ and,
> (if (INTEGRAL_TYPE_P (type))
> (with { tree itype = TREE_TYPE (@2); }
> (convert (minus @2 (convert:itype @1))))))
> +
> +/* Simplify (a < 0) ? ARG1 OP CONST : ARG1 to ((a < 0) ? 0 : CONST) OP ARG1
> + when CONST is a 2^n-1 constant. */
> +(for op (plus bit_ior bit_xor)
> + (simplify
> + (cond (lt @0 integer_zerop@1) (op:c @2 INTEGER_CST@3) @2)
No :c necessary on 'op'
> + (if (wi::exact_log2 (wi::to_wide (@3) + 1) != -1)
Why is it cheap to splat the sign bit? If it's cheap to splat
the sign bit it's cheap to select between zero and a constant:
<splat-sign-bit-to-full-width> & COSTANT.
That you leave in the (cond ...) hints strongly in that it's
an oddly special "canonicalization" and that it is really
a target dependent RTL thing?
> + (op:c (cond (lt @0 @1) @3 { build_zero_cst (type); }) @2))))
:c doesn't do anything in the result (I wonder why we don't diagnose it
...) Please re-use a captured (lt @0 @1).
Similar below.
> +
> +/* Same for (a < 0) ? ARG0 : ARG0 OP CONST. */
> +(for op (plus bit_ior bit_xor)
> + (simplify
> + (cond (lt @0 integer_zerop@1) @2 (op:c @2 INTEGER_CST@3))
> + (if (wi::exact_log2 (wi::to_wide (@3) + 1) != -1)
> + (op:c (cond (lt @0 @1) { build_zero_cst (type); } @3) @2))))
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-49.c
> b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-49.c
> new file mode 100644
> index 00000000000..61d2c8ca615
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-49.c
> @@ -0,0 +1,36 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-phiopt4" } */
> +
> +long f1(long c, long a)
> +{
> + return c < 0 ? a : a + 7;
> +}
> +
> +long f2(long c, long a)
> +{
> + return c < 0 ? a + 7 : a;
> +}
> +
> +long f3(long c, long a)
> +{
> + return c < 0 ? a : a | 15;
> +}
> +
> +long f4(long c, long a)
> +{
> + return c < 0 ? a | 15 : a;
> +}
> +
> +long f5(long c, long a)
> +{
> + return c < 0 ? a : a ^ 31;
> +}
> +
> +long f6(long c, long a)
> +{
> + return c < 0 ? a ^ 31 : a;
> +}
> +
> +/* { dg-final { scan-tree-dump-not "if" "phiopt4" } } */
> +/* { dg-final { scan-assembler-not "blt" } } */
> +/* { dg-final { scan-assembler-not "bge" } } */
> diff --git a/gcc/testsuite/gcc.target/sh/pr59533-1.c
> b/gcc/testsuite/gcc.target/sh/pr59533-1.c
> index b0469859df5..660cf1024c6 100644
> --- a/gcc/testsuite/gcc.target/sh/pr59533-1.c
> +++ b/gcc/testsuite/gcc.target/sh/pr59533-1.c
> @@ -2,19 +2,19 @@
> /* { dg-do compile } */
> /* { dg-options "-O1" } */
>
> -/* { dg-final { scan-assembler-times "shll" 1 } } */
> -/* { dg-final { scan-assembler-times "movt" 5 } } */
> +/* { dg-final { scan-assembler-times "shll" 5 } } */
> +/* { dg-final { scan-assembler-times "movt" 9 } } */
> /* { dg-final { scan-assembler-times "rotcl" 1 } } */
> /* { dg-final { scan-assembler-times "and" 3 } } */
> /* { dg-final { scan-assembler-times "extu.b" 5 } } */
>
> -/* { dg-final { scan-assembler-times "cmp/pz" 27 { target { ! sh2a } } } }
> */
> +/* { dg-final { scan-assembler-times "cmp/pz" 23 { target { ! sh2a } } } }
> */
> /* { dg-final { scan-assembler-times "addc" 4 { target { ! sh2a } } } } */
> -/* { dg-final { scan-assembler-times "subc" 16 { target { ! sh2a } } } } */
> +/* { dg-final { scan-assembler-times "subc" 12 { target { ! sh2a } } } } */
>
> -/* { dg-final { scan-assembler-times "cmp/pz" 25 { target { sh2a } } } } */
> +/* { dg-final { scan-assembler-times "cmp/pz" 21 { target { sh2a } } } } */
> /* { dg-final { scan-assembler-times "addc" 6 { target { sh2a } } } } */
> -/* { dg-final { scan-assembler-times "subc" 14 { target { sh2a } } } } */
> +/* { dg-final { scan-assembler-times "subc" 10 { target { sh2a } } } } */
> /* { dg-final { scan-assembler-times "bld" 2 { target { sh2a } } } } */
>
> int
>
--
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)