如何使用MSVC内建函数来取得这个愚者程式码的对等物?

ryevplcw  于 2022-12-03  发布在  其他
关注(0)|答案(6)|浏览(181)

下面的代码调用了愚者中clz/ctz的内置函数,在其他系统上,有C版本。显然,如果系统有内置的clz/ctz指令,如x86和ARM,C版本有点次优。

#ifdef __GNUC__
#define clz(x) __builtin_clz(x)
#define ctz(x) __builtin_ctz(x)
#else
static uint32_t ALWAYS_INLINE popcnt( uint32_t x )
{
    x -= ((x >> 1) & 0x55555555);
    x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
    x = (((x >> 4) + x) & 0x0f0f0f0f);
    x += (x >> 8);
    x += (x >> 16);
    return x & 0x0000003f;
}
static uint32_t ALWAYS_INLINE clz( uint32_t x )
{
    x |= (x >> 1);
    x |= (x >> 2);
    x |= (x >> 4);
    x |= (x >> 8);
    x |= (x >> 16);
    return 32 - popcnt(x);
}
static uint32_t ALWAYS_INLINE ctz( uint32_t x )
{
    return popcnt((x & -x) - 1);
}

#endif

我需要调用哪些函数,需要包含哪些头文件,等等,才能在这里为MSVC添加一个合适的ifdef?我已经看过this page,但是我不完全确定#pragma是做什么的(它是必需的吗?)以及它对编译的MSVC版本要求有什么限制。作为一个不真正使用MSVC的人,我也不知道这些内部函数在其他架构上是否有C等价物,或者在#定义它们时是否也必须#ifdef x86/x86_64。

slsn1g29

slsn1g291#

从sh0dan代码跳转,实现应按如下方式更正:

#ifdef _MSC_VER
#include <intrin.h>

uint32_t __inline ctz( uint32_t value )
{
    DWORD trailing_zero = 0;

    if ( _BitScanForward( &trailing_zero, value ) )
    {
        return trailing_zero;
    }
    else
    {
        // This is undefined, I better choose 32 than 0
        return 32;
    }
}

uint32_t __inline clz( uint32_t value )
{
    DWORD leading_zero = 0;

    if ( _BitScanReverse( &leading_zero, value ) )
    {
       return 31 - leading_zero;
    }
    else
    {
         // Same remarks as above
         return 32;
    }
}
#endif

正如代码中所注解的,如果value为0,ctz和clz都是未定义的。在我们的抽象中,我们将__builtin_clz(value)固定为(value?__builtin_clz(value):32),但这是一个选择

eivnm1vs

eivnm1vs2#

  1. MSVC中int __builtin_ctz(unsigned int x)的等效函数为unsigned int _tzcnt_u32(unsigned int a)(对于 *32位 * 整数),并返回尾随零的计数。对于 *64位 *,请使用**unsigned __int64 _tzcnt_u64(unsigned __int64 a)**1。
  2. MSVC中int __builtin_clz(unsigned int x)的等效函数为unsigned int _lzcnt_u32(unsigned int a)(对于 *32位 * 整数),并返回前导零的计数。对于 *64位 *,请使用**unsigned __int64 _lzcnt_u64(unsigned __int64 a)**2
    C++信头:immintrin.h
wmvff8tz

wmvff8tz3#

我在一个韩国网站https://torbjorn.tistory.com/317中找到的在msvc编译器中,可以用__lzcnt(unsigned int)来替换gcc编译器中的__builtin_clz(unsigned int)

piwo6bdm

piwo6bdm4#

如果MSVC有一个编译器内部函数,它将位于:
Compiler Intrinsics on MSDN
否则,您必须使用__asm编写它

n53p2ov0

n53p2ov05#

在Linux和Windows(x86)上测试:

#ifdef WIN32
    #include <intrin.h>
    static uint32_t __inline __builtin_clz(uint32_t x) {
        unsigned long r = 0;
        _BitScanReverse(&r, x);
        return (31-r);
    }
#endif

uint32_t clz64(const uint64_t x)
{
    uint32_t u32 = (x >> 32);
    uint32_t result = u32 ? __builtin_clz(u32) : 32;
    if (result == 32) {
        u32 = x & 0xFFFFFFFFUL;
        result += (u32 ? __builtin_clz(u32) : 32);
    }
    return result;
}
prdp8dxp

prdp8dxp6#

有两个内部函数“_BitScanForward”和“_BitScanReverse”,它们适合MSVC的相同目的。包括。函数为:

#ifdef _MSC_VER
#include <intrin.h>

static uint32_t __inline ctz( uint32_t x )
{
   int r = 0;
   _BitScanReverse(&r, x);
   return r;
}

static uint32_t __inline clz( uint32_t x )
{
   int r = 0;
   _BitScanForward(&r, x);
   return r;
}
#endif

存在等效的64位版本“_BitScanForward64”和“_BitScanReverse64”。
点击此处阅读更多信息:
x86 Intrinsics on MSDN

相关问题