我正在查看代码中关键函数的反汇编 . 它需要大约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

现在我无法理解这里的重点 . 根据条件,我们执行 vzerouppervzeroupper (!),但在第二种情况下,我们使用 two 跳转来跳过一些代码 . 为什么使用这样的片段,而不是更合乎逻辑的方式:

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 编译,使用GCC 4.9.2-10(Debian)for x64 .

[编辑]从 -fverbose-asm 选项,GCC对这些片段的评论实际上是 # i, .