What's the easiest way to determine if a register's value is equal to zero or not?

后端 未结 3 778
太阳男子
太阳男子 2020-12-06 18:03

I\'m using x86 assembly with the Irvine library.

What\'s the easiest way to check if a register value is equal to zero or not?

I used cmp instruction but i\'

相关标签:
3条回答
  • 2020-12-06 18:19

    Probably the "easiest", or simplest, "not-caring about details" answer how to determine is:

        ; 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".
    

    There's a detailed answer from Peter Cordes to "testl eax against eax?" question reasoning about flags being set, etc. Also has a link to yet another similar answer, but reasoning about why it is best way from performance point of view. :)

    How to set some other register (I will pick eax) to 1 when ebx is zero, and to 0 when ebx is non-zero (non destructive way for ebx itself):

        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)
    

    Or how to convert ebx itself into 1/0 when ebx is zero/non-zero:

        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
    

    Or how to convert ebx itself into 1/0 when ebx is zero/non-zero, other variant (faster on "P6" to "Haswell" cores):

        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
    

    etc, etc... It depends what happens before your testing, and also what you really want as output of test, there're many possible ways (optimal for different situations, and optimal for different target CPU).


    I will add few more extremely common situations... A counter-loop down-counting from N to zero, to loop N times:

        mov   ebx,5   ; loop 5 times
    exampleLoop:
        ; ... doing something, preserving ebx
        dec   ebx
        jnz   exampleLoop ; loop 5 times till ebx is zero
    

    How to process 5 elements of word (16b) array (accessing them in array[0], array[1], ... order):

        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
    

    One more example, I somehow like this one a lot:

    How to set target register (eax in example) to ~0 (-1)/0 when ebx is zero/non-zero and you already have value 1 in some register (ecx in example):

        ; 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
    

    The -1 may look as practical as 1 (for indexing purposes at least), but -1 works also as full bitmask for further and/xor/or operations, so sometimes it is more handy.

    0 讨论(0)
  • 2020-12-06 18:37

    Use the flags, Luke
    You test if a register is zero by checking the zero flag.
    If the register obtained its value by some operation that affected the flags (or, more specifically, the zero flag) then you don't have to do anything, because the zero flag will already reflect the value stored in that register.

    Test only if needed
    If you cannot guarantee that the flags have been set, you'll have to use a test operation.
    These operations come in two flavors: destructive and non-destructive.

    You can see a list of instructions and the flags it alters at: http://ref.x86asm.net—more specifically, at: http://ref.x86asm.net/coder32-abc.html

    The mov and lea instructions never alter flags and thus need assistance. Most other instructions set at least one flag.

    Don't create false dependencies
    If you need to test a register for zero, but don't want to alter its value, you use the test instruction.
    You should not use a or or and instruction to check a register, because the CPU may not know that or/and can be used non-destructively and may not be able to apply certain optimizations. The technical term for this is a 'false dependency'. The register needs ebx and 'thinks' it was changed recently so it waits for the result to finalize.

    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.
    

    If you want the zero state to reflect in another register, you can simply mov ebx to another register.

    Reduce a value to a boolean
    If you want to reduce the register to a boolean (True if non-zero, False otherwise), you use one of the following sequences:

    ; 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.
    

    Note that the use of byte registers can be problematic due to stalls related to "partial register writes".

    0 讨论(0)
  • 2020-12-06 18:43

    You could use:

    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:
    
    0 讨论(0)
提交回复
热议问题