|
CSE Home | About Us | Search | Contact Info |
Procedure Call Conventions
Lecture Key Points
Critical points:
- the stack
- the convention for the use of registers $sp, $fp, $ra
- the steps required in procedure linkage
- "stack frame"
- Cebollita linkage
- caller-saved vs. callee-saved registers
- the heap
Other points of interest:
- MIPS linkage
- the use of registers $v0, $v1, and $a0-$a3
- the use of registers $s0-$s7 and $t0-$t9
- how do you pass an array? a structure? a function?
- "call by value" vs. "call by reference"
- stack frame traceback: debugging and exception handling
- stack overflow security vulnerability
- Hey,
main()
is just a subroutine! But, who calls it?The Problem
Imagine these two routines are presented to the compiler.
Of course, this code must be turned into assembly language. Here's a generic view of what happens at the assembler level:
Caller Callee main() { int result; result = max( 10, 2 ); printInt( result ); } int max(int a, int b) { int result; if ( a < b ) { result = b; } else { result = a; } return result; }
- The CPU is happily fetching instructions in the caller, ta-de-dum.
- The caller enters a "pre-call" sequence of instructions that sets things up. Among other things, it puts the arguments of the call somewhere the callee will be able to find them.
- The caller executes a
jal max
instruction to simultaneously (a) put the current PC in register $ra, and (b) jump to the memory location corresponding to symbolmax
.- Now the subroutine (
max
) is executing. The first thing it does is execute some "subroutine entry" code. Among other things, it makes space on the stack for its own local variables (in this case,result
).max
executes - ta-de-dummax
is done. It executes some "subroutine exit" code - among other things, it places the return value somewhere the caller can find it, and frees the stack space it allocated for its own use.- The callee returns to the caller with the instruction
jr $ra
.- Now we're back to the caller, at the instruction following the
jal
. The caller executes a "post-call" sequence of instructions.Exactly how things are done at each step is decided by convention, an agreement among all software components on how they use the hardware resources. (The hardware doesn't care - it just fetch-increment-execute's.) There are multiple such conventions in the course materials, unfortunately - the book has two or three, and Cebollita uses a different one.
To help fill in what all the decisions are a convention must make, let's examine the Cebollita one in more detail.
Cebollita Procedure Linkage
Caller: Prepare for Call
- Some registers may contain values that you want to use after making the call. When you invoke the subroutine, it may use those registers and those values will be gone. For the registers/values you want to preserve, allocate space on the stack using
addiu $sp, $sp, -<4 * #regs saved>
Copy the registers/values to be saved there.- If the subroutine takes arguments, allocate space on the stack for them, and then copy the values to be passed into that memory.
jal <subroutine-entry-point>
Callee: Procedure Entry
- I'm required to save the return address register, $ra, and the frame pointer register, $fp. I also need to allocate space for all my local variables. Allocate space on the stack for all of these, and copy $ra/$fp them there.
- Set $fp to the current value of $sp, i.e., remember where the stack pointer is right now.
- If this routine needs to access either a local variable or a passed argument, it is addressed by its known offset from $fp.
Callee: Procedure Exit
- Load any result to be returned in register $v0 (and $v1, if needed).
- Load registers $ra and $fp with the values previously saved on the stack.
addiu
to $sp to free the space allocated on entry.jr $ra
Caller: Call Cleanup
- Free up the stack space you allocated to pass arguments.
- Restore any registers saved before the call, and free the stack space allocated for them.
- Start using the return value (in $v0)...
Cebollita Linkage in Pictures
Start condition:
Caller saves registers:
Caller pushes arguments:
Callee saves $ra/$fp and allocates space for local vars:
At this point, $sp may move, but $fp is fixed (so long as the callee is executing). Its local variables and arguments are both accessible at known, constant offsets from $fp.When the callee returns, the stack is adjusted in the opposite order, passing through the stages shown above from bottom to top. The result is that it the caller ends up with memory looking like it was when the call was made, and all registers that are important to it having the values they did just before the call (except $v0 (and possibly $v1), which hold the function return value).
Cebollita Linkage in Code
Here is an annotated version of the Cebollita-produced assembler code for
max.c
above.Here is an annotated version of the Cebollita-produced assembler code for
main.c
above.(Other) Procedure Linkage Options: MIPS
Reading and writing memory is slow - you'd like to operate out of registers as much as you can. For that reason, "the MIPS subroutine linkage convention" (described in a couple of variants in the book) differs from the Cebollita one. First, rather that passing all arguments using space allocated on the stack, MIPS always puts the up-to-first-four arguments in registers (named $a0 through $a3). The callee knows to look for them there. If there are more than four arguments, the callee pushes the extras onto the stack.
Second, the MIPS convention distinguishes caller-saved from callee-saved resgisters. (In Cebollita, all registers are caller-saved.) Registers $s0-$s7 are callee-saved: if the callee uses them, it has the responsiblity to save the original values no the stack at entry and to restore them on exit. (If it can avoid using them, it avoids having to save them, which is the point.) Registers $t0-$t9 are caller-saved: the callee is free to write into them without preserving their contents, so the caller must save them if it wants to keep the values they hold.
Additionally, the MIPS linkage differs in where it points $fp, and in the locations within the call frame of local storage and saved registers. (Finally, the two versions of the MIPS convention differ in that one of them doesn't use $fp at all - it makes do with just $sp.)
This work is licensed under a Creative Commons Attribution-Share Alike 2.5 License.Department of Computer Science & Engineering
University of Washington
Box 352350
Seattle, WA 98195-2350
(206) 543-1695 voice, (206) 543-2969 FAX
[comments to zahorjan]