Assembly Language DISCUSSION

Why do compilers emit xor eax, eax and test eax, eax instead of mov eax, 0 and cmp eax, 0?

Started by pendor xor zeroing idiomtest instructionx86-64 encodingflags registercompiler idioms
5 replies 248 views 6 participants
Latest activity · 30 Sep 2026

Why do compilers emit xor eax, eax and test eax, eax instead of mov eax, 0 and cmp eax, 0?

pendor Assembly Language Forum
#1

I am learning x86-64 by reading the output of GCC at -O2. To return zero it never writes mov eax, 0; it writes xor eax, eax. Before a je or jne it uses test eax, eax where I would have written cmp eax, 0. It also zeroes eax when the function clearly returns a 64-bit zero in rax.

Are these just shorter encodings, or is there a real speed difference? And is it safe to copy these idioms into hand-written code?

Community replies 5

Re: Why do compilers emit xor eax, eax and test eax, eax instead of mov eax, 0 and cmp eax, 0?

#2

Start with size. mov eax, 0 is 5 bytes: opcode B8 followed by a 32-bit immediate. xor eax, eax is 2 bytes, 31 C0. Anything XORed with itself is zero, so the result is identical. A 64-bit mov rax, 0 in its straightforward encoding is 7 bytes. Smaller code means more instructions fit in each cache line, which adds up across a whole program.

Re: Why do compilers emit xor eax, eax and test eax, eax instead of mov eax, 0 and cmp eax, 0?

#3

The 32-bit register is used because, in 64-bit mode, every write to a 32-bit register zero-extends into the full 64-bit register. xor eax, eax therefore clears all of RAX and saves the REX prefix byte that xor rax, rax would need. Writes to 8-bit and 16-bit registers such as AL and AX do not do this; they leave the upper bits unchanged.

The same rule explains mov eax, edi appearing as a way to zero-extend a 32-bit value to 64 bits. For R8 to R15 a prefix is needed either way, so xor r8d, r8d and xor r8, r8 are both 3 bytes.

Re: Why do compilers emit xor eax, eax and test eax, eax instead of mov eax, 0 and cmp eax, 0?

#4

There is a speed aspect beyond size. Modern Intel and AMD cores recognise xor reg, reg with identical operands as a zeroing idiom during register renaming. The result is known not to depend on the old register value, so it breaks any dependency on whatever computed that value earlier, and on many cores it is resolved in the renamer without using an execution unit. and eax, 0 also produces zero but is not treated this way, so it has to wait for the previous value of EAX.

One difference to remember: XOR writes the flags and MOV does not. If a register must be zeroed between a compare and the jump or SETcc that uses the flags, use mov eax, 0 there, or zero it before the compare as compilers do in xor eax, eax / cmp edi, esi / sete al.

Re: Why do compilers emit xor eax, eax and test eax, eax instead of mov eax, 0 and cmp eax, 0?

#5

test a, b computes a AND b, sets the flags and throws the result away, just as cmp does with a subtraction. test eax, eax ANDs the register with itself, so ZF is set exactly when EAX is zero and SF is a copy of its top bit. CF and OF are cleared. cmp eax, 0 produces the same ZF, SF, CF and OF, so every conditional jump behaves identically after either instruction.

The encoding is 2 bytes (85 C0) against 3 bytes (83 F8 00) for the compare with an 8-bit immediate. Read test eax, eax / jz as "jump if eax is zero" and test eax, eax / js as "jump if eax is negative".

Re: Why do compilers emit xor eax, eax and test eax, eax instead of mov eax, 0 and cmp eax, 0?

#6

TEST is also the general bit-test tool: test al, 1 followed by jnz branches on bit 0 without changing AL, and test eax, 0x80 checks bit 7. Do not read test eax, ebx as an equality check, though. With two different registers it asks whether they have any set bit in common; equality needs cmp.

All of these idioms are safe in hand-written code, and you are expected to write them yourself: an assembler will not replace a MOV with an XOR or a CMP with a TEST for you. A good way to pick up more of them is to compile small C functions with gcc -O2 -S -masm=intel and read the output.

TEP COMMUNITY