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). */ +(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) + integer_minus_onep@2) + (negate (bit_ior (bit_xor (convert @0) (negate @2)) @1))) + (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)
