The Same Instructions, Two Spellings

There is one confusing surprise waiting for anyone who starts reading x86-64 assembly: the same machine instructions can be written in two different notations. They describe identical code, but they look quite different on the page.

The two notations are:

  • Intel syntax, used by Microsoft's tools, the Intel manuals, many disassemblers, and this course.
  • AT&T syntax, the default output of the GNU tools (gcc, objdump) on Unix-like systems.

You will run into both, so the goal of this lesson is simple: be able to tell which one you are looking at, and translate between them in your head.

A Side-by-Side Example

Take our add function again:

int add(int a, int b)
{
    return a + b;
}

Compiled at -O2, here it is in both notations:

; Intel syntax
add(int, int):
    lea eax, [rdi+rsi]
    ret
# AT&T syntax
add(int, int):
    leal (%rdi, %rsi), %eax
    ret

Same two instructions. Same machine code. Different spelling. Let us list exactly what changed.

The Four Differences That Matter

1. Operand order is reversed

This is the big one, and the easiest to get wrong.

  • Intel: destination, source
  • AT&T: source, destination

In the Intel line, eax (the destination) comes first. In the AT&T line, %eax (the destination) comes last. The result is identical, but if you read them with the wrong convention, you will get everything backwards.

2. AT&T puts a % in front of every register

  • Intel: eax, rdi, rsi
  • AT&T: %eax, %rdi, %rsi

If you see percent signs on the registers, you are reading AT&T.

3. AT&T puts a $ in front of immediate (literal) values

Consider returning the constant 42:

mov eax, 42        ; Intel
movl $42, %eax     # AT&T

In AT&T, the literal 42 is written $42. In Intel it is just 42.

4. AT&T encodes operand size as a suffix letter

Look closely at the mnemonics: Intel wrote lea, AT&T wrote lea**l**. That trailing l means "long", a 4-byte operation. The size suffixes are:

Suffix Size Typical C++ type
b 1 byte char
w 2 bytes short
l 4 bytes int
q 8 bytes long / pointer

So movl, addl, popq are AT&T. Intel instead writes the size next to the operand only when it is ambiguous, using keywords like DWORD PTR (4 bytes) or QWORD PTR (8 bytes).

Memory Operands Look Different Too

The biggest visual difference is how memory addresses are written. Both express the same idea, "base register plus an offset", but the shape differs.

Reading the value at 4 bytes below rbp:

mov eax, DWORD PTR -4[rbp]     ; Intel: displacement[base]
movl -4(%rbp), %eax            # AT&T: displacement(base)

The general forms are:

  • Intel: displacement[base + index*scale]
  • AT&T: displacement(base, index, scale)

These describe exactly the same address computation; they just arrange the pieces differently. You will study addressing modes in depth in Chapter 2. For now, just notice that square brackets mean Intel, and parentheses mean AT&T.

A Full Unoptimized Comparison

Here is add at -O0 in both notations so you can apply all four rules at once:

; Intel
add(int, int):
    push rbp
    mov  rbp, rsp
    mov  DWORD PTR -4[rbp], edi
    mov  DWORD PTR -8[rbp], esi
    mov  edx, DWORD PTR -4[rbp]
    mov  eax, DWORD PTR -8[rbp]
    add  eax, edx
    pop  rbp
    ret
# AT&T
add(int, int):
    pushq %rbp
    movq  %rsp, %rbp
    movl  %edi, -4(%rbp)
    movl  %esi, -8(%rbp)
    movl  -4(%rbp), %edx
    movl  -8(%rbp), %eax
    addl  %edx, %eax
    popq  %rbp
    ret

Run your eyes down both. The percent signs, the size suffixes, the reversed operand order, and the parentheses for memory all jump out once you know to look for them.

Why This Course Uses Intel

Both notations are equally valid, but this course uses Intel syntax because it is generally easier to read when you are learning: no register or immediate prefixes to filter out, sizes stated only when needed, and the destination-first order that most people find natural. It is also the syntax used by the popular Compiler Explorer (godbolt.org) by default, so what you learn here will match the examples you find online.

When you view assembly in this course's editor, it will be in Intel syntax. Just remember that the GNU tools on your own machine default to AT&T, so if you ever run gcc -S yourself and the output looks different, add -masm=intel to switch.

Key Takeaways

  • The same x86-64 instructions can be written in Intel or AT&T syntax.
  • Operand order is reversed: Intel is dest, src; AT&T is src, dest.
  • AT&T prefixes registers with %, immediates with $, and encodes operand size as a mnemonic suffix (b/w/l/q).
  • Intel uses no such prefixes and states size with keywords like DWORD PTR only when needed.
  • Memory operands: Intel uses [ ... ], AT&T uses disp(base, index, scale).
  • This course (and Compiler Explorer) use Intel syntax; GNU tools default to AT&T but accept -masm=intel.