6.8. Nested Function Calls¶
The basic calling convention introduced earlier does not provide for a function calling another function. When the first function is called, the lr
will be set to point to the return address that function should use. If that function calls a second function using BL
, the lr
will be replaced with the address to return to within the first function and wipe out where the first function needs to return to!
To solve this problem, we need to store the current value of the lr
to the stack as we enter the function, and restore it at the end of the function. That way it does not matter if the lr
gets changed during the execution of the function.
Now the calling procedure looks like:
6.8.1. Calling Procedure (With LR storage)¶
- Function Call (Done by caller)
Any data in r0-r3 that needs to be stored is pushed to the stack.
Parameters are placed in r0, r1, r2, and r3 (in order).
Use
BL
to branch to the function.- Function Prologue (Done by called function)
Push lr to the stack
If the function will use any registers in r4-r9, push the current values to the stack.
- Function Body (Done by called function)
Do the work of the function. Access passed parameters in r0-r3. Only modify registers r0-r3 and any registers that were saved in the prologue.
- Function Epilogue (Done by called function)
Place any return value(s) in r0, r1, r2, r3. (C/C++ generally only return one value, but there is no reason in assembly you can’t return more than one.)
Pop any stored registers (r4-r9) from the stack to restore their old values.
Pop lr from the stack
Return to the caller with
BR LX
- Resume Control (Done by caller)
Any returned value(s) are in registers r0-r3.
Pop stored registers (r0-r3) that were preserved before calling.
Note
Since storing and restoring the lr
is only important if a function will call another function, compilers will generally use this version of the prologue and epilogue while building a function that calls another function, and the more basic version that skips that when building a function that does not make a call to another function.
This simple program demonstrates how saving and restoring the lr
allows a function to call another function and still successfully return to its caller. To see what happens without doing so, try commenting out lines 31 and 41. The simulator will realize something is amiss and give an error message when you hit the return in plusTwo. A real processor would happily “return” at the end of plusTwo back to line 42 (address 0x00000018) (where the last call to plusOne returned to) instead of back to line 10 (0x00000008). This would set up an infinite loop of line 42 returning to itself.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | /*
Main program calls plusTwo(5) and places result in r6
*/
.text
.global _start
_start:
@save any r0-r3 I need... (none)
MOV r0, #5 @load 5 as
BL plusTwo
MOV r6, r0 @get my return value, store into r6
@restore any r0-r3... (none)
@stop program
end: B end
@exit linux style
@MOV r7, #1
@SWI 0
@----------------------------------------------------------------------
/* plusTwo - adds two to the value passed in by calling plusOne twice
Params:
r0 = x
Return:
r0 = x + 2
*/
plusTwo:
@@@ Prologue ----------------------------------------
PUSH {lr} @store my return address
@@@ Body --------------------------------------------
@r0 already has the parameter to pass
BL plusOne
@r0 has x + 1
BL plusOne
@r0 has x + 2
@@@ Epilog --------------------------------------------
POP {lr} @Restore my return address and saved registers
BX lr @return (return value is in r0)
@----------------------------------------------------------------------
@----------------------------------------------------------------------
/* plusOne - adds one to the value passed in
Params:
r0 = x
Return:
r0 = x + 1
*/
plusOne:
@@@ Prologue ----------------------------------------
PUSH {lr} @store my return address
@@@ Body --------------------------------------------
ADD r0, r0, #1
@r0 has x + 1
@@@ Epilog --------------------------------------------
POP {lr} @Restore my return address and saved registers
BX lr @return (return value is in r0)
@----------------------------------------------------------------------
|