Lesson 6 of 10
The Stack & Functions
You'll learn how the holds return addresses and local , and how `push`, `pop`, `call` and `ret` work together. This is how calls and work at the machine level.
; Stack mechanics
; rsp = stack pointer, always points to the last pushed value
; Stack grows DOWNWARD (rsp decreases as you push)
push rax ; rsp -= 8, [rsp] = rax
pop rbx ; rbx = [rsp], rsp += 8
; Manually allocate stack space for local variables
sub rsp, 32 ; reserve 32 bytes for locals
; ... use [rsp], [rsp+8], [rsp+16], [rsp+24] ...
add rsp, 32 ; free the space (must match the sub!)
; call and ret — the function call mechanism
; call label:
; push the return address (rip + instruction size) onto the stack
; jmp label
; ret:
; pop the return address from the stack
; jmp to it
call my_function
; execution resumes here after ret
my_function:
; Standard function prologue
push rbp ; save caller's base pointer
mov rbp, rsp ; establish new stack frame
sub rsp, 16 ; reserve 16 bytes for locals (keep rsp 16-byte aligned!)
; Local variables live at [rbp-8], [rbp-16], etc.
mov qword [rbp-8], 42
; ... function body ...
; Standard function epilogue
mov rsp, rbp ; restore rsp (discard locals)
pop rbp ; restore caller's rbp
ret ; pop return address, jump to it; x86-64 System V ABI — the calling convention used on Linux/macOS
; Integer/pointer arguments passed in: rdi, rsi, rdx, rcx, r8, r9
; Further arguments go on the stack
; Return value in rax (and rdx for 128-bit returns)
; Caller-saved: rax, rcx, rdx, rsi, rdi, r8-r11 (callee may trash these)
; Callee-saved: rbx, rbp, r12-r15 (callee must restore these)
; rsp must be 16-byte aligned before a call instruction
; Example: add(a, b) → rdi=a, rsi=b, return rax
add_two:
push rbp
mov rbp, rsp
mov rax, rdi ; rax = a
add rax, rsi ; rax = a + b
; return value is in rax — caller reads it from there
pop rbp
ret
; Calling it:
; mov rdi, 10 ; first arg
; mov rsi, 32 ; second arg
; call add_two
; ; rax = 42
; Recursive factorial
; factorial(n): if n <= 1 return 1; else return n * factorial(n-1)
factorial:
push rbp
mov rbp, rsp
push rbx ; save rbx (callee-saved)
mov rbx, rdi ; save n in rbx
cmp rdi, 1
jle .base
dec rdi ; n - 1
call factorial ; recursive call — result in rax
imul rax, rbx ; rax = n * factorial(n-1)
jmp .done
.base:
mov rax, 1 ; base case: return 1
.done:
pop rbx
pop rbp
retFinished reading?Mark it done to track your progress.
Tip: use the ← and → keys to move between lessons.