The great thing about calling conventions on the
x86 platform is that there are so many to choose from! In the 16-bit world, part of the calling convention was fixed by the instruction set: The
BP register defaults to the
SS selector, whereas the other registers default to the
DS selector. So the
BP register was necessarily the register used for accessing stack-based parameters.
The registers for return values were also chosen automatically by the instruction set. The
AX register acted as the accumulator and therefore was the obvious choice for passing the return value. The 8086 instruction set also has special instructions which treat the
DX:AX pair as a single 32-bit value, so that was the obvious choice to be the register pair used to return 32-bit values. That left
CX. (Terminology note: Registers that do not need to be preserved across a function call are often called “scratch”.)
When deciding which registers should be preserved by a calling convention, you need to balance the needs of the caller against the needs of the callee. The caller would prefer that all registers be preserved, since that removes the need for the caller to worry about saving/restoring the value across a call. The callee would prefer that no registers be preserved, since that removes the need to save the value on entry and restore it on exit.
If you require too few registers to be preserved, then callers become filled with register save/restore code. But if you require too many registers to be preserved, then callees become obligated to save and restore registers that the caller might not have really cared about. This is particularly important for leaf functions (functions that do not call any other functions).
The non-uniformity of the
x86 instruction set was also a contributing factor. The
CX register could not be used to access memory, so you wanted to have some register other than
CX be scratch, so that a leaf function can at least access memory without having to preserve any registers. So
BX was chosen to be scratch, leaving
DI as preserved.
Blogpost series by Raymond Chen:
- a few more interesting articles
- Why does the x64 calling convention reserve four home spaces for parameters