Consequences of using the runtime stack to store parameters and local variables

Necesary condition for correctly returning to its caller:

  • A subroutine returns to its caller by executing the pop {pc} instruction

  • The pop {pc} instruction will (= must) pop the return address from the runtime stack (and update the PC register with the return address)

  • Therefore:

      • When the subroutine executes the pop {pc} instruction, the (correct) return address must be at top of the runtime stack:

          

Consequences of using the runtime stack to store parameters and local variables

When we store parameters and local variables in the runtime stack:

  • A subroutine cannot arbitrarily execute the pop {pc} instruction to return to its caller !!!

      • Because the return address is not at the top of the program stack !!!!

A subroutine must clean up the runtime stack correctly before it can return !!

The postlude code of a subroutine

  • Recall the prelude code of a subroutine that builds the stack frame (activation record):

          push  {lr}
          push  {fp}
          mov   fp, sp
          sub   sp, sp, #n         
      

  • A (non-leaf) subroutine must tear down (= pop) the stack frame in an orderly manner before it can return to its caller

  • The sequence of assembler instructions used to tear down (= pop) the stack frame is called:

      • The postlude code           (or postlude for short)     

I will show you the subroutine postlude code next....

The subroutine postlude code - step-by-step

Before the postlude code starts tearing down the stack, the stack frame looks like this:

 Postlude code:



   

 

The subroutine postlude code - step-by-step

(1) De-allocate the space reserved for the local variables

 Postlude code:
          mov   sp, fp


   

 

The subroutine postlude code - step-by-step

(2) Restore the saved value of FP back into the FP register (used by the caller)

 Postlude code:
          mov   sp, fp
	  pop	{fp}

   

Finally, we return to the caller because the return address is the top of the runtime stack !

The subroutine postlude code - step-by-step

(3) return to the caller (by popping the return address into PC):

 Postlude code:
          mov   sp, fp
	  pop	{fp}
	  pop   {pc} // Execution goes back to the caller !
   

Note: the program will return to its caller and execute the instruction after the bl instruction

Summary: the prelude and postlude code of a (non-leaf) subroutine
 

A (non-leaf) subroutine always executes this prelude code at its start:

    push  {lr}
    push  {fp}
    mov   fp, sp
    sub   sp, sp, #n  // n depends on # local variables   
 

A (non-leaf) subroutine always executes this postlude code when it returns (to its caller):

   mov   sp, fp
   pop	 {fp}
   pop   {pc}   

Notice the LIFO ordering:
          insert:     push lr, push fp
          remove:  pop fp, push pc

Important fact about the function return
 

Important fact:

  • After a function has returned to its caller, the runtime stack still contains the parameters !

The runtime stack contains the following when the function returns (pop {pc}:

Important fact about the function return

The caller must clean up the parameters from the runtime stack after the function return:

The caller's function call code:

    push  parameters on stack
    bl    function
    clean up the parameters pushed on the stack !!!     

Important fact about the function return

The caller must clean up the parameters from the runtime stack after the function return:

The caller's function call code:

    push  parameters on stack
    bl    function
    add   sp, sp #n  // n = 4 * # params pushed    

DEMO:   /home/cs255001/demo/asm/8-sub/pre+postlude.s