Skip to content

Instructions

x86-64

CMP / TEST

CMP subtracts operands and discards the result to set flags; TEST ANDs them and discards the result — both are the standard way to condition a Jcc.

CMP and TEST exist solely to set EFLAGS for a subsequent conditional jump or SETcc instruction. Neither writes a result to a register.

CMP

asm
cmp rax, rbx      ; EFLAGS ← rax - rbx  (result discarded)
cmp rax, 0        ; test for zero/negative
cmp dword [rsp+4], 0x10

Sets ZF, CF, SF, OF, PF, AF — identical to SUB but without storing the difference.

TEST

asm
test rax, rax     ; EFLAGS ← rax & rax  (result discarded) — idiom for zero/sign check
test rax, 1       ; check bit 0 (LSB) of rax
test al,  0xF0    ; check upper nibble of AL

Sets ZF, SF, PF; always clears CF and OF — identical to AND but discarding the result.

Canonical patterns

PatternMeaning
test rax, rax; je labeljump if rax == 0 (null pointer / false check)
test rax, rax; js labeljump if rax < 0 (sign check)
test al, 1; jne labeljump if LSB set (odd / flag check)
cmp rax, rbx; jl labeljump if rax < rbx (signed)
cmp rax, 0; je labelredundant with test rax,rax but compiler may emit it

Reverse-engineering notes

  • test rax, rax is the most common idiom in compiled code for a null or boolean check. Decompilers render it as if (rax) or if (!rax).
  • When you see test eax, eax immediately after a call, the function's return value (in rax) is being checked for zero/non-zero — a typical error-code test (if (func() == 0)).
  • test al, al after a string function often checks for a null terminator ('\0').
  • Because test clears OF and CF, you can only use je/jne/js safely after it — using jl or jb after test is unusual and worth flagging.