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.
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.
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.
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.
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.
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.
objdump -d /bin/true | grep -A5 "<main>"
perf stat -e cycles,instructions,mem_inst_retired.all_loads trueWhat 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.