0

我有时会使用这种模式来迭代一些东西的数组:

    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 ?

我正在询问可以减少引入错误的可能性的具体提示。

4

1 回答 1

3

我一直有点喜欢测试一个范围而不是仅仅为了相等的想法,以防万一意外翻转或什么的。但是在 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 来处理一些事情,但只保存/恢复一次。

于 2016-09-18T05:03:53.657 回答