


















Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
Lecture notes on Oops in Strings/pointers, Functions in MIPS, Control flow in C, Function control flow MIPS, and a note about types. It also discusses the big problem of overwriting values and the possibility of saving registers by either the caller or the callee. 400 characters long.
Typology: Lecture notes
1 / 26
This page cannot be seen from the preview
Don't miss anything!



















Announcements:
Today:
— Oops in Strings/pointers (example from last time) — Functions in MIPS
We’ll talk about the 3 steps in handling function calls:
And how they are handled in MIPS:
— New instructions for calling functions. — Conventions for sharing registers between functions. — Use of a stack.
MIPS uses the jump-and-link instruction jal to call functions.
— The jal saves the return address (the address of the next instruction) in the dedicated register $ra, before jumping to the function. — jal is the only MIPS instruction that can access the value of the program counter, so it can store the return address PC+4 in $ra.
jal Fact
To transfer control back to the caller, the function just has to jump to the address that was stored in $ra.
jr $ra
Let’s now add the jal and jr instructions that are necessary for our factorial example.
Functions accept arguments and produce return values.
The blue parts of the program show the parameters and arguments of the fact function.
The purple parts of the code deal with returning and using a result.
int main() { ... t1 = fact(8); t2 = fact(3); t3 = t1 + t2; ... }
int fact(int n) { int i, f = 1; for (i = n; i > 1; i--) f = f * i; return f; }
Assembly language is untyped—there is no distinction between integers, characters, pointers or other kinds of values.
It is up to you to “type check” your programs. In particular, make sure your function arguments and return values are used consistently.
For example, what happens if somebody passes the address of an integer (instead of the integer itself) to the fact function?
There is a big problem here!
— The main code uses $t1 to store the result of fact(8). — But $t1 is also used within the fact function!
The subsequent call to fact(3) will overwrite the value of fact(8) that was stored in $t1.
The CPU has a limited number of registers for use by all functions, and it’s possible that several functions will need the same registers.
We can keep important registers from being overwritten by a function call, by saving them before the function executes, and restoring them after the function completes.
But there are two important questions.
— Who is responsible for saving registers—the caller or the callee? — Where exactly are the register contents saved?
Who is responsible for saving important registers across function calls?
— The caller knows which registers are important to it and should be saved. — The callee knows exactly which registers it will use and potentially overwrite.
However, in the typical “black box” programming approach, the caller and callee do not know anything about each other’s implementation. — Different functions may be written by different people or companies. — A function should be able to interface with any client, and different implementations of the same function should be substitutable.
So how can two functions cooperate and share registers when they don’t know anything about each other?
Another possibility is if the callee saves and restores any registers it might overwrite.
For instance, a gollum function that uses registers $a0, $a2, $s and $s2 could save the original values first, and restore them before returning.
But the callee does not know what registers are important to the caller, so again it may save more registers than necessary.
gollum:
li $a0, 2 li $a2, 7 li $s0, 1 li $s2, 8 ...
jr $ra
MIPS uses conventions again to split the register spilling chores.
The caller is responsible for saving and restoring any of the following caller-saved registers that it cares about.
$t0-$t9 $a0-$a3 $v0-$v
In other words, the callee may freely modify these registers, under the assumption that the caller already saved them if necessary.
The callee is responsible for saving and restoring any of the following callee-saved registers that it uses. (Remember that $ra is “used” by jal.)
$s0-$s7 $ra
Thus the caller may assume these registers are not changed by the callee. — $ra is tricky; it is saved by a callee who is also a caller.
Be especially careful when writing nested functions, which act as both a caller and a callee!
In the factorial example, main (the caller) should save two registers.
— $t1 must be saved before the second call to fact. — $ra will be implicitly overwritten by the jal instructions.
But fact (the callee) does not need to save anything. It only writes to registers $t0, $t1 and $v0, which should have been saved by the caller.
Now we know who is responsible for saving which registers, but we still need to discuss where those registers are saved.
It would be nice if each function call had its own private memory area.
— This would prevent other function calls from overwriting our saved registers—otherwise using memory is no better than using registers. — We could use this private memory for other purposes too, like storing local variables.