Initially the runtime stack is empty:
Effect when the function call f(3.14) passes the paramter 3.14 using the runtime stack:
Recall that:
The runtime (program) stack
is also used
to
store
return addresses !!!
We must be very careful
and
organize the
data on the
runtime (program) stack !!
Let's enumerate all the information that we will be storing in the runtime (program) stack....
You have seen that the runtime stack was used to store (save):
|
Example: saving return address on the runtime stack
|
|
Depicted more clearly with a diagram:
|
Now, we will also use the the runtime stack to store:
|
Let's first figure out when we can reserve space (on the runtime stack) to store the parameters and the local variables
When can/do we pass the parameters to a function ?
f1: +----> f2: ... | push {lr} | ... | ... | ... | ... | bl f2 ----------+ ... | ... <--------------+ ... | ... | ... V ... +------ pop {pc} |
When can/do we pass the parameters to a function ?
f1:
+----> f2:
... | push {lr} |
... | ... |
pass params | ... |
bl f2 ----------+ ... |
... <--------------+ ... |
... | ... V
... +------ pop {pc}
|
Answer: it must happen before the bl f2 where we call the function !!!
(It's too late to pass parameters after the bl f2 instruction !)
When can/do we create the local variables for the function f2 ?
f1: +----> f2: ... | push {lr} | ... | ... | pass params | ... | bl f2 ----------+ ... | ... <--------------+ ... | ... | ... V ... +------ pop {pc} |
When can/do we create the local variables for the function f2 ?
f1:
+----> f2:
... | push {lr} |
... | alloc local vars
pass params | ... |
bl f2 ----------+ ... |
... <--------------+ ... |
... | ... V
... +------ pop {pc}
|
Answer: immediately after we saved the return address
(Because the local variables are created when a function starts executing)
Due to the ordering of the events, the runtime stack will have the following (data) structure:
|
This (data) structure is called: an (function) activation record or stack frame
Activation record or stack frame: ( click here)
|
Activation record or stack frame: ( click here)
|
|
Recall the events of the function calls are as follows:
f1: +----> f2: ... | (2) push {lr} | ... | | (1) pass params | | bl f2 ----------+ (3) alloc local vars ... <--------------+ ... | ... | ... V ... +------ pop {pc} |
The program stack after (1) passing parameters and (2) saving return address is as follows:
f1: +----> f2: ... | (2) push {lr} | ... | | (1) pass params | | bl f2 ----------+ (3) alloc local vars ... <--------------+ ... | ... | ... V ... +------ pop {pc} |
Before we allocate the local variables, we must save the original value of the FP register:
f1: +----> f2: ... | (2) push {lr} | ... | push {fp} | (1) pass params | | bl f2 ----------+ (3) alloc local vars ... <--------------+ ... | ... | ... V ... +------ pop {pc} |
Soon you will learn why we need to save the original value of the FP register....
We can now iniatialize the FP register to the base address of the stack frame object with mov fp, sp:
f1:
+----> f2:
... | (2) push {lr} |
... | push {fp} |
(1) pass params | mov fp, sp |
bl f2 ----------+ (3) alloc local vars
... <--------------+ ... |
... | ... V
... +------ pop {pc}
|
|
|
(1) Right before main( ) calls a function:
Notice that the main( ) function is using the FP register to access main's parameter and local variables.
(2) The caller (main) passes the parameters by pushing them on the stack:
The stack frame first contains the parameters of the function
(3) The bl instruction will jump to the function:
The stack frame is unchanged
(4) The function f will save the the return address on the stack:
The stack frame now contains parameters + return address
(5) The function f will set the FP register to the base address of its own stack frame:
Result: the main( ) function has lost it's FP register value !!! ---- ERROR !!!
|
BTW: a similar problem has been solved previously - do you remember which ???
The same problem occurs with the LR register in this example:
main: mov r0, #1111 mov r1, #1111 bl A // Save return address in reg LR mov r2, #1111 mov r3, #1111 Stop: A: mov r0, #2222 mov r1, #2222 bl B // Overwrites LR (A's return address to main !) mov r2, #2222 mov r3, #2222 mov pc, lr // A fails to return to main !!! B: mov r0, #9999 mov r1, #9999 mov pc, lr // B succeeds to return to A |
The caller overwrites the LR register containing the return address !!!
We have solved this problem by saving the LR register on the stack:
main: mov r0, #1111 mov r1, #1111 bl A // Save return address in reg LR mov r2, #1111 mov r3, #1111 Stop: A: push {lr} // Save LR on stack mov r0, #2222 mov r1, #2222 bl B // Overwrites LR (A's return address to main !) mov r2, #2222 mov r3, #2222 pop {lr} // restore old LR mov pc, lr // A returns correctly B: mov r0, #9999 mov r1, #9999 mov pc, lr // B succeeds to return to A |
Therefore: we can apply the same solution technique to save and restore the FP register