From: Kyrylo Tkachov Date: Fri, 12 Jun 2026 00:00:00 +0000 (+0000) Subject: match.pd: fold (0/1) * -(0/1) into -((0/1) & (0/1)) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6bbe931ea0e8f412c81fed8a3df4a07df97821f4;p=thirdparty%2Fgcc.git match.pd: fold (0/1) * -(0/1) into -((0/1) & (0/1)) For operands known to be in the range [0, 1], multiplying a 0/1 value by a negated 0/1 value is the negation of their bitwise AND: x * -y == -(x & y) when x, y are in { 0, 1 }. This complements the existing "{ 0, 1 } * { 0, 1 } -> { 0, 1 } & { 0, 1 }" simplification, which does not handle a negated operand. For the comparison-derived 0/1 masks produced by if-conversion this exposes a plain bitwise AND of the original conditions to later passes (replacing a COND_EXPR). This triggers a few times in astcenc in SPEC2026 where it simplifies the codegen of one of the hot kernels and gives a ~2.4% improvement on aarch64, though the real winners for that kernel are described in PR125750. This is just a small cleanup. Signed-off-by: Kyrylo Tkachov gcc/ChangeLog: PR tree-optimization/125750 * match.pd (mult of a 0/1 value by a negated 0/1 value): New simplification. gcc/testsuite/ChangeLog: PR tree-optimization/125750 * g++.dg/tree-ssa/mult-negate-zeroone-1.C: New test. * g++.dg/tree-ssa/mult-negate-zeroone-2.C: New test. --- diff --git a/gcc/match.pd b/gcc/match.pd index ea4a447f081..92607189619 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -2525,6 +2525,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) (if (INTEGRAL_TYPE_P (type)) (bit_and @0 @1))) +/* Transform { 0 or 1 } * -{ 0 or 1 } into -({ 0 or 1 } & { 0 or 1 }). */ +(simplify + (mult:c (negate zero_one_valued_p@0) zero_one_valued_p@1) + (negate (bit_and @0 @1))) + /* Fold `(a ? b : 0) | (a ? 0 : c)` into (a ? b : c). Handle also ^ and + in replacement of `|`. */ (for cnd (cond vec_cond) diff --git a/gcc/testsuite/g++.dg/tree-ssa/mult-negate-zeroone-1.C b/gcc/testsuite/g++.dg/tree-ssa/mult-negate-zeroone-1.C new file mode 100644 index 00000000000..002b2d697c5 --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/mult-negate-zeroone-1.C @@ -0,0 +1,12 @@ +// Test that { 0, 1 } * -{ 0, 1 } folds to -({ 0, 1 } & { 0, 1 }) and does not +// keep a multiply. Also check the commutative (negated operand first) form. +// { dg-do compile } +// { dg-options "-O2 -fdump-tree-optimized" } + +int f1 (int x, int y) { return (x > 5) * -(y > 5); } +long f2 (long x, long y) { return (long) (x > 5) * -(long) (y > 5); } +unsigned f3 (unsigned x, unsigned y) { return (x > 5u) * -(unsigned) (y > 5u); } +int f4 (int x, int y) { return -(y > 5) * (x > 5); } + +// { dg-final { scan-tree-dump-not " \* " "optimized" } } +// { dg-final { scan-tree-dump-times " & " 4 "optimized" } } diff --git a/gcc/testsuite/g++.dg/tree-ssa/mult-negate-zeroone-2.C b/gcc/testsuite/g++.dg/tree-ssa/mult-negate-zeroone-2.C new file mode 100644 index 00000000000..736e664be1f --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/mult-negate-zeroone-2.C @@ -0,0 +1,17 @@ +// Verify { 0, 1 } * -{ 0, 1 } == -({ 0, 1 } & { 0, 1 }) at runtime, including +// the commutative (negated operand first) form. +// { dg-do run } +// { dg-options "-O2" } + +int __attribute__((noipa)) f (int x, int y) { return (x > 5) * -(y > 5); } +int __attribute__((noipa)) g (int x, int y) { return -(y > 5) * (x > 5); } + +int main () { + for (int x = -3; x < 12; x++) + for (int y = -3; y < 12; y++) + { + if (f (x, y) != -((x > 5) & (y > 5))) __builtin_abort (); + if (g (x, y) != -((x > 5) & (y > 5))) __builtin_abort (); + } + return 0; +}