[callstack] Explain the concept of a stack frame in a nutshell

Programmers may have questions about stack frames not in a broad term (that it is a singe entity in the stack that serves just one function call and keeps return address, arguments and local variables) but in a narrow sense – when the term stack frames is mentioned in context of compiler options.

Whether the author of the question has meant it or not, but the concept of a stack frame from the aspect of compiler options is a very important issue, not covered by the other replies here.

For example, Microsoft Visual Studio 2015 C/C++ compiler has the following option related to stack frames:

  • /Oy (Frame-Pointer Omission)

GCC have the following:

  • -fomit-frame-pointer (Don't keep the frame pointer in a register for functions that don't need one. This avoids the instructions to save, set up and restore frame pointers; it also makes an extra register available in many functions)

Intel C++ Compiler have the following:

  • -fomit-frame-pointer (Determines whether EBP is used as a general-purpose register in optimizations)

which has the following alias:

  • /Oy

Delphi has the following command-line option:

  • -$W+ (Generate Stack Frames)

In that specific sense, from the compiler’s perspective, a stack frame is just the entry and exit code for the routine, that pushes an anchor to the stack – that can also be used for debugging and for exception handling. Debugging tools may scan the stack data and use these anchors for backtracing, while locating call sites in the stack, i.e. to display names of the functions in the order they have been called hierarchically. For Intel architecture, it is push ebp; mov ebp, esp or enter for entry and mov esp, ebp; pop ebp or leave for exit.

That’s why it is very important to understand for a programmer what a stack frame is in when it comes to compiler options – because the compiler can control whether to generate this code or not.

In some cases, the stack frame (entry and exit code for the routine) can be omitted by the compiler, and the variables will directly be accessed via the stack pointer (SP/ESP/RSP) rather than the convenient base pointer (BP/ESP/RSP). Conditions for omission of the stack frame, for example:

  • the function is a leaf function (i.e. an end-entity that doesn’t call other functions);
  • there are no try/finally or try/except or similar constructs, i.e. no exceptions are used;
  • no routines are called with outgoing parameters on the stack;
  • the function has no parameters;
  • the function has no inline assembly code;
  • etc...

Omitting stack frames (entry and exit code for the routine) can make code smaller and faster, but it may also negatively affect the debuggers’ ability to backtrace the data in the stack and to display it to the programmer. These are the compiler options that determine under which conditions a function should have the entry and exit code, for example: (a) always, (b) never, (c) when needed (specifying the conditions).