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
|