1

I'm a freshman to the assembly field. I'm wondering why %rbp is prescribed for callee-saved.
Because I think the frame pointer describes the beginning of the current frame, aka the caller, so the task to save this value should belong to the caller, why delay the process to the callee?
Is there something deep in this that I don't understand? Or is it just a convention? Please correct me if I am wrong.

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
li tang
  • 21
  • 1
  • 5
  • 6
    It is just a convention. However, storing the old `rbp` in the qword pointed to by the new `rbp` simplifies walking the stack frames of a running program. This would not be as simple if the caller saved it somewhere. – ecm Aug 24 '21 at 10:56

1 Answers1

4

Yes, custom calling conventions are possible (as long as you don't want to call or be called by compiler-generated code), but usually this change would be for the worse.


The major point of a frame pointer is that you set it up once for the lifetime of the function. If you had to save/restore it somewhere else around every function call, that would suck.

The other point is that you save it in a standard place in your stack frame, so backtraces can follow the linked list of saved frame pointer values that walk the stack frame. That couldn't happen if it didn't get saved to a standard place within a stack frame (relative to the return address).


You'd just pick a different register as your frame pointer (a call-preserved one in whatever calling convention you're inventing, like RBP is in the standard conventions) instead of doing mov %rbp, %r13 / call foo / mov %r13, %rbp or something around every function call. (Or just leave it in R13.) You wouldn't want to push %rbp / pop %rbp around each call either because that's even less efficient, and makes it more trouble to get stack alignment correct.

That's fine, nothing (else) is special about RBP, it just means you can't save code-size with leave if you're using a reg other than RBP as your frame pointer.

Well actually, RBP (and R13) are somewhat special from an efficiency POV: the modes with RBP as the base, like (%rbp), can only be encoded with a disp8 or disp32, not without a displacement. (That encoding actually means "no base"). So RBP is obviously a good choice for a frame pointer, less good for other uses where you might hold a pointer you want to deref with no offset.

See Why are rbp and rsp called general purpose registers? for more about that.

R13 has that same downside but wouldn't be a great choice because it would mean every access to the stack always needs a REX prefix, like 4-byte mov -4(%r13), %eax vs. 3-byte with RBP.

So yes, you can do this, but there are a lot of reasons not to.


Most functions don't need a frame pointer anyway, though; gcc -O1 and higher implies -fomit-frame-pointer, so it only uses a frame pointer at all in functions that do stuff like alloca or C99 variable-length arrays. (Or that over-align the stack, e.g. by 32 or 64 bytes.)

In that case (-fomit-frame-pointer), using some other register as a frame pointer in the few functions that use one would be less wasteful of code-size. Or an actual improvement if you used say R13 and left RBP free in a function that almost never actually referenced its own stack frame, just needed it aligned for a couple instructions.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 1
    Is `mov %rbp, %r13` / `call foo` / `mov %rbx, %r13` correct? Unless I'm mis-understanding this, but shouldn't the 3rd instruction read `mov %r13, %rbp`? – Sep Roland Aug 24 '21 at 19:41
  • @SepRoland: Thanks, fixed. I think I started typing that with RBX as the call-preserved register of choice, but this 64-bit mov needs a REX prefix anyway so I changed to R13, leaving RBX free for better uses... And then didn't finish editing everything :P (And finding a use for R13 where it doesn't matter that `(%r13)` needs a disp8=0 to encode, because it's the high-half twin of RBP). – Peter Cordes Aug 24 '21 at 21:04
  • @PeterCordes: As I know `%rbp` will not be saved everytime, it will be saved only when it's value being changed. Does this mean functions that do not hold `%rbp` are untraceable through the method you mentioned above? – li tang Aug 25 '21 at 05:15
  • @litang: If it's "caller-saved" (i.e. call-clobbered), functions have to assume that every call they make *does* clobber it. So only leaf functions can just set it up with `mov %rsp, %rbp`. But leaf functions have all call-clobbered registers to play with for values so often don't need to save things on the stack (and thus often don't benefit from having a frame pointer at all). – Peter Cordes Aug 25 '21 at 05:19
  • @litang: Also no, if `%rbp` is call clobbered, it normally won't be saved *anywhere*. That's the whole point of call-clobbered registers. See [What are callee and caller saved registers?](https://stackoverflow.com/a/56178078) (Unless you had some extra standardization of stack-frame layout in mind that you wanted to add to your invented calling convention, with the caller having saved something in memory in a standard location relative to the return address, but then that's inconvenient if you run out of arg regs and need stack args.) – Peter Cordes Aug 25 '21 at 05:21
  • @litang: If you have some asm instruction sequences in mind for the top/bottom of functions, and for call sites, you should say so. Otherwise I'm just making up ideas for what one might do. If you wanted a frame pointer at all, the most sensible thing would just be to use a different register that *is* call-preserved for it, if RBP isn't. There are I guess a few ways you could go about it if you really did want to use RBP as a frame pointer even without it being call-preserved. – Peter Cordes Aug 25 '21 at 05:24