https://gcc.gnu.org/g:6bbe931ea0e8f412c81fed8a3df4a07df97821f4

commit r17-1528-g6bbe931ea0e8f412c81fed8a3df4a07df97821f4
Author: Kyrylo Tkachov <[email protected]>
Date:   Fri Jun 12 00:00:00 2026 +0000

    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 <[email protected]>
    
    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:
---
 gcc/match.pd                                          |  5 +++++
 gcc/testsuite/g++.dg/tree-ssa/mult-negate-zeroone-1.C | 12 ++++++++++++
 gcc/testsuite/g++.dg/tree-ssa/mult-negate-zeroone-2.C | 17 +++++++++++++++++
 3 files changed, 34 insertions(+)

diff --git a/gcc/match.pd b/gcc/match.pd
index ea4a447f0814..926071896194 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 000000000000..002b2d697c59
--- /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 000000000000..736e664be1fa
--- /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;
+}

Reply via email to