TRT
Sign in
mediumCoreconcept

What happens, step by step, when a function is called?

Walk through the calling convention, the call and return instructions, the prologue and epilogue, and what a call really costs.

TRT practice prompt — not a verified question from a named employer.

The Runtime Theory Team2 min read
Learn it firstHow a function call worksPro — members onlyA call is a jump that remembers where to come back to. Arguments travel in registers, the return address goes on the stack, and a few instructions set up and tear down the frame.

A strong answer

The caller and the function follow a calling convention. On x86-64 Linux and macOS (System V), the caller puts the first six integer or pointer arguments in the registers rdi, rsi, rdx, rcx, r8 and r9, floating-point arguments in vector registers, and any further arguments on the stack. Before the call, it saves any caller-saved registers whose values it still needs.

The call instruction pushes the return address, the address of the next instruction, and jumps to the function. The function's prologue typically saves the old frame pointer, sets a new one, and subtracts from the stack pointer to reserve space for locals that don't fit in registers; it also saves any callee-saved registers it will use. The body runs and leaves the return value in rax. The epilogue restores the saved registers and the stack pointer, and ret pops the return address and jumps back.

The direct cost is a handful of instructions, and return addresses are predicted by a hardware return stack, so a well-behaved call costs a few cycles. The indirect costs are larger: values may be spilled to memory around the call, and the compiler can't optimize across it. That is why compilers inline small functions, which removes the call entirely. Indirect calls, through function pointers or virtual methods, can't always be inlined and depend on branch prediction. Interpreted languages pay far more per call, since they build interpreter frames and bind arguments in software.

What the interviewer is listening for

  • Arguments in registers, return address on the stack, return value in a register.
  • Prologue, epilogue and the caller/callee-saved split.
  • The real costs, and inlining as the main optimization.

Common mistakes

  • Saying all arguments are pushed on the stack, which was true of older 32-bit conventions.
  • Confusing the stack frame with heap allocation.
  • Treating call overhead as negligible in tight loops, or as large in compiled code where it is usually inlined.

This answer walks

Practice follow-ups

  1. 01Where do the arguments go if a function takes ten integers?
  2. 02What is the difference between caller-saved and callee-saved registers?
  3. 03Why might an optimized build not have a frame pointer, and what does that do to profilers?
  4. 04What does a virtual or indirect call cost compared with a direct one?

Not started

Sign in to save your learning progress.

Sign in to save