Image goes here
Function Calls
CptS 260 - Intro to Computer Architecture
Washington State University

Function Calls

(and multiply along the way)

Consider recursive function:

   int fact(int n) {
      if (n==0) {
         return 1;
      }
      else { return fact(n-1)*n; }
   }
When called with argument greater than 0 this function calls itself. We now have two variables named n; and clearly for large enough n we would run out of registers if we were to try to keep all of them in registers.

To overcome this, people realized early on (in the history of CS) that a LIFO call stack would solve the problem. As each function is called, some amount of space is claimed from the call stack, used while the function executes, then released when it returns. This block of storage is called at stack frame or procedure call frame.

The rules for using registers and the stack are usually developed by the designers of a processor architecture. Why?

  • so that the architecture and the stack rules can be optimized for each other
  • so that programs written by different people can interoperate
  • so that programs written in different languages can interoperate
It is very important that you learn to follow conventions, so we will deduct points on assignments where they are not followed, and questions about the conventions are fair game for exams.

The SPIM assembler (and most language compilers) place the call stack in its own segment which is often placed high in memory. (Drawing of segments). The stack then grows toward lower addresses. The advantage of this arrangement is that no decision needs to be made beforehand about allocating memory to the stack or to data.

The assempler or operating system initializes register $sp to the highest address in the stack before your program starts executing.

Argument passing conventions

"in" parameters
values that are passed to the function. The first 4 "in" parameters are passed on register $a0 to $a4 ("a" mnemonic for "argument"). Additional "in" parameters are passed on the stack. Space is reserved on the stack for the 4 arguments passed in registers. Why? Provides a place to save the registers when making a recursive call. These are "caller-save" registers.
function results
are returned in $v0 and $v1
callee-save registers
$s0-$s7; the caller may assume that these registers will contain the same values they had before the call, after the call returns. The callee must save and restore these if it uses them.
caller-save registers
$t0-$t9; the caller may not assume that these register contain the same values. If they need to be preserved it is up to the caller to save and restore them.
the stack pointer
$sp always points to the lowest address of the current stack frame which is always double-word (8-byte) aligned.
the frame pointer
$fp always points to the highest address of the current stack frame (4 less than the stack pointer of the next older frame).
the return pointer
$ra points to the instruction after the branch instruction that called the procedure.

In addition to callee save registers and extra arguments the stack frame design must provide for local variables and for saving $sp, $fp, $ra. The minimum stack frame size is 24 bytes.

Basic procedure entry conventions

#    adjust the stack pointer
     addiu  $sp, $sp, -framesize  # note typos in the book, pp A-27 & A-28 use subu
#    save return address and caller's frame pointer
     sw     $ra, 16($sp)   # offset depends on other stuff in the stack
     sw     $fp, 20($sp)   # ditto
     addiu  $fp, $sp, (framesize-4) # you have to do the subtraction!
#    save any other callee-save registers
#     --------------------
     ... do your thing
#     -------------------- 
#    restore callee-save registers
#    restore caller's fp
     lw     $fp, 20($sp)
#    restore return address
     lw     $ra, 16($sp)
#    restore the stack pointer
     addiu  $sp, $sp, framesize
     jr     $ra
Note that the instructions below the bottom line must exactly undo the instructions above the top line.

Fact implementation

fact:
    addiu  $sp, $sp, -32
    sw     $ra, 20($sp)
    sw     $fp, 16($sp)
    addiu  $fp, $sp, 28
    sw     $a0, 0($fp)
    bgtz   $a0, L2
    
    
    li     $v0, 1
    b      suffix

L2:
    addi   $a0, $a0, -1        # or addiu ?
    jal    fact
#   fact(n-1) now in $v0
    lw     $t0, 0($fp)         # pick up n
    mul    $v0, $v0, $t0

suffix:
    # note: return value must be already set in $v0
    lw     $fp, 16($sp)
    lw     $ra, 20($sp)
    addiu  $sp, $sp, 32
    jr     $ra

main:
    li     $a0, 5
    j      fact

Note: there are many mistakes in the fact example in the appndix in the textbook. How many can you find?

About multiply

Multiplying 2 32-bit numbers can produce a 64-bit result. The hardware mult instruction takes account of this putting the results in special registers $lo and $hi.
    mult   $op1, $op2   # note no destination
The pseudoinstruction mul assumes that the result is no more than 32 bits.
    mul    $dst, $op1, $op2
It does a mult followed by a mflo and ignores the part of the result in $hi.
(c) 2004-2006 Carl H. Hauser           E-mail questions or comments to Prof. Carl Hauser