public class Recurse { public static int f(int a, int b) { int i, s; s = 0; for (i = a; i < b; i++) { s = s + f(a+1, b-1) + 1; } return s; } static int result; public static void main( ) { result = f(2, 5); } } |
|
|
|
Stack frame of the fib( ) method: SP -----> +---------------------+ -8 | Local var i | addr(i) = FP - 8 +---------------------+ -4 | Local var s | addr(s) = FP - 4 FP -----> +---------------------+ 0 | old Frame Pointer | (4 bytes) +---------------------+ 4 | Return Address | (4 bytes) +---------------------+ 8 | Parameter a | addr(a) = FP + 8 +---------------------+ 12 | Parameter b | addr(b) = FP + 12 +---------------------+ |
Note: :
|
main: /* ------------------------------------------------- Pass parameters (using stack) ------------------------------------------------- */ mov r0, #5 push {r0} // Pass 5 (param 2) using the program stack mov r0, #2 push {r0} // Pass 2 (param 1) using the program stack /* ------------------------------------------------------ call f(2,5) ------------------------------------------------------ */ bl f add sp, sp, #8 // Clean up the parameters 2,5 from the stack /* ----------------------------------------------------------------- Assign return value (in r0) to variable result ----------------------------------------------------------------- */ movw r1, #:lower16:result // Do NOT use r0 !!! movt r1, #:upper16:result // (Because r0 contains the return value) str r0, [r1] // This will store return value in result |
f: // When f begins, we will have: a,b on the stack /* ========================================================== Function Prelude: complete the stack frame structure ========================================================== */ push {lr} // Save LR (return address) push {fp} // Save FP (used by caller) mov fp, sp // Mark the stack top location before // allocating any local variables sub sp, sp, #8 // Allocate 2 int variables on the stack /* =============================================== We completed the stack frame Now we can write the function body =============================================== */ // s = 0 mov r0, #0 str r0, [fp, #-4] // assign 0 to s // i = a ldr r0, [fp, #8] // r0 = a str r0, [fp, #-8] // assign to i while: // while ( i < b ) ldr r0, [fp, #-8] // r0 = i ldr r1, [fp, #12] // r1 = b cmp r0, r1 // i ? b bge whileEnd // Exit for loop // We CANNOT compute s + f(a+1, b-1) + 1 without the // value (= a number) for: f(a+1, b-1) // Compute f(a+1, b-1) first ! ldr r0, [fp, #12] // r0 = b sub r0, r0, #1 // r0 = b-1 push {r0} // pass param2 (b-1) ldr r0, [fp, #8] // r0 = a add r0, r0, #1 // r0 = a+1 push {r0} // pass param1 (a+1) to f on stack bl f add sp, sp, #8 // Clean up parameter (n-1) from stack // We can now compute s + f(a+1, b-1) + 1 ldr r1, [fp, #-4] // r1 = s add r0, r0, r1 // r0 = s + f(a+1, b-1) add r0, r0, #1 // r0 = s + f(a+1, b-1) + 1 str r0, [fp, #-4] // Assign s + f(a+1, b-1) + 1 to s // i++ ldr r0, [fp, #-8] // r0 = i add r0, r0, #1 // r0 = i + 1 str r0, [fp, #-8] // Assign i+1 to i b while whileEnd: // return s in r0 ldr r0, [fp, #-4] // r0 = s /* ============================================================= Function Postlude: de-allocate local variable and restore FP ============================================================= */ mov sp, fp // De-allocate local variables pop {fp} // Restore fp pop {pc} // Return to the caller |
How to run the program:
|
mov r0, #5 push {r0} // Pass 5 (param 2) using the program stack mov r0, #2 push {r0} // Pass 2 (param 1) using the program stack |
This will create the following stack structure:
+---------------------+ <------------ Stack pointer (SP) | parameter1 (a = 2) | +---------------------+ | parameter2 (b = 5) | +---------------------+ |
The main program calls the recursive function with a bl instruction:
bl f |
This will save the return address to main( ) in the LR register and jump to the f method
The f( ) function will start running, so let's take a look at the f( ) function
The prelude of the f( ) function consists of these instructions:
/* ========================================================== Function Prelude: complete the stack frame structure ========================================================== */ push {lr} // Save LR (return address) push {fp} // Save FP (used by caller) mov fp, sp // Mark the stack top location before // allocating any local variables sub sp, sp, #8 // Allocate 2 int variables on the stack |
I will explain what each one instruction does below.
Make sure that you realise that the structure of the stack frame is like this when the prelude of the fib( ) method starts executing:
+---------------------+ <------------ Stack pointer (SP) | parameter1 (a = 2) | +---------------------+ | parameter2 (b = 5) | +---------------------+ |
This instruction will save the return address in the LR register on the program stack.
The program stack now looks like this:
+---------------------+ <------------ Stack pointer (SP) | return address | +---------------------+ | parameter1 (a = 2) | +---------------------+ | parameter2 (b = 5) | +---------------------+ |
This will save the frame pointer on the stack and the program stack now looks like this:
+---------------------+ <------------ Stack pointer (SP) | old Frame Pointer | +---------------------+ | return address | +---------------------+ | parameter1 (a = 2) | +---------------------+ | parameter2 (b = 5) | +---------------------+ |
This will make the frame pointer FP points to the stack frame that is being built:
+---------------------+ <------------ Frame pointer FP & Stack pointer SP | old Frame Pointer | point to the same location.... +---------------------+ | return address | +---------------------+ | parameter1 (a = 2) | +---------------------+ | parameter2 (b = 5) | +---------------------+ |
The will enable the f( ) to use offset from the frame pointer to access the parameters and local variables that are stored in the program stack
The subtract instruction is used to allocate 2 local variables on the program stack
We have now completed the stack frame:
+---------------------+ <---- Stack pointer SP | Local var1 (i) | +---------------------+ | Local var2 (s) | +---------------------+ <---- Frame pointer FP | old Frame Pointer | +---------------------+ | return address | +---------------------+ | parameter1 (a = 2) | +---------------------+ | parameter2 (b = 5) | +---------------------+ |
When the prelude is finish, the stack frame is complete and the actual function can begin.
So the base register (FP) + offset 8 will let you access this variable
The instruction ldr r0, [fp,#8] will get the value of the parameter a into register r0
The instruction str r0, [fp,#8] will assign the value in r0 into the parameter a
So the base register (FP) + offset 12 will let you access this variable
The instruction ldr r0, [fp,#12] will get the value of the parameter b into register r0
The instruction str r0, [fp,#12] will assign the value in r0 into the parameter b
It is no different from how the main program calls the f( ) function.
The f( ) method can call the f( ) method by passing the parameters on the program stack and then use bl f to call the f( ) method.
But make sure you pop the parameter from the stack after the function returns - because the parameter has not been cleaned up.
The following is the program fragment where f( ) calls f(a+1, b-1):
// call f(a+1, b-1) ldr r0, [fp, #12] // r0 = b sub r0, r0, #1 // r0 = b-1 push {r0} // pass param2 (b-1) ldr r0, [fp, #8] // r0 = a add r0, r0, #1 // r0 = a+1 push {r0} // pass param1 (a+1) to f on stack bl f add sp, sp, #8 // Clean up parameter (n-1) from stack |