nasm-know-hows

nasm assembly related stuff

View on GitHub

CMP vs TEST - What’s the Difference?

Question

What is the difference between CMP and TEST instructions?


Quick Answer

┌────────┬─────────────────┬──────────────────────────────┐
│ Instr  │ Operation       │ Primary Use                  │
├────────┼─────────────────┼──────────────────────────────┤
│ CMP    │ Subtraction     │ Comparing values (equality,  │
│        │ (doesn't store) │ greater/less than)           │
├────────┼─────────────────┼──────────────────────────────┤
│ TEST   │ AND             │ Testing bits, checking zero, │
│        │ (doesn't store) │ checking sign                │
└────────┴─────────────────┴──────────────────────────────┘

Detailed Explanation

CMP - Compare (Subtraction)

cmp destination, source     ; Performs: destination - source
                           ; Sets flags, discards result

What it does:

Flags set: CF, ZF, SF, OF, AF, PF (all arithmetic flags)

Example:

mov rax, 10
cmp rax, 5              ; Internally: 10 - 5 = 5

; Flags after CMP:
; ZF = 0 (result is not zero)
; SF = 0 (result is positive)
; CF = 0 (no borrow needed)
; OF = 0 (no signed overflow)

TEST - Test Bits (AND)

test operand1, operand2    ; Performs: operand1 & operand2
                           ; Sets flags, discards result

What it does:

Flags set: ZF, SF, PF (logic flags), CF and OF cleared to 0

Example:

mov rax, 10              ; RAX = 0b00001010
test rax, rax            ; Internally: 0b00001010 & 0b00001010 = 0b00001010

; Flags after TEST:
; ZF = 0 (result is not zero)
; SF = 0 (bit 63 is 0)
; CF = 0 (always cleared by TEST)
; OF = 0 (always cleared by TEST)

Side-by-Side Comparison

The Math

mov rax, 10
mov rbx, 5

; CMP does subtraction
cmp rax, rbx            ; Computes: 10 - 5 = 5
                        ; Sets flags based on 5

; TEST does AND
test rax, rbx           ; Computes: 0b1010 & 0b0101 = 0b0000
                        ; Sets flags based on 0

Flag Behavior

; After: cmp rax, 10
; All arithmetic flags affected:
; - CF can be 0 or 1 (based on borrow)
; - ZF can be 0 or 1 (based on equality)
; - SF can be 0 or 1 (based on sign of result)
; - OF can be 0 or 1 (based on overflow)

; After: test rax, 10
; Logic flags affected, arithmetic cleared:
; - CF always 0 (cleared by TEST)
; - ZF can be 0 or 1 (based on AND result)
; - SF can be 0 or 1 (based on MSB of result)
; - OF always 0 (cleared by TEST)

When to Use Each

Use CMP when:

1. Comparing for Equality

cmp rax, 42
je equal                ; Jump if RAX == 42
jne not_equal           ; Jump if RAX != 42

2. Comparing Magnitude (Greater/Less)

cmp rax, rbx
jg greater              ; RAX > RBX (signed)
jl less                 ; RAX < RBX (signed)
ja above                ; RAX > RBX (unsigned)
jb below                ; RAX < RBX (unsigned)

3. Range Checking

cmp rax, 10
jl too_small            ; RAX < 10
cmp rax, 100
jg too_large            ; RAX > 100

4. Checking Against Non-Zero Values

cmp rax, 0              ; Check if zero
je is_zero

cmp rax, -1             ; Check if -1
je is_minus_one

Use TEST when:

1. Checking if Zero

test rax, rax           ; Better than cmp rax, 0
jz is_zero              ; Jump if RAX == 0
jnz not_zero            ; Jump if RAX != 0

Why better than CMP?

2. Checking Specific Bits

test al, 0x01           ; Check bit 0
jnz bit_is_set

test al, 0x80           ; Check bit 7 (sign bit)
jnz bit7_set

test rax, 0xFF00        ; Check if any bits 8-15 are set
jnz has_high_bits

3. Checking Even/Odd

test rax, 1             ; Check bit 0
jz is_even              ; Bit 0 = 0, even
jnz is_odd              ; Bit 0 = 1, odd

4. Checking Sign (Negative)

test rax, rax
js is_negative          ; Jump if sign flag set
jns is_positive         ; Jump if sign flag clear

5. Multiple Bit Checks

test al, 0b11110000     ; Check if any upper 4 bits set
jnz has_upper_bits

test al, 0b00001111     ; Check if any lower 4 bits set
jnz has_lower_bits

Practical Examples

Example 1: Zero Check

; ❌ Less efficient with CMP
mov rax, [value]
cmp rax, 0              ; 4-7 bytes encoding
je is_zero

; ✅ More efficient with TEST
mov rax, [value]
test rax, rax           ; 2-3 bytes encoding
jz is_zero

Example 2: Range Check (Use CMP)

; Check if RAX is in range [10, 20]
cmp rax, 10
jl out_of_range         ; RAX < 10
cmp rax, 20
jg out_of_range         ; RAX > 20
; In range!

Can’t use TEST for this - TEST can’t compare magnitudes!

Example 3: Bit Flags (Use TEST)

; Check status flags (bit field)
; Bit 0 = ready, Bit 1 = error, Bit 2 = done

test al, 0x01           ; Check ready bit
jz not_ready

test al, 0x02           ; Check error bit
jnz has_error

test al, 0x04           ; Check done bit
jnz is_done

Can’t use CMP for this - CMP is for comparing values, not testing bits!

Example 4: Null Pointer Check

; Check if pointer is NULL

; Method 1: CMP
cmp rax, 0
je null_pointer

; Method 2: TEST (preferred)
test rax, rax
jz null_pointer         ; More idiomatic

Example 5: Powers of 2 Check

; Check if RAX is a power of 2
; Power of 2 has exactly one bit set
; Algorithm: (x & (x-1)) == 0 for powers of 2

mov rbx, rax
dec rbx                 ; RBX = RAX - 1
test rax, rbx           ; RAX & (RAX-1)
jz is_power_of_two      ; Zero means power of 2

Must use TEST - We’re checking a bitwise operation result!


Performance Comparison

Instruction Encoding Size

; CMP encodings
cmp rax, 0              ; 7 bytes: 48 83 F8 00
cmp rax, rbx            ; 3 bytes: 48 39 D8
cmp eax, 42             ; 3 bytes: 83 F8 2A

; TEST encodings  
test rax, rax           ; 3 bytes: 48 85 C0
test eax, eax           ; 2 bytes: 85 C0
test al, 0x01           ; 2 bytes: A8 01

Winner for zero check: TEST (smaller encoding)

CPU Performance

On modern CPUs (Intel/AMD), both are similarly fast:

But TEST has advantages:


Common Mistakes

Mistake 1: Using CMP for Bit Testing

; ❌ WRONG: Trying to test bit 0 with CMP
cmp rax, 1
je bit_is_set           ; This checks if RAX == 1, not if bit 0 is set!

; If RAX = 5 (0b0101), bit 0 is set but RAX != 1
; CMP will say "not equal"!

; ✅ CORRECT: Use TEST
test rax, 1
jnz bit_is_set          ; This correctly checks bit 0

Mistake 2: Using TEST for Magnitude Comparison

; ❌ WRONG: Trying to check if RAX > 10
test rax, 10
jg greater              ; This doesn't work! TEST does AND, not subtraction

; ✅ CORRECT: Use CMP
cmp rax, 10
jg greater

Mistake 3: Forgetting TEST Clears CF/OF

; Checking carry flag after TEST
add rax, rbx            ; May set CF
test rax, rax           ; CF cleared to 0!
jc carry_set            ; This will NEVER jump!

; If you need to preserve flags:
add rax, rbx            ; May set CF
jc carry_set            ; Check CF immediately
test rax, rax           ; Now safe to use TEST

Equivalent Operations

Both Can Check Zero

; These are equivalent for zero check:
cmp rax, 0
je is_zero

test rax, rax
jz is_zero

or rax, rax             ; Also works but less common
jz is_zero

Both Can Check Sign

; For checking if negative (signed):
cmp rax, 0
jl is_negative          ; Less than zero

test rax, rax
js is_negative          ; Sign flag set (MSB = 1)

Decision Tree

Need to check a value?
│
├─ Comparing two values?
│  └─ Use CMP
│     ├─ Equality: je/jne
│     ├─ Greater/Less: jg/jl, ja/jb
│     └─ Range: multiple CMPs
│
├─ Checking if zero?
│  └─ Use TEST (more efficient)
│     └─ test reg, reg; jz/jnz
│
├─ Checking specific bits?
│  └─ Use TEST
│     └─ test reg, mask; jz/jnz
│
├─ Checking sign?
│  └─ Use TEST (clearer intent)
│     └─ test reg, reg; js/jns
│
└─ Even/Odd check?
   └─ Use TEST
      └─ test reg, 1; jz(even)/jnz(odd)

Memory Reference

CMP vs TEST Quick Reference

┌────────────────────────┬─────────────┬─────────────┐
│ Operation              │ Use CMP     │ Use TEST    │
├────────────────────────┼─────────────┼─────────────┤
│ x == y                 │     ✅      │      -      │
│ x != y                 │     ✅      │      -      │
│ x > y                  │     ✅      │      -      │
│ x < y                  │     ✅      │      -      │
│ x >= y                 │     ✅      │      -      │
│ x <= y                 │     ✅      │      -      │
├────────────────────────┼─────────────┼─────────────┤
│ x == 0                 │     ✅      │     ✅✅    │
│ x != 0                 │     ✅      │     ✅✅    │
│ x < 0 (negative)       │     ✅      │     ✅✅    │
│ x >= 0 (positive/zero) │     ✅      │     ✅✅    │
├────────────────────────┼─────────────┼─────────────┤
│ Test bit N             │      -      │     ✅      │
│ Even/Odd               │      -      │     ✅      │
│ Multiple bits set      │      -      │     ✅      │
│ Power of 2             │      -      │     ✅      │
└────────────────────────┴─────────────┴─────────────┘

Legend: ✅ Works  ✅✅ Better choice  - Not suitable

TL;DR

CMP:

TEST:

Golden Rules:

  1. Use CMP when comparing two different values
  2. Use TEST for zero checks (test reg, reg)
  3. Use TEST for bit testing (test reg, mask)
  4. When in doubt for zero: TEST is better


← Back to Q&A Index