From: Samarth Tandale <[email protected]>

Fold (a != 0) | (popcount(a) == n) into (a != 0) | (n == 0), and
(a == 0) & (popcount(a) != n) into (a == 0) & (n != 0).

This follows from x == 0 => popcount(x) == 0: when a == 0 the
popcount comparison reduces to a comparison against zero, and when
a != 0 the outer OR/AND already determines the result.

        PR tree-optimization/112094

gcc/ChangeLog:
        * match.pd: Add fold for (a != 0) | (popcount(a) == n) and
        (a == 0) & (popcount(a) != n).

gcc/testsuite/ChangeLog:
        * gcc.dg/fold-popcount-12.c: New test.
        * gcc.dg/fold-popcount-13.c: New test.

Signed-off-by: Samarth Tandale <[email protected]>
---
Fold (a != 0) | (popcount(a) == n) into (a != 0) | (n == 0), and
(a == 0) & (popcount(a) != n) into (a == 0) & (n != 0).

This follows from x == 0 => popcount(x) == 0: when a == 0 the
popcount comparison reduces to a comparison against zero, and when
a != 0 the outer OR/AND already determines the result.

        PR tree-optimization/112094

```
gcc/ChangeLog:

        * match.pd: Add fold for (a != 0) | (popcount(a) == n) and
        (a == 0) & (popcount(a) != n).

gcc/testsuite/ChangeLog:

        * gcc.dg/fold-popcount-12.c: New test.
        * gcc.dg/fold-popcount-13.c: New test.
```

Signed-off-by: Samarth Tandale <[email protected]>



This is a forge pull request published on the gcc-patches mailing list mailing 
list
as requested by Samarth Tandale via Sourceware Forge 
<[email protected]>.
Forge discussion: https://forge.sourceware.org/gcc/gcc/pulls/233

Get it locally using:
```
git fetch forge-upstream "+refs/versioned_pull/233/*:refs/versioned_pull/233/*"
git switch -c "pr-233-v1" "refs/versioned_pull/233/1/head"
```
Or, download the patch at: https://forge.sourceware.org/gcc/gcc/pulls/233.diff
Created on: 2026-09-24 11:25:25+00:00
Latest update: 2026-09-24 11:48:15+00:00
Changes: 3 changed files, 195 additions, 0 deletions
Head revision: sam-100/gcc ref pr112094-popcount commit 
19af6b5f77682efdc05756d9e5ff39cd12d54434 
Base revision: gcc/gcc ref trunk commit 
b679fda6b4f2d41323e19f780b52805e7a06bfcc r17-4589-gb679fda6b4f2d4
Merge base: b679fda6b4f2d41323e19f780b52805e7a06bfcc
Requested Reviewers: 

Changed files:
- A: gcc/testsuite/gcc.dg/fold-popcount-12.c
- A: gcc/testsuite/gcc.dg/fold-popcount-13.c
- M: gcc/match.pd

 gcc/match.pd                            |  13 +++
 gcc/testsuite/gcc.dg/fold-popcount-12.c | 149 ++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/fold-popcount-13.c |  33 ++++++
 3 files changed, 195 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/fold-popcount-12.c
 create mode 100644 gcc/testsuite/gcc.dg/fold-popcount-13.c

diff --git a/gcc/match.pd b/gcc/match.pd
index b6a9d4e79be21..c2a66c2606b71 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -11508,6 +11508,19 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
       (cmp (popcount @0) integer_zerop)
       (rep @0 { build_zero_cst (TREE_TYPE (@0)); }))))
 
+/* (a != 0) | (popcount (a) == n)  ->  (a != 0) | (n == 0)
+   (a == 0) & (popcount (a) != n)  ->  (a == 0) & (n != 0)  */
+(for popcount (POPCOUNT)
+ (for bop (bit_ior bit_and)
+      popcmp (eq ne)
+      zerocmp (ne      eq)
+  (simplify
+   (bop:c (zerocmp@2 @0 integer_zerop)
+         (popcmp:cs (popcount (convert?@3 @0)) @1))
+   (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
+       && INTEGRAL_TYPE_P (TREE_TYPE (@3)))
+    (bop @2 (popcmp @1 { build_zero_cst (TREE_TYPE (@1)); }))))))
+
 /* popcount(bswap(x)) is popcount(x).  */
 (for popcount (POPCOUNT)
   (for bswap (BSWAP BITREVERSE)
diff --git a/gcc/testsuite/gcc.dg/fold-popcount-12.c 
b/gcc/testsuite/gcc.dg/fold-popcount-12.c
new file mode 100644
index 0000000000000..3f6666e228c67
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/fold-popcount-12.c
@@ -0,0 +1,149 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* PR tree-optimization/112094 */
+/* Test that
+     (a != 0) | (popcount(a) == n) -> (a != 0) | (n == 0)
+     (a == 0) & (popcount(a) != n) -> (a == 0) & (n != 0)
+   and their commuted forms are folded away.  */
+
+/* OR cases.  */
+int f (unsigned int a, int n)
+{
+  int t = __builtin_popcount (a);
+  return (a != 0) | (t == n);
+}
+
+int fl (unsigned long a, int n)
+{
+  int t = __builtin_popcountl (a);
+  return (a != 0) | (t == n);
+}
+
+int fll (unsigned long long a, int n)
+{
+  int t = __builtin_popcountll (a);
+  return (a != 0) | (t == n);
+}
+
+/* Signed-to-unsigned conversions inserted for popcount.  */
+int f_signed (int a, int n)
+{
+  int t = __builtin_popcount (a);
+  return (a != 0) | (t == n);
+}
+
+int fl_signed (long a, int n)
+{
+  int t = __builtin_popcountl (a);
+  return (a != 0) | (t == n);
+}
+
+int fll_signed (long long a, int n)
+{
+  int t = __builtin_popcountll (a);
+  return (a != 0) | (t == n);
+}
+
+/* Integral conversions: promotion, widening, and narrowing.  */
+int f_promoted (short a, int n)
+{
+  int t = __builtin_popcount (a);
+  return (a != 0) | (t == n);
+}
+
+int f_widened (int a, int n)
+{
+  int t = __builtin_popcountll (a);
+  return (a != 0) | (t == n);
+}
+
+int f_narrowed (long long a, int n)
+{
+  int t = __builtin_popcount (a);
+  return (a != 0) | (t == n);
+}
+
+/* Commuted operator and comparison operands.  */
+int f_comm (unsigned int a, int n)
+{
+  int t = __builtin_popcount (a);
+  return (t == n) | (a != 0);
+}
+
+int f_comm2 (unsigned int a, int n)
+{
+  int t = __builtin_popcount (a);
+  return (a != 0) | (n == t);
+}
+
+int f_comm3 (unsigned int a, int n)
+{
+  int t = __builtin_popcount (a);
+  return (0 != a) | (t == n);
+}
+
+/* AND cases.  */
+int g (unsigned int a, int n)
+{
+  int t = __builtin_popcount (a);
+  return (a == 0) & (t != n);
+}
+
+int gl (unsigned long a, int n)
+{
+  int t = __builtin_popcountl (a);
+  return (a == 0) & (t != n);
+}
+
+int gll (unsigned long long a, int n)
+{
+  int t = __builtin_popcountll (a);
+  return (a == 0) & (t != n);
+}
+
+/* Signed-to-unsigned conversions inserted for popcount.  */
+int g_signed (int a, int n)
+{
+  int t = __builtin_popcount (a);
+  return (a == 0) & (t != n);
+}
+
+/* Integral conversions: promotion, widening, and narrowing.  */
+int g_promoted (short a, int n)
+{
+  int t = __builtin_popcount (a);
+  return (a == 0) & (t != n);
+}
+
+int g_widened (int a, int n)
+{
+  int t = __builtin_popcountll (a);
+  return (a == 0) & (t != n);
+}
+
+int g_narrowed (long long a, int n)
+{
+  int t = __builtin_popcount (a);
+  return (a == 0) & (t != n);
+}
+
+/* Commuted operator and comparison operands.  */
+int g_comm (unsigned int a, int n)
+{
+  int t = __builtin_popcount (a);
+  return (t != n) & (a == 0);
+}
+
+int g_comm2 (unsigned int a, int n)
+{
+  int t = __builtin_popcount (a);
+  return (a == 0) & (n != t);
+}
+
+int g_comm3 (unsigned int a, int n)
+{
+  int t = __builtin_popcount (a);
+  return (0 == a) & (t != n);
+}
+
+/* { dg-final { scan-tree-dump-not "popcount" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/fold-popcount-13.c 
b/gcc/testsuite/gcc.dg/fold-popcount-13.c
new file mode 100644
index 0000000000000..4b6e32a83c54d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/fold-popcount-13.c
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* PR tree-optimization/112094 */
+/* Verify that popcount comparisons are preserved when they cannot be
+   safely simplified.  */
+
+/* The comparison result is used twice.  */
+int f_multiuse (unsigned int a, int n, int *out)
+{
+  int c = __builtin_popcount (a) == n;
+  *out = c;
+  return (a != 0) | c;
+}
+
+extern int get_value (void);
+volatile unsigned int value;
+
+/* Both calls to get_value are observable and must be preserved.  */
+int f_side_effect (int n)
+{
+  return (get_value () != 0)
+         | (__builtin_popcount (get_value ()) == n);
+}
+
+/* Both volatile reads are observable and must be preserved.  */
+int f_volatile (int n)
+{
+  return (value != 0)
+         | (__builtin_popcount (value) == n);
+}
+
+/* One popcount remains for each negative case.  */
+/* { dg-final { scan-tree-dump-times "popcount" 3 "optimized" } } */
-- 
2.54.0

Reply via email to