v0 today, a0 tomorrow
When one call's result is exactly the next call's argument, no stack slot is needed — the value goes straight from v0 into a0. Here's chainTwo(x), which returns second(first(x)):
addiu sp, sp, -24
sw ra, 20(sp)
jal first # first(x) — x already in a0
nop
jal second
or a0, v0, zero # first's result becomes second's argument
lw ra, 20(sp)
addiu sp, sp, 24
jr ra
nop
The whole idiom is that one or a0, v0, zero, sitting in the second jal's delay slot. Nested calls in C read inside-out — first runs, then second — and the assembly lays them out top to bottom in execution order. When you see back-to-back jals glued by or a0, v0, zero, write nesting, not two statements.
And note what's missing: no home-slot traffic. Nothing here is needed after a call except the value already flowing forward in v0, so there's nothing to protect. Compare that with the last lesson — the presence or absence of those stores tells you whether a value crosses a call.
The target is the same pipeline with a small coda after the second call. Don't forget it.
Your task
extern s32 shape(s32 v); and extern s32 polish(s32 v); are declared for you. Write func_8005d47c to reproduce the target assembly.