In general user-thread context switch implementations(like
setjmp/longjmp and the
function return way), we save and restore callee-saved registers, but golang only save and restore
%rbp in gobuf.
So why does golang do it in this way?
Apparently GoLang still uses an inefficient calling convention where the only call-preserved (aka non-volatile) registers are RSP and RBP.
A call to
runtime.gosave looks to the compiler like any other function call (i.e. it eventually returns after doing some stuff, and doesn’t modify anything above its own stack frame). Like any other function call, the caller has to assume it destroys all the call-clobbered (volatile) registers (everything except RSP and RBP). Thus any values it wants to survive the call have to be spilled to stack slots (or other memory location where they belong).
For the same reason, C
setjmp only has to save the call-preserved registers. And kernel context-switch functions are the same.
This 2017 google groups post says that’s how its calling convention / ABI works, and from the linked code it looks like that still hasn’t been improved.
Go’s calling convention also inefficiently passes all args on the stack, unlike the x86-64 System V ABI which passes the first 6 integer args (and first 8 FP) in registers.
Answered By – Peter Cordes
Answer Checked By – Willingham (GoLangFix Volunteer)