Topic 3 · Class activity 4 · Week 7 · Friday

Trace a Call, Including a Nested One

Caller versus callee, $a0–$a3, $v0, $ra — and why f(1, g(2)) works at all.

Format pairs, at a laptopTime 50 min

The point

Trace a call through the register conventions, including the case that breaks naive implementations: a call inside a call.

Do this

  1. Write the calling sequence out as two columns — what the caller does, what the callee does — for your own compiler. Then compare with the division in the lecture notes. Anything on the wrong side?
  2. Predict the TAC for f(1, g(2)): write the param and call instructions in order. Compile and compare.
  3. Explain the ordering. Both calls emit arguments into the same buffer. Why does the inner call take the right ones? Work through f(g(1), 2) as well — it comes out right for a different reason.
  4. Recursion. Run fact(5). Then answer: which registers had to be preserved across the recursive call, and who preserved them — caller or callee?
  5. Find the limit. Write a function with five parameters. What does the compiler say? Is that a good error message? Improve it if not.
  6. Design. Sketch how you would pass a fifth argument. Real MIPS puts it on the stack — but where, relative to the caller's frame, and who pops it? Use Dragon's second design principle (§7.2.3) to justify your answer.
On step 3

A call consumes the LAST n buffered arguments, where n comes from the call instruction itself. Trace f(1, g(2)) under that rule and watch it come out right; then trace f(g(1), 2) and notice that the reason is different.

Deliverable

Your two-column calling sequence with any corrections; predicted vs actual TAC; your answer to step 3; the register-preservation answer from step 4; and your two-line design for step 6. Feeds Lab Question 15.