Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Use direct function calls for pg_popcount{32,64} on non-x86 platforms
authorJohn Naylor <john.naylor@postgresql.org>
Mon, 16 Aug 2021 15:45:21 +0000 (11:45 -0400)
committerJohn Naylor <john.naylor@postgresql.org>
Mon, 16 Aug 2021 15:51:15 +0000 (11:51 -0400)
Previously, all pg_popcount{32,64} calls were indirected through
a function pointer, even though we had no fast implementation for
non-x86 platforms. Instead, for those platforms use wrappers around
the pg_popcount{32,64}_slow functions.

Review and additional hacking by David Rowley
Reviewed by Álvaro Herrera

Discussion: https://www.postgresql.org/message-id/flat/CAFBsxsE7otwnfA36Ly44zZO%2Bb7AEWHRFANxR1h1kxveEV%3DghLQ%40mail.gmail.com

src/include/port/pg_bitutils.h
src/port/pg_bitutils.c

index 086bd08132ff961f09ec13072ce64f5b0b320de9..7dd7fef4f7447b93d082e277f64682a89c270e3f 100644 (file)
@@ -253,10 +253,40 @@ pg_ceil_log2_64(uint64 num)
        return pg_leftmost_one_pos64(num - 1) + 1;
 }
 
-/* Count the number of one-bits in a uint32 or uint64 */
+/*
+ * With MSVC on x86_64 builds, try using native popcnt instructions via the
+ * __popcnt and __popcnt64 intrinsics.  These don't work the same as GCC's
+ * __builtin_popcount* intrinsic functions as they always emit popcnt
+ * instructions.
+ */
+#if defined(_MSC_VER) && defined(_M_AMD64)
+#define HAVE_X86_64_POPCNTQ
+#endif
+
+/*
+ * On x86_64, we can use the hardware popcount instruction, but only if
+ * we can verify that the CPU supports it via the cpuid instruction.
+ *
+ * Otherwise, we fall back to a hand-rolled implementation.
+ */
+#ifdef HAVE_X86_64_POPCNTQ
+#if defined(HAVE__GET_CPUID) || defined(HAVE__CPUID)
+#define TRY_POPCNT_FAST 1
+#endif
+#endif
+
+#ifdef TRY_POPCNT_FAST
+/* Attempt to use the POPCNT instruction, but perform a runtime check first */
 extern int (*pg_popcount32) (uint32 word);
 extern int (*pg_popcount64) (uint64 word);
 
+#else
+/* Use a portable implementation -- no need for a function pointer. */
+extern int pg_popcount32(uint32 word);
+extern int pg_popcount64(uint64 word);
+
+#endif                         /* TRY_POPCNT_FAST */
+
 /* Count the number of one-bits in a byte array */
 extern uint64 pg_popcount(const char *buf, int bytes);
 
index 10676b285cae9380f0a2a8030abb8e03a98618e5..9d0e2016c3140552f69fe19000bb80841d633f93 100644 (file)
@@ -103,29 +103,6 @@ const uint8 pg_number_of_ones[256] = {
    4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
 };
 
-/*
- * With MSVC on x86_64 builds, try using native popcnt instructions via the
- * __popcnt and __popcnt64 intrinsics.  These don't work the same as GCC's
- * __builtin_popcount* intrinsic functions as they always emit popcnt
- * instructions.
- */
-#if defined(_MSC_VER) && defined(_M_AMD64)
-#define HAVE_X86_64_POPCNTQ
-#endif
-
-/*
- * On x86_64, we can use the hardware popcount instruction, but only if
- * we can verify that the CPU supports it via the cpuid instruction.
- *
- * Otherwise, we fall back to __builtin_popcount if the compiler has that,
- * or a hand-rolled implementation if not.
- */
-#ifdef HAVE_X86_64_POPCNTQ
-#if defined(HAVE__GET_CPUID) || defined(HAVE__CPUID)
-#define TRY_POPCNT_FAST 1
-#endif
-#endif
-
 static int pg_popcount32_slow(uint32 word);
 static int pg_popcount64_slow(uint64 word);
 
@@ -138,9 +115,6 @@ static int  pg_popcount64_fast(uint64 word);
 
 int            (*pg_popcount32) (uint32 word) = pg_popcount32_choose;
 int            (*pg_popcount64) (uint64 word) = pg_popcount64_choose;
-#else
-int            (*pg_popcount32) (uint32 word) = pg_popcount32_slow;
-int            (*pg_popcount64) (uint64 word) = pg_popcount64_slow;
 #endif                         /* TRY_POPCNT_FAST */
 
 #ifdef TRY_POPCNT_FAST
@@ -291,6 +265,28 @@ pg_popcount64_slow(uint64 word)
 #endif                         /* HAVE__BUILTIN_POPCOUNT */
 }
 
+#ifndef TRY_POPCNT_FAST
+
+/*
+ * When the POPCNT instruction is not available, there's no point in using
+ * function pointers to vary the implementation between the fast and slow
+ * method.  We instead just make these actual external functions when
+ * TRY_POPCNT_FAST is not defined.  The compiler should be able to inline
+ * the slow versions here.
+ */
+int
+pg_popcount32(uint32 word)
+{
+   return pg_popcount32_slow(word);
+}
+
+int
+pg_popcount64(uint64 word)
+{
+   return pg_popcount64_slow(word);
+}
+
+#endif                         /* !TRY_POPCNT_FAST */
 
 /*
  * pg_popcount