Как использовать встроенные функции MSVC, чтобы получить эквивалент этого кода GCC?

Следующий код вызывает встроенные функции для clz / ctz в GCC и в других системах имеет C-версии. Очевидно, что версии C немного неоптимальны, если в системе есть встроенная инструкция clz / ctz, например, x86 и ARM.

#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

Какие функции мне нужно вызывать, какие заголовки нужно включать и т. Д., Чтобы добавить здесь правильный ifdef для MSVC? Я уже посмотрел эту страницу , но я не совсем уверен, для чего нужна #pragma (требуется?) И какие ограничения она накладывает на требования к версии MSVC для компиляции. Как человек, который на самом деле не использует MSVC, я также не знаю, имеют ли эти встроенные компоненты C-эквиваленты на других архитектурах, или мне также нужно #ifdef x86 / x86_64 при # определении их.

10.12.2008 13:00:57
Страница, на которую вы ссылаетесь выше, относится к функции, являющейся частью среды выполнения .NET. Вы пытаетесь собрать свою программу для .NET или как собственный исполняемый файл Windows?
Timo Geusch 10.12.2008 13:49:46
Это родной исполняемый файл Windows - одна из причин, по которой я спрашиваю, состоит в том, что мне было довольно трудно найти страницы документации Microsoft, которые на самом деле говорят о C в наши дни.
Dark Shikari 10.12.2008 18:04:59
Реализация Libcxx github.com/llvm-mirror/libcxx/blob/…
KindDragon 3.04.2017 10:35:54
4 ОТВЕТА

Если MSVC имеет встроенный компилятор для этого, он будет здесь:

Встроенные функции компилятора в MSDN

В противном случае вам придется написать это с помощью __asm

1
14.03.2017 15:37:35

Существует две встроенные функции _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 в MSDN

-3
14.03.2017 15:41:01
ctz & clz вызывают неправильные функции (они должны использовать _BitScanForward & BitScanReverse соответственно, а не BitScanReverse / BitScanForward) & clz неверно, так как возвращает смещение набора бит вместо числа ведущих нулей.
Vitali 16.12.2011 00:54:09

Отскакивая от кода 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)но это выбор

22
9.12.2013 10:23:48
Почти 1 к 1 замена для __builtin_clz()MSVC является __lzcnt(). Аппаратное обеспечение должно поддерживать SSE4. Более подробная информация .
rustyx 27.01.2016 10:23:26
Мое оборудование поддерживает SSE4, но не BMI1, поэтому __lzcnt () компилирует, но не делает то, что я ожидал, а работает как BSR.
GregC 14.03.2017 15:36:29
31 ^__builtin_clz_BitScanReverse
KindDragon 16.02.2018 13:16:55

Протестировано на 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;
}
-3
13.09.2014 13:07:35
Вы тестировали производительность вашего clz64? Я не удивлюсь, что все это ветвление делает его медленнее, чем реализация OP.
plamenko 24.09.2016 13:13:22