Use direct function calls for pg_popcount{32,64} on non-x86 platforms
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
This commit is contained in:
parent
ea499f3d28
commit
4864c8e8f1
@ -253,10 +253,40 @@ pg_ceil_log2_64(uint64 num)
|
|||||||
return pg_leftmost_one_pos64(num - 1) + 1;
|
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_popcount32) (uint32 word);
|
||||||
extern int (*pg_popcount64) (uint64 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 */
|
/* Count the number of one-bits in a byte array */
|
||||||
extern uint64 pg_popcount(const char *buf, int bytes);
|
extern uint64 pg_popcount(const char *buf, int bytes);
|
||||||
|
|
||||||
|
@ -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
|
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_popcount32_slow(uint32 word);
|
||||||
static int pg_popcount64_slow(uint64 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_popcount32) (uint32 word) = pg_popcount32_choose;
|
||||||
int (*pg_popcount64) (uint64 word) = pg_popcount64_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 */
|
#endif /* TRY_POPCNT_FAST */
|
||||||
|
|
||||||
#ifdef TRY_POPCNT_FAST
|
#ifdef TRY_POPCNT_FAST
|
||||||
@ -291,6 +265,28 @@ pg_popcount64_slow(uint64 word)
|
|||||||
#endif /* HAVE__BUILTIN_POPCOUNT */
|
#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
|
* pg_popcount
|
||||||
|
Loading…
x
Reference in New Issue
Block a user