Как создать командную инструкцию sse4.2 popcnt
Использование программы c:
int main(int argc , char** argv)
{
return __builtin_popcountll(0xf0f0f0f0f0f0f0f0);
}
и строка компилятора (gcc 4.4 - Intel Xeon L3426):
gcc -msse4.2 poptest.c -o poptest
Я НЕ получаю встроенную сборку popcnt, а компилятор генерирует таблицу поиска и вычисляет popcount таким образом. Итоговый двоичный файл содержит более 8000 байт. (ЮК!)
Большое спасибо за любую помощь.
Ответы
Ответ 1
Вы должны сообщить GCC о генерации кода для архитектуры, которая поддерживает
команда popcnt:
gcc -march=corei7 popcnt.c
Или просто включите поддержку popcnt:
gcc -mpopcnt popcnt.c
В вашем примере программы параметр __builtin_popcountll
является
константа, поэтому компилятор, вероятно, выполнит вычисления при компиляции
времени и никогда не выдавать команду popcnt. GCC делает это, даже если не
попросил оптимизировать программу.
Итак, попробуйте передать ему то, что он не может знать во время компиляции:
int main (int argc, char** argv)
{
return __builtin_popcountll ((long long) argv);
}
$ gcc -march=corei7 -O popcnt.c && objdump -d a.out | grep '<main>' -A 2
0000000000400454 <main>:
400454: f3 48 0f b8 c6 popcnt %rsi,%rax
400459: c3 retq
Ответ 2
Вам нужно сделать это следующим образом:
#include <stdio.h>
#include <smmintrin.h>
int main(void)
{
int pop = _mm_popcnt_u64(0xf0f0f0f0f0f0f0f0ULL);
printf("pop = %d\n", pop);
return 0;
}
$ gcc -Wall -m64 -msse4.2 popcnt.c -o popcnt
$ ./popcnt
pop = 32
$
ИЗМЕНИТЬ
Упс - я просто проверил вывод разборки с помощью gcc 4.2 и ICC 11.1 - в то время как ICC 11.1 правильно генерирует popcntl
или popcntq
, по какой-либо причине gcc не выполняет - вместо этого он вызывает ___popcountdi2
. Weird. Я попробую новую версию gcc, когда я получу шанс и посмотрю, исправлено ли это. Я предполагаю, что единственным обходным решением является использование ICC вместо gcc.
Ответ 3
Для __builtin_popcountll
в GCC все, что вам нужно сделать, это добавить -mpopcnt
#include <stdlib.h>
int main(int argc, char **argv) {
return __builtin_popcountll(atoi(argv[1]));
}
с -mpopcnt
$ otool -tvV a.out
a.out:
(__TEXT,__text) section
_main:
0000000100000f66 pushq %rbp
0000000100000f67 movq %rsp, %rbp
0000000100000f6a subq $0x10, %rsp
0000000100000f6e movq %rdi, -0x8(%rbp)
0000000100000f72 movq -0x8(%rbp), %rax
0000000100000f76 addq $0x8, %rax
0000000100000f7a movq (%rax), %rax
0000000100000f7d movq %rax, %rdi
0000000100000f80 callq 0x100000f8e ## symbol stub for: _atoi
0000000100000f85 cltq
0000000100000f87 popcntq %rax, %rax
0000000100000f8c leave
0000000100000f8d retq
без -mpopcnt
a.out:
(__TEXT,__text) section
_main:
0000000100000f55 pushq %rbp
0000000100000f56 movq %rsp, %rbp
0000000100000f59 subq $0x10, %rsp
0000000100000f5d movq %rdi, -0x8(%rbp)
0000000100000f61 movq -0x8(%rbp), %rax
0000000100000f65 addq $0x8, %rax
0000000100000f69 movq (%rax), %rax
0000000100000f6c movq %rax, %rdi
0000000100000f6f callq 0x100000f86 ## symbol stub for: _atoi
0000000100000f74 cltq
0000000100000f76 movq %rax, %rdi
0000000100000f79 callq 0x100000f80 ## symbol stub for: ___popcountdi2
0000000100000f7e leave
0000000100000f7f retq
Заметки
Обязательно проверьте бит ABM (бит 23) функциональных битов CPUID перед использованием POPCNTQ.