If the argument to popcount is known to fit in unsigned int, convert
it to unsigned int before applying the popcount operation.  This allows
the target to use a narrower popcount operation when appropriate.

        PR tree-optimization/125221

gcc/ChangeLog:

        * gimple-match-head.cc: Include vr-values.h.
        * match.pd (POPCOUNT): Narrow the argument when its value range
        fits in unsigned int.

gcc/testsuite/ChangeLog:

        * gcc.dg/popcountll-narrow.c: New test.

Bootstrapped and tested on x86_64-pc-linux-gnu.

Signed-off-by: Shivam Gupta <[email protected]>
---
 gcc/gimple-match-head.cc                 |  1 +
 gcc/match.pd                             | 15 ++++++
 gcc/testsuite/gcc.dg/popcountll-narrow.c | 60 ++++++++++++++++++++++++
 3 files changed, 76 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/popcountll-narrow.c

diff --git a/gcc/gimple-match-head.cc b/gcc/gimple-match-head.cc
index 757e324cb..9cf6e3160 100644
--- a/gcc/gimple-match-head.cc
+++ b/gcc/gimple-match-head.cc
@@ -51,6 +51,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "attribs.h"
 #include "asan.h"
+#include "vr-values.h"
 
 tree do_valueize (tree, tree (*)(tree), bool &);
 tree do_valueize (tree (*)(tree), tree);
diff --git a/gcc/match.pd b/gcc/match.pd
index 54cad1cd2..ad032092e 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -11228,6 +11228,21 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 #endif
 
 /* POPCOUNT simplifications.  */
+#if GIMPLE
+/* If the value passed to popcount is known to fit in unsigned int,
+   use the narrower popcount operation.  */
+(simplify
+  (POPCOUNT @0)
+  (if (TYPE_PRECISION (TREE_TYPE (@0)) > TYPE_PRECISION (unsigned_type_node))
+    (with { int_range_max vr0; }
+      (if (gimple_match_range_of_expr (vr0, @0)
+           && range_fits_type_p (&vr0,
+                                 TYPE_PRECISION (unsigned_type_node),
+                                 TYPE_SIGN (unsigned_type_node)))
+        (with { tree utype = unsigned_type_node; }
+          (POPCOUNT (convert:utype @0)))))))
+#endif
+
 /* popcount(X) + popcount(Y) is popcount(X|Y) when X&Y must be zero.  */
 (simplify
   (plus (POPCOUNT:s @0) (POPCOUNT:s @1))
diff --git a/gcc/testsuite/gcc.dg/popcountll-narrow.c 
b/gcc/testsuite/gcc.dg/popcountll-narrow.c
new file mode 100644
index 000000000..4c5a7db78
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/popcountll-narrow.c
@@ -0,0 +1,60 @@
+/* { dg-do compile } */
+/* { dg-options "-Os -fdump-tree-optimized" } */
+
+int func1(unsigned long long x) {
+    return __builtin_popcountll(x & 0x87878787);
+}
+int func2(unsigned long long x) {
+    return __builtin_popcountll(x >> 32);
+}
+int func2b(unsigned long long x) {
+    return __builtin_popcountll((x >> 32) & 0xFFFFFFFF);
+}
+int func3(unsigned char x) {
+    if (x >= 64) {
+        __builtin_unreachable();
+    }
+    return __builtin_popcountll(0x87878787ULL >> x);
+}
+int func3b(unsigned char x) {
+    if (x >= 64) {
+        __builtin_unreachable();
+    }
+    return __builtin_popcountll((0x87878787ULL >> x) & 0xFFFFFFFF);
+}
+int func4(unsigned long long x) {
+    return __builtin_popcountll(x % 4294967291U);
+}
+int func4b(unsigned long long x) {
+    return __builtin_popcountll((x % 4294967291U) & 0xFFFFFFFF);
+}
+int func5(unsigned long long x) {
+    return __builtin_popcountll(x / 4294967311U);
+}
+int func5b(unsigned long long x) {
+    return __builtin_popcountll((x / 4294967311U) & 0xFFFFFFFF);
+}
+int func6(unsigned long long x) {
+    if (x > 0xFFFFFFFFULL) {
+        __builtin_unreachable();
+    }
+    return __builtin_popcountll(x);
+}
+int func6b(unsigned long long x) {
+    if (x > 0xFFFFFFFFULL) {
+        __builtin_unreachable();
+    }
+    return __builtin_popcountll(x & 0xFFFFFFFF);
+}
+
+/* Negative test case */ 
+int func7(unsigned long long x) {
+    return __builtin_popcountll(x);
+}
+
+/* Verify that all(f1-f6) popcount arguments have been narrowed to unsigned int
+   temporaries.  */
+/* { dg-final { scan-tree-dump-times {__builtin_popcountll \(_[0-9]+\)} 11 
"optimized" } } */
+
+/* Verify that an argument whose range is not known to fit is unchanged.  */
+/* { dg-final { scan-tree-dump-times {__builtin_popcountll \(x_} 1 "optimized" 
} } */
-- 
2.43.0

Reply via email to