Reading Assembly

Part 1 · The register machine

`lea` is arithmetic

The most misread instruction in x86-64. It has brackets, its name says "load", and it never touches memory.

30 mincontrasting casesbeacon trainingPOE
What this lesson is trying to break (3)
  • `lea` loads from memory.
  • Brackets in an operand always mean a memory access.
  • Array indexing needs a separate multiply.

Start with the contrast

Two instructions, near-identical text, completely different behaviour.

Varies: only the mnemonic — the bracketed operand is identical

InstructionWhat happensTouches memory?Result in rax
mov rax, [rdi+8]Read 8 bytes from address rdi+8Yesthe value stored there
lea rax, [rdi+8]Compute rdi+8Nothe number rdi+8

Same brackets, opposite meanings. The brackets describe an address computation; the mnemonic decides whether that address is then used.

So: brackets mean “an address is being computed here”. They do not mean an access happens. lea computes and stops.

Why compilers love it

x86-64’s addressing modes can encode a surprising amount in one operand:

[ base + index*scale + displacement ]
   │       │      │        │
   │       │      │        └─ any 32-bit constant
   │       │      └────────── 1, 2, 4 or 8 only
   │       └───────────────── any register
   └───────────────────────── any register

The scale values are exactly the sizes of char, short, int/float and long/double/pointer — because the whole mechanism exists to make array indexing free. arr[i] for int arr[] is [rdi + rsi*4], one operand, no multiply instruction.

And because lea exposes that hardware without the access, the compiler gets a free three-input adder with a built-in shift.

Predict firstcommit in writing, then look
int t3(int x) {
    return x * 3;
}
Beaconsee this → think this, without decoding it

lea rax, [rdi+rdi*2] · lea rax, [rax+rax*4]

A multiplication by a small constant. Nothing is being loaded.

The pattern [r + r*n] computes r × (n+1), so scales 1, 2, 4, 8 give free multiplies by 2, 3, 5 and 9.

You will also see lea used for plain addition when the compiler wants to keep both inputs intact — lea eax, [rdi+rsi] is eax = rdi + rsi without destroying either, which two-operand add cannot do. That is lea compensating for x86’s missing three-operand arithmetic, and it is why ARM64 needs no equivalent trick.

What ARM64 does instead

ARM64 has no lea, and does not need one, for two reasons.

Its arithmetic is already three-operand, so add w0, w1, w2 keeps both inputs — the main non-multiply use of lea disappears.

And it can shift an operand as part of the instruction:

add     x0, x1, x2, lsl 3      ; x0 = x1 + (x2 << 3)

That covers base + index*8 directly in an ordinary add. So the capability transfers exactly; only the spelling differs. Compile x * 3 for ARM64 and you will see it use an add-with-shift or a plain mul — either way, no bracketed operand, because ARM64 reserves brackets strictly for real memory access.

This is the recurring lesson of Part 1 in one instruction: the capability is essential, the syntax is incidental, and a single-ISA reader cannot tell which is which.

Explain it to yourselfwrite it — thinking it does not count

Someone reviewing a disassembly says:

“There are five memory accesses in this loop — look at all the brackets.”

They have counted three leas among them. Write the correction you would give, and add the one thing they should look at instead to count real accesses.

What you should now be able to say

  • Brackets mean address computation, not access. The mnemonic decides.
  • lea gives you base + index*scale + displacement as pure arithmetic.
  • Scale is 1, 2, 4 or 8 — the element sizes — which is why array indexing costs no separate multiply.
  • [r + r*n] is a free multiply by n+1; 2, 3, 5 and 9 are the reachable ones.
  • lea also substitutes for the three-operand add x86 lacks.
  • ARM64 achieves the same things with three-operand arithmetic and shifted register operands, and keeps brackets for real accesses only.

Next: 1.4 — Control flow is compare-and-jump.

In the wildgo read the real thing now, while it is fresh
  • x86 addressing modesSkim to the addressing-mode section. The thing to take away is that one operand can encode base + index*scale + displacement, which is why so much arithmetic hides inside brackets.
Scheduled for recall3 items added to your review queue

These are not shown to you now on purpose. They will surface in Review tomorrow, mixed in among items from other parts of the course — not grouped by topic. Sorting out which idea applies is the part that transfers.