The Function Prologue and Epilogue

In script Subsection 9.3.3: The Function Prologue and Epilogue S is defined as

the maximum size of the stack frame, i.e. the maximum offset used in rule [Var] plus 4.

So by a function prologue, the stack pointer is decremented by S+4 (Is this 4 the same 4 byte required for the return address?).
However, when it comes to the lecture notes 10_Compilers, the stack pointer was decremented by S+8.
I really got confused, how can I determine S?

image

Moreover, according to the call convention, $ra is a caller-save register. Nonetheless, it seems to be a callee-save register since the return address is stored at the entrance of the subroutine (the function). Does it really violate the call convention? If yes, why?

It depends on what exactly you define S as.
If you define it as the “number of bytes used” by the local variables, it is S+4 (S bytes for variables, 4 for $ra).
If you define it as largest offset for the variables used, it should be S+8 as you need the four bytes as offset S and then one additional cell space for $ra.

The script states:

S is the maximum size of the stack frame, i.e. the maximum offset used in rule [Var] plus 4.

Therefore, S+4 is correct here.
In the lecture, Prof. Hack showed both variants and settled on the maximum offset for the explanation. This is also shown in the blue explanation text on the top right.
It depends on your definition of what S is or correspondingly, how you compute it.

You statically (from the code) know how many local variables you have {\color{gray}\text{(without metaprogramming)}}.
For each variable, you need a slot in the stack to store its value (in syntax guided code generation).

$ra (register 31) is a caller-save register.
The callee is free to change it.
Instructions to call a function (like jal) will overwrite it.
Now at the beginning of a function, you have the address in $ra where to go back in the end (to the caller).
But you need this information at the end to go back.
Therefore, you need to save it if you ever call another function.
As an optimization for functions with many calls and for convenience, we save $ra directly at the entry once and for all for the function and restore it at the end, when we need it again.

It is caller-save in the regard, that a caller needs to save it around every call to keep the content intakt.

1 Like

There is a mistake in the machine code given in the lecture note, in sw $ra S($sp) and lw $ra S($sp). The return address in stack will be always overwritten by the value of local variable. Solution is sw $ra S+4($sp)

image

A counter example:
Let S be the maximum offset in the stack frame used for local variables.
Assume we have one local variable a and no parameters. Then S must be 0. The stack pointer is decremented by 8. However, the return address in stack will be overwritten by the value of a, or vice versa if $ra stored after a.

f:
     .globl f
     subiu $sp $sp 0+8  # I substituted S by 0
     sw    $ra 0($sp)
     sw    $t0 0($sp)  # local var with max_offset 
     # further body code
f_end:
     lw    $ra
     addiu $sp $sp 0+8
     jr $ra
3 Likes