Assembly 如何正确测试条件:je或jge
我有时使用此模式来迭代某个数组:Assembly 如何正确测试条件:je或jge,assembly,x86,idioms,loop-counter,Assembly,X86,Idioms,Loop Counter,我有时使用此模式来迭代某个数组: 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]
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]
我理解测试平等就足够了,但不应该“安全地”测试“更大或相等”(防止不会发生的情况)
在这种情况下应该使用je或jge吗
我想问一些具体的技巧,可以减少引入bug的可能性。我一直喜欢测试范围的想法,而不是仅仅测试相等性,以防一点意外翻转或其他情况。但在x86 asm中,请记住,
cmp/jge
不能在Core2上运行(在32位模式下),但是cmp/je
可以。我认为这会更加相关,直到我检查发现只有Core2,而不是Nehalem,无法融合,因为在Core2上,宏融合在64位模式下根本不起作用。(后来的微体系结构没有这种限制,宏可以融合越来越多的组合。)
根据计数器的不同,您通常可以在没有CMP的情况下倒计时(dec/jnz)。通常您知道它不需要是64位,所以您可以使用dec-esi/jnz
或其他任何东西dec esi/jge
对签名计数器有效,但dec
未设置CF,因此无法(有效地)使用JA
你的循环结构,在中间有一个<代码> If()断开,最后一个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进行一些操作,但只保存/恢复一次。我一直喜欢这样一种想法,即测试一个范围,而不是仅仅测试相等性,以防发生意外或其他情况。但在x86 asm中,请记住,
cmp/jge
不能在Core2上运行(在32位模式下),但是cmp/je
可以。我认为这会更加相关,直到我检查发现只有Core2,而不是Nehalem,无法融合,因为在Core2上,宏融合在64位模式下根本不起作用。(后来的微体系结构没有这种限制,宏可以融合越来越多的组合。)
根据计数器的不同,您通常可以在没有CMP的情况下倒计时(dec/jnz)。通常您知道它不需要是64位,所以您可以使用dec-esi/jnz
或其他任何东西dec esi/jge
对签名计数器有效,但dec
未设置CF,因此无法(有效地)使用JA
你的循环结构,在中间有一个<代码> If()断开,最后一个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进行一些操作,但只保存/恢复一次。我一直喜欢这样一种想法,即测试一个范围,而不是仅仅测试相等性,以防发生意外或其他情况。但在x86 asm中,请记住,
cmp/jge
不能在Core2上进行宏融合(在32位模式下),但cmp/je
可以。我认为这会更加相关,直到我检查发现只有Core2,而不是Nehalem,无法融合,因为在Core2上,宏融合在64位模式下根本不起作用。(后来的微体系结构没有这个限制,宏可以融合越来越多的组合。)不过,为什么你要展示一些奇怪的r12溢出/重新加载来释放它作为临时计数器使用呢?这完全不相关(而且看起来不像是高效的代码)。当然,有些寄存器已经死了,你可以不用保存就可以使用。@Peter,一个人应该如何正确地写它呢?我认为r12是一个不错的选择,因为像printf这样的循环中的函数调用不会修改调用保存的r12寄存器,并且我们不需要手动保存和恢复调用周围的计数器。请更正,如果它是错误的。哦,这就是你对代码注释的意思吗?当然,r12是一个不错的选择,但是如果您不需要64位计数器(没有REX前缀),那么ebx或ebp可能是更好的选择。最重要的是,在堆栈框架中有一个名为.r12
的命名点很奇怪。通常,如果你要给某个东西命名,你会使用一个有语义的标签。因此,如果r12
之前持有某个值,则您将存储该值。在循环之后,也许用r12做些别的。不,这和我说的正好相反。更新了我的答案,谢谢你让我知道你在简短的评论中迷失了方向,这样我就可以扩展它,而不是浪费我们的时间:)我一直有点喜欢测试一个范围的想法,而不仅仅是为了平等,以防有点意外或什么。但在x86 asm中,请记住,cmp/jge
不能在Core2上进行宏融合(在32位模式下),但cmp/je
可以。我认为这会更加相关,直到我检查发现只有Core2,而不是Nehalem,无法融合,因为在Core2上,宏融合在64位模式下根本不起作用。(后来的微体系结构没有这个限制,宏可以融合越来越多的组合。)不过,为什么你要展示一些奇怪的r12溢出/重新加载来释放它作为临时计数器使用呢?这完全不相关(而且看起来不像是高效的代码)。当然有一些寄存器已经死了,你可以不用保存就可以使用@