On 7/9/2026 10:46 AM, Kael Andrew Franco wrote:
> From 5e592f98e7cd79b3097bedeb2d39bd8e448adf4e Mon Sep 17 00:00:00 2001
> From: Kael Andrew Alonzo Franco <[email protected]>
> Date: Wed, 8 Jul 2026 18:43:32 -0400
> Subject: [PATCH] match: Fold (X + C) + (Y & ~C) to X + (Y | C) [PR126173]
>
> Since `Y & ~C` clears all bits set in C, adding C cannot generate carry
> through those bits and is equivalent to setting them with `or`.
>
> Bootstrapped and tested on x86_64-pc-linux-gnu.
>
> PR tree-optimization/126173
>
> gcc/ChangeLog:
>
> * match.pd: Fold (X + C) + (Y & ~C) to X + (Y | C).
>
> gcc/testsuite/ChangeLog:
>
> * gcc.dg/pr126173.c: New test.
>
> Signed-off-by: Kael Franco <[email protected]>
> ---
> gcc/match.pd | 6 ++++++
> gcc/testsuite/gcc.dg/pr126173.c | 12 ++++++++++++
> 2 files changed, 18 insertions(+)
> create mode 100644 gcc/testsuite/gcc.dg/pr126173.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index 4520a23a026..1e1885d16d4 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -1952,6 +1952,12 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> (op:c (bit_and @0 @1) (bit_xor @0 @1))
> (bit_ior @0 @1)))
>
> +/* (y & ~c) + (x + c) -> ((y | c) + x) */
> +(simplify
> + (plus:c (bit_and @1 INTEGER_CST@3) (plus @0 INTEGER_CST@2))
> + (if (wi::eq_p (wi::bit_not (wi::to_wide (@3)), wi::to_wide (@2)))
> + (plus (bit_ior @1 @2) @0)))
We probably need a !TYPE_SATURATING (type) in the condition.
Consider if X = 120, Y = -128 and C = 10 for a signed 8 bit type.
X+C will saturate to 127 rather than wrapping to -128 making the full
original expression ultimately have the value -1. In the transformed
form we don't saturate anywhere and we end up with the value 2.
> +
> /* (x & y) + (x | y) -> x + y */
> (simplify
> (plus:c (bit_and @0 @1) (bit_ior @0 @1))
> diff --git a/gcc/testsuite/gcc.dg/pr126173.c b/gcc/testsuite/gcc.dg/pr126173.c
> new file mode 100644
> index 00000000000..9d5d9e86849
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/pr126173.c
> @@ -0,0 +1,12 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +
> +#define C 10
> +
> +_Bool
> +x_plus_c_plus_y_and_not_c (int X, int Y)
> +{
> + return ((X + C) + (Y & ~C)) == (X + (Y | C));
> +}
> +
> +/* { dg-final { scan-tree-dump-times "return 1;" 1 "optimized" } } */
Note this test verifies the identity. It's unlikely but possible that
another pass would have knowledge of that identity and rewrite
independent of match.pd.
Point being something like this might be better
return (X + C) + (Y & ~C);
THen you check that you have a bit-ior and there is no bit-and. That's a
more direct test of the match.pd transformation. That's probably worth
modifying.
You could also move the test to an earlier point in the gimple pipeline,
essentially into whatever gimple pass it happens. -fdump-tree-all would
help identify when that happens. I consider this less important, though
if we're adjusting the test, we might as well fix this too.
To recap, the transformation needs to be avoided for saturating types
and the test needs a bit of improvement. But overall it's definitely on
a path for integration.
jeff