On Mon, Jul 20, 2026 at 8:16 AM <[email protected]> wrote:
>
> From: Kyrylo Tkachov <[email protected]>
>
> For one-bit values, forward propagation can leave an inverted value and a
> comparison on opposite sides of a bit operation:
>
>   _1 = ~a_2;
>   _3 = b_4 <= 0;
>   _5 = _1 | _3;
>
> Apply De Morgan's law and invert the comparison to expose the associative
> operation:
>
>   _3 = b_4 > 0;
>   _5 = a_2 & _3;
>   _6 = ~_5;


I am not sure which way we want here; ~(a&b) or (a & ~b). Especially
when both are reasonable to do. And `a & ~b` has an optab too.There is
an optab for `a | ~b` too:
OPTAB_D (andn_optab, "andn$a3")
OPTAB_D (iorn_optab, "iorn$a3")

I have seen some cases where we have:
_1 = _2 CMP _3;
_4 = _5 CMP _6;
_7 = _1 & _4;
_8  = ~_1;
which was not folding info:
_t = _2 CMP' _3;
_tt = _5 CMP' _6;
_8 = _t | _tt;



>
> Handle the corresponding AND form in the same way.  Use
> invert_tree_comparison so that unordered floating-point behavior is
> preserved, and leave the expression unchanged when the comparison cannot be
> inverted.

This reads very verbose. And really does not mention why the
comparison can't be inverted; an ordered comparison traps while an
unordered one does not.


> Require the negation and comparison to have single uses to avoid
> duplicating work.

This is not exactly right either. Yes it is a 1 bit complement
negation but normally it is called a bitwise not. These all together
make it feel like a LLM write this.

> Restrict the simplification to scalar integral one-bit
> results.

But why? The reason is because in generic the result of a comparison
can be any integral type rather than a boolean type.

>
> The focused tests pass on AArch64 and x86_64.  They cover both bit operations
> and the unordered comparison needed for a NaN operand.  On AArch64 the integer
> OR case changes from:
>
>   cmp
>   eor
>   cset
>   orr
>
> to:
>
>   and
>   cmp
>   ccmp
>   cset
>
> The instruction count is unchanged, but the new form avoids materializing an
> intermediate comparison result and exposes conditional-compare formation.
>
> This comes out of the thread at:
> https://gcc.gnu.org/pipermail/gcc-patches/2026-July/723788.html
> and this patch is needed as a prerequisite for that work.

For which part? Because it is not obvious.

So I think for the testcases here we should be producing:
```
f_ior:
        cmp     w1, #1
        cset    w8, lt
        orn     w8, w8, w0
        and     w0, w8, #0x1
        ret

f_and:
        cmp     w1, #1
        cset    w9, lt
        andn     w0, w9, w0
        and   w0, w0, #0x1
        ret
```

That is form BIT_ANDN/BIT_IORN internal functions for this if they
exist for `a & ~b`/`a | ~b` during the last match.

Thanks,
Andrea

>
> Bootstrapped and tested on aarch64-linux-gnu and x86_64-linux.
>
> gcc/ChangeLog:
>
>         * match.pd: Apply De Morgan's law to a one-bit negation combined with 
> a
>         comparison.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.dg/tree-ssa/forwprop-44.c: New test.
>         * gcc.dg/tree-ssa/forwprop-45.c: Likewise.
>
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
>  gcc/match.pd                                | 20 ++++++++++++++
>  gcc/testsuite/gcc.dg/tree-ssa/forwprop-44.c | 25 ++++++++++++++++++
>  gcc/testsuite/gcc.dg/tree-ssa/forwprop-45.c | 29 +++++++++++++++++++++
>  3 files changed, 74 insertions(+)
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/forwprop-44.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/forwprop-45.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index d1a12c35ed3..20a1f9e79d3 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -2057,6 +2057,26 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>         && element_precision (type) <= element_precision (TREE_TYPE (@1)))
>     (bit_not (rop (convert @0) (convert @1))))))
>
> +/* For one-bit values, expose an associative operation by applying De 
> Morgan's
> +   law and inverting the comparison:
> +     ~X & (Y CMP Z) -> ~(X | (Y ICMP Z))
> +     ~X | (Y CMP Z) -> ~(X & (Y ICMP Z))
> +   where ICMP is the inverse of CMP.  */
> +(for op (bit_and bit_ior)
> +     rop (bit_ior bit_and)
> + (for cmp (tcc_comparison)
> +      icmp (inverted_tcc_comparison)
> +      ncmp (inverted_tcc_comparison_with_nans)
> +  (simplify
> +   (op:c (bit_not:s @0) (cmp:s @1 @2))
> +   (if (INTEGRAL_TYPE_P (type) && TYPE_PRECISION (type) == 1)
> +    (with { enum tree_code ic = invert_tree_comparison
> +                    (cmp, HONOR_NANS (@1)); }
> +     (if (ic == icmp)
> +      (bit_not (rop @0 (icmp @1 @2)))
> +      (if (ic == ncmp)
> +       (bit_not (rop @0 (ncmp @1 @2))))))))))
> +
>  /* If we are XORing or adding two BIT_AND_EXPR's, both of which are and'ing
>     with a constant, and the two constants have no bits in common,
>     we should treat this as a BIT_IOR_EXPR since this may produce more
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/forwprop-44.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-44.c
> new file mode 100644
> index 00000000000..6cb79c742cc
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-44.c
> @@ -0,0 +1,25 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-forwprop1" } */
> +
> +_Bool
> +f_ior (_Bool a, int b)
> +{
> +  _Bool na = !a;
> +  _Bool cmp = b <= 0;
> +  return na | cmp;
> +}
> +
> +_Bool
> +f_and (_Bool a, int b)
> +{
> +  _Bool na = !a;
> +  _Bool cmp = b <= 0;
> +  return na & cmp;
> +}
> +
> +/* The inverted comparisons expose an AND in f_ior and an OR in f_and.  */
> +/* { dg-final { scan-tree-dump-times " > 0;" 2 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-not " <= 0;" "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times " & " 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times " \\| " 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times " = ~" 2 "forwprop1" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/forwprop-45.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-45.c
> new file mode 100644
> index 00000000000..f2dfcb0f04e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-45.c
> @@ -0,0 +1,29 @@
> +/* { dg-do run } */
> +/* { dg-require-effective-target flt_dbl_ldbl_inf_nan } */
> +/* { dg-options "-O2 -fno-trapping-math -fdump-tree-forwprop1" } */
> +/* { dg-additional-options "-fno-finite-math-only" } */
> +
> +__attribute__ ((noipa)) _Bool
> +f_ior_nan (_Bool a, float b)
> +{
> +  _Bool na = !a;
> +  _Bool cmp = b <= 0.0f;
> +  return na | cmp;
> +}
> +
> +int
> +main (void)
> +{
> +  float nan = __builtin_nanf ("");
> +
> +  if (f_ior_nan (1, nan) != 0
> +      || f_ior_nan (0, nan) != 1
> +      || f_ior_nan (1, -1.0f) != 1
> +      || f_ior_nan (1, 1.0f) != 0)
> +    __builtin_abort ();
> +  return 0;
> +}
> +
> +/* The inverse of <= is unordered greater when NaNs are honored.  */
> +/* { dg-final { scan-tree-dump-times " u> " 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times " = ~" 1 "forwprop1" } } */
> --
> 2.50.1 (Apple Git-155)
>

Reply via email to