The call stack is a specific implementation of a stack which stores relevant information for a procedure to execute.
The call stack is typically at the highest memory address possible for a given program and then grows downwards (to lower memory addresses) towards the heap.
Stack Frame
Each time a procedure is called a new entry is created (called a "stack frame"). Each stack frame stores local variables of the procedure, the return address and any arguments passed to the procedure.
Additionally, the stack frame might include saved registers if the procedure calls another procedure. When a procedure is called usually the start of this stack frame is written to a common register before invocation.
Allocation
When a new stack frame is allocated, space is reserved on the call stack for all data the procedure needs during its execution. This process typically involves:
- Adjusting the Stack Pointer: The stack pointer register is moved (decremented) to allocate enough space for the new frame.
- Saving the Return Address: The address of the next instruction (the point where the program should continue after the procedure returns) is pushed onto the stack.
- Saving the Old Frame Pointer: The previous frame pointer is pushed onto the stack so it can be restored when returning.
- Setting the New Frame Pointer: The frame pointer is updated to the current top of the stack, marking the base of the new frame. This allows local variables and parameters to be accessed using fixed offsets.
- Allocating Space for Local Variables: The compiler reserves space for all local variables defined within the procedure.
- Saving Registers (Optional): If the procedure will call other procedures or use certain registers, it may save the current register values in the stack frame to preserve their state.
Deallocation
When a procedure finishes executing, its stack frame must be removed from the call stack so control can return to the calling procedure. This process, known as stack frame deallocation or stack unwinding, typically involves the following steps:
- Restoring Saved Registers (if any): Any registers saved at the beginning of the procedure are restored to their previous values to ensure the caller's state is unchanged.
- Releasing Local Variable Space: The stack pointer is adjusted (incremented) to free the memory that was used for local variables and temporary data.
- Restoring the Old Frame Pointer: The previously saved frame pointer is popped from the stack, restoring the caller's frame context.
- Returning to the Caller: The return address is popped from the stack, and control is transferred back to that address, resuming execution in the calling procedure.