1

I'm confused about why if you wanted to allocate 8-byte storage on the stack, you use the following command

subq $8,%rsp 

%rsp stores an address. Why does subtracting the literal value 8 from the address stored in %rsp allocate 8 bytes? In particular, why does $x correspond to x bytes?

Edit: My question was answered in this stackoverflow response. It's in terms of bytes because that's the size of an int, which is the literal argument being passed in.

Edit 2: The above is incorrect. See Bo's comment.

7
  • Why wouldn't it? I mean, I would like to understand what is unclear about it? The pointer is into a stack. If you move the pointer eight bytes back you create a hole of eight bytes to use. Commented Mar 11, 2017 at 6:21
  • I'm confused why a literal value is interpreted in units of bytes. Commented Mar 11, 2017 at 7:28
  • 2
    Because a minimal unit of memory is a byte Commented Mar 11, 2017 at 7:29
  • 1
    No. The other question was about typed pointers in C, which works differently from addresses in type-less assembly. The type of $8 has nothing to do with the result, it is just the value 8. On x86 hardware the memory unit is the byte, so we just count bytes. There are word-addresses systems where the unit of memory is perhaps 16 or 32 bits. There it would work differently. Commented Mar 11, 2017 at 10:34
  • 2
    @AisforAmbition - Assembly is untyped, so what you do with rax depends on your interpretation of the result. It can be just about anything. However, rsp is surely a pointer, so it contains a memory address. And you have to use subq because of the size of rsp. Using subb just doesn't work. Commented Mar 12, 2017 at 1:58

1 Answer 1

7

You're right. Stack pointer is just a pointer to a memory location, i.e. it holds an integer value that is a valid address. That memory block is actually allocated as a large chunk at thread initialization. It is usually large enough to avoid stack overflows. Stack pointer points to the end of that block at the beginning and gets decremented whenever a new value is pushed (and incremented whenever popped). Pushing a value to stack just moves the given value to the memory slot RSP points at and decrements it. For instance:

push rax 

is synonymous to this (except for not affecting FLAGS):

sub rsp, 8 mov [rsp], rax 

Only subtracting from RSP (instead of doing a push) just leaves you free space in the stack that you can use for your own purposes, without also overwriting whatever old value was there, and the size can be larger than one 8-byte stack slot1. That's how local variables work actually. So:

sub rsp, 16 

Moves stack pointer down 16 bytes so you have space for 16 bytes you can use for whatever you want in your own function, as any combination of sizes. To release it you either need to remember to increment rsp register accordingly, or use a frame pointer like:

mov rbp, rsp sub rsp, 16 ; and here access the values using rbp-xxx instead of rsp+xxx mov rsp, rbp 

So the compiler could have done push rax instead of a sub rsp, 8 but that would actually require a write to memory (because the contents of rax needed to be stored). That would be a waste. Instead, moving the pointer itself is a much cheaper operation until that memory block is really needed.

(let me know if Intel assembly syntax I used here creates confusion, I just find it much easier to write)


Footnote 1: A multiple of 8 bytes is a good idea to keep the stack aligned. In fact the calling convention lets callees assume that RSP was aligned by 16 before a call, so callers need to do that except when calling your own functions that don't depend on that. RSP%16==0 before a call means RSP%16==8 after a call (which pushes a return address). Counting both push and sub rsp, total movement of RSP since function entry should be an odd multiple of 8.

Some compilers will use push rax in a function prologue to move RSP, because that's more efficient that sub rsp, 8 on some CPUs, even though it does a store we don't care about. (Why does this function push RAX to the stack as the first operation?). Freshly-stored garbage is equivalent to old garbage as far as the value in that space; if you want to be clever, push 0 or something that's actually useful as an initializer for the you're reserving, instead of a mov later. (What C/C++ compiler can use push pop instructions for creating local variables, instead of just increasing esp once?).

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.