it's a mess of legacy and ABI considerations, there's way too many calling conventions, and even those get optimized to whatever registers are free to use most times
@matrix@cope not on expert on this, but I've always assumed that most compilers would put all arguments in registers during a function call, except when the arguments being passed would be too large to fit in a register (i.e. if you're trying to pass a particularly big struct by value)
https://en.wikipedia.org/wiki/X86_calling_conventions