我一直有点喜欢测试一个范围而不是仅仅为了相等的想法,以防万一意外翻转或什么的。但是在 x86 asm 中,请记住cmp/jge
不能在 Core2 上进行宏融合(在 32 位模式下),但cmp/je
可以。我认为这会更相关,直到我检查了Agner Fog 的 microarch pdf并发现只有 Core2 而不是 Nehalem 不能融合它,因为宏融合在 64 位模式下根本不起作用核心2。(后来的微架构没有这个限制,可以宏融合越来越多的组合。)
根据计数器的不同,您通常可以在没有 CMP 的情况下倒计时 (dec/jnz)。通常你知道它不需要是 64 位的,所以你可以使用dec esi / jnz
或其他。 dec esi / jge
确实适用于签名计数器,但dec
没有设置 CF,因此您不能(有用地)使用 JA。
你的循环结构,if() break
中间是一个,最后是一个jmp,对于asm来说不是惯用的。正常是:
mov ecx, 100
.loop: ; do{
;; stuff
dec ecx
jge .loop ; }while(--ecx >= 0)
您可以使用 jg 仅重新启动具有正 ecx 的循环,即从 100..1 而不是 100..0 循环。
在循环中具有未采用的条件分支和采用的无条件分支效率较低。
扩展有关保存/恢复 r12 的问题评论中的讨论:通常您会执行以下操作:
my_func:
; push rbp
; mov rbp, rsp ; optional: make a stack frame
push rbx ; save the caller's value so we can use it
sub rsp, 32 ; reserve some space
imul edi, esi, 11 ; calculate something that we want to pass as an arg to foo
mov ebx, edi ; and save it in ebx
call foo
add eax, ebx ; and use value. If we don't need the value in rbx anymore, we can use the register for something else later.
... ;; calculate an array size in ecx
test ecx, ecx ; test for the special case of zero iterations *outside* the loop, instead of adding stuff inside. We can skip some of the loop setup/cleanup as well.
jz .skip_the_loop
; now use rbx as a loop counter
mov ebx, ecx
.loop:
lea edi, [rbx + rbx*4 + 10]
call bar ; bar(5*ebx+10);
; do something with the return value? In real code, you would usually want at least one more call-preserved register, but let's keep the example simple
dec ebx
jnz .loop
.skip_the_loop:
add rsp, 32 ; epilogue
pop rbx
;pop rbp ; pointless to use LEAVE; rsp had to already be pointing to the right place for POP RBX
ret
注意我们如何在函数内部使用 rbx 来处理一些事情,但只保存/恢复一次。