What Does the TEST Instruction Do in Assembly?

The TEST instruction in x86 assembly performs a bitwise AND between two operands, sets the CPU’s condition flags based on the result, then throws the result away. It never modifies either operand. Think of it as asking a question about the bits in a value without changing anything, then using a conditional jump right afterward to act on the answer.

How TEST Works Internally

When the processor executes test op2, op1, it computes op1 & op2 (bitwise AND), updates the status flags, and discards the AND result completely. Neither operand changes. This makes TEST the flag-setting counterpart to AND, the same way CMP is the flag-setting counterpart to SUB. CMP subtracts without storing; TEST ANDs without storing.

The flags TEST modifies are:

  • Zero Flag (ZF): set to 1 if the AND result is zero, cleared to 0 otherwise.
  • Sign Flag (SF): set to 1 if the highest bit of the result is 1 (meaning the result would be negative if interpreted as a signed number).
  • Parity Flag (PF): set based on whether the lowest byte of the result has an even number of 1-bits.

TEST also always clears the overflow flag (OF) and carry flag (CF) to zero. This is guaranteed regardless of the operands, because a bitwise AND can never produce an arithmetic overflow or carry.

Valid Operand Combinations

TEST accepts two operands. The source can be an immediate constant, a register, or a memory location. The destination (listed first in Intel syntax) must be a register or memory location. At most one of the two operands can be a memory address. So test eax, 1, test eax, ebx, and test [edi], 1 are all valid, but you can’t test one memory location directly against another.

The Most Common Pattern: TEST REG, REG

You’ll see test eax, eax (or the 64-bit equivalent testq %rax, %rax) constantly in compiled code. ANDing a register with itself produces the register’s own value, so the result is zero if and only if the register is zero. After this instruction, the zero flag tells you whether the register holds zero.

This means test eax, eax followed by jz somewhere jumps when EAX is zero, and jnz somewhere jumps when EAX is nonzero. Compilers prefer this over cmp eax, 0 because it accomplishes the same thing. The encoding of test reg, reg is slightly shorter since it doesn’t need to encode an immediate zero value, and on some processors it can be marginally faster.

Checking Individual Bits With a Mask

The real power of TEST shows up when you want to know whether specific bits are set in a value. You provide a bitmask as the second operand, where only the bits you care about are 1. The AND operation zeroes out everything else, leaving only the bits you’re interested in. If those bits were all zero in the original value, the zero flag gets set.

For example:

test al, 00000011b checks whether either of the two lowest bits in AL is set. If both bits are zero, ZF becomes 1 and a jz would branch. If at least one of those bits is 1, ZF becomes 0 and a jnz would branch.

This is how low-level code checks hardware status registers, permission flags, feature bits, or any situation where individual bits carry meaning. You can test a single bit (test eax, 1 for bit 0, test eax, 4 for bit 2, test eax, 80h for bit 7) or multiple bits at once.

Common Conditional Jumps After TEST

Because TEST clears the overflow and carry flags, the jumps you’ll see paired with it almost always depend on the zero flag or the sign flag:

  • JZ / JE: jump if the zero flag is set (the tested bits were all zero).
  • JNZ / JNE: jump if the zero flag is clear (at least one tested bit was set).
  • JS: jump if the sign flag is set (the high bit of the result was 1).
  • JNS: jump if the sign flag is clear.

A typical sequence in real code looks like this: a function returns a value in EAX, and the caller immediately does test eax, eax followed by jz error_handler. This is the assembly equivalent of if (result == 0) goto error in C.

Another common pattern checks a specific flag bit in a status word. test dword ptr [edi], 1 followed by jz L2 reads a value from memory, checks whether bit 0 is set, and branches accordingly, all without modifying the value in memory.

TEST vs. CMP vs. AND

These three instructions overlap in what they can do, so knowing when to reach for each one matters.

TEST vs. AND: Both perform a bitwise AND and set the same flags. The difference is that AND stores the result back into the destination operand, overwriting it. TEST discards the result. Use TEST when you only want to inspect bits. Use AND when you want to actually clear bits.

TEST vs. CMP: CMP performs subtraction and sets flags; TEST performs AND and sets flags. Neither stores its result. To check if a register is zero, both test eax, eax and cmp eax, 0 work, but TEST is the idiomatic choice. To check whether a value is greater or less than some threshold, CMP is the right tool. To check whether specific bits are on or off, TEST is the right tool.

When reading disassembled code, seeing TEST is a strong hint that the programmer (or compiler) cares about individual bits or is doing a simple zero/nonzero check, while CMP signals an arithmetic comparison.