What are callee and caller saved registers?

136,489

Solution 1

Caller-saved registers (AKA volatile registers, or call-clobbered) are used to hold temporary quantities that need not be preserved across calls.

For that reason, it is the caller's responsibility to push these registers onto the stack or copy them somewhere else if it wants to restore this value after a procedure call.

It's normal to let a call destroy temporary values in these registers, though.

Callee-saved registers (AKA non-volatile registers, or call-preserved) are used to hold long-lived values that should be preserved across calls.

When the caller makes a procedure call, it can expect that those registers will hold the same value after the callee returns, making it the responsibility of the callee to save them and restore them before returning to the caller. Or to not touch them.

Solution 2

Callee vs caller saved is a convention for who is responsible for saving and restoring the value in a register across a call. ALL registers are "global" in that any code anywhere can see (or modify) a register and those modifications will be seen by any later code anywhere. The point of register saving conventions is that code is not supposed to modify certain registers, as other code assumes that the value is not modified.

In your example code, NONE of the registers are callee save, as it makes no attempt to save or restore the register values. However, it would seem to not be an entire procedure, as it contains a branch to an undefined label (l$loop). So it might be a fragment of code from the middle of a procedure that treats some registers as callee save; you're just missing the save/restore instructions.

Solution 3

The caller-saved / callee-saved terminology is based on a pretty braindead inefficient model of programming where callers actually do save/restore all the call-clobbered registers (instead of keeping long-term-useful values elsewhere), and callees actually do save/restore all the call-preserved registers (instead of just not using some or any of them).

Or you have to understand that "caller-saved" means "saved somehow if you want the value later".

In reality, efficient code lets values get destroyed when they're no longer needed. Compilers typically make functions that save a few call-preserved registers at the start of a function (and restore them at the end). Inside the function, they use those regs for values that need to survive across function calls.

I prefer "call-preserved" vs. "call-clobbered", which are unambiguous and self-describing once you've heard of the basic concept, and don't require any serious mental gymnastics to think about from the caller's perspective or the callee's perspective. (Both terms are from the same perspective).

Plus, these terms differ by more than one letter.

The terms volatile / non-volatile are pretty good, by analogy with storage which loses its value on power-loss or not, (like DRAM vs. Flash). But the C volatile keyword has a totally different technical meaning, so that's a downside to "(non)-volatile" when describing C calling conventions.


  • Call-clobbered, aka caller-saved or volatile registers are good for scratch / temporary values that aren't needed after the next function call.

From the callee's perspective, your function can freely overwrite (aka clobber) these registers without saving/restoring.

From a caller's perspective, call foo destroys (aka clobbers) all the call-clobbered registers, or at least you have to assume it does.

You can write private helper functions that have a custom calling convention, e.g. you know they don't modify a certain register. But if all you know (or want to assume or depend on) is that the target function follows the normal calling convention, then you have to treat a function call as if it does destroy all the call-clobbered registers. That's literally what the name come from: a call clobbers those registers.

Some compilers that do inter-procedural optimization can also create internal-use-only definitions of functions that don't follow the ABI, using a custom calling convention.

  • Call-preserved, aka callee-saved or non-volatile registers keep their values across function calls. This is useful for loop variables in a loop that makes function calls, or basically anything in a non-leaf function in general.

From a callee's perspective, these registers can't be modified unless you save the original value somewhere so you can restore it before returning. Or for registers like the stack pointer (which is almost always call-preserved), you can subtract a known offset and add it back again before returning, instead of actually saving the old value anywhere. i.e. you can restore it by dead reckoning, unless you allocate a runtime-variable amount of stack space. Then typically you restore the stack pointer from another register.

A function that can benefit from using a lot of registers can save/restore some call-preserved registers just so it can use them as more temporaries, even if it doesn't make any function calls. Normally you'd only do this after running out of call-clobbered registers to use, because save/restore typically costs a push/pop at the start/end of the function. (Or if your function has multiple exit paths, a pop in each of them.)


The name "caller-saved" is misleading: you don't have to specially save/restore them. Normally you arrange your code to have values that need to survive a function call in call-preserved registers, or somewhere on the stack, or somewhere else that you can reload from. It's normal to let a call destroy temporary values.


An ABI or calling convention defines which are which

See for example What registers are preserved through a linux x86-64 function call for the x86-64 System V ABI.

Also, arg-passing registers are always call-clobbered in all function-calling conventions I'm aware of. See Are rdi and rsi caller saved or callee saved registers?

But system-call calling conventions typically make all the registers except the return value call-preserved. (Usually including even condition-codes / flags.) See What are the calling conventions for UNIX & Linux system calls on i386 and x86-64

Solution 4

Caller-Saved (AKA volatile or call-clobbered) Registers

  • The values in caller-saved registers are short term and are not preserved from call to call  
  • It holds temporary (i.e. short term) data

Callee-Saved (AKA non-volatile or call-preserved) Registers

  • The callee-saved registers hold values across calls and are long term
  • It holds non-temporary (i.e. long term) data that is used through multiple functions/calls

Solution 5

I'm not really sure if this adds anything but,

Caller saved means that the caller has to save the registers because they will be clobbered in the call and have no choice but to be left in a clobbered state after the call returns (for instance, the return value being in eax for cdecl. It makes no sense for the return value to be restored to the value before the call by the callee, because it is a return value).

Callee saved means that the callee has to save the registers and then restore them at the end of the call because they have the guarantee to the caller of containing the same values after the function returns, and it is possible to restore them, even if they are clobbered at some point during the call.

The issue with the above definition though is that for instance on Wikipedia cdecl, it says eax, ecx and edx are caller saved and rest are callee saved, this suggests that the caller must save all 3 of these registers, when it might not if none of these registers were used by the caller in the first place. In which case caller 'saved' becomes a misnomer, but 'call clobbered' still correctly applies. This is the same with 'the rest' being called callee saved. It implies that all other x86 registers will be saved and restored by the callee when this is not the case if some of the registers are never used in the call anyway. With cdecl, eax:edx may be used to return a 64 bit value. I'm not sure why ecx is also caller saved if needed, but it is.

Share:
136,489
mugetsu
Author by

mugetsu

SOreadytohelp

Updated on July 05, 2022

Comments

  • mugetsu
    mugetsu almost 2 years

    I'm having some trouble understanding the difference between caller and callee saved registers and when to use what.

    I am using the MSP430 :

    procedure:

    mov.w #0,R7 
    mov.w #0,R6 
    add.w R6,R7 
    inc.w R6 
    cmp.w R12,R6 
    jl l$loop 
    mov.w R7,R12
    ret
    

    the above code is a callee and was used in a textbook example so it follows the convention. R6 and R7 are callee saved and R12 is caller saved. My understanding is that the callee saved regs aren't "global" in the sense that changing its value in a procedure will not affect it's value outside the procedure. This is why you have to save a new value into the callee reg at the beginning.

    R12, the caller saved is "global", for lack of better words. What the procedure does has a lasting effect on R12 after the call.

    Is my understanding correct? Am I missing other things?

  • Peter Cordes
    Peter Cordes over 7 years
    Another term I like to use is "call-clobbered" vs. "call-preserved". caller vs. callee only differs by one letter, and compilers don't actually save/restore call-clobbered regs across calls (they just put values in call-preserved regs instead.) volatile vs. non-volatile can cause confusion with C's volatile keyword. So "call-clobbered" accurately describes what a function needs to assume about other functions, rather than how it might implement the calling convention / ABI.
  • Paul A. Clayton
    Paul A. Clayton almost 5 years
    How about a new terminology: caller-preserved/callee-preserved (where preservation may implicitly include not using)? I like having the one responsible for action being declared, but declaring who can use the registers without special action may be okay. Call-clobbered may weakly imply that the saving happens at the call site (which is kind of true for XTensa and some ISAs include stack pointer adjust instructions that also save/restore registers). Naming is hard. Mentioning that interprocedural optimization can bypass the ABI might have been worthwhile.
  • Peter Cordes
    Peter Cordes almost 5 years
    @PaulA.Clayton: I really like that each of the terms call-preserved vs. clobbered can be looked at from the perspective of the caller or the callee. They much better fit the model that compilers actually use for code-gen: save/restore some call-preserved regs at the start/end of the function, and use those for any variables that need to survive across a call. callee vs. caller-preserved avoids the word "saved" and addresses one of my objections to the traditional terminology, but not the more fundamental point about being able to apply the term directly whichever direction you think from.
  • Peter Cordes
    Peter Cordes almost 5 years
    @PaulA.Clayton: They key point is that call-clobbered registers usually don't get preserved at all; they tend to get used for calculating args for the call and those values simply die at the function call (as far as the caller is concerned). Putting a "preserved" label on that is actually just as bad as "caller-saved". (I even had to edit this comment after getting the logic wrong and writing "callee-saved" in the last sentence. Those terms are just so dumb because you have to flip one of them around when you're talking about the caller's perspective.)
  • Lewis Kelsey
    Lewis Kelsey about 4 years
    Obviously, this nomenclature was used in the education system. It took me a while to work out why the 'saved' scenario wasn't back to front. It's counter intuitive because 'caller saved' to me constantly was being interpreted as 'saved by the caller in a register that won't be clobbered in the call'
  • Peter Cordes
    Peter Cordes about 4 years
    ecx was a good choice of a 3rd register to be call-clobbered because it's needed for variable-count shifts, as well as for rep stos/movs. It's easily the 3rd most-needed after EAX and EDX (for div, widening multiply, and compact encodings for AL/EAX). In general you want a balance of call-preserved and call-clobbered registers so leaf functions don't need too much push/pop to get some scratch space to work with, vs. functions that use call in a loop not needing too much spill/reload inside a loop.
  • Peter Cordes
    Peter Cordes over 3 years
    It saves values before calling a function - no it doesn't. Values in volatile aka call-clobbered registers just get destroyed. The whole idea of actually wasting instructions copying that data somewhere else and back before/after a call is part of the brain-death of the "caller-saved" terminology. Real code doesn't work that way.
  • Peter Cordes
    Peter Cordes over 3 years
    Also, your phrasing of "it saves" implies that the register itself does that for you automatically, which is of course not the case. (Same for the "it saves" in describing call-preserved regs. You have to save the caller's value around your use of it.) With those 2 "it saves" bullet points removed, it would be a complete and accurate summary of those terms.
  • Abu Shoeb
    Abu Shoeb over 3 years
    Thanks for the clarification, @PeterCordes. I'm going to modify it by removing the second bullet points for both registers.
  • NAND
    NAND over 3 years
    Does static variable in c use registers like t in MIPS?
  • Chris Dodd
    Chris Dodd over 3 years
    @NAND: static variables are generally stored in memory. On MIPS they'll need to be loaded in to registers (temporarily) to be used, but the generally "live" in memory