2008-12-10 36 views
9

以下代碼在GCC中調用clz/ctz的內置函數,並在其他系統上調用C版本。顯然,如果系統具有像x86和ARM這樣的內置clz/ctz指令,那麼C版本就不太理想。如何使用MSVC intrinsics獲得這個GCC代碼的等價物?

#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。

+0

上述引用的頁面指的是.NET運行時的一部分功能,您是試圖構建.NET程序還是本機Windows可執行文件? – 2008-12-10 13:49:46

+0

這是一個本地Windows可執行文件 - 我要求的部分原因是我發現現在很難找到真正討論C的微軟文檔頁面。 – 2008-12-10 18:04:59

+0

Libcxx實現https://github.com/llvm-mirror/libcxx/blob/9dcbb46826fd4d29b1485f25e8986d36019a6dca/include/support/win32/support.h#L106-L182 – KindDragon 2017-04-03 10:35:54

回答

1

如果MSVC有一個編譯器的內部這一點,這將是在這裏:

Compiler Intrinsics on MSDN

否則,你將不得不使用__asm

-2

有兩個內在「_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

+11

ctz&clz調用錯誤的函數(它們應該使用_BitScanForward和BitScanReverse而不是BitScanReverse/BitScanForward)&clz錯誤,因爲它返回位集的偏移量而不是前導零的數量。 – Vitali 2011-12-16 00:54:09

15

從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 

在代碼中註釋,既CTZ和CLZ是不確定的,如果值爲0.在我們的抽象中,我們修復__builtin_clz(value)(value?__builtin_clz(value):32),但它是一個選擇

-2

T在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; 
}