What is the relationship between %rax, %eax, %ax, %ah, and %al?
They are all overlapping views of the same physical 64-bit register at different widths — write the small one and you change part of the big one.
* The named sub-registers are overlapping windows into %rax; a 32-bit write to %eax zeros the upper 32 bits, but %ax/%al writes do not. *
x86-64 keeps decades of register history layered on top of each other. %rax is the full 64-bit register; %eax is its low 32 bits; %ax its low 16; and %ax itself splits into two bytes, %ah (the high byte) and %al (the low byte).
|63--------------------32|31-----------16|15--8|7--0|
| | %eax |
| %rax | %ax |
| | %ah | %al |
| Name | Size | Bits |
|---|---|---|
| %rax | 64-bit | 0–63 |
| %eax | 32-bit | 0–31 |
| %ax | 16-bit | 0–15 |
| %ah | 8-bit | 8–15 |
| %al | 8-bit | 0–7 |
The one behavior that surprises everyone: writing a 32-bit subregister zeros the upper 32 bits of the 64-bit register, but writing a 16- or 8-bit subregister leaves the upper bits untouched.
mov $0x123456789ABCDEF0, %rax # %rax fully set
mov $0x1, %eax # %rax now 0x0000000000000001 — top half wiped!
This zero-extension rule is a constant source of bugs (and useful idioms) in x86-64.
Go deeper:
x86 registers (Wikipedia) — the register section documenting the overlapping sub-register views.