x86 Function Calls in Assembly

This deck walks through x86-64 function-call assembly in depth, working from GCC -O0 output. It compares AT&T with Intel syntax and covers stack frames, RSP and RBP, addressing modes, arithmetic, and compare-and-branch control flow, then inspects the stack in GDB. Along the way it targets three traps that catch most people: reversing the operands, confusing an address with the value stored there, and treating stack allocation as though it were initialization.

Subject: Computer Architecture · 94 slides · code lesson

Open the interactive version of this deck · Homework for this lesson

What this lesson covers

The lesson, slide by slide

1. What you will be able to do

Objectives

By the end of this deck you can:

1. Read a small GCC -O0 x86-64 assembly listing and split it into prologue, body, and epilogue.

2. Explain what RSP, RBP, RIP, argument registers, and EAX/RAX are doing during a function call.

3. Annotate mov, lea, add, sub, cmp, je, jne, and jmp with their effects on registers, memory, flags, and control flow.

4. Draw the stack frame for a real function and point to RBP-8, RBP+8, saved RBP, locals, and the return address.

5. Use basic GDB commands to step one instruction at a time and inspect the live stack.

2. Assembly is the compiler's diary

Concept

Assembly is a line-by-line record of tiny machine actions: move this value, reserve these bytes, compare this number, jump there if the comparison says so.

The important move today is not memorizing every instruction. It is learning to translate each line into a state change: which register changed, which memory slot changed, or where execution goes next.

state — The current contents of registers, stack memory, flags, and the instruction pointer. Reading assembly means updating this state one instruction at a time.

3. Break it if you can: Assembly is the compiler's diary

Counterexample

Discussion prompt

Assembly is a line-by-line record of tiny machine actions: move this value, reserve these bytes, compare this number, jump there if the comparison says so.

That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.

Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.

4. Picture it first: The stack is a workbench with a moving edge

Picture it

Figure (svg): Stack diagram showing higher addresses above lower addresses, with caller data, return address, saved RBP, and local variables.

Discussion prompt

Read the picture before the words. What is this showing, and what is the one thing it is built to make obvious? Commit to an answer, then read on.

Hint: Name the parts, then say what changes between them — and if nothing changes, say what is being held still.

Answer:

A function call borrows a piece of the stack for temporary work. RSP marks the moving bottom edge of the borrowed area; RBP often marks a stable reference point inside it.

5. The stack is a workbench with a moving edge

Intuition

Figure (svg): Stack diagram showing higher addresses above lower addresses, with caller data, return address, saved RBP, and local variables.

A function call borrows a piece of the stack for temporary work. RSP marks the moving bottom edge of the borrowed area; RBP often marks a stable reference point inside it.

The stack grows downward: allocating space means subtracting from RSP, and giving space back means adding to RSP or restoring it from RBP.

6. By analogy: The stack is a workbench with a moving edge

Analogy

Discussion prompt

Explain The stack is a workbench with a moving edge by analogy to something with no Computer Architecture in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.

Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.

Answer:

A function call borrows a piece of the stack for temporary work. RSP marks the moving bottom edge of the borrowed area; RBP often marks a stable reference point inside it.

7. The four registers to watch first

Concept

registerplain meaningwhy you care
RSPstack pointertop/edge of current stack space
RBPbase/frame pointerstable anchor for this function's locals
RIPinstruction pointerwhich instruction executes next
RAX/EAXreturn/value registerwhere many integer results appear

On x86-64, EAX is the low 32 bits of RAX. A function returning an int normally leaves that return value in EAX.

The ABI decides the calling convention. In the System V AMD64 ABI used by common Linux GCC examples, the first two int arguments arrive in EDI and ESI.

8. Fill in: plain meaning for The four registers to watch first

Comparison

Comparison matrix

From The four registers to watch first: refill the plain meaning column from what you know. The rest of the table is as it appeared.

registerplain meaningwhy you care
RSPstack pointertop/edge of current stack space
RBPbase/frame pointerstable anchor for this function's locals
RIPinstruction pointerwhich instruction executes next
RAX/EAXreturn/value registerwhere many integer results appear

9. The command that makes a .s file

Concept

gcc -O0 -S -mno-red-zone -fno-stack-protector -fno-asynchronous-unwind-tables score.c -o score.s
piecejob
-Sstop after producing assembly
-O0keep a literal, beginner-readable shape
-mno-red-zoneforce visible stack allocation in this leaf function
-fno-stack-protectoravoid extra canary code
score.sthe assembly file to read

Different compiler versions and flags can change the exact listing. For teaching, fix the flags first so everyone is annotating the same text.

10. What each one costs: The command that makes a .s file

Trade off

Comparison matrix

From The command that makes a .s file: every row here is a choice with a cost. Fill the job column, then say which row you would actually pick and what you give up for it.

piecejob
-Sstop after producing assembly
-O0keep a literal, beginner-readable shape
-mno-red-zoneforce visible stack allocation in this leaf function
-fno-stack-protectoravoid extra canary code
score.sthe assembly file to read

11. Predict the next row: The C function we will compile

Pattern

Predict first

The table runs: a | first int argument | 2 · b | second int argument | 3 · sum | local variable | 5 · doubled | local variable | 10

In The C function we will compile, given the rows so far: what is the next one — the row where source thing is return?

Correct: return | function result | 11

source thingrolevalue for score(2, 3)
afirst int argument2
bsecond int argument3
sumlocal variable5
doubledlocal variable10
returnfunction result11

Why: The relationship between the columns, not the individual numbers, is what generates the next row. a and b are parameters. sum and doubled are local variables that the compiler will place somewhere in the frame at -O0.

12. The C function we will compile

Worked example

int score(int a, int b) {
    int sum = a + b;
    int doubled = sum * 2;
    if (doubled == 10) {
        return doubled + 1;
    }
    return doubled - 1;
}

Name the source-level variables

Why: a and b are parameters. sum and doubled are local variables that the compiler will place somewhere in the frame at -O0.

source thingrolevalue for score(2, 3)
afirst int argument2
bsecond int argument3
sumlocal variable5
doubledlocal variable10
returnfunction result11

Predict the high-level behavior first

Why: Assembly is easier when you already know what the C code is trying to do. For score(2,3), the branch is the true branch because doubled becomes 10.

13. Inspect it line by line: The C function we will compile

Error analysis

Annotate

Walk the callouts on The C function we will compile. Each one is a place this is easy to get subtly wrong.

  • a and b are parameters. sum and doubled are local variables that the compiler will place somewhere in the frame at -O0.
  • Assembly is easier when you already know what the C code is trying to do. For score(2,3), the branch is the true branch because doubled becomes 10.

14. The compiler output has three jobs

Concept

regionwhat it doestypical clues
prologueset up this function's framepush rbp; mov rbp,rsp; sub rsp,N
bodyimplement C statementsloads, stores, arithmetic, comparisons, jumps
epiloguetear down the frame and returnleave; ret or mov/pop/ret

The prologue and epilogue are not the point of the C function. They are the bookkeeping that lets the function use stack memory and then return cleanly.

The body is where source-level variables and control flow appear: adding a+b, doubling, comparing with 10, and choosing a return value.

15. Teach it back: The compiler output has three jobs

Explain it

Discussion prompt

Explain The compiler output has three jobs to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.

Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.

Answer:

The prologue and epilogue are not the point of the C function. They are the bookkeeping that lets the function use stack memory and then return cleanly.

16. Real GCC output: first read

Worked example

score:
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $24, %rsp
        movl    %edi, -20(%rbp)
        movl    %esi, -24(%rbp)
        movl    -20(%rbp), %edx
        movl    -24(%rbp), %eax
        addl    %edx, %eax
        movl    %eax, -4(%rbp)
        movl    -4(%rbp), %eax
        addl    %eax, %eax
        movl    %eax, -8(%rbp)
        cmpl    $10, -8(%rbp)
        jne     .L2
        movl    -8(%rbp), %eax
        addl    $1, %eax
        jmp     .L3
.L2:
        movl    -8(%rbp), %eax
        subl    $1, %eax
.L3:
        leave
        ret
rangerolefirst question to ask
pushq through subqprologuehow is the frame created?
argument storesparameter spillwhere did a and b land?
loads through storesbody arithmeticwhich C expression is this?
cmp/jne/jmp labelsbranchwhich return path is chosen?
leave/retepiloguehow is the caller restored?

Read it in chunks before reading individual lines

Why: Chunking keeps the listing from feeling like noise. Once the chunks are named, each instruction has a local purpose.

17. AT&T syntax: source then destination

Concept

movl    %edi, -20(%rbp)
addl    %edx, %eax
cmpl    $10, -8(%rbp)
AT&T cluemeaning
%register name: %edi, %rbp, %eax
$immediate literal: $10
source, destinationmovl %edi, -20(%rbp) stores EDI into memory
suffix l/ql is 32-bit, q is 64-bit

The most common beginner bug is reading AT&T operands backward. In AT&T, the rightmost operand is usually what changes.

18. Something is wrong here: reading AT&T like Intel

Anomaly

Predict first

A student writes this, and it looks reasonable:

Tempting mistake: read movl %edi, -20(%rbp) as if the left side is the destination.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: That is Intel-style thinking applied to AT&T text.

Correct AT&T read: source first, destination second.

Why: That is Intel-style thinking applied to AT&T text. It reverses the data flow.

19. Trap: reading AT&T like Intel

Trap

The trap

Tempting mistake: read movl %edi, -20(%rbp) as if the left side is the destination.

movl    %edi, -20(%rbp)

Wrong annotation: memory loads into EDI

Why: That is Intel-style thinking applied to AT&T text. It reverses the data flow.

wrong readactual problem
-20(%rbp) -> %edioperands were reversed

The fix

Correct AT&T read: source first, destination second.

movl    %edi, -20(%rbp)

Right annotation: store EDI into the stack slot at RBP-20

Why: %edi is the source. -20(%rbp) is the destination memory address.

sourcedestinationeffect
%edi-20(%rbp)memory[rbp-20] = edi

20. Same listing in Intel syntax

Worked example

score:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 24
        mov     DWORD PTR [rbp-20], edi
        mov     DWORD PTR [rbp-24], esi
        mov     edx, DWORD PTR [rbp-20]
        mov     eax, DWORD PTR [rbp-24]
        add     eax, edx
        mov     DWORD PTR [rbp-4], eax
        mov     eax, DWORD PTR [rbp-4]
        add     eax, eax
        mov     DWORD PTR [rbp-8], eax
        cmp     DWORD PTR [rbp-8], 10
        jne     .L2
        mov     eax, DWORD PTR [rbp-8]
        add     eax, 1
        jmp     .L3
.L2:
        mov     eax, DWORD PTR [rbp-8]
        sub     eax, 1
.L3:
        leave
        ret
same ideaAT&TIntel
store first argmovl %edi, -20(%rbp)mov DWORD PTR [rbp-20], edi
add valuesaddl %edx, %eaxadd eax, edx
comparecmpl $10, -8(%rbp)cmp DWORD PTR [rbp-8], 10
unconditional jumpjmp .L3jmp .L3

Use one syntax consistently while learning

Why: Switching syntax mid-lesson creates fake confusion. GCC's default Unix assembly is AT&T; many textbooks and debuggers can also show Intel.

21. A memory operand is an address recipe

Concept

Figure (svg): Diagram showing -8(%rbp) as base register RBP plus a negative displacement, equivalent to Intel [rbp-8].

movl    -8(%rbp), %eax
movl    %eax, -4(%rbp)
operandkindread it as
-8(%rbp)memorybytes at address RBP - 8
%eaxregisterthe low 32 bits of RAX
$10immediatethe literal integer 10

Memory operands are not variables by themselves. They are recipes for finding a location in memory.

22. Picture it first: RBP makes local-variable addresses boring

Picture it

Figure (svg): Stack frame after prologue, showing saved caller RBP, return address, local slots at RBP minus offsets, and RSP at the bottom.

Discussion prompt

Read the picture before the words. What is this showing, and what is the one thing it is built to make obvious? Commit to an answer, then read on.

Hint: Name the parts, then say what changes between them — and if nothing changes, say what is being held still.

Answer:

During the body, RSP may move for calls or temporary pushes, so compilers often use RBP as a stable anchor in unoptimized teaching output.

23. RBP makes local-variable addresses boring

Intuition

Figure (svg): Stack frame after prologue, showing saved caller RBP, return address, local slots at RBP minus offsets, and RSP at the bottom.

During the body, RSP may move for calls or temporary pushes, so compilers often use RBP as a stable anchor in unoptimized teaching output.

That is why locals appear as negative offsets like -4(%rbp) and -8(%rbp), while the return address sits above the anchor at 8(%rbp).

24. Make the frame map

Worked example

subq    $24, %rsp
movl    %edi, -20(%rbp)
movl    %esi, -24(%rbp)
movl    %eax, -4(%rbp)
movl    %eax, -8(%rbp)

Reserve 24 bytes below RBP

Why: subq $24, %rsp moves the stack pointer down, making room for stack slots in this frame.

Assign a meaning to each offset only after seeing the stores

Why: -20(%rbp) gets a, -24(%rbp) gets b, -4(%rbp) later gets sum, and -8(%rbp) later gets doubled.

offsetC-level meaningsize used here
-4(%rbp)sum4 bytes
-8(%rbp)doubled4 bytes
-20(%rbp)a4 bytes
-24(%rbp)b4 bytes

25. Function calls follow a contract

Concept

piece of contractSystem V x86-64 teaching version
first int argumentEDI
second int argumentESI
integer return valueEAX
return addresspushed by call
callee cleanuprestore RSP/RBP, then ret

This contract is called a calling convention. It lets separately compiled functions agree on where arguments, return values, and saved control-flow information go.

Important boundary: Windows x64 uses a different first-argument register order. Today we are reading the Linux/System V style used by this GCC example.

26. call and ret are a bookmark system

Intuition

Figure (svg): Diagram showing call pushing the return address, then ret popping it back into RIP.

A call does two things: it saves the address of the next instruction on the stack, then jumps to the function.

A ret does the matching action: it removes that saved address from the stack and jumps back to it.

27. What has to happen first: Prologue: line-by-line state changes

Ranking

Put in order

Put the moves of Prologue: line-by-line state changes into the order they have to happen.

  1. pushq %rbp saves the caller's frame pointer
  2. movq %rsp, %rbp makes a stable anchor
  3. subq $24, %rsp allocates local stack space

Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. The CPU subtracts 8 from RSP, then writes the old RBP at the new top of stack.

28. Prologue: line-by-line state changes

Worked example

pushq   %rbp
movq    %rsp, %rbp
subq    $24, %rsp

pushq %rbp saves the caller's frame pointer

Why: The CPU subtracts 8 from RSP, then writes the old RBP at the new top of stack.

movq %rsp, %rbp makes a stable anchor

Why: After the push, RSP points at the saved old RBP. Copying RSP into RBP makes that location the frame base.

subq $24, %rsp allocates local stack space

Why: Subtracting moves RSP downward. Those 24 bytes become the area where this function can spill values.

instructionRSP effectRBP effectmemory effect
pushq %rbpRSP = RSP - 8unchangedmemory[RSP] = old RBP
movq %rsp,%rbpunchangedRBP = RSPnone
subq $24,%rspRSP = RSP - 24unchangedreserves 24 bytes

29. RSP moves; RBP names the frame

Intuition

Figure (svg): Timeline showing RSP changing at entry, push rbp, sub 24, and leave.

Think of RBP as the label on the current function's stack frame. Think of RSP as the live edge of the stack.

When annotating, mark RSP changes loudly. Those are the lines that actually change the size or position of the current stack area.

30. Guess the shape of the answer: Spilling arguments into the frame

Estimation

Predict first

At -O0, GCC often writes parameters to stack slots so the debugger has stable locations for source variables.

Commit before you compute: what does Spilling arguments into the frame come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.

Correct: Store the second parameter into its stack slot

Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. ESI holds b. The store copies that 32-bit value into memory at RBP-24.

31. Spilling arguments into the frame

Worked example

movl    %edi, -20(%rbp)
movl    %esi, -24(%rbp)

Store the first parameter into its stack slot

Why: EDI holds a. The store copies that 32-bit value into memory at RBP-20.

Store the second parameter into its stack slot

Why: ESI holds b. The store copies that 32-bit value into memory at RBP-24.

instructionbefore score(2,3)after
movl %edi,-20(%rbp)EDI = 2memory[RBP-20] = 2
movl %esi,-24(%rbp)ESI = 3memory[RBP-24] = 3

At -O0, GCC often writes parameters to stack slots so the debugger has stable locations for source variables.

32. Work backwards from the answer: Spilling arguments into the frame

Reverse engineer

Discussion prompt

Work backwards. The example finished here:

Store the second parameter into its stack slot

What was it asked to do, and what must it have been given? Reconstruct the problem from its answer.

Hint: Every quantity in the result had to enter somewhere. Account for each one.

Answer:

At -O0, GCC often writes parameters to stack slots so the debugger has stable locations for source variables.

33. Plan first: Body: compute sum

Step zero

Discussion prompt

Body: compute sum — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.

Hint: It starts with: Load a into EDX

Answer:

  1. Load a into EDX
  2. Load b into EAX
  3. Add EDX into EAX
  4. Store the result as sum

34. Body: compute sum

Worked example

movl    -20(%rbp), %edx
movl    -24(%rbp), %eax
addl    %edx, %eax
movl    %eax, -4(%rbp)

Load a into EDX

Why: The first movl reads memory at RBP-20. For score(2,3), EDX becomes 2.

Load b into EAX

Why: The second movl reads memory at RBP-24. EAX becomes 3.

Add EDX into EAX

Why: AT&T order is source then destination, so addl %edx,%eax means EAX = EAX + EDX. Here 3 + 2 = 5.

Store the result as sum

Why: The final move writes EAX into RBP-4, which is the slot GCC uses for sum.

lineEDXEAXmemory written
12--
223-
325-
425sum = 5 at RBP-4

35. Draw the shape of it: Body: compute sum

Blank canvas

Draw it

Draw what Body: compute sum just did — the shape of it, not the line-by-line working. One picture, labels only where you need them. Then check it against the steps: anything you could not draw is a step you followed rather than understood.

36. mov copies bits; it does not connect places

Concept

movl    -20(%rbp), %edx
movl    %edx, -4(%rbp)
instructiondirectionimportant detail
movl -20(%rbp), %edxmemory to registerEDX gets a copy
movl %edx, -4(%rbp)register to memorymemory gets a copy
bothcopy, not linklater changes do not automatically sync

Assembly has no idea that a slot is called a or sum. Those names are source-level meanings we infer from the compiler's pattern.

37. Something is wrong here: mov is not a permanent alias

Anomaly

Predict first

A student writes this, and it looks reasonable:

Tempting mistake: after loading memory into EAX, assume EAX stays connected to that stack slot.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: addl $1,%eax changes EAX. It does not write back to memory unless a later store does that.

Correct: the first line copies the value; the second line changes only the register.

Why: addl $1,%eax changes EAX. It does not write back to memory unless a later store does that.

38. Trap: mov is not a permanent alias

Trap

The trap

Tempting mistake: after loading memory into EAX, assume EAX stays connected to that stack slot.

movl    -4(%rbp), %eax
addl    $1, %eax

Wrong annotation: memory at RBP-4 also changed

Why: addl $1,%eax changes EAX. It does not write back to memory unless a later store does that.

mistakewhy wrong
memory[RBP-4] becomes memory[RBP-4]+1the destination was EAX, not memory

The fix

Correct: the first line copies the value; the second line changes only the register.

movl    -4(%rbp), %eax
addl    $1, %eax

Right annotation: EAX increments, memory is unchanged

Why: A register result returns through EAX just fine; memory only changes when the destination operand is memory.

after lineEAXmemory[RBP-4]
movold sumold sum
addold sum + 1old sum

39. Plan first: Body: compute doubled

Step zero

Discussion prompt

Body: compute doubled — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.

Hint: It starts with: Load sum into EAX

Answer:

  1. Load sum into EAX
  2. Double EAX by adding it to itself
  3. Store doubled

40. Body: compute doubled

Worked example

movl    -4(%rbp), %eax
addl    %eax, %eax
movl    %eax, -8(%rbp)

Load sum into EAX

Why: The compiler reads sum from its stack slot at RBP-4.

Double EAX by adding it to itself

Why: addl %eax,%eax means EAX = EAX + EAX. If sum is 5, EAX becomes 10.

Store doubled

Why: The result is written to RBP-8, the stack slot for doubled.

linemeaningvalue for score(2,3)
1EAX = sum5
2EAX = EAX + EAX10
3doubled = EAX10

41. add and sub change the destination

Concept

addl    %edx, %eax
subq    $24, %rsp
subl    $1, %eax
instructiondestinationeffect
addl %edx,%eaxEAXEAX = EAX + EDX
subq $24,%rspRSPRSP = RSP - 24
subl $1,%eaxEAXEAX = EAX - 1

For annotation, always ask: which operand is the destination? That is the location that changes.

42. lea computes an address; mov reads or writes data

Concept

leaq    -8(%rbp), %rax
movl    -8(%rbp), %eax
instructionwhat EAX/RAX receivesmemory read?
leaq -8(%rbp), %raxthe address RBP - 8no
movl -8(%rbp), %eaxthe 4-byte value stored thereyes

effective address — The numeric address produced by an addressing expression such as RBP - 8. lea loads this address itself, not the value stored at that address.

43. Take the definitions apart: state vs effective address

Definition probe

Sort into buckets

Every line below is part of the definition of state or of effective address — one or the other, never both. Put each where it belongs.

state
The current contents of registers, stack memory, flags, and the instruction pointer.; Reading assembly means updating this state one instruction at a time.
effective address
The numeric address produced by an addressing expression such as RBP - 8.; lea loads this address itself, not the value stored at that address.
b1
The current contents of registers, stack memory, flags, and the instruction pointer. Reading assembly means updating this state one instruction at a time.
b2
The numeric address produced by an addressing expression such as RBP - 8. lea loads this address itself, not the value stored at that address.

44. Something is wrong here: lea does not dereference

Anomaly

Predict first

A student writes this, and it looks reasonable:

Tempting mistake: treat lea like a memory load because its operand looks like a memory operand.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: That annotation describes a mov load, not lea.

Correct: lea performs address arithmetic.

Why: That annotation describes a mov load, not lea.

45. Trap: lea does not dereference

Trap

The trap

Tempting mistake: treat lea like a memory load because its operand looks like a memory operand.

leaq    -8(%rbp), %rax

Wrong annotation: RAX = memory[RBP-8]

Why: That annotation describes a mov load, not lea.

wrong readactual issue
RAX gets doubledLEA never touched memory

The fix

Correct: lea performs address arithmetic.

leaq    -8(%rbp), %rax

Right annotation: RAX = RBP - 8

Why: The result is a pointer/address. To read the int stored there, use a mov from that address.

instructionRAX after
leaq -8(%rbp), %raxthe address RBP - 8

46. Break it on purpose: lea does not dereference

Break the constraint

Discussion prompt

The rule this trap just fixed:

The result is a pointer/address. To read the int stored there, use a mov from that address.

Now break it on purpose. Build a case that violates it and follow the consequences until something visibly fails. Where does the failure first show up — and would you have noticed it if you had not been looking?

Hint: The dangerous rules are the ones whose violation still produces an answer. If yours fails loudly, try to find one that fails quietly.

Answer:

That annotation describes a mov load, not lea.

47. cmp sets flags for the next jump

Concept

cmpl    $10, -8(%rbp)
jne     .L2
piecemeaning
cmpl $10,-8(%rbp)compare doubled with 10 by setting flags
ZFzero flag: 1 if the compared values are equal
jne .L2jump if ZF is 0
fall throughcontinue to the next line if the jump is not taken

A compare instruction usually does not store a normal result. Its result is the condition flags, and the next conditional jump reads those flags.

48. What has to happen first: Branch trace: equal case

Ranking

Put in order

Put the moves of Branch trace: equal case into the order they have to happen.

  1. Assume doubled is 10
  2. cmp sees equality, so ZF becomes 1
  3. jne is not taken
  4. EAX becomes doubled + 1

Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. For score(2,3), sum is 5 and doubled is 10.

49. Branch trace: equal case

Worked example

cmpl    $10, -8(%rbp)
jne     .L2
movl    -8(%rbp), %eax
addl    $1, %eax
jmp     .L3

Assume doubled is 10

Why: For score(2,3), sum is 5 and doubled is 10.

cmp sees equality, so ZF becomes 1

Why: The compared values match: memory at RBP-8 is 10 and the immediate is 10.

jne is not taken

Why: jne means jump if not equal. Since ZF is 1, execution falls through to the true-return path.

EAX becomes doubled + 1

Why: The function prepares the return value 11 in EAX.

momentZFnext instructionEAX
after cmp1fall through-
after mov1addl $1,%eax10
after add1jmp .L311

50. Branch trace: not-equal case

Worked example

cmpl    $10, -8(%rbp)
jne     .L2
movl    -8(%rbp), %eax
addl    $1, %eax
jmp     .L3
.L2:
        movl    -8(%rbp), %eax
        subl    $1, %eax

Assume doubled is 8

Why: For score(1,3), sum is 4 and doubled is 8.

cmp sees not equal, so ZF becomes 0

Why: The compared values do not match: 8 is not 10.

jne jumps to .L2

Why: jne reads ZF = 0, so execution skips the doubled+1 path.

EAX becomes doubled - 1

Why: At .L2, the function loads 8 and subtracts 1, so the return value is 7.

momentZFcontrol flowEAX
after cmp0jump to .L2-
after .L2 mov0next: subl8
after sub0next: .L37

51. A branch turns linear text into a map

Intuition

Figure (svg): Control-flow graph showing cmp leading either to the fall-through path when ZF is 1 or to .L2 when ZF is 0, then joining at .L3.

Assembly is printed in one column, but jumps make it a graph. When teaching, draw the arrows as soon as you see labels and jumps.

A conditional jump chooses an edge based on flags. An unconditional jmp is just a direct edge to another label.

52. je, jne, and jmp

Concept

je      .Lsame
jne     .Ldifferent
jmp     .Ldone
jumpconditionplain reading
jeZF = 1jump if equal
jneZF = 0jump if not equal
jmpalwaysjump no matter what

The names je and jne only make sense relative to a previous compare or arithmetic instruction that set the flags.

53. Fill in: condition for je, jne, and jmp

Comparison

Comparison matrix

From je, jne, and jmp: refill the condition column from what you know. The rest of the table is as it appeared.

jumpconditionplain reading
jeZF = 1jump if equal
jneZF = 0jump if not equal
jmpalwaysjump no matter what

54. Epilogue: leave and ret

Worked example

.L3:
        leave
        ret

leave destroys this stack frame

Why: leave is shorthand for restoring RSP from RBP and then popping the saved caller RBP.

ret jumps back to the caller

Why: ret pops the return address into RIP. EAX already holds the function's return value.

instructionRSP effectRBP effectRIP/control effect
leaveRSP = old frame base, then RSP = RSP + 8RBP = saved caller RBPnone
retRSP = RSP + 8unchangedRIP = return address

55. leave is two instructions in disguise

Concept

leave

# equivalent idea:
movq    %rbp, %rsp
popq    %rbp
lineeffect
movq %rbp,%rspthrow away local stack space all at once
popq %rbprestore caller's RBP and add 8 to RSP
retrestore caller's RIP from the return address

This is why a corrupted saved RBP or return address is serious: the epilogue trusts the stack frame layout.

56. Something is wrong here: confusing allocation with initialization

Anomaly

Predict first

A student writes this, and it looks reasonable:

Tempting mistake: read subq $24,%rsp as if it creates variables and fills them with zero.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: Subtracting from RSP only moves the pointer.

Correct: stack space exists after the pointer moves; stores give slots meaningful values.

Why: Subtracting from RSP only moves the pointer. It does not clear the memory.

57. Trap: confusing allocation with initialization

Trap

The trap

Tempting mistake: read subq $24,%rsp as if it creates variables and fills them with zero.

subq    $24, %rsp

Wrong annotation: 24 bytes are zeroed

Why: Subtracting from RSP only moves the pointer. It does not clear the memory.

wrong assumptionactual
locals are zerospace is reserved but contents are unspecified

The fix

Correct: stack space exists after the pointer moves; stores give slots meaningful values.

subq    $24, %rsp
movl    %edi, -20(%rbp)

Right annotation: allocation first, meaningful writes later

Why: Only the movl store gives the RBP-20 slot the value of a.

instructionmemory meaning
subq $24,%rspspace reserved
movl %edi,-20(%rbp)slot now holds a

58. Which instructions modify RSP?

Concept

pushq   %rbp
movq    %rsp, %rbp
subq    $24, %rsp
movl    %edi, -20(%rbp)
leave
ret
instructiondoes RSP change?why
pushq %rbpyespush moves stack down by 8
movq %rsp,%rbpnocopies RSP into RBP
subq $24,%rspyesRSP is the destination
movl %edi,-20(%rbp)nowrites memory, not RSP
leaveyesrestores and pops
retyespops return address

Shortcut: if RSP is the destination, or the instruction is stack/control-flow machinery like push, pop, call, leave, or ret, check the stack pointer.

59. Which is which, by does RSP change?

Discrimination

Sort into buckets

Sort these by does RSP change?, from memory, without looking back at Which instructions modify RSP?. Telling them apart on the spot is the skill; the table is only where the answer happens to be written down.

yes
pushq %rbp; subq $24,%rsp; leave; ret
no
movq %rsp,%rbp; movl %edi,-20(%rbp)
g1
does RSP change? is "yes" for pushq %rbp, subq $24,%rsp, leave, ret — that is what the table on "Which instructions modify RSP?" records, and it is the single property separating this group from the rest.
g2
does RSP change? is "no" for movq %rsp,%rbp, movl %edi,-20(%rbp) — that is what the table on "Which instructions modify RSP?" records, and it is the single property separating this group from the rest.

60. Plan first: Annotate a 15-line listing

Step zero

Discussion prompt

Annotate a 15-line listing — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.

Hint: It starts with: First pass: bracket prologue and epilogue

Answer:

  1. First pass: bracket prologue and epilogue
  2. Second pass: name the data slots
  3. Third pass: draw branch arrows

61. Annotate a 15-line listing

Worked example

        pushq   %rbp
        movq    %rsp, %rbp
        subq    $16, %rsp
        movl    %edi, -4(%rbp)
        movl    -4(%rbp), %eax
        addl    $7, %eax
        movl    %eax, -8(%rbp)
        cmpl    $20, -8(%rbp)
        jne     .Lelse
        movl    $1, %eax
        jmp     .Ldone
.Lelse:
        movl    $0, %eax
.Ldone:
        leave
        ret

First pass: bracket prologue and epilogue

Why: Lines 1-3 build the frame. Lines 15-16 destroy it and return.

Second pass: name the data slots

Why: Line 4 stores the argument at RBP-4. Line 7 stores a computed local at RBP-8.

Third pass: draw branch arrows

Why: Line 8 sets flags; line 9 jumps to .Lelse if the stored value is not 20; line 11 skips over the else block.

line groupannotation
1-3prologue: save RBP, establish RBP, allocate 16 bytes
4-7body: store arg, add 7, store computed value
8-11if value equals 20, return 1 and jump to done
12-14else path returns 0
15-16epilogue: leave frame and return

62. Draw the shape of it: Annotate a 15-line listing

Blank canvas

Draw it

Draw what Annotate a 15-line listing just did — the shape of it, not the line-by-line working. One picture, labels only where you need them. Then check it against the steps: anything you could not draw is a step you followed rather than understood.

63. GDB lets you watch the state change live

Concept

gcc -O0 -g -mno-red-zone -fno-stack-protector score.c -o score
gdb ./score
piecewhy use it
-ginclude debug info so GDB knows source lines
-O0keep source and assembly easy to match
gdb ./scorestart a debugger session on the executable

When the student can see RSP and memory change one instruction at a time, the stack frame stops being a drawing and becomes a live object.

64. Teach it back: GDB lets you watch the state change live

Explain it

Discussion prompt

Explain GDB lets you watch the state change live to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.

Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.

Answer:

When the student can see RSP and memory change one instruction at a time, the stack frame stops being a drawing and becomes a live object.

65. Predict the next row: A full sandbox program

Pattern

Predict first

The table runs: score(2,3) | function under inspection | returns 11 · printf | visible confirmation | prints 11

In A full sandbox program, given the rows so far: what is the next one — the row where part is return 0?

Correct: return 0 | normal program exit | exit status 0

partpurposeexpected behavior
score(2,3)function under inspectionreturns 11
printfvisible confirmationprints 11
return 0normal program exitexit status 0

Why: The relationship between the columns, not the individual numbers, is what generates the next row. The main function calls score(2,3), so the equal branch returns 11 and printf prints 11.

66. A full sandbox program

Worked example

#include <stdio.h>

int score(int a, int b) {
    int sum = a + b;
    int doubled = sum * 2;
    if (doubled == 10) {
        return doubled + 1;
    }
    return doubled - 1;
}

int main(void) {
    int out = score(2, 3);
    printf("%d\n", out);
    return 0;
}

Compile this exact file for GDB practice

Why: The main function calls score(2,3), so the equal branch returns 11 and printf prints 11.

partpurposeexpected behavior
score(2,3)function under inspectionreturns 11
printfvisible confirmationprints 11
return 0normal program exitexit status 0

67. Plan first: GDB: break and start

Step zero

Discussion prompt

GDB: break and start — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.

Hint: It starts with: Set a breakpoint at score

Answer:

  1. Set a breakpoint at score
  2. Run until the breakpoint
  3. Show the assembly

68. GDB: break and start

Worked example

break score
run
disassemble /r score

Set a breakpoint at score

Why: break score tells GDB to stop when the function begins.

Run until the breakpoint

Why: run starts the program. Execution pauses at or near the beginning of score.

Show the assembly

Why: disassemble /r score prints the function's instructions, with raw bytes if GDB supports /r.

commandquestion it answers
break scorewhere should execution stop?
runcan we reach this function?
disassemble /r scorewhat instructions will we step through?

69. Inspect it line by line: GDB: break and start

Error analysis

Annotate

Walk the callouts on GDB: break and start. Each one is a place this is easy to get subtly wrong.

  • break score tells GDB to stop when the function begins.
  • run starts the program. Execution pauses at or near the beginning of score.
  • disassemble /r score prints the function's instructions, with raw bytes if GDB supports /r.

70. What has to happen first: GDB: step one instruction

Ranking

Put in order

Put the moves of GDB: step one instruction into the order they have to happen.

  1. stepi executes exactly one machine instruction
  2. info registers shows the live register state
  3. x/8gx $rsp displays stack memory

Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. Use it on the prologue so the student can see RSP drop after pushq %rbp.

71. GDB: step one instruction

Worked example

stepi
info registers rsp rbp rip eax edi esi
x/8gx $rsp

stepi executes exactly one machine instruction

Why: Use it on the prologue so the student can see RSP drop after pushq %rbp.

info registers shows the live register state

Why: Ask for the small set you care about first: stack pointer, frame pointer, instruction pointer, return register, and argument registers.

x/8gx $rsp displays stack memory

Why: x examines memory; 8g means eight giant words, each 8 bytes; x means print them in hexadecimal.

commandstate object
stepiadvances RIP by one instruction's effect
info registers ...registers
x/8gx $rspmemory starting at the current stack pointer

72. Where the cost goes: GDB: step one instruction

Cost model

Annotate

In GDB: step one instruction, before reading the notes: mark where the time actually goes. Which line dominates?

  • Use it on the prologue so the student can see RSP drop after pushq %rbp.
  • Ask for the small set you care about first: stack pointer, frame pointer, instruction pointer, return register, and argument registers.
  • x examines memory; 8g means eight giant words, each 8 bytes; x means print them in hexadecimal.

73. How to read x/8gx $rsp

Concept

x/8gx $rsp
piecemeaning
xexamine memory
8show 8 units
ggiant word: 8 bytes per unit
xformat as hexadecimal
$rspstart at the address currently stored in RSP

GDB prints addresses on the left and the memory contents on the right. After pushq %rbp, the first 8-byte value at $rsp should be the saved old RBP.

74. By analogy: How to read x/8gx $rsp

Analogy

Discussion prompt

Explain How to read x/8gx $rsp by analogy to something with no Computer Architecture in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.

Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.

Answer:

GDB prints addresses on the left and the memory contents on the right. After pushq %rbp, the first 8-byte value at $rsp should be the saved old RBP.

75. A teaching loop for GDB

Concept

display/i $rip
info registers rsp rbp rip
x/6gx $rsp
stepi
loop stepstudent says aloud
display/i $ripwhich instruction is next?
info registerswhat are RSP and RBP right now?
x/6gx $rspwhat is on the stack right now?
stepiwhat changed after exactly one instruction?

The key habit is prediction before stepping: ask the student what should change, then let GDB check the prediction.

76. Break it if you can: A teaching loop for GDB

Counterexample

Discussion prompt

The key habit is prediction before stepping: ask the student what should change, then let GDB check the prediction.

That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.

Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.

77. Something is wrong here: optimized output may erase the frame

Anomaly

Predict first

A student writes this, and it looks reasonable:

Tempting mistake: compile with aggressive optimization, then expect the beginner stack-frame diagram to match exactly.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: At higher optimization levels, the compiler may keep values in registers, fold arithmetic, omit RBP, or remove branches.

Correct for this lesson: compile for readability first.

Why: At higher optimization levels, the compiler may keep values in registers, fold arithmetic, omit RBP, or remove branches.

78. Trap: optimized output may erase the frame

Trap

The trap

Tempting mistake: compile with aggressive optimization, then expect the beginner stack-frame diagram to match exactly.

gcc -O2 -S score.c -o score.s

Wrong expectation: every source variable gets a stack slot

Why: At higher optimization levels, the compiler may keep values in registers, fold arithmetic, omit RBP, or remove branches.

expectationwhy it fails
RBP frame appearsoptimizer may omit frame pointer
sum slot existsoptimizer may never store sum

The fix

Correct for this lesson: compile for readability first.

gcc -O0 -g -mno-red-zone -fno-stack-protector score.c -o score

Right expectation: source structure remains visible

Why: -O0 keeps the listing closer to the C program, which is what you want while learning annotation.

flagteaching benefit
-O0literal structure
-gsource/debug info
-mno-red-zonevisible local allocation

79. Which of these survive contact with x86 Function Calls in Assembly?

Two truths and a lie

Sort into buckets

Some of these hold up and some are the exact mistakes this lesson is built to prevent. Sort them.

Holds up
Assembly is a line-by-line record of tiny machine actions: move this value, reserve these bytes, compare this number, jump there if the comparison says so.; A function call borrows a piece of the stack for temporary work. RSP marks the moving bottom edge of the borrowed area; RBP often marks a stable reference point inside it.; On x86-64, EAX is the low 32 bits of RAX. A function returning an int normally leaves that return value in EAX.
Breaks
Tempting mistake: read movl %edi, -20(%rbp) as if the left side is the destination.; Tempting mistake: after loading memory into EAX, assume EAX stays connected to that stack slot.
sound
These are stated as this lesson states them — each one survives the edge cases x86 Function Calls in Assembly puts it through.
flawed
Each of these is lifted from a trap in this deck: reasonable-sounding, and wrong in a way that only shows up once you rely on it.

80. The assembly annotation recipe

Pattern

1. Identify the syntax

Why: AT&T reads source then destination and marks registers with %; Intel usually reads destination then source.

2. Split the listing into regions

Why: Mark prologue, body, branch labels, and epilogue before explaining every line.

3. Build the frame map

Why: Use stores to -offset(%rbp) to infer which stack slot holds which source-level value.

4. Annotate each instruction by state change

Why: For every line, write one of: register changed, memory changed, flags changed, or control flow changed.

5. Trace with one concrete call

Why: Pick values such as score(2,3). Write a table for registers, stack slots, flags, and next instruction.

6. Verify live in GDB

Why: Predict the effect, run stepi, inspect registers and stack memory, then correct the mental model.

81. Where this shows up: x86 Function Calls in Assembly

Real world

Discussion prompt

Outside this lesson: where does x86 Function Calls in Assembly actually turn up? Name one concrete situation — a job, a piece of software someone ships, a decision somebody has to make — and say which part of The assembly annotation recipe is doing the work in it.

Hint: Vague is the failure mode here. "Engineering" is not a situation; "deciding whether this build is fast enough to ship" is.

Answer:

That deck walks through x86-64 function-call assembly in depth, working from GCC -O0 output. It compares AT&T with Intel syntax and covers stack frames, RSP and RBP, addressing modes, arithmetic, and compare-and-branch control flow, then inspects the stack in GDB. Along the way it targets three traps that catch most people: reversing the operands, confusing an address with the value stored there, and treating stack allocation as though it were initialization.

82. Rule out three: Check: which line changes RSP?

Elimination

Eliminate the wrong options

Which instruction directly subtracts 24 from RSP?

3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.

  • A. pushq %rbp
  • B. movq %rsp, %rbp
  • C. subq $24, %rsp
  • D. movl %edi, -20(%rbp)

Survives elimination: C

Why: subq $24, %rsp has RSP as the destination, so it directly updates RSP to RSP - 24. pushq also changes RSP, but by 8 and for a different purpose: saving old RBP.

83. Check: which line changes RSP?

Check

Choose the instruction that directly decreases RSP in this prologue.

pushq   %rbp
movq    %rsp, %rbp
subq    $24, %rsp
movl    %edi, -20(%rbp)
lineRSP changes?
1?
2?
3?
4?

Check your understanding

Which instruction directly subtracts 24 from RSP?

  • A. pushq %rbp
  • B. movq %rsp, %rbp
  • C. subq $24, %rsp (correct)
  • D. movl %edi, -20(%rbp)

Answer: C

Why: subq $24, %rsp has RSP as the destination, so it directly updates RSP to RSP - 24. pushq also changes RSP, but by 8 and for a different purpose: saving old RBP.

Why A tempts people
pushq %rbp does change RSP, but it subtracts 8 and stores old RBP. The question asks which instruction subtracts 24.
Why B tempts people
movq %rsp, %rbp copies the current RSP into RBP. RSP is the source, not the destination, so RSP does not change.
Why D tempts people
movl %edi, -20(%rbp) writes to a stack memory slot. It uses RBP to form an address but does not change RSP.

84. What stays fixed: Check: which line changes RSP?

Invariant

Step through it

Step through Check: which line changes RSP? one row at a time. One of these columns never changes — find it, and say why it cannot.

  1. Step 1: line is 1
  2. Step 2: line is 2
  3. Step 3: line is 3
  4. Step 4: line is 4

85. Answer it before you see the options: Check: what does this operand name?

Prediction

Predict first

In movl -8(%rbp), %eax, what does -8(%rbp) mean?

Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.

Correct: The memory at address RBP - 8

Why: -8(%rbp) is an AT&T memory addressing expression. It means compute the address RBP - 8, then read the 4-byte value stored at that address into EAX.

86. Check: what does this operand name?

Check

Read the memory operand in AT&T syntax.

movl    -8(%rbp), %eax
operandkindmeaning
-8(%rbp)??
%eax??

Check your understanding

In movl -8(%rbp), %eax, what does -8(%rbp) mean?

  • A. The literal number -8
  • B. The memory at address RBP - 8 (correct)
  • C. The register named RBP minus the register named 8
  • D. The return address

Answer: B

Why: -8(%rbp) is an AT&T memory addressing expression. It means compute the address RBP - 8, then read the 4-byte value stored at that address into EAX.

Why A tempts people
A literal immediate would use $-8 in AT&T syntax. Parentheses around %rbp make this an address expression.
Why C tempts people
There is no register named 8. The 8 is a displacement subtracted from the base register RBP.
Why D tempts people
The return address is normally above RBP at 8(%rbp) after the standard prologue, not below it at -8(%rbp).

87. What each one costs: Check: what does this operand name?

Trade off

Comparison matrix

From Check: what does this operand name?: every row here is a choice with a cost. Fill the meaning column, then say which row you would actually pick and what you give up for it.

operandkindmeaning
-8(%rbp)??
%eax??

88. Answer it before you see the options: Check: will jne jump?

Prediction

Predict first

If -8(%rbp) holds 10, what happens at jne .L2?

Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.

Correct: It does not jump because ZF is 1

Why: Comparing 10 with 10 sets the zero flag to 1 because the values are equal. jne means jump if not equal, which requires ZF = 0, so this jump is not taken.

89. Check: will jne jump?

Check

Assume memory at RBP-8 holds 10 just before these instructions.

cmpl    $10, -8(%rbp)
jne     .L2
value at RBP-8ZF after cmpjump?
10??

Check your understanding

If -8(%rbp) holds 10, what happens at jne .L2?

  • A. It jumps because the values are equal
  • B. It does not jump because ZF is 1 (correct)
  • C. It always jumps
  • D. It changes RSP before jumping

Answer: B

Why: Comparing 10 with 10 sets the zero flag to 1 because the values are equal. jne means jump if not equal, which requires ZF = 0, so this jump is not taken.

Why A tempts people
je would jump when the values are equal. jne is the opposite condition.
Why C tempts people
jmp always jumps. jne is conditional and reads the flags set by the previous compare.
Why D tempts people
jne changes control flow by updating RIP if taken. It does not push, pop, or adjust RSP.

90. Rule out three: Check: lea or mov?

Elimination

Eliminate the wrong options

Which instruction gives RAX the address RBP - 8 without reading the value stored there?

3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.

  • A. leaq -8(%rbp), %rax
  • B. movl -8(%rbp), %eax
  • C. cmpl $10, -8(%rbp)
  • D. ret

Survives elimination: A

Why: leaq -8(%rbp), %rax computes the effective address RBP - 8 and stores that address in RAX. movl -8(%rbp), %eax would read the 4-byte value from that address instead.

91. Check: lea or mov?

Check

You want the address of the local slot, not the integer stored in it.

leaq    -8(%rbp), %rax
movl    -8(%rbp), %eax
goalinstruction
address RBP-8?
value at RBP-8?

Check your understanding

Which instruction gives RAX the address RBP - 8 without reading the value stored there?

  • A. leaq -8(%rbp), %rax (correct)
  • B. movl -8(%rbp), %eax
  • C. cmpl $10, -8(%rbp)
  • D. ret

Answer: A

Why: leaq -8(%rbp), %rax computes the effective address RBP - 8 and stores that address in RAX. movl -8(%rbp), %eax would read the 4-byte value from that address instead.

Why B tempts people
movl -8(%rbp), %eax dereferences the memory operand and loads the stored value, not the address.
Why C tempts people
cmpl $10, -8(%rbp) reads the value only to set flags for a later branch. It does not put the address in RAX.
Why D tempts people
ret returns to the caller by popping a return address into RIP. It is not an address-arithmetic instruction.

92. Fill in: instruction for Check: lea or mov?

Comparison

Comparison matrix

From Check: lea or mov?: refill the instruction column from what you know. The rest of the table is as it appeared.

goalinstruction
address RBP-8?
value at RBP-8?

93. Connect it up: x86 Function Calls in Assembly

Connect it up

Draw it

One page, no notation unless you need it: draw how these connect — The assembly annotation recipe · Assembly is the compiler's diary · The stack is a workbench with a moving edge · The four registers to watch first · The command that makes a .s file. Put an arrow wherever one of them is what makes another possible, and label the arrow with why.

94. What you can do now

Recap

You can read a GCC -O0 function listing as a sequence of state changes: prologue builds the frame, body moves and computes values, compare/jump chooses a path, and epilogue restores the caller.

thing you seequestion to ask
-8(%rbp)which frame slot does this address?
movwhich direction is the copy?
add or subwhich destination changes?
cmpwhich flags will the next jump read?
je/jne/jmpwhere can RIP go next?
push/sub/leave/rethow did RSP change?

The durable habit is prediction plus verification: annotate the line, predict the register/stack effect, then use GDB to check the live machine state.

Sources

  1. GCC Manual - Overall Options (-S)
  2. GCC Manual - x86 Options (-masm, -mno-red-zone)
  3. Debugging with GDB - current manual
  4. System V Application Binary Interface AMD64 Architecture Processor Supplement
  5. Intel 64 and IA-32 Architectures Software Developer's Manual
  6. Compiler output verified with Compiler Explorer API, x86-64 GCC 15.2, flags -O0 -g0 -mno-red-zone -fno-asynchronous-unwind-tables -fno-stack-protector; comments/directives stripped for teaching. — Author verification run, 2026-06-13.

Want this taught 1-on-1? Alexander tutors Computer Architecture — $55/session, free consultation.

Book on Wyzant · Text (657) 465-8108