Procedure Example C MIPS - ---- int a, b, c, d, x; # a=$s0, b=$s1, c=$s2, d=$s3, x=$s7 ... ... x = leaf_example (a, b, c, d); move $a0, $s0 # save args ... move $a1, $s1 move $a2, $s2 move $a3, $s3 jal leaf_example # jump & link move $s7, $v0 # get ret. val. ... int leaf_example (int g, int h, leaf_example: int i, int j) { addi $sp, $sp, -4 # sp -= 4 int f; sw $s0, 0($sp) # save s0 add $t0, $a0, $a1 # t0 = g + h f = (g + h) - (i + j); add $t1, $a2, $a3 # t1 = i + j return f; sub $s0, $t0, $t1 # f = t0 - t1 } move $v0, $s0 # pass back f lw $s0, 0($sp) # restore s0 addi $sp, $sp, 4 # sp += 4 jr $ra # return NOTE 1: Calling program did not save argument registers or temporary registers because it wasn't going to use any values in them after the call to leaf_example. NOTE 2: Called program saved $s0 only because that is the only saved register that it needed to use. All others are "saved" just by not being overwritten. What about the strange handling of multiple items on the stack (p. 99)? Book does not decrement/increment stack pointer everytime it pushes/pops something off the stack. Why not? * slight efficiency * eliminates need for $fp (frame pointer) -- see p. 103 Frame pointer points to beginning of stack segment used by this procedure activation (activation record). Would be useful if stack pointer is constantly changing. (Where is 1st thing pushed on stack? Started out as top of stack, but then became buried. So frame pointer points to initial top of stack at beginning of procedure, before things got pushed onto it.) BUT, book just adjusts stack pointer once at beginning of procedure and then readjusts it once at the end. So it is stable (just like $fp), and they don't need to introduce $fp. How do they do it? * decrement stack pointer by enough to cover all values that will be put on the stack at the very beginning addi $sp, $sp, -12 # subtract 12 from sp (push 3 words) * but this overshoots the mark for the first items to be pushed, so add offsets back in to refer to the various items on the stack sw $t1, 8($sp) # 1st item goes at orig $sp - 4 ($sp + 8) sw $t0, 4($sp) # 2nd item goes at orig $sp - 8 ($sp + 4) sw $s0, 0($sp) # 3rd item goes at orig $sp - 12 ($sp) * do the same thing to pop items off the stack: lw $s0, 0($sp) # 3rd item is at $sp lw $t0, 4($sp) # 2nd item is at $sp + 4 lw $t1, 8($sp) # 1st item is at $sp + 8 addi $sp, $sp, 12 # add 12 to sp (popped 3 words) -------------------------------------------------------------------- int fact (int n) { if ( n < 1 ) return 1; else return (n * fact (n - 1)); } fact: addi $sp, $sp, -8 # adjust stack for 2 items sw $ra, 4($sp) # save return address sw $a0, 0($sp) # save the argument slti $t0, $a0, 1 # t0 = 1 if n < 1 beq $t0, $zero, L1 # go to L1 if n >= 1 addi $v0, $zero, 1 # return value = 1 addi $sp, $sp, 8 # restore the stack pointer jr $ra # return to instruction after jal L1: addi $a0, $a0, -1 # argument to recursive call is n - 1 jal fact # make recursive call lw $a0, 0($sp) # return from jal; restore arg n lw $ra, 4($sp) # restore the return address addi $sp, $sp, 8 # popped 2 items off stack mul $v0, $a0, $v0 # return value = n * fact (n - 1) jr $ra Why are the 2 pops before the multiply, and not right before the return? (By the way, the mul operation is covered in Chapter 3.)