mirror of
https://github.com/postgres/postgres.git
synced 2025-05-02 11:44:50 +03:00
677319746 added support for making use of MSVC's bit scanning functions. However, that commit failed to consider 32-bit MSVC builds where the 64-bit versions of these functions are unavailable. This resulted in compilation failures on 32-bit MSVC. Here we adjust the code so we fall back on the manual way of finding the bit positions for 64-bit integers when building on 32-bit MSVC. Bug: #17967 Reported-by: Youmiu Mo Discussion: https://postgr.es/m/17967-cd21e34a314141b2@postgresql.org
340 lines
7.5 KiB
C
340 lines
7.5 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* pg_bitutils.h
|
|
* Miscellaneous functions for bit-wise operations.
|
|
*
|
|
*
|
|
* Copyright (c) 2019-2023, PostgreSQL Global Development Group
|
|
*
|
|
* src/include/port/pg_bitutils.h
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#ifndef PG_BITUTILS_H
|
|
#define PG_BITUTILS_H
|
|
|
|
#ifdef _MSC_VER
|
|
#include <intrin.h>
|
|
#define HAVE_BITSCAN_FORWARD
|
|
#define HAVE_BITSCAN_REVERSE
|
|
|
|
#else
|
|
#if defined(HAVE__BUILTIN_CTZ)
|
|
#define HAVE_BITSCAN_FORWARD
|
|
#endif
|
|
|
|
#if defined(HAVE__BUILTIN_CLZ)
|
|
#define HAVE_BITSCAN_REVERSE
|
|
#endif
|
|
#endif /* _MSC_VER */
|
|
|
|
extern PGDLLIMPORT const uint8 pg_leftmost_one_pos[256];
|
|
extern PGDLLIMPORT const uint8 pg_rightmost_one_pos[256];
|
|
extern PGDLLIMPORT const uint8 pg_number_of_ones[256];
|
|
|
|
/*
|
|
* pg_leftmost_one_pos32
|
|
* Returns the position of the most significant set bit in "word",
|
|
* measured from the least significant bit. word must not be 0.
|
|
*/
|
|
static inline int
|
|
pg_leftmost_one_pos32(uint32 word)
|
|
{
|
|
#ifdef HAVE__BUILTIN_CLZ
|
|
Assert(word != 0);
|
|
|
|
return 31 - __builtin_clz(word);
|
|
#elif defined(_MSC_VER)
|
|
unsigned long result;
|
|
bool non_zero;
|
|
|
|
non_zero = _BitScanReverse(&result, word);
|
|
Assert(non_zero);
|
|
return (int) result;
|
|
#else
|
|
int shift = 32 - 8;
|
|
|
|
Assert(word != 0);
|
|
|
|
while ((word >> shift) == 0)
|
|
shift -= 8;
|
|
|
|
return shift + pg_leftmost_one_pos[(word >> shift) & 255];
|
|
#endif /* HAVE__BUILTIN_CLZ */
|
|
}
|
|
|
|
/*
|
|
* pg_leftmost_one_pos64
|
|
* As above, but for a 64-bit word.
|
|
*/
|
|
static inline int
|
|
pg_leftmost_one_pos64(uint64 word)
|
|
{
|
|
#ifdef HAVE__BUILTIN_CLZ
|
|
Assert(word != 0);
|
|
|
|
#if defined(HAVE_LONG_INT_64)
|
|
return 63 - __builtin_clzl(word);
|
|
#elif defined(HAVE_LONG_LONG_INT_64)
|
|
return 63 - __builtin_clzll(word);
|
|
#else
|
|
#error must have a working 64-bit integer datatype
|
|
#endif /* HAVE_LONG_INT_64 */
|
|
|
|
#elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_ARM64))
|
|
unsigned long result;
|
|
bool non_zero;
|
|
|
|
non_zero = _BitScanReverse64(&result, word);
|
|
Assert(non_zero);
|
|
return (int) result;
|
|
#else
|
|
int shift = 64 - 8;
|
|
|
|
Assert(word != 0);
|
|
|
|
while ((word >> shift) == 0)
|
|
shift -= 8;
|
|
|
|
return shift + pg_leftmost_one_pos[(word >> shift) & 255];
|
|
#endif /* HAVE__BUILTIN_CLZ */
|
|
}
|
|
|
|
/*
|
|
* pg_rightmost_one_pos32
|
|
* Returns the position of the least significant set bit in "word",
|
|
* measured from the least significant bit. word must not be 0.
|
|
*/
|
|
static inline int
|
|
pg_rightmost_one_pos32(uint32 word)
|
|
{
|
|
#ifdef HAVE__BUILTIN_CTZ
|
|
Assert(word != 0);
|
|
|
|
return __builtin_ctz(word);
|
|
#elif defined(_MSC_VER)
|
|
unsigned long result;
|
|
bool non_zero;
|
|
|
|
non_zero = _BitScanForward(&result, word);
|
|
Assert(non_zero);
|
|
return (int) result;
|
|
#else
|
|
int result = 0;
|
|
|
|
Assert(word != 0);
|
|
|
|
while ((word & 255) == 0)
|
|
{
|
|
word >>= 8;
|
|
result += 8;
|
|
}
|
|
result += pg_rightmost_one_pos[word & 255];
|
|
return result;
|
|
#endif /* HAVE__BUILTIN_CTZ */
|
|
}
|
|
|
|
/*
|
|
* pg_rightmost_one_pos64
|
|
* As above, but for a 64-bit word.
|
|
*/
|
|
static inline int
|
|
pg_rightmost_one_pos64(uint64 word)
|
|
{
|
|
#ifdef HAVE__BUILTIN_CTZ
|
|
Assert(word != 0);
|
|
|
|
#if defined(HAVE_LONG_INT_64)
|
|
return __builtin_ctzl(word);
|
|
#elif defined(HAVE_LONG_LONG_INT_64)
|
|
return __builtin_ctzll(word);
|
|
#else
|
|
#error must have a working 64-bit integer datatype
|
|
#endif /* HAVE_LONG_INT_64 */
|
|
|
|
#elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_ARM64))
|
|
unsigned long result;
|
|
bool non_zero;
|
|
|
|
non_zero = _BitScanForward64(&result, word);
|
|
Assert(non_zero);
|
|
return (int) result;
|
|
#else
|
|
int result = 0;
|
|
|
|
Assert(word != 0);
|
|
|
|
while ((word & 255) == 0)
|
|
{
|
|
word >>= 8;
|
|
result += 8;
|
|
}
|
|
result += pg_rightmost_one_pos[word & 255];
|
|
return result;
|
|
#endif /* HAVE__BUILTIN_CTZ */
|
|
}
|
|
|
|
/*
|
|
* pg_nextpower2_32
|
|
* Returns the next higher power of 2 above 'num', or 'num' if it's
|
|
* already a power of 2.
|
|
*
|
|
* 'num' mustn't be 0 or be above PG_UINT32_MAX / 2 + 1.
|
|
*/
|
|
static inline uint32
|
|
pg_nextpower2_32(uint32 num)
|
|
{
|
|
Assert(num > 0 && num <= PG_UINT32_MAX / 2 + 1);
|
|
|
|
/*
|
|
* A power 2 number has only 1 bit set. Subtracting 1 from such a number
|
|
* will turn on all previous bits resulting in no common bits being set
|
|
* between num and num-1.
|
|
*/
|
|
if ((num & (num - 1)) == 0)
|
|
return num; /* already power 2 */
|
|
|
|
return ((uint32) 1) << (pg_leftmost_one_pos32(num) + 1);
|
|
}
|
|
|
|
/*
|
|
* pg_nextpower2_64
|
|
* Returns the next higher power of 2 above 'num', or 'num' if it's
|
|
* already a power of 2.
|
|
*
|
|
* 'num' mustn't be 0 or be above PG_UINT64_MAX / 2 + 1.
|
|
*/
|
|
static inline uint64
|
|
pg_nextpower2_64(uint64 num)
|
|
{
|
|
Assert(num > 0 && num <= PG_UINT64_MAX / 2 + 1);
|
|
|
|
/*
|
|
* A power 2 number has only 1 bit set. Subtracting 1 from such a number
|
|
* will turn on all previous bits resulting in no common bits being set
|
|
* between num and num-1.
|
|
*/
|
|
if ((num & (num - 1)) == 0)
|
|
return num; /* already power 2 */
|
|
|
|
return ((uint64) 1) << (pg_leftmost_one_pos64(num) + 1);
|
|
}
|
|
|
|
/*
|
|
* pg_prevpower2_32
|
|
* Returns the next lower power of 2 below 'num', or 'num' if it's
|
|
* already a power of 2.
|
|
*
|
|
* 'num' mustn't be 0.
|
|
*/
|
|
static inline uint32
|
|
pg_prevpower2_32(uint32 num)
|
|
{
|
|
return ((uint32) 1) << pg_leftmost_one_pos32(num);
|
|
}
|
|
|
|
/*
|
|
* pg_prevpower2_64
|
|
* Returns the next lower power of 2 below 'num', or 'num' if it's
|
|
* already a power of 2.
|
|
*
|
|
* 'num' mustn't be 0.
|
|
*/
|
|
static inline uint64
|
|
pg_prevpower2_64(uint64 num)
|
|
{
|
|
return ((uint64) 1) << pg_leftmost_one_pos64(num);
|
|
}
|
|
|
|
/*
|
|
* pg_ceil_log2_32
|
|
* Returns equivalent of ceil(log2(num))
|
|
*/
|
|
static inline uint32
|
|
pg_ceil_log2_32(uint32 num)
|
|
{
|
|
if (num < 2)
|
|
return 0;
|
|
else
|
|
return pg_leftmost_one_pos32(num - 1) + 1;
|
|
}
|
|
|
|
/*
|
|
* pg_ceil_log2_64
|
|
* Returns equivalent of ceil(log2(num))
|
|
*/
|
|
static inline uint64
|
|
pg_ceil_log2_64(uint64 num)
|
|
{
|
|
if (num < 2)
|
|
return 0;
|
|
else
|
|
return pg_leftmost_one_pos64(num - 1) + 1;
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
|
|
/*
|
|
* Rotate the bits of "word" to the right/left by n bits.
|
|
*/
|
|
static inline uint32
|
|
pg_rotate_right32(uint32 word, int n)
|
|
{
|
|
return (word >> n) | (word << (32 - n));
|
|
}
|
|
|
|
static inline uint32
|
|
pg_rotate_left32(uint32 word, int n)
|
|
{
|
|
return (word << n) | (word >> (32 - n));
|
|
}
|
|
|
|
/* size_t variants of the above, as required */
|
|
|
|
#if SIZEOF_SIZE_T == 4
|
|
#define pg_leftmost_one_pos_size_t pg_leftmost_one_pos32
|
|
#define pg_nextpower2_size_t pg_nextpower2_32
|
|
#define pg_prevpower2_size_t pg_prevpower2_32
|
|
#else
|
|
#define pg_leftmost_one_pos_size_t pg_leftmost_one_pos64
|
|
#define pg_nextpower2_size_t pg_nextpower2_64
|
|
#define pg_prevpower2_size_t pg_prevpower2_64
|
|
#endif
|
|
|
|
#endif /* PG_BITUTILS_H */
|