9

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'm searching for alternative way. This is my code using cmp instruction and the register is ebx

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

This "booleanizes" a value, producing a 0 or 1 like int ebx = !!ebx would in C.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Zeyad Etman
  • 2,250
  • 5
  • 25
  • 42
  • 4
    You are using at least 80386 instructions (`ebx` does not exist on 8086). Also the answer depends on the instructions ahead of this code, the presence of zero value may sort of "leak" by previous instructions, saving you one more at the test, otherwise common idiom is `test ebx,ebx` to set ZF. Then to turn ZF into 0/1 value in ebx is another story, where that `test` may be again not part of optimal solution. Make up your mind, whether you want to determine if register is zero, or if you want to set some register to 0/1 according to it. # `mov ebx,0` = `xor ebx,ebx` when you can destroy flags. – Ped7g Dec 15 '16 at 23:22
  • To illustrate my point (of importance of details), let's say the last (flag AND ebx) modifying instruction ahead of this was `neg ebx`. Then `sbb ebx,ebx` will set `ebx` to -1 when `ebx` was equal to zero, and to 0 for non-zero values (further `neg ebx` will turn that into your original 0/1 way of working). – Ped7g Dec 15 '16 at 23:32
  • didn't work with me. – Zeyad Etman Dec 15 '16 at 23:40
  • BTW I got that `sbb` after `neg` result the other way.. for zero it will keep zero, for non zero it will do -1 ... So to return your 0/1 then `inc ebx` would do. # *"didn't work with me"* me neither. (I have no idea what you are talking about) – Ped7g Dec 15 '16 at 23:44
  • I could have sworn we had a duplicate of this question, but I haven't had any luck finding it. I do, however, find a bunch of questions that *implicitly* contain the answer to your question, *e.g.* http://stackoverflow.com/questions/147173/x86-assembly-testl-eax-against-eax and http://stackoverflow.com/questions/13064809/the-point-of-test-eax-eax. They're all about what `test` does when both operands are the same register—which is exactly what you want here. `test ebx, ebx` + `jz` – Cody Gray - on strike Dec 16 '16 at 15:23
  • @CodyGray: [This one](http://stackoverflow.com/questions/33721204/x86-assembly-cmp-reg-0-vs-or-reg-reg/33724806#33724806) is asking about cmp-with-zero vs. `or reg,reg`, and my answer there explains why TEST is the *best* way (for performance). It doesn't say anything about "easiest", or branching on the results, so I wasn't sure it was a dup. It also took me a while to find; I think I only found it by searching on my own name, since I knew I'd written an answer but forgot what the question was like. (I just retitled it to be more searchable) – Peter Cordes Dec 16 '16 at 18:48
  • @CodyGray: Oh, now I remember why I didn't close this as a dup: **this question is about booleanizing a value (`int x_nonzero = !!x`), not just branching on it**, so Ped7g's selection of branchless sequences is important to mention. – Peter Cordes Dec 16 '16 at 18:55
  • Possible duplicate of [Test whether a register is zero with CMP reg,0 vs OR reg,reg?](https://stackoverflow.com/questions/33721204/test-whether-a-register-is-zero-with-cmp-reg-0-vs-or-reg-reg) – phuclv Apr 05 '18 at 16:13

3 Answers3

13

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.

Ped7g
  • 16,236
  • 3
  • 26
  • 63
  • Thanks it works correctly :) i appreciate your help. – Zeyad Etman Dec 16 '16 at 00:16
  • 2
    For the same-register case without using any extra regs: `test ebx,ebx` / `setz bl` / `movzx ebx, bl` should be more efficient on P6 to Haswell (SBB is 2c latency / 2 uop). `neg/sbb/inc` is more efficient on Pentium4 (slow setcc), and I think they're equal on Broadwell and later, and on AMD (all 1c latency), and has smaller code-size (especially in 32-bit). – Peter Cordes Dec 16 '16 at 04:36
  • 1
    test/setz/movzx is what gcc6.2 `-mtune=haswell` does if you give it a situation where it wants to do it in-place: https://godbolt.org/g/076BSI. Clang zeros a separate register and then uses a mov. – Peter Cordes Dec 16 '16 at 04:50
  • 1
    @PeterCordes thanks, added to answer as another variant... plus put emphasis on "many" .. ;) :D ... (but practically, if anyone needs most optimal "set ebx to 1/0 when zero/non-zero", there's probably way to change the other part of algorithm to get better performance without that `ebx = ebx ? 0 : 1;` :)) .. except some similar curiosities as sgn(number) implementation, which has some good answer here on SO too, I think 1-2 months back you were after it :). – Ped7g Dec 16 '16 at 08:25
  • Yeah, booleanizing an integer into a 32-bit register is usually not necessary in the first place, but I could imagine a use-case for indexing a 2-element array. (But then you can use NEG/SBB without the INC, and index from the last element.) – Peter Cordes Dec 16 '16 at 14:54
  • 2
    Curiously, Intel's compiler virtually *always* uses `CMOV` instructions in situations like this, even when it seems to me that an alternate sequence of instructions (like those @Peter suggests) would be more optimal. So either the Intel engineers have special information that conditional moves are actually always better, or the optimizer is just written with this as a fixed path. (Or maybe this is one of the ways they stick it to AMD, since AMD processors tend to have slightly higher latencies for conditional moves.) – Cody Gray - on strike Dec 16 '16 at 15:12
2

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

Johan
  • 74,508
  • 24
  • 191
  • 319
  • As Ped7g's answer points out, `neg ebx` sets flags from `0 - ebx` in one instruction (but also modifies the original value). – Peter Cordes Dec 16 '16 at 19:04
-3

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:
LeCalore
  • 80
  • 3
  • 8
  • 3
    Downvoting as you go straight for `or ebx,0`, even when I already posted in comment the common idiom is `test ebx,ebx`. You could have at least use `or ebx,ebx` to avoid `0` immediate, but the `test` is still better, as that one is widely used by C/C++ compilers, so CPU can have extra optimizations for that, while the `or` may be treated as ordinary arithmetic operation. – Ped7g Dec 16 '16 at 00:06
  • 1
    Yeah, I saw your answer after I posted mine, learnt something! Thanks! – LeCalore Dec 16 '16 at 01:53
  • 3
    @Ped7g: More about why `or` is a bad choice for this: http://stackoverflow.com/a/33724806/224132. Summary: Besides the obvious code-size, `test ebx,ebx` can macro-fuse with a JCC, and it doesn't write EBX, introducing an extra cycle of latency into the dependency chain for the next thing to read EBX. So yes, CPUs do "have extra optimizations" for TEST/JCC vs. OR/JCC, and the OR is just treated the same as any other OR. – Peter Cordes Dec 16 '16 at 04:30