我正在尝试为高度优化的x86-64位操作代码编写一个小型库,并且正在摆弄内联asm .
在测试这个特殊情况时引起了我的注意:
unsigned long test = 0;
unsigned long bsr;
// bit test and set 39th bit
__asm__ ("btsq\t%1, %0 " : "+rm" (test) : "rJ" (39) );
// bit scan reverse (get most significant bit id)
__asm__ ("bsrq\t%1, %0" : "=r" (bsr) : "rm" (test) );
printf("test = %lu, bsr = %d\n", test, bsr);
在gcc和icc中编译并运行良好,但是当我检查程序集时,我会得到差异
gcc -S -fverbose-asm -std=gnu99 -O3
movq $0, -8(%rbp)
## InlineAsm Start
btsq $39, -8(%rbp)
## InlineAsm End
movq -8(%rbp), %rax
movq %rax, -16(%rbp)
## InlineAsm Start
bsrq -16(%rbp), %rdx
## InlineAsm End
movq -8(%rbp), %rsi
leaq L_.str(%rip), %rdi
xorb %al, %al
callq _printf
我想知道为什么这么复杂?我正在编写高性能代码,其中指令的数量至关重要 . 我特别想知道为什么gcc在将它传递给第二个内联asm之前复制了我的变量 test
?
使用icc编译的相同代码可以提供更好的结果:
xorl %esi, %esi # test = 0
movl $.L_2__STRING.0, %edi # has something to do with printf
orl $32832, (%rsp) # part of function initiation
xorl %eax, %eax # has something to do with printf
ldmxcsr (%rsp) # part of function initiation
btsq $39, %rsi #106.0
bsrq %rsi, %rdx #109.0
call printf #111.2
尽管gcc决定将我的变量保留在堆栈而不是寄存器中,但我不明白为什么在将它传递给第二个asm之前复制 test
?如果我把 test
作为输入/输出变量放在第二个asm中
__asm__ ("bsrq\t%1, %0" : "=r" (bsr) , "+rm" (test) );
然后那些线条消失了 .
movq $0, -8(%rbp)
## InlineAsm Start
btsq $39, -8(%rbp)
## InlineAsm End
## InlineAsm Start
bsrq -8(%rbp), %rdx
## InlineAsm End
movq -8(%rbp), %rsi
leaq L_.str(%rip), %rdi
xorb %al, %al
callq _printf
这个gcc搞砸了优化还是我错过了一些重要的编译器开关?我的 生产环境 系统确实有icc,但如果我决定在某个时候分发源代码,那么它也必须能够用gcc编译 .
使用的编译器:
gcc版本4.2.1(基于Apple Inc. build 5658)(LLVM build 2336.1.00)
icc版本12.0.2
1 回答
我在Linux上试过这样的例子(通过在
printf
中使用&test
强制test
的堆栈引用/ loc来使其成为"evil":):并使用各种版本的
gcc -O3
编译它...以下结果:虽然创建的代码存在显着差异(包括
bsr
加入test
作为寄存器或内存),但是没有一个经过测试的版本重新创建了您怀疑MacOSX上使用的4.2.x版本中的错误的程序集,但是那么我既没有你的测试用例也没有特定的编译器版本 .Edit: 上面的代码在强制
test
进入堆栈的意义上明显不同;如果没有这样做,那么我测试过的所有"plain" gcc版本都会直接对bts $39, %rsi
/bsr %rsi, %rdx
.但我发现
clang
在那里创建了不同的代码:所以差别似乎确实在clang / llvm和"gcc proper"的代码生成器之间 .