> On 21 Jul 2026, at 08:14, Richard Biener <[email protected]> wrote:
> 
> On Tue, Jul 21, 2026 at 4:11 AM Andrea Pinski
> <[email protected]> wrote:
>> 
>> 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;
> 
> It's difficult to say which one we should make canonical as we are likely not
> seeing the larger expression context.  IMO most of the patterns that do not
> reduce expression complexity in this area should be integrated into a
> framework like backprop (get rid of ~), reassoc (simplify larger expressions)
> and VN/reassoc (reassoc to form common subexpressions).
> 
> That said, I'm not sure we want this kind of pattern - those tend to 
> facilitate
> oscillations of some sort.

Yeah, given Andrea’s comments as well I think a more targeted fix in ccmp.cc is 
a better fix to avoid regressing the ssa-ifcombine-ccmp-1.c tests that I was 
trying to maintain.
So I’ll drop this patch (patch 2/2 is still needed for match.pd IMO, I’ve sent 
out a respin addressing Andrea’s comments).

Thanks,
Kyrill

> 
>>> 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