int fac(int n) { if (n == 0) return(1); <--- easy case else { return( n * fac(n-1) ); <--- Return the solution for fac(n) } } main( ) { int result, k; result = fac(k); } |
Notice that:
|
push parameter (k) on the stack bl fac add sp, sp, #4 // Clean up the parameter (n) from the stack |
|
|
Stack frame of the fac( ) method: SP FP -----> +---------------------+ | old Frame Pointer | +---------------------+ | Return Address | +---------------------+ | Parameter n | addr(n) = FP + 8 +---------------------+ |
You can see that the fac( ) function will access the parameter variable (n) using offset 8 from the frame pointer FP
main: // High level programming statement: // // result = fac(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 fac(k) ------------------------------------------------------ */ bl fac add sp, sp, #4 // Clean up the parameter k /* ----------------------------------------------------------------- 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 |
/* ---------------------------------------------------------------- Function int fac(int n) Stack frame structure: old FP <--------------------------------- FP old LR (ret addr) n +8 Body of fac(n) if ( n == 0 ) return 1; else return n*fac(n-1); ---------------------------------------------------------------- */ fac: // When fac 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 else // n != 0 --> Goto "else" 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 else: // Compute: n*fac(n-1) //// Compute fac(n-1) first ldr r0, [fp, #8] // r0 = n sub r0, r0, #1 // r0 = n-1 push {r0} // pass (n-1) to fac on stack bl fac // Calls: fac( ) with parameter = (n-1) !!! add sp, sp, #4 // Clean up parameter (n-1) from stack //// ** Right now, r0 = fac(n-1) !!! //// Compute n*fac(n-1) next ldr r1, [fp, #8] // r0 = n mul r0, r1, r0 // r0 = n*fac(n-1) // NOTE: r0 has the correct return value !!! // return n*fac(n-1) 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 factorial 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) | k = parameter n | +---------------------+ |
The main program calls the factorial function with a bl instruction:
bl fac |
This will save the return address to main( ) in the LR register and jump to the fac method
The fac( ) function will start running, so let's take a look at the fac( ) function
The prelude of the factorial 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 fac( ) 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 fac( ) 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 fac( ) 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.
+---------------------+ <-------------- Frame pointer FP | old Frame Pointer | (4 bytes) +---------------------+ | return address | (4 bytes) +---------------------+ | parameter n | <--- offset = 8 +---------------------+ |
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 fac( ) to get the parameter:
ldr r1, [fp, #8] // r0 = n |
It is no different from how the main program calls the factorial function.
The factorial method can call the factorial method by passing the parameter on the program stack and then use bl fac to call the factorial method.
But make sure you pop the parameter from the stack after the factorial function returns - because the parameter has not been cleaned up.
The following is the program fragment where factorial calls fac(n-1):
ldr r0, [fp, #8] // r0 = n sub r0, r0, #1 // r0 = n-1 push {r0} // pass (n-1) to fac on stack bl fac // Calls: fac( ) with parameter = (n-1) !!! add sp, sp, #4 // Clean up parameter (n-1) from stack |