我正在查看一些汇编代码,我看到了这个:
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?
答案 0 :(得分:3)
这看起来就像是脑死亡代码,特别是如果返回地址标签总是在jmp _bzero
之后,就像你在评论中说的那样。
也许作者认为他们不能使用call
"因为函数会调用clobber寄存器"。如果您正在调用不属于同一代码库的函数,则必须根据调用约定假设这一点。但您可以call
/ ret
使用自定义调用约定。
当然,对于这么小的代码,它应该是内联的(即使其成为宏,而不是函数)。
更重要的是,通常可以比一次存储一个字节更聪明,并且如果有多个字节为零,则可能值得潜在的分支误预测。如果始终需要将至少8个(或更好,16个)字节的数据归零,则可以使用宽存储执行此操作。使最终存储写入要归零的缓冲区的最后一个字节,可能与前一个存储重叠。 (这比以分支结束决定最终的4B商店,2B商店和1B商店要好得多。)请参阅x86标签wiki,了解有关编写高效asm的资源。
如果返回地址位于jmp _bzero
之后的某个位置,那么最糟糕的可能是push _read_loopr
/ jmp _bzero
和{{在ret
中的1}}。那会打破return-address predictor stack,导致对调用树的下一个〜_bzero
进行误预测。
最好是内联循环并在其后面直接ret
。
我不确定如何将jmp
的地址传递给_bzero
,以便与jmp
/ call
和ret
进行比较在jmp
之后。
call
/ call
相当便宜,但不是英特尔的单用指令。如果只有一个来电者,ret
/ jmp _bzero
会更好。