0002-SVE-popcount-support.patch
text/plain
Filename: 0002-SVE-popcount-support.patch
Type: text/plain
Part: 2
Message:
Re: [PATCH] SVE popcount support
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch 0002
Subject: SVE popcount support
| File | + | − |
|---|---|---|
| config/c-compiler.m4 | 33 | 0 |
| configure | 104 | 0 |
| configure.ac | 15 | 0 |
| meson.build | 37 | 0 |
| src/include/pg_config.h.in | 3 | 0 |
| src/include/port/pg_bitutils.h | 9 | 0 |
| src/Makefile.global.in | 2 | 0 |
| src/port/Makefile | 6 | 0 |
| src/port/meson.build | 1 | 0 |
| src/port/pg_bitutils.c | 18 | 0 |
| src/port/pg_popcount_sve.c | 103 | 0 |
From 0b51becb0505d5bde5f8e2acc90a7f4f4b604fe3 Mon Sep 17 00:00:00 2001
From: Rama Malladi <rvmallad@amazon.com>
Date: Wed, 27 Nov 2024 07:15:23 -0600
Subject: [PATCH] SVE popcount support
---
config/c-compiler.m4 | 33 +++++++++++
configure | 104 +++++++++++++++++++++++++++++++++
configure.ac | 15 +++++
meson.build | 37 ++++++++++++
src/Makefile.global.in | 2 +
src/include/pg_config.h.in | 3 +
src/include/port/pg_bitutils.h | 9 +++
src/port/Makefile | 6 ++
src/port/meson.build | 1 +
src/port/pg_bitutils.c | 18 ++++++
src/port/pg_popcount_sve.c | 103 ++++++++++++++++++++++++++++++++
11 files changed, 331 insertions(+)
create mode 100644 src/port/pg_popcount_sve.c
diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index a129edb88e..eee9720931 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -754,3 +754,36 @@ if test x"$Ac_cachevar" = x"yes"; then
fi
undefine([Ac_cachevar])dnl
])# PGAC_AVX512_POPCNT_INTRINSICS
+
+# PGAC_SVE_POPCNT_INTRINSICS
+# ----------------------------
+# Check if the compiler supports the SVE popcount instructions.
+#
+# An optional compiler flag can be passed as argument (e.g.
+# -march=armv8-a+sve). If the intrinsics are supported, sets
+# pgac_sve_popcnt_intrinsics, and CFLAGS_POPCNT.
+AC_DEFUN([PGAC_SVE_POPCNT_INTRINSICS],
+[define([Ac_cachevar], [AS_TR_SH([pgac_cv_sve_popcnt_intrinsics_$1])])dnl
+AC_CACHE_CHECK([for svcnt_u8_z with CFLAGS=$1], [Ac_cachevar],
+[pgac_save_CFLAGS=$CFLAGS
+CFLAGS="$pgac_save_CFLAGS $1"
+AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <arm_sve.h>],
+ [svuint8_t accum = svdup_u8(0);
+ svuint8_t buf = svdup_u8(0);
+ svbool_t pgTrue = svptrue_b8();
+ uint64_t popcnt = 0;
+
+ accum = svcnt_u8_z(pgTrue, buf);
+ popcnt = svaddv_u8(pgTrue, accum);
+
+ /* return computed value, to prevent the above being optimized away */
+ return popcnt == 0;])],
+ [Ac_cachevar=yes],
+ [Ac_cachevar=no])
+CFLAGS="$pgac_save_CFLAGS"])
+if test x"$Ac_cachevar" = x"yes"; then
+ CFLAGS_POPCNT="$1"
+ pgac_sve_popcnt_intrinsics=yes
+fi
+undefine([Ac_cachevar])dnl
+])# PGAC_SVE_POPCNT_INTRINSICS
diff --git a/configure b/configure
index 199d666aa7..ab6092d3c5 100755
--- a/configure
+++ b/configure
@@ -646,7 +646,9 @@ MSGMERGE
MSGFMT_FLAGS
MSGFMT
PG_CRC32C_OBJS
+PG_POPCNT_OBJS
CFLAGS_CRC
+CFLAGS_POPCNT
LIBOBJS
OPENSSL
ZSTD
@@ -17653,6 +17655,108 @@ fi
+# Check for SVE popcount intrinsics
+#
+# First check if svcnt_u8_z intrinsics can be used with the default compiler
+# flags. If not, check if adding -march=armv8-a+sve flag helps.
+# CFLAGS_POPCNT is set if the extra flag is required.
+CFLAGS_POPCNT=""
+PG_POPCNT_OBJS=""
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for svcnt_u8_z with CFLAGS=" >&5
+$as_echo_n "checking for svcnt_u8_z with CFLAGS=... " >&6; }
+if ${pgac_cv_sve_popcnt_intrinsics_+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ pgac_save_CFLAGS=$CFLAGS
+CFLAGS="$pgac_save_CFLAGS "
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <arm_sve.h>
+
+int main(void)
+{
+ svuint8_t accum = svdup_u8(0);
+ svuint8_t buf = svdup_u8(0);
+ svbool_t pgTrue = svptrue_b8();
+ uint64_t popcnt = 0;
+
+ accum = svcnt_u8_z(pgTrue, buf);
+ popcnt = svaddv_u8(pgTrue, accum);
+
+ /* return computed value, to prevent the above being optimized away */
+ return popcnt == 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ pgac_cv_sve_popcnt_intrinsics_=yes
+else
+ pgac_cv_sve_popcnt_intrinsics_=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+CFLAGS="$pgac_save_CFLAGS"
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_sve_popcnt_intrinsics_" >&5
+$as_echo "$pgac_cv_sve_popcnt_intrinsics_" >&6; }
+if test x"$pgac_cv_sve_popcnt_intrinsics_" = x"yes"; then
+ CFLAGS_POPCNT=""
+ pgac_sve_popcnt_intrinsics=yes
+fi
+
+if test x"$pgac_sve_popcnt_intrinsics" != x"yes"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for svcnt_u8_z with CFLAGS=-march=armv8-a+sve" >&5
+$as_echo_n "checking for svcnt_u8_z with CFLAGS=-march=armv8-a+sve... " >&6; }
+if ${pgac_cv_sve_popcnt_intrinsics__march_armv8_apsve+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ pgac_save_CFLAGS=$CFLAGS
+CFLAGS="$pgac_save_CFLAGS -march=armv8-a+sve"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <arm_sve.h>
+
+int main(void)
+{
+ svuint8_t accum = svdup_u8(0);
+ svuint8_t buf = svdup_u8(0);
+ svbool_t pgTrue = svptrue_b8();
+ uint64_t popcnt = 0;
+
+ accum = svcnt_u8_z(pgTrue, buf);
+ popcnt = svaddv_u8(pgTrue, accum);
+
+ /* return computed value, to prevent the above being optimized away */
+ return popcnt == 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ pgac_cv_sve_popcnt_intrinsics__march_armv8_apsve=yes
+else
+ pgac_cv_sve_popcnt_intrinsics__march_armv8_apsve=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+CFLAGS="$pgac_save_CFLAGS"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_sve_popcnt_intrinsics__march_armv8_apsve" >&5
+$as_echo "$pgac_cv_sve_popcnt_intrinsics__march_armv8_apsve" >&6; }
+if test x"$pgac_cv_sve_popcnt_intrinsics__march_armv8_apsve" = x"yes"; then
+ CFLAGS_POPCNT="-march=armv8-a+sve"
+ pgac_sve_popcnt_intrinsics=yes
+fi
+
+if test x"$pgac_sve_popcnt_intrinsics" = x"yes"; then
+ PG_POPCNT_OBJS="pg_popcount_sve.o"
+
+$as_echo "#define USE_SVE_POPCNT_WITH_RUNTIME_CHECK 1" >>confdefs.h
+
+fi
+fi
+
+
+
+
# Select CRC-32C implementation.
#
# If we are targeting a processor that has Intel SSE 4.2 instructions, we can
diff --git a/configure.ac b/configure.ac
index 4f56bb5062..3c0b8ffdbe 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2107,6 +2107,21 @@ PGAC_LOONGARCH_CRC32C_INTRINSICS()
AC_SUBST(CFLAGS_CRC)
+# Check for ARMv8 SVE popcount intrinsics
+#
+CFLAGS_POPCNT=""
+PG_POPCNT_OBJS=""
+PGAC_SVE_POPCNT_INTRINSICS([])
+if test x"$pgac_sve_popcnt_intrinsics" != x"yes"; then
+ PGAC_SVE_POPCNT_INTRINSICS([-march=armv8-a+sve])
+fi
+if test x"$pgac_sve_popcnt_intrinsics" = x"yes"; then
+ PG_POPCNT_OBJS="pg_popcount_sve.o"
+ AC_DEFINE(USE_SVE_POPCNT_WITH_RUNTIME_CHECK, 1, [Define to 1 to use SVE popcount instructions with a runtime check.])
+fi
+AC_SUBST(CFLAGS_POPCNT)
+AC_SUBST(PG_POPCNT_OBJS)
+
# Select CRC-32C implementation.
#
# If we are targeting a processor that has Intel SSE 4.2 instructions, we can
diff --git a/meson.build b/meson.build
index 83e61d0f4a..7c927883af 100644
--- a/meson.build
+++ b/meson.build
@@ -2205,6 +2205,43 @@ int main(void)
endif
+###############################################################
+# Check for the availability of SVE popcount intrinsics.
+###############################################################
+cflags_popcnt = []
+if host_cpu == 'arm' or host_cpu == 'aarch64'
+
+ prog = '''
+#include <arm_sve.h>
+
+int main(void)
+{
+ svuint8_t accum = svdup_u8(0);
+ svuint8_t buf = svdup_u8(0);
+ svbool_t pgTrue = svptrue_b8();
+ uint64_t popcnt = 0;
+
+ accum = svcnt_u8_z(pgTrue, buf);
+ popcnt = svaddv_u8(pgTrue, accum);
+
+ /* return computed value, to prevent the above being optimized away */
+ return popcnt == 0;
+}
+'''
+
+ if cc.links(prog, name: 'SVE popcount without -march=armv8-a+sve',
+ args: test_c_args)
+ # Use ARM POPCNT Extension, with runtime check
+ cdata.set('USE_SVE_POPCNT_WITH_RUNTIME_CHECK', 1)
+ elif cc.links(prog, name: 'SVE popcount with -march=armv8-a+sve',
+ args: test_c_args + ['-march=armv8-a+sve'])
+ # Use ARM POPCNT Extension, with runtime check
+ cflags_popcnt += ['-march=armv8-a+sve']
+ cdata.set('USE_SVE_POPCNT_WITH_RUNTIME_CHECK', 1)
+ endif
+endif
+
+
###############################################################
# Select CRC-32C implementation.
#
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 0f38d712d1..523072a0db 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -263,6 +263,7 @@ CXXFLAGS_SL_MODULE = @CXXFLAGS_SL_MODULE@
CFLAGS_UNROLL_LOOPS = @CFLAGS_UNROLL_LOOPS@
CFLAGS_VECTORIZE = @CFLAGS_VECTORIZE@
CFLAGS_CRC = @CFLAGS_CRC@
+CFLAGS_POPCNT = @CFLAGS_POPCNT@
PERMIT_DECLARATION_AFTER_STATEMENT = @PERMIT_DECLARATION_AFTER_STATEMENT@
PERMIT_MISSING_VARIABLE_DECLARATIONS = @PERMIT_MISSING_VARIABLE_DECLARATIONS@
CXXFLAGS = @CXXFLAGS@
@@ -769,6 +770,7 @@ LIBOBJS = @LIBOBJS@
# files needed for the chosen CRC-32C implementation
PG_CRC32C_OBJS = @PG_CRC32C_OBJS@
+PG_POPCNT_OBJS = @PG_POPCNT_OBJS@
LIBS := -lpgcommon -lpgport $(LIBS)
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 40e4b2e381..6baec9549a 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -666,6 +666,9 @@
/* Define to 1 to use AVX-512 popcount instructions with a runtime check. */
#undef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+/* Define to 1 to use SVE popcount instructions with a runtime check. */
+#undef USE_SVE_POPCNT_WITH_RUNTIME_CHECK
+
/* Define to 1 to build with Bonjour support. (--with-bonjour) */
#undef USE_BONJOUR
diff --git a/src/include/port/pg_bitutils.h b/src/include/port/pg_bitutils.h
index 4d88478c9c..d6a9aee00e 100644
--- a/src/include/port/pg_bitutils.h
+++ b/src/include/port/pg_bitutils.h
@@ -321,11 +321,20 @@ extern uint64 pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask);
/* Use a portable implementation -- no need for a function pointer. */
extern int pg_popcount32(uint32 word);
extern int pg_popcount64(uint64 word);
+#ifdef USE_SVE_POPCNT_WITH_RUNTIME_CHECK
+extern PGDLLIMPORT uint64 (*pg_popcount_optimized) (const char *buf, int bytes);
+#else
extern uint64 pg_popcount_optimized(const char *buf, int bytes);
+#endif
extern uint64 pg_popcount_masked_optimized(const char *buf, int bytes, bits8 mask);
#endif /* TRY_POPCNT_FAST */
+#ifdef USE_SVE_POPCNT_WITH_RUNTIME_CHECK
+extern uint64 pg_popcount_sve(const char *buf, int bytes);
+extern int check_sve_support(void);
+#endif
+
/*
* Returns the number of 1-bits in buf.
*
diff --git a/src/port/Makefile b/src/port/Makefile
index 366c814bd9..7ecf776069 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -38,6 +38,7 @@ LIBS += $(PTHREAD_LIBS)
OBJS = \
$(LIBOBJS) \
$(PG_CRC32C_OBJS) \
+ $(PG_POPCNT_OBJS) \
bsearch_arg.o \
chklocale.o \
inet_net_ntop.o \
@@ -92,6 +93,11 @@ pg_crc32c_armv8.o: CFLAGS+=$(CFLAGS_CRC)
pg_crc32c_armv8_shlib.o: CFLAGS+=$(CFLAGS_CRC)
pg_crc32c_armv8_srv.o: CFLAGS+=$(CFLAGS_CRC)
+# pg_popcount_sve.o and its _srv.o version need CFLAGS_POPCNT
+pg_popcount_sve.o: CFLAGS+=$(CFLAGS_POPCNT)
+pg_popcount_sve_shlib.o: CFLAGS+=$(CFLAGS_POPCNT)
+pg_popcount_sve_srv.o: CFLAGS+=$(CFLAGS_POPCNT)
+
#
# Shared library versions of object files
#
diff --git a/src/port/meson.build b/src/port/meson.build
index 83a0632520..7af85c8111 100644
--- a/src/port/meson.build
+++ b/src/port/meson.build
@@ -91,6 +91,7 @@ replace_funcs_pos = [
['pg_crc32c_armv8', 'USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK', 'crc'],
['pg_crc32c_armv8_choose', 'USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK'],
['pg_crc32c_sb8', 'USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK'],
+ ['pg_popcount_sve', 'USE_SVE_POPCNT_WITH_RUNTIME_CHECK', 'popcnt'],
# loongarch
['pg_crc32c_loongarch', 'USE_LOONGARCH_CRC32C'],
diff --git a/src/port/pg_bitutils.c b/src/port/pg_bitutils.c
index 87f56e82b8..168bf24635 100644
--- a/src/port/pg_bitutils.c
+++ b/src/port/pg_bitutils.c
@@ -125,6 +125,22 @@ uint64 (*pg_popcount_optimized) (const char *buf, int bytes) = pg_popcount_choo
uint64 (*pg_popcount_masked_optimized) (const char *buf, int bytes, bits8 mask) = pg_popcount_masked_choose;
#endif /* TRY_POPCNT_FAST */
+#ifdef USE_SVE_POPCNT_WITH_RUNTIME_CHECK
+static uint64 pg_popcount_choose(const char *buf, int bytes);
+uint64 (*pg_popcount_optimized) (const char *buf, int bytes) = pg_popcount_choose;
+
+static inline uint64
+pg_popcount_choose(const char *buf, int bytes)
+{
+ if (check_sve_support())
+ pg_popcount_optimized = pg_popcount_sve;
+ else
+ pg_popcount_optimized = pg_popcount_slow;
+ return pg_popcount_optimized(buf, bytes);
+}
+
+#endif /* USE_SVE_POPCNT_WITH_RUNTIME_CHECK */
+
#ifdef TRY_POPCNT_FAST
/*
@@ -507,6 +523,7 @@ pg_popcount64(uint64 word)
return pg_popcount64_slow(word);
}
+#ifndef USE_SVE_POPCNT_WITH_RUNTIME_CHECK
/*
* pg_popcount_optimized
* Returns the number of 1-bits in buf
@@ -516,6 +533,7 @@ pg_popcount_optimized(const char *buf, int bytes)
{
return pg_popcount_slow(buf, bytes);
}
+#endif
/*
* pg_popcount_masked_optimized
diff --git a/src/port/pg_popcount_sve.c b/src/port/pg_popcount_sve.c
new file mode 100644
index 0000000000..04a08fbcc3
--- /dev/null
+++ b/src/port/pg_popcount_sve.c
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_popcount_sve.c
+ * pg_popcount() using SVE population count instruction
+ *
+ * IDENTIFICATION
+ * src/port/pg_popcount_sve.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+
+#ifdef USE_SVE_POPCNT_WITH_RUNTIME_CHECK
+
+#include <arm_sve.h>
+
+#include "port/pg_bitutils.h"
+
+// check if sve supported
+int check_sve_support(void)
+{
+ // Read ID_AA64PFR0_EL1 register
+ uint64_t pfr0;
+ __asm__ __volatile__(
+ "mrs %0, ID_AA64PFR0_EL1"
+ : "=r" (pfr0));
+
+ // SVE bits are 32-35
+ return (pfr0 >> 32) & 0xf;
+}
+
+/*
+ * pg_popcount_sve
+ * Returns the number of 1-bits in buf
+ */
+uint64
+pg_popcount_sve(const char *buf, int bytes)
+{
+ svuint8_t cnt8 = svdup_u8(0);
+ svuint8_t accum8 = svdup_u8(0), val8;
+ svbool_t pg8True = svptrue_b8(), pg64True, pg;
+ svuint64_t cnt64, accum64, accum64_1, val64;
+
+ int64_t popcount = 0;
+ const char *aligned_buf, *epilogue_buf;
+ int i, prologue_loop_bytes, kernel_loop_sve_cnt, epilogue_loop_bytes;
+
+ // for small buffer sizes (<= 128-bytes), execute 1-byte SVE instructions
+ // for larger buffer sizes (> 128-bytes), execute 1-byte + 8-byte SVE instructions
+ if (bytes <= 128)
+ prologue_loop_bytes = bytes;
+ else
+ {
+ aligned_buf = (const char *) TYPEALIGN_DOWN(sizeof(uint64_t), buf) + sizeof(uint64_t);
+ prologue_loop_bytes = aligned_buf - buf;
+ }
+
+ for (i = 0; i < prologue_loop_bytes; i += svcntb())
+ {
+ pg = svwhilelt_b8(i, prologue_loop_bytes);
+ val8 = svld1_u8(pg, (uint8_t*)(buf + i));
+ cnt8 = svcnt_u8_x(pg, val8);
+ popcount += svaddv_u8(pg, cnt8);
+ }
+
+ if (bytes > 128)
+ {
+ cnt64 = svdup_u64(0);
+ accum64 = svdup_u64(0);
+ accum64_1 = svdup_u64(0);
+ pg64True = svptrue_b64();
+
+ kernel_loop_sve_cnt = ((bytes - prologue_loop_bytes) / 8) / svcntd() / 2;
+ epilogue_loop_bytes = bytes - prologue_loop_bytes - (kernel_loop_sve_cnt * 8 * svcntd() * 2);
+ epilogue_buf = (const char *) buf + prologue_loop_bytes + (kernel_loop_sve_cnt * 8 * svcntd() * 2);
+
+ /* loop unroll by 2 */
+ for (i = 0; i < kernel_loop_sve_cnt * 2 * (int)svcntd(); i += svcntd() * 2)
+ {
+ cnt64 = svld1_u64(pg64True, (uint64_t*)(aligned_buf + sizeof(uint64_t) * i));
+ val64 = svcnt_u64_m(cnt64, pg64True, cnt64);
+ accum64 = svadd_u64_x(pg64True, val64, accum64);
+ cnt64 = svld1_u64(pg64True, (uint64_t*)(aligned_buf + sizeof(uint64_t) * (i + svcntd())));
+ val64 = svcnt_u64_m(cnt64, pg64True, cnt64);
+ accum64_1 = svadd_u64_x(pg64True, val64, accum64_1);
+ }
+ popcount += svaddv_u64(pg64True, accum64);
+ popcount += svaddv_u64(pg64True, accum64_1);
+
+ accum8 = svdup_u8(0);
+ for (i = 0; i < epilogue_loop_bytes; i += svcntb())
+ {
+ pg = svwhilelt_b8(i, epilogue_loop_bytes);
+ val8 = svld1_u8(pg, (uint8_t*)(epilogue_buf + i));
+ cnt8 = svcnt_u8_z(pg, val8);
+ accum8 = svadd_u8_m(pg8True, cnt8, accum8);
+ }
+ popcount += svaddv_u8(pg8True, accum8);
+ }
+
+ return popcount;
+}
+#endif /* USE_SVE_POPCNT_WITH_RUNTIME_CHECK */
--
2.45.1