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

问题描述 投票:6回答:3

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

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

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

    cmp ebx,0
    je equ1
    mov ebx,0
    jmp cont
equ1:
    mov ebx,1
    jmp cont
cont:
    exit

这个“booleanizes”一个值,产生0或1像int ebx = !!ebx将在C中。

assembly x86 micro-optimization
3个回答
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为非零时(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)

或者当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操作的完整位掩码,所以有时它更方便。


2
投票

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

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

你可以看到一个指令列表和它改变的标志: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.

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


-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:
© www.soinside.com 2019 - 2024. All rights reserved.