如何使用MSVC内部函数来获得此GCC代码的等效项?
下面的代码在GCC中调用clz/ctz的内置函数,在其他系统上有C版本。显然,如果系统有一个内置的clz/ctz指令,比如x86和ARM,那么C版本有点不理想如何使用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) &
#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)
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;
}