GCC优化器发出奇怪的条件跳转,为什么?
我正在研究代码中的关键功能的反汇编。 它大约需要90%的表现。 这并不令人惊讶,因为内联后它是一个非常大的功能,所以它最终会做很多事情。
但是,我注意到有一些程序集我不明白这样做的理由:
test rsi, rsi je setecx0 cmp rsi, 0x1 je setecx1 cmp rsi, 0x2 je setecx2 cmp rsi, 0x3 je setecx3 cmp rsi, 0x4 je setecx4 mov ecx, 0x5 ; Code that doesn't use ECX yet ecxnotzero: cmp r9, rsi je epilog ecxzero: ; Logical code below epilog: ; Standard cleanup stuff ret; ; 5kB more code ; 36 code fragments at the end of the function: NOP WORD PTR[rax+rax*1+0x0] ; 16 byte alignment of the next label setecx0: xor ECX,ECX jmp ecxzero ; similar functions, but with other labels NOP WORD PTR[rax+rax*1+0x0] ; 16 byte alignment of the next label setecx4: mov ecx, 0x4 JMP ecxnotzero ; Similar code with other "return" addresses NOP WORD PTR[rax+rax*1+0x0] setecx1: mov ecx, 0x1 JMP ecxnotzero ; Similar code with other "return" addresses setecx3: mov ecx, 0x3 JMP ecxnotzero setecx3: mov ecx, 0x2 JMP ecxnotzero
我很惊讶的原因是,这似乎是一个非常复杂的将ECX设置为max(RSI,5)
。 另外,我不明白为什么海湾合作委员会将这些片段中的大约三十个放在函数的末尾 - 这些跳跃不能自由,而且它们当然不是本地的。 CMOV不会更有意义吗?
这只是一个更为理智的案例。 我还有以下代码:
cmp rsi, QWORD PTR[rbp-0x70] je fragment vzeroupper ; Code to be skipped skipped: ; cleanup jmp epilog ; amongst the other fragments nop WORD PTR[] fragment: vzeroupper jmp skipped
现在我无法理解这里的重点。 根据条件,我们执行vzeroupper
或vzeroupper
(!),但在第二种情况下,我们使用两个跳转来跳过一些代码。 为什么使用这样一个片段,而不是更合乎逻辑的方式:
cmp rsi, QWORD PTR[rbp-0x70] vzeroupper ; Unconditionally je skipped ; vzeroupper doesn't touch ZF ; Code to be skipped skipped:
(我并不感到惊讶,因为epilog接近这个函数的开头,原始的C ++代码有一个7路开关,每个开关调用不同的方法,并且看起来epilog放置在第一个和第二个内联之间方法,函数末尾的其他片段似乎来自其他6种方法)
我们用-O3 -march=haswell -funroll-loops -fPIC -mfma
进行编译,对于x64使用GCC 4.9.2-10(Debian)。
[编辑]从-fverbose-asm
选项,对这些片段的GCC注释是字面上# i,
上一篇: GCC optimizer emits strange conditional jumps, why?
下一篇: (VC++) Runtime Check for Uninitialized Variables: How is the test Implemented?