确定寄存器的值是否等于零的最简单方法是什么?

Zey*_*man 6 x86 assembly micro-optimization

我正在使用与Irvine库的x86程序集.

检查寄存器值是否等于零的最简单方法是什么?

我使用cmp指令,但我正在寻找替代方法.这是我使用cmp指令的代码,寄存器是ebx

    cmp ebx,0
    je equ1
    mov ebx,0
    jmp cont
equ1:
    mov ebx,1
    jmp cont
cont:
    exit
Run Code Online (Sandbox Code Playgroud)

这个"booleanizes"一个值,int ebx = !!ebx在C中产生0或1 .

Ped*_*d7g 11

可能最"最简单",或最简单,"不关心细节"的答案如何确定是:

    ; here ebx is some value, flags are set to anything
    test   ebx,ebx   ; CF=0, ZF=0/1 according to ebx
    jz     whereToJumpWhenZero
    ; "non-zero ebx" will go here

    ; Or you can use the inverted "jnz" jump to take
    ; a branch when value was not zero instead of "jz".
Run Code Online (Sandbox Code Playgroud)

彼得·科德斯(Peter Cordes)testl有一个详细的答案是" 对抗eax?" 关于标志被设置的问题推理等.还有一个链接到另一个类似的答案,但从性能的角度推断为什么它是最好的方式.:)

eaxebx零为零时,如何将其他寄存器(我将选择)设置为1,当非零时ebx(非破坏性ebx自身方式)将如何设置为0 :

    xor   eax,eax  ; eax = 0 (upper 24 bits needed to complete "al" later)
    test  ebx,ebx  ; test ebx, if it is zero (ZF=0/1)
    setz  al       ; al = 1/0 when ZF=1/0 (eax = 1/0 too)
Run Code Online (Sandbox Code Playgroud)

或者ebxebx零/非零时如何将自身转换为1/0 :

    neg   ebx      ; ZF=1/0 for zero/non-zero, CF=not(ZF)
    sbb   ebx,ebx  ; ebx = 0/-1 for CF=0/1
    inc   ebx      ; 1 when ebx was 0 at start, 0 otherwise
Run Code Online (Sandbox Code Playgroud)

或者ebxebx零/非零时如何将自身转换为1/0 ,其他变体("P6"到​​"Haswell"核心更快):

    test  ebx,ebx  ; ZF=1/0 for zero/non-zero ebx
    setz  bl       ; bl = 1/0 by ZF (SETcc can target only 8b r/m)
    movzx ebx,bl   ; ebx = bl extended to 32 bits by zeroes
Run Code Online (Sandbox Code Playgroud)

等等......这取决于你的测试之前发生了什么,以及你真正想要的测试输出,有很多可能的方法(最适合不同的情况,最适合不同的目标CPU).


我将添加一些非常常见的情况......一个从N到零的反向循环计数,循环N次:

    mov   ebx,5   ; loop 5 times
exampleLoop:
    ; ... doing something, preserving ebx
    dec   ebx
    jnz   exampleLoop ; loop 5 times till ebx is zero
Run Code Online (Sandbox Code Playgroud)

如何处理word(16b)数组的5个元素(在数组[0],数组[1],......顺序中访问它们):

    mov   ebx,-5
    lea   esi,[array+5*2]
exampleLoop:
    mov   ax,[esi+ebx*2]  ; load value from array[i]
    ; process it ... and preserve esi and ebx
    inc   ebx
    jnz   exampleLoop  ; loop 5 times till ebx is zero
Run Code Online (Sandbox Code Playgroud)

还有一个例子,我在某种程度上喜欢这个:

eaxebx零/非零时,如何将目标寄存器(例如)设置为~0(-1)/ 0,并且1在某些寄存器中已经有值(ecx例如):

    ; ecx = 1, ebx = some value
    cmp   ebx,ecx  ; cmp ebx,1 => CF=1/0 for ebx zero/non-zero
    sbb   eax,eax  ; eax = -1 (~0) / 0 for CF=1/0 ; ebx/ecx intact
Run Code Online (Sandbox Code Playgroud)

-1可能看起来像1一样实用(至少用于索引),但-1也可用作进一步and/xor/or操作的完整位掩码,因此有时它更方便.

  • 对于相同寄存器的情况,不使用任何额外的寄存器:`test ebx,ebx` /`setz bl` /`movzx ebx,bl`应该在P6到Haswell(SBB是2c延迟/ 2 uop)时更有效.`neg/sbb/inc`在Pentium4(慢setcc)上效率更高,我认为它们在Broadwell和后来以及在AMD(所有1c延迟)上都是相同的,并且具有更小的代码大小(特别是在32位中) ). (2认同)
  • 奇怪的是,英特尔的编译器几乎*总是*在这种情况下使用`CMOV`指令,即使在我看来替代指令序列(如@Peter建议的那样)会更优化。因此,要么英特尔工程师有特殊信息表明条件移动实际上总是更好,要么优化器只是将其作为固定路径编写。(或者,这可能是他们坚持使用 AMD 的方式之一,因为 AMD 处理器的条件移动延迟往往略高。) (2认同)