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