0

Assume we are passing arguments to a subroutine using the stack frame as follows:

addi $sp, $sp, -8
sw $s0, 0($sp)
jal sub
lw $s1, 4($sp)
addi $sp, $sp, 8

sub: lw $t0, 0($sp)
... do stuff ...
sw $t1, 4($sp)
jr $ra

I understand the concepts of passing parameters through the stack and returning to the caller using the $ra register.

What is not so clear to me is this:

addi $sp, $sp, 8

This restores space in the stack frame. Can someone help me understand:

  1. What happens if I don't do this?
  2. Does the assembler "cares" if I don't restore the space?
  3. Is this in a way similar to memory management in c++? (i.e.: deleting pointers, destructors, etc)
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
hendrix
  • 21
  • 4
  • 1
    1) your caller will be very unhappy because its `$sp` will not point to where it was and it won't find its own local variables 2) no 3) somewhat – Jester Mar 31 '21 at 20:42

2 Answers2

2

The stack pointer is a call-preserved register, like $s0 .. $s7 - the caller expects its value to be unchanged after a jal so it can find its own stack stuff (like its own saved return address).

See also a MIPS calling convention summary I found with google: https://courses.cs.washington.edu/courses/cse410/09sp/examples/MIPSCallingConventionsSummary.pdf

Think about what would happen if you wanted to call a function, but that function would leave $sp pointing some unknown distance farther down. How would you recover from that? It would be a big pain, so instead we require functions to restore $sp (or don't touch it in the first place).

In C terms, it's the mechanism that deallocates automatic storage (local vars like int i;) when leaving a function. (This works even if you leave via longjmp back up over multiple levels of parent functions instead of returning one level at a time.)

It's not like running destructors for each variable separately, that's why you can free the space for four words at once with one large addiu $sp, $sp, 16.


Does the assembler "cares" if I don't restore the space?

No, the assembler itself just translates source lines to bytes in the output file (or current section), one line at a time. The only things that reference other lines are symbols and macros. It doesn't do anything at all to enforce structured programming.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • When you say "or don't touch it in the first place" do you mean it's best to use $a0.... to pass arguments and $v0.... to return values instead of using the stack pointer? – hendrix Mar 31 '21 at 20:50
  • @hendrix: Yes, of course, if you don't need to store/reload anything, don't. Look at optimized compiler output, e.g. https://godbolt.org/z/s9WTKn7aT. The standard MIPS calling convention / ABI only passes up to 4 args in registers, hence naming them `$a0..3` . $v0 / $v1 are traditionally used for return values, not args. IDK what you mean by "returning values via the stack pointer" - that would be weird. If you want to return a large struct or array, normally you'd pass a pointer to it, not have the function leave it on the stack. – Peter Cordes Mar 31 '21 at 20:54
  • If you don't need to call or be called by code generated by a C compiler, you can certainly use more register args or do whatever you want, though, as long as you write the caller to expect what your function actually does. – Peter Cordes Mar 31 '21 at 20:54
  • Thank you! It makes more sense now. – hendrix Mar 31 '21 at 20:56
0

every time we want to save any register's value that we will need later before overwriting it we save it in a stack.

for example if we overwrite the return address register $ra the program will crash or end up in endless loop.

To overcome overwriting the return address of each function we store it in a stack.

so answering your questions:

1- you better clean (set to zero) the memory you used.

2- the assembler doesn't care

3- no it is not like c++. in c++ pointers allocating space using malloc or constructors take space in the heap, not the stack. the heap has an organized structure consisting of blocks and headers. each header hold information about the block, e.g. block size - next free block - this block is free/reserved.

when we free such blocks in c/c++ we basically reset the header to free state.

and btw the code you provided should be more like:

addi $sp, $sp, -8
sw $s0, 4($sp)
sw $ra, 0($sp)
jal sub
lw $ra, 0($sp)
lw $s0, 4($sp)
addi $sp, $sp, 8
jr $ra

sub: lw $t0, 0($sp)
... do stuff ...
sw $t1, 4($sp)
jr $ra
LazerDance
  • 177
  • 1
  • 8