What are the four x86 condition flags (CF, ZF, SF, OF) and what does each record?
CF = unsigned carry/overflow, ZF = result was zero, SF = result was negative, OF = signed overflow — every arithmetic, cmp, and test instruction sets these, and conditional jumps read them.
* Each of CF/ZF/SF/OF is set by the last cmp/test/arithmetic op; the conditional jump you pick decides which flags (and thus signed vs unsigned) matter. *
These four bits of the %eflags/%rflags register are the glue between an arithmetic/compare instruction and the branch that follows it. After an instruction computes a result t:
| Flag | Name | Set when | Used for |
|---|---|---|---|
| CF | Carry | a carry/borrow out of the most significant bit (unsigned overflow) | unsigned comparisons (ja, jb) |
| ZF | Zero | t == 0 |
equality (je/jne), zero-tests |
| SF | Sign | t < 0 as signed (top bit is 1) |
sign tests (js/jns) |
| OF | Overflow | the true signed result didn't fit (two's-complement overflow) | signed comparisons (jl, jg) |
Why two "overflow" flags? CF tracks overflow as if the operands were unsigned; OF tracks it as if they were signed. The same add sets both, and the CPU has no idea which interpretation you meant — that's why the jump you pick (ja vs jg) is what actually chooses signed or unsigned semantics.
Key consequence: A signed "greater" (jg) is really the condition ~(SF^OF) & ~ZF — it combines the sign and overflow flags so that comparisons stay correct even when the subtraction itself overflows. You don't compute this by hand; you just remember l/g = signed, b/a = unsigned.
Go deeper: