Hello.

Attached is a small patch that enables hardware popcount on RISC-V when 
available and also sets the arch flag to 'rv64gc_zbb' flag when appropriate.

best.

-greg
From 35e84cb058b7e8a6df71b6234c0dd9b45a228b70 Mon Sep 17 00:00:00 2001
From: Greg Burd <[email protected]>
Date: Sat, 21 Mar 2026 12:20:27 -0400
Subject: [PATCH v1 1/2] Add RISC-V Zbb popcount optimization

Enable hardware popcount instruction (cpop) from the RISC-V Zbb bitmanip
extension. The compiler automatically emits cpop when
__builtin_popcountll() is compiled with Zbb support.

Build system changes:
- configure.ac: Add Zbb detection with/without -march=rv64gc_zbb
- meson.build: Add Zbb detection with/without -march=rv64gc_zbb

When Zbb is available, USE_RVV_ZBB is defined and -march flags are added
automatically. The existing pg_popcount() implementation already uses
__builtin_popcountll(), so no C code changes needed.

This provides single-cycle popcount vs multi-instruction software
fallback on RISC-V systems with Zbb support.
---
 configure.ac | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 meson.build  | 29 ++++++++++++++++++++++++
 2 files changed, 91 insertions(+)

diff --git a/configure.ac b/configure.ac
index f8327a7020a..68513b39711 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2154,6 +2154,37 @@ if test x"$host_cpu" = x"aarch64"; then
   if test x"$pgac_sve_popcnt_intrinsics" = x"yes"; then
     AC_DEFINE(USE_SVE_POPCNT_WITH_RUNTIME_CHECK, 1, [Define to 1 to use SVE popcount instructions with a runtime check.])
   fi
+
+# Check for RISC-V Zbb bitmanip extension (provides 'cpop' for popcount).
+#
+# GCC/Clang emit the cpop instruction from __builtin_popcountll() when the
+# target march includes _zbb.  We first try without extra flags; if that
+# fails we retry with -march=rv64gc_zbb.  On success we define USE_RVV_ZBB
+# so that future code can guard on it, and we keep the extended CFLAGS so
+# the compiler uses cpop throughout the build.
+AC_MSG_CHECKING([for RISC-V Zbb extension (cpop/popcount)])
+pgac_save_CFLAGS_zbb="$CFLAGS"
+AC_COMPILE_IFELSE(
+  [AC_LANG_PROGRAM(
+    [/* Test that the compiler will emit cpop from __builtin_popcountll */
+     static inline int test_cpop(unsigned long long x)
+     { return __builtin_popcountll(x); }],
+    [volatile int r = test_cpop(0xdeadbeefULL); (void) r;])],
+  [AC_DEFINE(USE_RVV_ZBB, 1,
+    [Define to 1 if RISC-V Zbb bitmanip extension is available.])
+   AC_MSG_RESULT([yes])],
+  [CFLAGS="$CFLAGS -march=rv64gc_zbb"
+   AC_COMPILE_IFELSE(
+     [AC_LANG_PROGRAM(
+       [static inline int test_cpop(unsigned long long x)
+        { return __builtin_popcountll(x); }],
+       [volatile int r = test_cpop(0xdeadbeefULL); (void) r;])],
+     [AC_DEFINE(USE_RVV_ZBB, 1,
+       [Define to 1 if RISC-V Zbb bitmanip extension is available.])
+      AC_MSG_RESULT([yes, with -march=rv64gc_zbb])],
+     [CFLAGS="$pgac_save_CFLAGS_zbb"
+      AC_MSG_RESULT([no])])])
+
 fi
 
 # Check for Intel SSE 4.2 intrinsics to do CRC calculations.
@@ -2596,3 +2627,34 @@ AC_OUTPUT
 if test "$vpath_build" = "no"; then
   touch meson.build
 fi
+
+# Check for RISC-V Zbb bitmanip extension (provides 'cpop' for popcount).
+#
+# GCC/Clang emit the cpop instruction from __builtin_popcountll() when the
+# target march includes _zbb.  We first try without extra flags; if that
+# fails we retry with -march=rv64gc_zbb.  On success we define USE_RVV_ZBB
+# so that future code can guard on it, and we keep the extended CFLAGS so
+# the compiler uses cpop throughout the build.
+AC_MSG_CHECKING([for RISC-V Zbb extension (cpop/popcount)])
+pgac_save_CFLAGS_zbb="$CFLAGS"
+AC_COMPILE_IFELSE(
+  [AC_LANG_PROGRAM(
+    [/* Test that the compiler will emit cpop from __builtin_popcountll */
+     static inline int test_cpop(unsigned long long x)
+     { return __builtin_popcountll(x); }],
+    [volatile int r = test_cpop(0xdeadbeefULL); (void) r;])],
+  [AC_DEFINE(USE_RVV_ZBB, 1,
+    [Define to 1 if RISC-V Zbb bitmanip extension is available.])
+   AC_MSG_RESULT([yes])],
+  [CFLAGS="$CFLAGS -march=rv64gc_zbb"
+   AC_COMPILE_IFELSE(
+     [AC_LANG_PROGRAM(
+       [static inline int test_cpop(unsigned long long x)
+        { return __builtin_popcountll(x); }],
+       [volatile int r = test_cpop(0xdeadbeefULL); (void) r;])],
+     [AC_DEFINE(USE_RVV_ZBB, 1,
+       [Define to 1 if RISC-V Zbb bitmanip extension is available.])
+      AC_MSG_RESULT([yes, with -march=rv64gc_zbb])],
+     [CFLAGS="$pgac_save_CFLAGS_zbb"
+      AC_MSG_RESULT([no])])])
+
diff --git a/meson.build b/meson.build
index 0a181909fab..9dbaa3945e9 100644
--- a/meson.build
+++ b/meson.build
@@ -2525,6 +2525,35 @@ int main(void)
 
   if cc.links(prog, name: 'SVE popcount', args: test_c_args)
     cdata.set('USE_SVE_POPCNT_WITH_RUNTIME_CHECK', 1)
+
+# ---------------------------------------------------------------------------
+# Check for RISC-V Zbb bitmanip extension (provides 'cpop' for popcount).
+#
+# __builtin_popcountll() already used by pg_popcount(); the compiler will
+# emit the single-cycle 'cpop' instruction when -march includes _zbb.
+# We probe by compiling a small test and checking that the compiler accepts
+# the march flag.  On success we add the flag to the whole build so that
+# every translation unit benefits.
+# ---------------------------------------------------------------------------
+zbb_test_code = '''
+static inline int test_cpop(unsigned long long x)
+{ return __builtin_popcountll(x); }
+int main(void) {
+  volatile int r = test_cpop(0xdeadbeefULL);
+  (void) r;
+  return 0;
+}
+'''
+
+if cc.compiles(zbb_test_code, name: 'RISC-V Zbb cpop (no extra flags)')
+  cdata.set('USE_RVV_ZBB', 1)
+elif cc.compiles(zbb_test_code,
+                 args: ['-march=rv64gc_zbb'],
+                 name: 'RISC-V Zbb cpop (-march=rv64gc_zbb)')
+  cdata.set('USE_RVV_ZBB', 1)
+  add_project_arguments('-march=rv64gc_zbb', language: 'c')
+endif
+
   endif
 
 endif
-- 
2.43.0

Reply via email to