我正在查看一些汇编代码,我看到了这个:
mov r12, _read_loopr
jmp _bzero
_read_loopr:
...
_bzero:
inc r8
mov byte [r8+r15], 0x0
cmp r8, 0xff
jle _bzero
jmp r12
而且我想知道这样做是否有任何特别的优势(mov _read_loopr到函数寄存器jmp然后jmp返回)而不是通常的调用_bzero和ret?
这看起来就像是脑死亡代码,特别是如果返回地址标签总是在jmp _bzero
之后,就像你在评论中说的那样。
也许作者认为他们不能使用call
“因为函数调用clobber寄存器”。如果您正在调用不属于同一代码库的函数,则必须根据调用约定进行假设。但是你可以使用call
/ ret
来实现自定义调用约定。
当然,对于这么小的代码,应该内联(即使其成为宏,而不是函数)。
更重要的是,通常可以比一次存储一个字节更聪明,并且如果有多个字节为零,则可能值得潜在的分支误预测。如果始终需要将至少8个(或更好,16个)字节的数据归零,则可以使用宽存储执行此操作。使最终存储写入要归零的缓冲区的最后一个字节,可能与前一个存储重叠。 (这比以分支机构结束决定进行最终4B商店,2B商店和1B商店要好得多。)有关编写高效asm的资源,请参阅x86标签wiki。
如果返回地址在jmp _bzero
之后的某个地方,那么最糟糕的可能是push _read_loopr
/ jmp _bzero
和ret
中的_bzero
。这将破坏return-address predictor stack,导致对呼叫树上的下一个~15 ret
s的误预测。
最好是内联循环并在其后放置直接jmp
。
我不确定如何将_bzero
的地址传递给jmp
,以便在call
之后与ret
/ jmp
和call
进行比较。
call
/ ret
相当便宜,但不是针对英特尔的单指令。如果只有一个来电者,jmp _bzero
/ jmp _read_loopr
会更好。