高度に最適化された 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
なぜそんなに複雑なのだろうか?命令数が重要な高性能コードを書いています。test
2 番目のインライン asm に渡す前に、gcc が変数のコピーを作成するのはなぜですか?
同じコードを 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
、2 番目の asm に渡す前にコピーを作成する理由です。test
2番目の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. ビルド 5658 に基づく) (LLVM ビルド 2336.1.00)
ICC バージョン 12.0.2