2008-12-10 19 views
9

Aşağıdaki kod GCC'de clz/ctz yerleşik işlevlerini ve diğer sistemlerde de C sürümlerini çağırır. Açıkçası, C sürümleri, sistem x86 ve ARM gibi bir yerleşik clz/ctz komutuna sahipse, biraz yetersizdir.Bu GCC kodunun eşdeğerini almak için MSVC intrinsics nasıl kullanılır?

#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 

hangi işlevleri hangi başlıklar burada MSVC için uygun bir ifdef eklemek vb dahil etmek gerekiyor, çağırmak gerekiyor? Zaten this page baktım, ama #pragma için ne (tamamen gerekli) ve derleme için MSVC sürümü gereksinimleri koyar ne kısıtlamaları tam olarak emin değilim. MSVC'yi gerçekten kullanmayan birisi olarak, bu içsellerin diğer mimarilerde C eşdeğerlerine sahip olup olmadıklarını veya #defining x86/x86_64 kodlarımda #fdef olup olmadığını da bilmiyorum. MSVC bunun için içsel bir derleyici varsa

+0

Yukarıdaki bakınız sayfa .NET için veya yerel bir Windows çalıştırılabilir olarak program oluşturmak için çalışıyoruz, .NET çalışma zamanı parçası olan bir işleve karşılık gelir ? –

+0

Bu yerel bir Windows yürütülebilir - soruyorum nedeni aslında bu günlerde C hakkında konuşmak Microsoft belge sayfaları bulmak oldukça zor buldum. –

+0

Libcxx uygulaması https://github.com/llvm-mirror/libcxx/blob/9dcbb46826fd4d29b1485f25e8986d36019a6dca/include/support/win32/support.h#L106-L182 – KindDragon

cevap

-2

__asm ​​kullanarak yazmak gerekecek ve MSVC için aynı amaca uygun "_BitScanReverse". Dahil etmek . işlevler şunlardır:

#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 

eşdeğer 64bit sürümleri "_BitScanForward64" ve "_BitScanReverse64" vardır. Buradan daha fazla

Okuma: sh0dan koddan Zıplayan

x86 Intrinsics on MSDN

+11

ctz & clz yanlış işlevleri çağırıyorlar (_BitScanForward & BitScanReverse kullanıyor olmalılar sırasıyla, BitScanReverse/BitScanForward) & clz, baştaki sıfır sayısı yerine bit kümesinin ofsetini döndürdüğü için yanlıştır. – Vitali

15

, uygulama aşağıdaki gibi düzeltilmelidir:

#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 

olarak kodunda yorumladı, CTZ ve CLZ hem eğer tanımlanmamış olan değer 0'dır. Bizim soyutlamada, 'u (value?__builtin_clz(value):32) olarak düzelttik, ancak bu seçenek

+1

MSVC'de __builtin_clz() 'için hemen hemen bire bir değiştirme' __lzcnt() 'dır. Donanım, SSE4'ü desteklemelidir. [Daha fazla bilgi] (https://msdn.microsoft.com/en-US/library/bb384809.aspx). – rustyx

+1

Donanımım SSE4'ü destekler, ancak BMI1'i desteklemez, bu yüzden __lzcnt() derler ancak beklediğim şeyi yapmaz, bir BSR olarak çalışır. – GregC

+0

'31^__ builtin_clz', '_BitScanReverse' değerine eşittir – KindDragon

-2

T ESTED linux ve pencereler (x86) tarih:

#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; 
} 
+0

Clz64'ün performansını test ettiniz mi? Tüm bu dallanmanın OP'nin uygulamasından daha yavaş olmasına şaşırmam. – plamenko

İlgili konular