int fib(int n) { if (n == 0) return(1); <--- easy case 1 else if (n == 1) return(1); <--- easy case 2 else { return( fib(n-1) + fib(n-2) ); <--- Return the solution for fib(n) } } main( ) { int result, k; result = fib(k); } |
Since the parameter of the Fibonacci function is n, I will use n to refer to this parameter variable in this webpage.
|
|
We have learned:
|
We have also learned how to save registers on the program stack to solve this interrupted computation problem.
We will need to use that technique when we write the Fibonacci function !!!
If you need to review the solution, visit this webpage: click here
push parameter (n) on the stack bl fib add sp, sp, #4 // Clean up the parameter (n) from the stack |
|
|
Stack frame of the fib( ) method: SP/FP -----> +---------------------+ | old Frame Pointer | +---------------------+ | Return Address | +---------------------+ | Parameter n | addr(n) = FP + 8 +---------------------+ |
You can see that the fib( ) function will:
|
main: // High level programming statement: // // result = fib(k) /* ------------------------------------------------- Pass parameter k (using stack) ------------------------------------------------- */ movw r0, #:lower16:k movt r0, #:upper16:k ldr r0, [r0] // r0 = k push {r0} // Pass k using the program stack /* ------------------------------------------------------ call result = fib(k) ------------------------------------------------------ */ bl fib add sp, sp, #4 // Clean up the parameter k /* ----------------------------------------------------------------- Save 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 sum |
/* ---------------------------------------------------------------- Function int fib(int n) Stack frame structure: old FP <--------------------------------- FP old LR (ret addr) n +8 Body of fib(n) if ( n == 0 ) return 1; if ( n == 1 ) return 1; else return ( fib(n-1) + fib(n-2) ); ---------------------------------------------------------------- */ fib: // When fib begins, we will have: parameter n 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, #0 // Allocate 0 int variables on the stack // (I could omit this instruction....) /* =============================================== We have completed the stack frame Now we can write the function body =============================================== */ // if ( n == 0 ) ldr r0, [fp, #8] // r0 = n cmp r0, #0 // Check n == 0 bne else1 // n != 0 --> Goto "else1" part // return 1 ///// put return value 1 in return location r0 mov r0, #1 /* ============================================================= 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 else1: // if ( n == 1 ) ldr r0, [fp, #8] // r0 = n cmp r0, #1 // Check n == 1 bne else1 // n != 1 --> Goto "else2" part // return 1 ///// put return value 1 in return location r0 mov r0, #1 /* ============================================================= 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 else2: // Compute: fib(n-1) + fib(n-2) //// Compute fib(n-1) first ldr r0, [fp, #8] // r0 = n sub r0, r0, #1 // r0 = n-1 push {r0} // pass (n-1) to fib on stack bl fib // Calls: fib( ) with parameter = (n-1) !!! add sp, sp, #4 // Clean up parameter (n-1) from stack //// ** Right now, r0 = fib(n-1) --- we can't lose this value !!! //// ** But we can't compute fib(n-1) + fib(n-2) without fib(n-2) //// ** We will call fib to compute fib(n-2) //// ** We MUST save r0 on the STACK !!! push {r0} // Save fib(n-1) on the stack //// Compute fib(n-2) ldr r0, [fp, #8] // r0 = n sub r0, r0, #2 // r0 = n-2 push {r0} // pass (n-2) to fib on stack bl fib add sp, sp, #4 // Clean up parameter (n-2) from stack //// ** Right now, r0 = fib(n-2) - we can now compute fib(n-1)+fib(n-2) pop {r1} // Retrieve fib(n-1) from the stack add r0, r1, r0 // r0 = fib(n-1) + fib(n-2) // NOTE: r0 has the correct return value !!! // return fib(n-1)+fib(n-2) in r0 /* ============================================================= 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:
|
The main program passes the variable k to the Fibonacci method by pushing the value of variable k onto the system stack with the following instruction:
movw r0, #:lower16:k movt r0, #:upper16:k ldr r0, [r0] // Now: r0 = k push {r0} // Pass k using the program stack |
This will create the following stack structure:
+---------------------+ <------------ Stack pointer (SP) | parameter n(= k) | +---------------------+ |
The main program calls the Fibonacci function with a bl instruction:
bl fib |
This will save the return address to main( ) in the LR register and jump to the fib method
The fib( ) function will start running, so let's take a look at the fib( ) function
The prelude of the Fibonacci 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, #0 // Allocate 0 int variables on the stack // (I could omit this instruction....) |
I will explain what each one does below.
Make sure that you realise that the structure of the stack frame is like this when the prelude of the fib( ) method is executed:
+---------------------+ <------------ Stack pointer (SP) | parameter n | +---------------------+ |
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 | +---------------------+ | parameter n | +---------------------+ |
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 | +---------------------+ | parameter n | +---------------------+ |
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 | +---------------------+ | parameter n | +---------------------+ |
The will enable the fib( ) 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 local variables on the program stack
But since fib( ) has no local variables, this instruction does nothing to the stack pointer SP... (and we could omit it - I left it in to keep the discussion uniform)
We have now completed the stack frame:
+---------------------+ <---- Frame pointer FP & Stack pointer SP | old Frame Pointer | point to the same location.... +---------------------+ | return address | +---------------------+ | parameter n | +---------------------+ |
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
That's why you see the use of the instruction ldr r1, [fp,#8] in fib( ) to get the parameter:
ldr r1, [fp, #8] // r0 = n |
It is no different from how the main program calls the Fibonacci function.
The Fibonacci method can call the Fibonacci method by passing the parameter on the program stack and then use bl fib to call the Fibonacci method.
But make sure you pop the parameter from the stack after the Fibonacci function returns - because the parameter has not been cleaned up.
The following is the program fragment where Fibonacci calls fib(n-1):
ldr r0, [fp, #8] // r0 = n sub r0, r0, #1 // r0 = n-1 push {r0} // pass (n-1) to fib on stack bl fib // Calls: fib( ) with parameter = (n-1) !!! add sp, sp, #4 // Clean up parameter (n-1) from stack |
This is done by pushing r0 on to the program stack:
push {r0} // Save fib(n-1) on the stack
|
The Fibonacci method can call the Fibonacci method by passing the value n-2 on the program stack and then use bl fib to call the Fibonacci method.
But make sure you pop the parameter from the stack after the Fibonacci function returns - because the parameter has not been cleaned up.
The following is the program fragment where Fibonacci calls fib(n-1):
ldr r0, [fp, #8] // r0 = n sub r0, r0, #2 // r0 = n-2 push {r0} // pass (n-1) to fib on stack bl fib // Calls: fib( ) with parameter = (n-1) !!! add sp, sp, #4 // Clean up parameter (n-1) from stack |
This is done by popping the top of the stack in register r1 (because register r0 contains fib(n-2)):
pop {r1} // Retrieve fib(n-1) from the stack
|