Реализация __builtin_clz

Какова реализация GCC (4.6+) __builtin_clz? Соответствует ли это некоторым инструкциям процессора Intel x86_64 (AVX)?

Ответы

Ответ 1

Он должен перевести на команду Bit Scan Reverse и вычесть. BSR дает индекс ведущего 1, а затем вы можете вычесть это из размера слова, чтобы получить число начальных нулей.

Редактирование: если ваш процессор поддерживает LZCNT (Leading Zero Count), то это, вероятно, тоже сделает трюк, но не все чипы x86-64 имеют эту инструкцию.

Ответ 2

Да и нет.

CLZ (начальный ноль отсчета) и BSR (бит-сканирование назад) связаны друг с другом, но разные. CLZ равно (тип бит ширина меньше один) - BSR. CTZ (отсчет конечного нуля), также известный как FFS (найти первый набор), совпадает с BSF (бит-сканирование вперед.)

Обратите внимание, что все они undefined при работе на ноль!

В ответ на ваш вопрос, большую часть времени на x86 и x86_64, __builtin_clz генерирует операцию BSR, вычитаемую из 31 (или независимо от вашей ширины вашего типа), а __builting_ctz генерирует операцию BSF.

Если вы хотите знать, что генерирует ассемблер GCC, лучше всего это посмотреть. Флаг -S будет иметь gcc вывод ассемблера, сгенерированного для данного входа:

gcc -S -o test.S test.c

Рассмотрим:

unsigned int clz(unsigned int num) {
    return __builtin_clz(num);
}

unsigned int ctz(unsigned int num) {
    return __builtin_ctz(num);
}

В x86 для clz gcc (-O2) генерируется:

bsrl    %edi, %eax
xorl    $31, %eax
ret

и для ctz:

bsfl    %edi, %eax
ret

Обратите внимание, что если вы действительно хотите bsr, а не clz, вам нужно сделать 31 - clz (для 32-битных целых чисел). Это объясняет XOR 31, так как x XOR 31 == 31 - x (это идентификация только истинно для чисел из 2 ^ y - 1) So:

num = __builtin_clz(num) ^ 31;

дает

bsrl    %edi, %eax
ret

Ответ 3

Да, это соответствует команде CPU BSR (бит сканирования назад).

Вот пример кода, который может вам помочь:

int a=20; //10100

//trailing zeroes
cout<<__builtin_ctz(a)<<endl;   //gives 2
cout<<__builtin_ctz(a<<4)<<endl;    //gives 6

//leading zeroes
cout<<__builtin_clz(a)<<endl;   //gives 27
cout<<__builtin_clz(a>>4)<<endl;    //gives 31

cout<<__builtin_clz(0)<<endl;   //gives 32