What happens when you pass parameters using the runtime stack

Initially the runtime stack is empty:

What happens when you pass parameters using the runtime stack

Effect when the function call f(3.14) passes the paramter 3.14 using the runtime stack:

         

Recall that:   The runtime (program) stack is also used to store return addresses !!!
We must be very careful and organize the data on the runtime (program) stack !!

Things that are stored in the runtime stack
 

Let's enumerate all the information that we will be storing in the runtime (program) stack....

You have seen that the runtime stack was used to store (save):

  1. The return address of the caller function ( click here)

Things that are stored in the runtime stack

Example: saving return address on the runtime stack

main:
      ...
      bl  A            
 RM:  // Return addr to main
      ...

A:    ...
      bl  B
 RA:  // Return addr to A
      ...

B:    ...
      bl  C
 RB:  // Return addr to B
      ...

C:    ...


Things that are stored in the runtime stack

Depicted more clearly with a diagram:

main:
      ...
      bl  A            
 RM:  // Return addr to main
      ...

A:    ...
      bl  B
 RA:  // Return addr to A
      ...

B:    ...
      bl  C
 RB:  // Return addr to B
      ...

C:    ...

Things that are stored in the runtime stack
 

Now, we will also use the the runtime stack to store:

  1. The return address of the function - (this is old stuff: click here)

  2. Parameters of the function              NEW
  3. Local variables of the function       NEW
 

 

Let's first figure out when we can reserve space (on the runtime stack) to store the parameters and the local variables

Sequencing of events in function call + return with parameters and local variables
 

When can/do we pass the parameters to a function ?

 f1:                                           
                       +---->  f2:           
       ...             |            push {lr}  |
       ...             |            ...        |
       ...             |            ...        |
       bl f2 ----------+            ...        |
       ...   <--------------+       ...        |
       ...                  |       ...        V 
       ...                  +------ pop {pc}
   

 

 

Sequencing of events in function call + return with parameters and local variables
 

When can/do we pass the parameters to a function ?

 f1:                                           
                       +---->  f2:           
       ...             |            push {lr}  |
       ...             |            ...        |
       pass params     |            ...        |
       bl f2 ----------+            ...        |
       ...   <--------------+       ...        |
       ...                  |       ...        V 
       ...                  +------ pop {pc}
   

Answer:   it must happen before the bl f2 where we call the function !!!

(It's too late to pass parameters after the bl f2 instruction !)

Sequencing of events in function call + return with parameters and local variables
 

When can/do we create the local variables for the function f2 ?

 f1:                                           
                       +---->  f2:           
       ...             |            push {lr}  |
       ...             |            ...        |
       pass params     |            ...        |
       bl f2 ----------+            ...        |
       ...   <--------------+       ...        |
       ...                  |       ...        V 
       ...                  +------ pop {pc}
   

 

 

Sequencing of events in function call + return with parameters and local variables
 

When can/do we create the local variables for the function f2 ?

 f1:                                           
                       +---->  f2:           
       ...             |            push {lr}  |
       ...             |            alloc local vars
       pass params     |            ...        |
       bl f2 ----------+            ...        |
       ...   <--------------+       ...        |
       ...                  |       ...        V 
       ...                  +------ pop {pc}
   

Answer:   immediately after we saved the return address

(Because the local variables are created when a function starts executing)

Things that are stored in the runtime stack

Due to the ordering of the events, the runtime stack will have the following (data) structure:

main:
   ...
   // main calls function A
   (1) pass params
   bl  A                     
   ...
   ...

A:
   (2) push {lr} // Return address 
   ((3) push {fp} // Base address)
   (Set up base address in fp register)
   (4) allocate local vars 
   ...
   ...
   pop {pc}

This (data) structure is called: an (function) activation record or stack frame

The activation record or stack frame

Activation record or stack frame: ( click here)

  • Stack frame is the data structure that is created in the runtime stack that contains (in order of insertion/creation - remember the stack order is FILO):

      1. The parameters of the function              
      2. The return address of the function
      3. The FP (frame pointer) register of the caller (discussed soon)        
      4. The local variables of the function

         

The activation record or stack frame

Activation record or stack frame: ( click here)

  • When a function begins to run, the function first constructs its stack frame

  • The stack frame exclusively store the parameters and the local variable of one function !!

  • The function will access its parameters and local variables (in the stack frame) using a base address that is stored in the FP (frame pointer) register.

    • Unlike the base address used to access arrays and/or objects (structs), the base address used to access a stack frame points to the middle of a stack frame !!

The stack frame as a data structure (= object)

  • A stack frame is a data structure (just like an object)

  • Fields in data structures (= objects) are accessed using a base address + an offset ( click here):

         

  • The program always use a special register named the frame pointer (FP) register to store the base address of a stack frame

  • Local variables are accessed using negative offset values
  • Parameter variables are accessed using positive offset values

How to set up the frame pointer register as base address of the stack frame

Recall the events of the function calls are as follows:

 f1:                                           
                       +---->  f2:           
       ...             |            (2) push {lr} |
       ...             |                          |
       (1) pass params |                          |
       bl f2 ----------+            (3) alloc local vars
       ...   <--------------+       ...           |
       ...                  |       ...           V 
       ...                  +------ pop {pc}










  

How to set up the frame pointer register as base address of the stack frame

The program stack after (1) passing parameters and (2) saving return address is as follows:

 f1:                                           
                       +---->  f2:           
       ...             |            (2) push {lr} |
       ...             |                          |
       (1) pass params |                          |
       bl f2 ----------+            (3) alloc local vars
       ...   <--------------+       ...           |
       ...                  |       ...           V 
       ...                  +------ pop {pc}


            

How to set up the frame pointer register as base address of the stack frame

Before we allocate the local variables, we must save the original value of the FP register:

 f1:                                           
                       +---->  f2:           
       ...             |            (2) push {lr} |
       ...             |            push {fp}     |
       (1) pass params |                          |
       bl f2 ----------+            (3) alloc local vars
       ...   <--------------+       ...           |
       ...                  |       ...           V 
       ...                  +------ pop {pc}


            

Soon you will learn why we need to save the original value of the FP register....

How to set up the frame pointer register as base address of the stack frame

We can now iniatialize the FP register to the base address of the stack frame object with mov fp, sp:

 f1:                                           
                       +---->  f2:           
       ...             |            (2) push {lr} |
       ...             |            push {fp}     |
       (1) pass params |            mov  fp, sp   |
       bl f2 ----------+            (3) alloc local vars
       ...   <--------------+       ...           |
       ...                  |       ...           V 
       ...                  +------ pop {pc}


            

Why do we need to save the original value of the FP register ?

             
  • Important caveat:

      • The frame pointer (FP) register is shared by all function calls !!!

        Therefore:

          • A function must perserve (= restore) the original value in the FP register when it updates the value

Experiment: what will go wrong if we do not save the FP register
 

  • In the next example, I will show you what will go wrong if you do not save the FP register:

       f1:                                           
                             +---->  f2:  push {lr}  
                             |            push {fp}  
             ...             |            mov  fp, sp
             ...             |            alloc local vars
             pass params     |            ...  |
             bl f2 ----------+            ...  V
             use return value <---+       save return value
             ...                  |       in a register
             ...                  +------ pop {pc}

The structure of the stack frame - build it from scratch

(1) Right before main( ) calls a function:

Notice that the main( ) function is using the FP register to access main's parameter and local variables.

The structure of the stack frame - build it from scratch

(2) The caller (main) passes the parameters by pushing them on the stack:

The stack frame first contains the parameters of the function

The structure of the stack frame - build it from scratch

(3) The bl instruction will jump to the function:

The stack frame is unchanged

The structure of the stack frame - build it from scratch

(4) The function f will save the the return address on the stack:

The stack frame now contains parameters + return address

The structure of the stack frame - build it from scratch

(5) The function f will set the FP register to the base address of its own stack frame:

Result: the main( ) function has lost it's FP register value !!! ---- ERROR !!!

What's the solution ???
 

  • The solution of an overwrite problem of a register is always:

      1. Save the original value of the register

      2. Then we can change (update) the register

      3. When we are done (e.g., return from subroutine), we restore the original value back into the register

 

BTW:   a similar problem has been solved previously - do you remember which ???

We have seen this problem before !!!

The same problem occurs with the LR register in this example:

main:
        mov     r0, #1111
        mov     r1, #1111
        bl      A           // Save return address in reg LR
        mov     r2, #1111
        mov     r3, #1111

Stop:

A:

        mov     r0, #2222
        mov     r1, #2222
        bl      B          // Overwrites LR (A's return address to main !)
        mov     r2, #2222
        mov     r3, #2222


        mov     pc, lr     // A fails to return to main !!!

B:
        mov     r0, #9999
        mov     r1, #9999

        mov     pc, lr     // B succeeds to return to A
   

The caller overwrites the LR register containing the return address !!!

We have seen this problem before !!!

We have solved this problem by saving the LR register on the stack:

main:
        mov     r0, #1111
        mov     r1, #1111
        bl      A           // Save return address in reg LR
        mov     r2, #1111
        mov     r3, #1111

Stop:

A:
        push    {lr}       // Save LR on stack
        mov     r0, #2222
        mov     r1, #2222
        bl      B          // Overwrites LR (A's return address to main !)
        mov     r2, #2222
        mov     r3, #2222

        pop     {lr}       // restore old LR  
        mov     pc, lr     // A returns correctly

B:
        mov     r0, #9999
        mov     r1, #9999

        mov     pc, lr     // B succeeds to return to A
   

Therefore: we can apply the same solution technique to save and restore the FP register