1
votes

From the wikipedia x86 calling convention, it says that for the Microsoft x64 calling convention:

The registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 are considered nonvolatile (callee-saved).

But for System V AMD64 ABI:

If the callee wishes to use registers RBX, RBP, and R12–R15, it must restore their original values before returning control to the caller.

It did not mention anything about rdi and rsi.

I also read that %rax, %rcx, %rdx, %rdi, %rsi, %rsp, and %r8-r11 are considered caller-save registers (from a pdf)

My question is, is calling convention various across different platform?(I try to write some libc function in asm for unix environment)

I could not find any article discussing about this topic, resources to this topic will be helpful as well. I wanted to know the advantage and disadvantage of these conventions.

1

1 Answers

2
votes

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.