我正在尝试为高度优化的 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 在将我的变量test
传递给第二个内联 asm 之前会对其进行复制?
使用 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 决定将我的变量保留在堆栈中而不是寄存器中,但我不明白为什么要test
在将其传递给第二个 asm 之前进行复制?如果我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