> Am 30.07.2026 um 23:18 schrieb Jakub Jelinek <[email protected]>:
>
> Hi!
>
> The following testcase is miscompiled.
> We have 2 different simplifications
> (a & b) ^ (a == b) -> !(a | b)
> (a & b) == (a ^ b) -> !(a | b)
> where both a and b are truth_valued_p. That doesn't mean they have
> boolean type, it means that either they have integral type with one bit
> precision (boolean, unsigned or signed) or they are result of comparisons
> etc.
> Now, because both a and b appear as operands of the same &, they necessarily
> have the same or uselessly compatible type. For the first case, the a == b
> comparison necessarily has to have the same type too and so type is the same
> type as well.
> For the second case that is not the case, e.g. in the problematic
> testcase both a and b are unsigned _BitInt(1) while == has int type, but
> it could very well be also that a and b are results of comparisons etc.
> and have int type.
> Now, the comment properly uses ! for the replacement, but the replacement
> of the simplification actually uses bit_not, so ~. ~ is fine for 1-bit
> precision, but not for wider ones.
> The following patch differentiates between the case when a and b have
> 1-bit precision type, then it ensures ~ is done in that type and only
> then it is converted to type, while for other cases it does ^ 1 instead.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk/16.2?
Ok
Richard
> 2026-07-30 Jakub Jelinek <[email protected]>
>
> PR tree-optimization/126490
> * match.pd ((a & b) == (a ^ b) -> !(a | b)): If @0 has
> integral one bit precision type, use (convert:type ...) around the
> bit_not just in case the comparison has a different result type
> from the type of its operands. Otherwise do that too but with
> bit_not replaced with bit_xor with one of the appropriate type.
>
> * gcc.dg/torture/bitint-104.c: New test.
>
> --- gcc/match.pd.jj 2026-07-30 10:30:26.284706405 +0200
> +++ gcc/match.pd 2026-07-30 16:14:57.423126308 +0200
> @@ -2720,7 +2720,10 @@ (define_operator_list SYNC_FETCH_AND_AND
> second_op (eq bit_xor)
> (simplify
> (first_op:c (bit_and:c truth_valued_p@0 truth_valued_p@1) (second_op @0 @1))
> - (bit_not (bit_ior @0 @1))))
> + (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
> + && TYPE_PRECISION (TREE_TYPE (@0)) == 1)
> + (convert:type (bit_not (bit_ior @0 @1)))
> + (convert:type (bit_xor (bit_ior @0 @1) { build_one_cst (TREE_TYPE (@0));
> })))))
>
> /* Convert ~ (A - 1) or ~ (A + -1) to -A. */
> (simplify
> --- gcc/testsuite/gcc.dg/torture/bitint-104.c.jj 2026-07-30
> 15:53:51.605133318 +0200
> +++ gcc/testsuite/gcc.dg/torture/bitint-104.c 2026-07-30
> 15:53:15.097577105 +0200
> @@ -0,0 +1,78 @@
> +/* PR tree-optimization/126490 */
> +/* { dg-do run { target bitint } } */
> +
> +typedef unsigned _BitInt(1) T;
> +
> +[[gnu::noipa]] int
> +foo (T a, T b)
> +{
> + return ((a & b) == (a ^ b)) + 1;
> +}
> +
> +[[gnu::noipa]] int
> +bar (T a, T b)
> +{
> + return ((a & b) == (a ^ b)) != 0;
> +}
> +
> +[[gnu::noipa]] int
> +baz (T a, T b)
> +{
> + return ((a & b) == (a ^ b)) == 0;
> +}
> +
> +[[gnu::noipa]] int
> +qux (T a, T b)
> +{
> + return ((a & b) == (a ^ b)) < 1;
> +}
> +
> +[[gnu::noipa]] int
> +corge (int a, int b)
> +{
> + return (a & b) == (a ^ b);
> +}
> +
> +[[gnu::noipa]] int
> +garply (T a, T b)
> +{
> + return ((a & b) ^ (a == b)) + 1;
> +}
> +
> +[[gnu::noipa]] int
> +fred (T a, T b)
> +{
> + return ((a & b) ^ (a == b)) != 0;
> +}
> +
> +[[gnu::noipa]] int
> +xyzzy (T a, T b)
> +{
> + return ((a & b) ^ (a == b)) == 0;
> +}
> +
> +[[gnu::noipa]] int
> +waldo (T a, T b)
> +{
> + return ((a & b) ^ (a == b)) < 1;
> +}
> +
> +int
> +main ()
> +{
> + for (int i = 0; i < 4; ++i)
> + {
> + int a = i & 1;
> + int b = i >> 1;
> + int c = corge (a, b);
> + if (foo (a, b) != c + 1
> + || bar (a, b) != (c != 0)
> + || baz (a, b) != (c == 0)
> + || qux (a, b) != (c < 1)
> + || garply (a, b) != c + 1
> + || fred (a, b) != (c != 0)
> + || xyzzy (a, b) != (c == 0)
> + || waldo (a, b) != (c < 1))
> + __builtin_abort ();
> + }
> +}
>
> Jakub
>