NOTE

3.1 Assembly Language

An x86-64 assembly primer covering AT&T syntax, registers, addressing, instructions, stack frames, calls/returns, ABIs, and disassembly of compiled programs.

Computer Architecture & AssemblyCreated Updated 2 min readhistorical

This is a historical learning note and may contain outdated or incomplete understanding.

1. What Is Assembly Language?

Machine code is encoded as binary instruction bytes. Assembly language gives those instructions symbolic names and human-readable operands.

Assembly is close to machine code, but it is not always a strict one-source-line-to-one-instruction abstraction: assemblers support labels, pseudo-operations, macros, and multiple textual syntaxes for the same ISA.

This note uses x86-64 with AT&T-style syntax unless stated otherwise.

2. AT&T Syntax Basics

Common conventions:

  • registers use %, such as %rax;
  • immediates use $, such as $1;
  • two-operand instructions generally write source, destination;
  • instruction suffixes can indicate operand width (b, w, l, q).

Example:

movq %rsp, %rbp
addq %rdx, %rax
subq $0x20, %rsp

3. Addressing

AT&T memory operands can use the form:

displacement(base, index, scale)

Example:

movl -8(%rbp), %edx

This loads a 32-bit value from memory relative to %rbp.

RIP-relative addressing is common in position-independent x86-64 code.

4. Important Instructions

mov

Copies data between registers/memory or loads an immediate value.

Arithmetic

addq %rdx, %rax
subq $8, %rsp

Branches

Instructions such as jmp, je, jne, jl, and jg implement control flow based on flags.

call and ret

call transfers control to a function and records a return address. ret returns to that saved address.

On x86-64, the hardware mechanism centers on the stack/return address, but how arguments, saved registers, stack alignment, and return values are handled is defined by the ABI.

5. Stack Frames

A traditional unoptimized function may contain a prologue like:

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

and a matching epilogue restoring the stack before ret.

Optimizing compilers can omit the frame pointer, inline functions, keep locals entirely in registers, eliminate stack slots, or restructure code significantly. Therefore, source-level functions do not map mechanically to one fixed stack-frame pattern.

6. Calling Conventions

Calling conventions are ABI-specific.

For example, System V AMD64 passes early integer/pointer arguments in registers including:

RDI, RSI, RDX, RCX, R8, R9

Windows x64 uses a different register order and stack conventions.

Go’s compiler/runtime has its own internal ABI, which has also evolved over Go versions.

Always identify the platform and ABI before interpreting argument registers from a disassembly.

7. Caller-Saved and Callee-Saved Registers

An ABI defines which registers a caller must assume may be overwritten and which a callee must preserve.

This lets separately compiled functions cooperate without saving every register on every call.

8. Reading Disassembly

Useful tools include:

objdump -d program
gdb program

Inside gdb:

disassemble main
info registers
x/... <address>

For Go:

go tool objdump binary
go tool compile -S file.go

Compiler output is version- and optimization-sensitive. Use it to understand one concrete build, not as a permanent definition of how a language feature must be implemented.

9. Why Assembly Matters for Concurrency

Assembly is useful when investigating:

  • whether a high-level operation compiles into one or multiple machine instructions;
  • atomic read-modify-write instructions;
  • fences/barriers;
  • compiler-generated loads/stores;
  • calling conventions and runtime scheduling paths.

But language-level correctness must come from the language memory model and synchronization primitives, not from assuming a particular current disassembly will remain unchanged.

Loading helpful count