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

时间:2016-12-15 23:14:29

标签: assembly x86 micro-optimization

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

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

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

VERSION_1_8

这" booleanizes"一个值,在C中产生0或1,如 cmp ebx,0 je equ1 mov ebx,0 jmp cont equ1: mov ebx,1 jmp cont cont: exit

3 个答案:

答案 0 :(得分: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".

有关于标志设置等的详细answer from Peter Cordes to "testl eax against eax?" question推理。还有一个链接到另一个类似的答案,但从性能的角度推断为什么它是最好的方法。 :)

eax为零时,如何将其他寄存器(我将选择ebx)设置为1,当ebx为非零时设置为0({ebx为非{ 1}}本身):

    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)

ebx为零/非零时,如何将ebx本身转换为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

或者当ebx为零/非零时,如何将ebx本身转换为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

等等......这取决于你的测试之前发生了什么,以及你真正想要的测试结果,还有很多可能的方式(最适合不同的情况,最适合不同的目标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

如何处理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

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

eax为零/非零时,如何将目标寄存器(示例中为ebx)设置为~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

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

答案 1 :(得分:2)

使用标志,Luke
通过检查零标志来测试寄存器是否为零 如果寄存器通过某些影响标志的操作(或者更具体地说,零标志)获得其值,那么您不必执行任何操作,因为零标志已经反映了该寄存器中存储的值。

仅在需要时进行测试
如果您无法保证已设置标志,则必须使用测试操作 这些操作有两种形式:破坏性和非破坏性。

您可以在以下位置查看说明列表及其改动的标记:http://ref.x86asm.net - 更具体地说,位于:http://ref.x86asm.net/coder32-abc.html

movlea指令永远不会改变标记,因此需要帮助。大多数其他指令设置至少一个标志。

不要创建错误依赖
如果需要将寄存器测试为零,但不想更改其值,则使用test指令。
您不应该使用orand指令来检查寄存器,因为CPU可能不知道or/and可以非破坏性地使用,并且可能无法应用某些优化。 对此的技术术语是“虚假依赖”。寄存器需要ebx并且“认为”它最近被更改,所以它等待结果最终确定。

test ebx, ebx  ; <-- CPU knows ebx was not altered, no stalls on subsequent reads.
or   ebx, ebx  ; <-- CPU 'thinks' ebx was changed, stall on subsequent read.

如果您希望零状态反映在另一个寄存器中,您只需mov ebx到另一个寄存器。

将值减少为布尔值
如果要将寄存器减少为布尔值(如果非零则为True,否则为False),则使用以下序列之一:

; ebx holds the value to reduce to a boolean.
; eax is an unused register.
xor eax, eax   ; eax = 0
sub eax, ebx   ; eax = 0 - ebx; CF (carry flag) = 1 if ebx <> 0
sbb ebx, ebx   ; ebx = ebx - ebx - CF
               ; <<-- ebx = -1 if non zero, 0 if zero
xor eax, eax   ; eax = 0
sub eax, ebx   ; eax = - ebx; CF = 1 if ebx <> 0
adc ebx, eax   ; ebx = (ebx + -ebx) aka 0 + CF
               ; <<== ebx = 1 if non zero, 0 if zero
test ebx, ebx  ; force ZF to be correct
setnz al       ; Store 1 if non-zero, 0 otherwise in byte register AL.

请注意,由于与“部分寄存器写入”相关的停顿,使用字节寄存器可能会出现问题。

答案 2 :(得分:-3)

您可以使用:

or ebx, 0 ;This does nothing, just triggers the zero flag if ebx is zero
jnz notZero 
or ebx, 1 ;ebx was zero, then ebx is 1
notZero: