TRT
Sign in
RuntimeDSAexecution

What happens when you call a function?

A step-by-step walk of a function call: registers, the call instruction, the stack frame, the return address, and what a single call actually costs.

The Runtime Theory Team1 min read05 steps

trace spine

  1. 01 Arguments land in registers
  2. 02 call pushes the return address
  3. 03 Prologue builds the frame
  4. 04 Body runs against registers and stack
  5. 05 Epilogue tears down, ret returns

Every abstraction you've ever relied on — every method, every closure, every syscall wrapper — bottoms out in the same physical act: a jump to an address with a return address saved on a stack. The call is the most executed instruction sequence in computing, so its mechanics are worth tracing exactly.

step 01Application

Before the call executes, the compiler arranges arguments per the calling convention. On x86-64 System V: rdi, rsi, rdx, rcx, r8, r9 for the first six integer/pointer args, the XMM registers for floats; anything beyond goes on the stack. The caller also ensures the stack is 16-byte aligned at the call site. At this point nothing has happened yet except register moves — nanoseconds, no memory traffic.

step 02Hardware

The call instruction does two things atomically: it pushes the return address (the address of the instruction after the call) onto the stack, then jumps to the callee's entry point. The return address push is the entire contract — the callee can always return by popping it. This push is one store to memory; with a hot stack it stays in L1 cache.

step 03Hardware

The callee runs its prologue: push rbp (saving the caller's frame pointer) and mov rbp, rsp establish the new frame; sub rsp, N reserves space for locals. The frame — return address, saved registers, locals, spilled temporaries — is just the region between rbp and rsp. Allocating it is a single arithmetic instruction: no malloc, no syscall, no page faults. The stack grows on demand and the kernel has already mapped the region lazily.

step 04Hardware

The body runs. Locals that fit in registers never touch memory; spills go to the frame. The stack's locality is its superpower: the frame just allocated is in the same cache lines the caller was just touching. Everything here — loads, stores, arithmetic — executes at L1 latency (~1 ns) unless it spills to L2/L3.

step 05Hardware

The epilogue reverses the prologue: leave restores rsp and rbp in one instruction, and ret pops the return address and jumps back to the caller. The frame is dead — its memory isn't cleared or freed, it's simply abandoned; the next call will overwrite it. Total cost of the whole round trip: roughly 1–5 ns for a hot call with register args — a handful of instructions, one store, one load.

bash
objdump -d /bin/true | grep -A5 "<main>"
perf stat -e cycles,instructions,mem_inst_retired.all_loads true

What the machine actually does on every call is a tiny, fixed-cost dance: move arguments, push one address, adjust one pointer, run, restore, pop, jump back. There is no allocator involved, no locking, no bookkeeping — which is exactly why a billion calls per second per core is routine, and why any optimization that avoids a call (inlining, tail calls) is measured in nanoseconds, not milliseconds.

Not started

Sign in to save your learning progress.

Sign in to save