- What is the general function of a subroutine (procedure, method)
- parameters are passed in
- The call is made
- Control is transferred to the subroutine
- Computation occurs
- A return value is passed back
- Control is returned to the calling routine.
- The calling routine
- Is the routine that calls the subroutine
- The Called routine is the routine that is called.
- The call tree is a graph showing called and calling routines for all active functions.
- Everything (except for the os) is a called routine
- A leaf routine does not call anything.
- Subroutine call, pass 1
-
# calling routine
...
operation
j subroutine
return:
operation
...
# subroutine
subroutine:
operation
...
j return
- What is the issue with this setup?
- The jal immediate instruction solves part of this problem
- This will save the pc+4 (lie) into the register $ra
- And will set the pc to the immediate (sort of truth)
- This keeps a record of "where we came from"
- The other piece, then is jr $ra
- This sets the pc to R[$ra]
- We normally use $ra for this, but any GP register will do.
- Pass Two
# calling routine
...
operation
jal subroutine
operation
...
jal subroutine
operation
...
# subroutine
subroutine:
operation
...
jr $ra
- What advantage does this have over pass I?
- Parameters become an issue
- The following is from B.6
- The Calling Convention also called register use conventions provide rules for making procedure calls.
- They are not enforced by the hardware.
- You are fine if you don't follow them and make no calls to outside routines.
- But you can really get messed up if you don't follow them.
-
- Look at page B-22 (and the green sheet too)
- $at - the assembler temporary. Don't mess!
- $k0 and $k1, for the kernel. Don't Mess!
- $a0-$a3 : used to pass arguments to routines. Can be wiped out
in the call.
- $v0 and $v1, used to return values from the function.
- $t0-$t9, caller saved registers, You can use these, but if
you make a subroutine call, there is no guarantee they will be preserved across the call. (IE the subroutine can wipe them out)
- $s0 - $s7: callee saved registers, You can use these, but you must preserve them, and restore them to the original condition when you exit. You can trust them not to change (as far as you know) over a subroutine call.
- $gp : static data. I bet this would be a bad thing to change.
- $sp: The last location in use on the stack. You will change this in a subroutine call, but put it back before you return.
- $fp: The $sp for the routine before you. You will change this in any subroutine call, but put it back before you return.
- $ra, as discussed already. You will change this when making a subroutine call, but you need to put it back (so you know where to return to).
- Pass III
# calling routine
...
put parameters into $a0 through $a3
jal subroutine
retrieve values from $v0 and $v1
operations
...
put parameters into $a0 through $a3
jal subroutine
retrieve values from $v0 and $v1
operations
...
subroutine:
access parameters in $a0 through $a3
operations
put return values in $v0 and $v1
jr $ra
- This accomplishes most of what needs to be done
- There is no ability to preserve registers or data across calls
- There is a problem if we have more than 4 parameters, or 2 return values.
- Recursion becomes a major difficulty. (We tend to clobber the $ra)