On Mon, Jul 20, 2026 at 8:16 AM <[email protected]> wrote:
>
> From: Kyrylo Tkachov <[email protected]>
>
> The existing zero-one conditional rules fold A ? B : 0 to A & B. They do
> not handle a negated Boolean arm, so these forms can retain a diamond:
>
> A ? -B : 0
> A ? -B : -1
>
> For zero-one A and B, fold them to:
>
> -(A & B)
> -((A ^ 1) | B)
>
> The zero_one_valued_p restriction on A is necessary because a GIMPLE
> conditional treats every nonzero scalar as true, whereas the identities use
> the numeric value of A. The restriction on B proves the identities and
> precludes signed overflow when B is negated. Mark the original negation
> with :s so the rules do not duplicate it when it has another use. For the
> second fold, use the negation of the captured minus-one arm as the
> result-typed
> constant one.
>
> Keep the rules in the GIMPLE-only group beside the related zero-one
> conditional folds. The focused tests pass on AArch64 and x86_64. They cover
> both identities for signed and unsigned values, and raw GIMPLE operands whose
> values are not zero or one. On AArch64, the conditional branch and fallback
> return in each direct form become a CMP, CCMP and CSETM sequence.
>
> This is needed as a prerequisite for the phiopt transformation from:
> https://gcc.gnu.org/pipermail/gcc-patches/2026-July/723788.html
> I've split it up in an indepenedent patch.
>
> Bootstrapped and tested on aarch64-linux-gnu and x86_64-linux.
>
> gcc/ChangeLog:
>
> * match.pd: Fold conditional negative zero-one values to negated
> bitwise operations.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.dg/tree-ssa/phiopt-neg-bool-mask-1.c: New test.
> * gcc.dg/tree-ssa/phiopt-neg-bool-mask-2.c: Likewise.
>
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
> gcc/match.pd | 11 +++
> .../gcc.dg/tree-ssa/phiopt-neg-bool-mask-1.c | 36 ++++++++++
> .../gcc.dg/tree-ssa/phiopt-neg-bool-mask-2.c | 71 +++++++++++++++++++
> 3 files changed, 118 insertions(+)
> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phiopt-neg-bool-mask-1.c
> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phiopt-neg-bool-mask-2.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index 20a1f9e79d3..9577e81ce12 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -7016,6 +7016,17 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> of `(type)(zero_one ==/!= 0)` to `(type)(zero_one)`
> and `(type)(zero_one^1)` are not done yet. See PR 110637.
> Even without those, reaching min/max/and/ior faster is better. */
> +/* For zero-one A and B, A ? -B : 0 -> -(A & B). */
Move this whole thing before the other comment. It does not apply here.
> +(simplify
> + (cond zero_one_valued_p@0 (negate:s zero_one_valued_p@1) integer_zerop)
> + (negate (bit_and (convert @0) @1)))
> +
> +/* For zero-one A and B, A ? -B : -1 -> -((A ^ 1) | B). */
> +(simplify
> + (cond zero_one_valued_p@0 (negate:s zero_one_valued_p@1)
No reason for zero_one_valued_p on @0 here. Since that will always be
true for gimple.
> + integer_minus_onep@2)
This should be integer_all_onesp.
> + (negate (bit_ior (bit_xor (convert @0) (negate @2)) @1)))
`{ build_one_cst (type); }` instead of `(negate @2)`.
> +
> (simplify
> (cond @0 zero_one_valued_p@1 zero_one_valued_p@2)
> (switch
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phiopt-neg-bool-mask-1.c
> b/gcc/testsuite/gcc.dg/tree-ssa/phiopt-neg-bool-mask-1.c
> new file mode 100644
> index 00000000000..a9313f4c6f7
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/phiopt-neg-bool-mask-1.c
> @@ -0,0 +1,36 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-phiopt2" } */
> +
> +int
> +direct_negative_mask (int outer, int value)
> +{
> + int mask = -(value > 0);
> + return outer > 0 ? mask : 0;
> +}
> +
> +int
> +direct_negative_mask_minus_one (int outer, int value)
> +{
> + int mask = -(value > 0);
> + return outer > 0 ? mask : -1;
> +}
> +
> +unsigned int
> +direct_negative_mask_unsigned (int outer, int value)
> +{
> + unsigned int mask = -(unsigned int) (value > 0);
> + return outer > 0 ? mask : 0;
> +}
> +
> +unsigned int
> +direct_negative_mask_minus_one_unsigned (int outer, int value)
> +{
> + unsigned int mask = -(unsigned int) (value > 0);
> + return outer > 0 ? mask : -1U;
> +}
> +
> +/* The zero else arms contribute two AND operations. */
> +/* { dg-final { scan-tree-dump-times " & " 2 "phiopt2" } } */
> +/* The minus-one else arms contribute two OR operations. */
> +/* { dg-final { scan-tree-dump-times " \\| " 2 "phiopt2" } } */
> +/* { dg-final { scan-tree-dump-not "if \\(" "phiopt2" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phiopt-neg-bool-mask-2.c
> b/gcc/testsuite/gcc.dg/tree-ssa/phiopt-neg-bool-mask-2.c
> new file mode 100644
> index 00000000000..91330a922d7
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/phiopt-neg-bool-mask-2.c
> @@ -0,0 +1,71 @@
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fgimple" } */
> +
> +/* A GIMPLE conditional treats every nonzero scalar as true. Do not use an
> + arbitrary condition as a numeric zero-one value in mask identities. */
> +
> +__attribute__ ((noipa)) int __GIMPLE ()
> +zero_else (int c, _Bool b)
> +{
> + int ib;
> + int nb;
> + int r;
> +
> + ib = (int) b_2(D);
> + nb = -ib;
> + r = c_1(D) ? nb : 0;
> + return r;
> +}
> +
> +__attribute__ ((noipa)) int __GIMPLE ()
> +minus_one_else (int c, _Bool b)
> +{
> + int ib;
> + int nb;
> + int r;
> +
> + ib = (int) b_2(D);
> + nb = -ib;
> + r = c_1(D) ? nb : _Literal (int) -1;
> + return r;
> +}
> +
> +/* Likewise, do not use an arbitrary negated operand as a numeric zero-one
> + value in the mask identities. */
> +
> +__attribute__ ((noipa)) int __GIMPLE ()
> +zero_else_non_boolean_value (_Bool c, int b)
> +{
> + int ic;
> + int nb;
> + int r;
> +
> + ic = (int) c_1(D);
> + nb = -b_2(D);
> + r = ic ? nb : 0;
> + return r;
> +}
> +
> +__attribute__ ((noipa)) int __GIMPLE ()
> +minus_one_else_non_boolean_value (_Bool c, int b)
> +{
> + int ic;
> + int nb;
> + int r;
> +
> + ic = (int) c_1(D);
> + nb = -b_2(D);
> + r = ic ? nb : _Literal (int) -1;
> + return r;
> +}
> +
> +int
> +main (void)
> +{
> + if (zero_else (2, 1) != -1 || minus_one_else (2, 0) != 0)
> + __builtin_abort ();
> + if (zero_else_non_boolean_value (1, 2) != -2
> + || minus_one_else_non_boolean_value (0, 2) != -1)
> + __builtin_abort ();
> + return 0;
> +}
> --
> 2.50.1 (Apple Git-155)
>