Yes, in all function-calling conventions I'm aware of, the arg-passing registers are call-clobbered. (Except for system-call calling conventions, where normally all regs are preserved except a return value, including arg-passing. Except that x86-64 syscall
destroys RCX and R11...)
Specifically in x86-64 System V, all registers other than RBX, RBP, RSP, and R12-R15 are call-clobbered. (That includes xmm0-15, x87/mmx registers, and AVX512 zmm0-31 and k0-k7 mask regs.)
What registers are preserved through a linux x86-64 function call shows the table from the ABI doc.
The calling convention / ABI defines the status of registers as call-preserved or call-clobbered. Different conventions can make different choices.
And yes, Microsoft Windows chose a different calling convention from everyone else: Why does Windows64 use a different calling convention from all other OSes on x86-64? In Windows x64, RDI is call-preserved, like in most 32-bit calling conventions.
But in x86-64 System V, the designers chose registers from scratch, and (as my answer on that linked question shows) found that using RDI and RSI for the first 2 args saved instructions (when building SPECint with an early x86-64 port of gcc). Probably because gcc at the time liked to inline memset
or memcpy
using rep stosd
, or the library implementation used that.
(It makes no sense to say that RDI is intrinsically call-clobbered, the x86-64 ISA doesn't define that. It's up to each platform to choose that.)
Terminology:
I hate the "caller saved" vs. "callee saved" terminology: It's confusing to think from 2 different perspectives (caller and callee), and wrongly implies that every register does get saved somewhere on every call
. Also, the names only differ by 1 letter, so aren't very visually distinct when reading.
"preserved" or "clobbered" are great; they work from either perspective. (What a callee will do to your regs, or what you're allowed to do to the caller's regs.) Moreover, they're self-explanatory.