![]()
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?
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 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 $raNote that the instructions below the bottom line must exactly undo the instructions above the top line. Fact implementationfact: 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 factNote: there are many mistakes in the fact example in the appndix in the textbook. How many can you find? About multiplyMultiplying 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 destinationThe pseudoinstruction mul assumes that the result is no more than 32 bits. mul $dst, $op1, $op2It does a mult followed by a mflo and ignores the part of the result in $hi. |
|