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.
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.