如何使用MSVC内部函数来获得此GCC代码的等效项?

如何使用MSVC内部函数来获得此GCC代码的等效项?,c,visual-c++,intrinsics,C,Visual C++,Intrinsics,下面的代码在GCC中调用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) &

下面的代码在GCC中调用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?我已经看过了,但我不完全确定#pragma是用来做什么的(它是必需的吗?),以及它对编译的MSVC版本要求有什么限制。作为一个不真正使用MSVC的人,我也不知道这些内部函数在其他体系结构上是否有C等价物,或者在定义它们时是否也必须#ifdef x86/x86#u 64。

如果MSVC有一个用于此的编译器,它将在这里:

否则,您必须使用uu asm编写它。有两个内部函数“_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
\ifdef\u MSC\u VER
#包括
静态uint32\U t\U内联ctz(uint32\U t x)
{
int r=0;
_比特扫描反向(&r,x);
返回r;
}
静态uint32_t_uu内联clz(uint32_t x)
{
int r=0;
_位扫描转发(&r,x);
返回r;
}
#恩迪夫
有等效的64位版本“\u BitScanForward64”和“\u BitScanReverse64”

请在此处阅读更多信息:


从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
\ifdef\u MSC\u VER
#包括
uint32\U t\U内联ctz(uint32\U t值)
{
DWORD尾随_0=0;
if(_BitScanForward(&training_zero,value))
{
返回0;
}
其他的
{
//这是未定义的,我最好选择32而不是0
返回32;
}
}
uint32_t____内联clz(uint32_t值)
{
DWORD前导_0=0;
if(_BitScanReverse(&leading_zero,value))
{
返回31-前导零;
}
其他的
{
//同上
返回32;
}
}
#恩迪夫

如代码中所述,如果值为0,则ctz和clz都未定义。在我们的抽象中,我们将
\uuuuuuBuiltin\uCLZ(值)
固定为
(值?\uuuuBuiltin\uCLZ(值):32)
,但这是一个在linux和windows(x86)上测试的选择:

#ifdef WIN32
#包括
静态uint32_t______内联内置clz(uint32_t x){
无符号长r=0;
_比特扫描反向(&r,x);
返回(31-r);
}
#恩迪夫
uint32\u t clz64(常数uint64\u t x)
{
uint32_t u32=(x>>32);
uint32_t结果=u32?uu内置clz(u32):32;
如果(结果==32){
u32=x&0xFFFFFFFFUL;
结果+=(u32?uu内置clz(u32):32);
}
返回结果;
}

我在一个韩国网站上找到了它 在msvc编译器中,您可以使用
\uuuzcnt(unsigned int)
替换gcc编译器中的
\uuuuubuiltin\uclz(unsigned int)

  • MSVC中的int\uuuuu内置ctz(无符号int x)的等效函数是无符号int\utzcnt\u32(无符号int a)用于32位整数并返回尾随零的计数。对于64位,请使用无符号\uuu int64\u tzcnt\u64(无符号\uu int64 a)

  • MSVC中的int\uuuu内置clz(无符号intx)的等效函数是无符号int\uzcnt\u32(无符号inta)用于32位整数并返回前导零的计数。对于64位使用无符号\uuuu int64\ulzcnt\u64(无符号\uuuu int64 a)


  • C++标题:immintrin.h

    您上面提到的页面引用了作为.NET运行时一部分的函数,您正在尝试为.NET或本机Windows可执行文件构建程序吗?它是本机Windows可执行文件--我询问的部分原因是,我发现现在很难找到真正谈论C的Microsoft文档页。Libcxx实现ctz&clz调用了错误的函数(它们应该分别使用_BitScanForward和BitScanReverse,而不是BitScanReverse/BitScanForward)&clz是错误的,因为它返回的是位集的偏移量,而不是前导零的数量。MSVC中的
    \u builtin\u clz()
    。硬件必须支持SSE4。我的硬件支持SSE4,但不支持BMI1,所以编译但不做我期望的事情,而是作为BSR工作。
    31^\uuuu builtin\u clz
    等于
    \u BitScanReverse
    注意,当输入值为
    0
    ()时,GNU C
    \uu builtin\u ctz
    和clz也有未定义的行为;这允许它们作为单个
    bsf
    指令内联(或
    31^bsr
    适用于定义的输出范围)如果你需要处理可能为零的输入,那么你会希望GNU C内置类似的包装器,因此合适的做法是在BSF/31^BSR周围有一个可移植层,然后在其上进行零处理…并使用lzcnt
    #ifdef u BMI1_uu
    。相关:-MSVC不会公开未经修改的目标行为asm指令,即使它有一个可以实现这一点的API。(因此,您不需要初始化
    索引
    输出arg;不过这并没有什么坏处,编译器知道它是内在函数的仅输出操作数。)你测试过你的clz64的性能吗?我不会感到惊讶,所有这些分支都会使它比OP的实现慢。如果你想在GNU C上支持64位整数,就像普通人一样使用
    \uuuu builtin\u clzll
    。这样写可能会阻止GCC在6中使用单个64位
    bsr
    lzcnt
    4位编译。(但您也可以使用64位MSVC内部版本。)请注意,
    lzcnt
    指令需要BMI1。在较旧的CPU上,它以
    bsr
    的形式执行,给出
    31个lzcnt
    (并且不修改输入的目标寄存器=0)。GCC只会将
    \u builtin\u clz
    扩展为#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; }