如何正确测试条件:je 或 jge
how to test condition properly: je or jge
我有时使用这种模式来迭代一些东西的数组:
mov [rsp+.r12], r12 ; Choose a register that calls inside the loop won't modify
mov r12, -1
.i:
inc r12
cmp r12, [rbp-.array_size]
je .end_i
; ... program logic ...
jmp .i
.end_i:
mov r12, [rsp+.r12]
我知道测试是否相等就足够了,但不应该 "securely" 测试 "greater or equal"(防止不会发生的情况)。
在这种情况下应该使用 je 还是 jge?
我问的是可以减少引入错误的可能性的具体技巧。
我一直有点喜欢测试范围而不是仅仅测试相等性的想法,以防万一意外翻转或其他什么。但在 x86 asm 中,请记住 cmp/jge
不能 on Core2 (in 32-bit mode), but cmp/je
can. I thought that was going to be more relevant until I checked Agner Fog's microarch pdf 并且发现它只是 Core2,而不是 Nehalem,无法融合它,因为宏融合根本不起作用在 Core2 上的 64 位模式下。 (后来的微架构没有那个限制,可以宏融合越来越多的组合。)
根据计数器的不同,您通常可以在没有 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 开始循环。
在循环中有一个不被采纳的条件分支和一个被采纳的无条件分支效率较低。
扩展讨论 关于 saving/restoring 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 做一些事情,但只 save/restore 一次。
我有时使用这种模式来迭代一些东西的数组:
mov [rsp+.r12], r12 ; Choose a register that calls inside the loop won't modify
mov r12, -1
.i:
inc r12
cmp r12, [rbp-.array_size]
je .end_i
; ... program logic ...
jmp .i
.end_i:
mov r12, [rsp+.r12]
我知道测试是否相等就足够了,但不应该 "securely" 测试 "greater or equal"(防止不会发生的情况)。
在这种情况下应该使用 je 还是 jge?
我问的是可以减少引入错误的可能性的具体技巧。
我一直有点喜欢测试范围而不是仅仅测试相等性的想法,以防万一意外翻转或其他什么。但在 x86 asm 中,请记住 cmp/jge
不能 cmp/je
can. I thought that was going to be more relevant until I checked Agner Fog's microarch pdf 并且发现它只是 Core2,而不是 Nehalem,无法融合它,因为宏融合根本不起作用在 Core2 上的 64 位模式下。 (后来的微架构没有那个限制,可以宏融合越来越多的组合。)
根据计数器的不同,您通常可以在没有 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 开始循环。
在循环中有一个不被采纳的条件分支和一个被采纳的无条件分支效率较低。
扩展讨论 关于 saving/restoring 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 做一些事情,但只 save/restore 一次。