The pictures here show a small area of memory at various moments during the lifetime of a call to a subroutine named subr. The two fat black lines delimit this lifetime. Each view shows four words at addresses 0x7FFFFF1C to 0x7FFFFF28. At the top we are in subr's caller. Since it is making this call, the caller is not a leaf, so it has saved its $ra (0x004000340) in its stackframe. The other boxes have placeholder values (1, 3, 4). The green boxes are part of the stack; the lavender boxes are in the free area adjacent to the stack. The white boxes contain MIPS instructions. There is one instruction in the caller (the jal above the line), two instructions at the beginning of subr, and three instructions at the end. As you read down the diagram, you can compare the stack pictures above and below a white box and see the effect that instruction had on the stack.
In the middle is ... displayed vertically. What is being expressed by this notation is that the operations above the ... take place at the beginning of the subroutine, and the operations below the ... take place at the end of the subroutine. These operations are a sort of boilerplate code that all subroutines except leaves must have. The actual business of the subroutine is accomplished between them, i.e. where the ... notation is. The two instructions at the beginning constitute Beginning-of-Function Business (BOFB), and the three at the end constitute End-of-Function Business (EOFB). In the middle is Function Actual Business (FAB). Typical code for a non-leaf function then is BOFB followed by FAB and ending with EOFB. Think of BOFB and EOFB as bookends around FAB.
Operations on the stack are always word operations; i.e. the stack pointer must always be a multiple of 4. Violating this provision will almost certainly cause the program to crash and burn.
Each box in a memory group is a single word that has its address posted beside it (orange). Register $ra is shown in yellow at each stage of the process. The only operation shown here that changes it is the jal at the beginning, but it also gets changed (red) when the code in the middle makes another subroutine call. Each view into memory also shows the stack pointer, both numerically and as an arrow pointing to memory. The numerical value shown is always the address of the memory word being pointed to. The sequence of operations is:
The last figure shows things as they are when our caller regains control after our return. Notice that the caller finds everything just as it left it when it called us.
This code, illustrates two inviolable rules for subroutines in manipulating the stack:
This can be said in one rule: always leave the stack exactly as you found it. (The routines that put those values there will regain control after you exit, and they must find those values there just as they left them.)
If you compare the very first picture of the stack with the very last one, you will see that the value in address 0xFFFFFF20 is not the same in both places. This is not a violation of our rule here because address 0xFFFFFF20 is not part of the stack in either picture. Remember, all locations that are part of the stack are at addresses ≥ the address in the stack pointer and are colored green. You might think that we ought to zero out that word now since it is no longer part of the stack, but that would waste time and space and do no good. It is quite likely that that word will be in the stack at some later point in the program. Until that happens, we can leave it as it is, and it will do no harm. When that happens it will be written over before it is ever read, so it would be a waste of time to set it to zero or some other value now.