the stack pointer and frame pointer
Think of a spike on a desk where you stack paper memos, newest on top. To add a memo you slide it onto the spike; to remove one you take the top. The CPU has exactly such a spike — the call stack in memory — and the stack pointer is a register that always points at the top of the pile. As functions are called and return, the stack grows and shrinks, and the stack pointer moves with it.
On x86-64 the stack pointer is the register rsp. The stack conventionally grows toward lower addresses, so pushing a value subtracts from rsp and writes there, while popping reads and adds back. Every function call and every local variable that lives on the stack rides on rsp moving. A second register, the frame pointer (rbp on x86-64, also called the base pointer), is often parked at a fixed spot near the start of the current function's region and left there for the duration of the call. With rbp pinned, the function's arguments and locals sit at steady offsets from rbp (like rbp-8, rbp-16), so each can be reached by a known displacement even as rsp keeps moving for pushes and calls.
These two registers are why a function can have private local variables at all, and why recursion works: each call gets its own slice of the stack. Modern compilers often optimise the frame pointer away (the -fomit-frame-pointer option), reaching everything via rsp offsets instead — which frees rbp as an extra general-purpose register but makes a stack walk harder for a debugger. When you see a backtrace in a debugger, it is following these pointers from one frame to the next.
push rax does sub rsp, 8 then writes rax at [rsp]; with a frame set up, a local might be read as mov eax, [rbp-4].
rsp tracks the live top of the stack; rbp, when used, pins a stable reference for locals and arguments.
This 'stack' is a region of process memory that grows and shrinks with calls — not the abstract LIFO stack data structure, though it behaves like one. The frame pointer is optional: code compiled with -fomit-frame-pointer has no rbp-based frame, so do not assume every function sets one up.