Assembly, for people who will never write it
What did your code actually become?
This course does not teach you to write assembly. It teaches you toread compiler output with an accurate model of the machine, and to judge a performance claim on evidence — including your own team’s.
The organising claim: assembly is a contract, not a description. It records what the program asked for; the silicon decides what happens. Assembly presents four convenient fictions — sixteen registers, sequential execution, flat memory, one instruction per operation — and each is wrong in a way that costs money. Parts 1 to 3 make you fluent in the fictions, because you cannot read anything without them. Part 4 takes all four apart.
- Lessons
- 29
- Honest hours
- 17
- Threshold concepts
- 6
- Done
- 0
Every C and Rust snippet has x86-64 andARM64 buttons that compile it live and show the real assembly. Never press one before committing a prediction — thePredict boxes ask how many instructions, or whether there will be a branch, and guessing in writing is what makes the answer land. Rate your confidence honestly;Calibration shows you where you are overconfident, which for an experienced engineer is the main obstacle. Come back to Review most days.
New here? Start with0.1 — Why you will never write this. Coming fromReading Rust? You arrive unusually well prepared, and3.6 is where that course’s promises finally get checked.
Orientation
What reading assembly is for, why you will never write it, and the frame that holds the rest together.
- 0.1Why you will never write thisThe goal is reading, not writing — and that single decision changes the whole syllabus. Plus the five rules that make it work.
- 0.2The four fictionsAssembly is a contract, not a description. It presents four convenient lies, and fluency means knowing both the lie and where it breaks — plus a pretest you are meant to fail.
The register machine
Fiction one. Sixteen named boxes, no types at all, and arithmetic hiding inside something that looks like a memory access.
- 1.1Registers are a scarce physical resourceSixteen boxes, and the compiler spends most of its effort deciding who gets one. The first fiction, and the one you must accept before anything else makes sense.
- 1.2There are no types, only widthsTypes do not survive compilation. `add` cannot tell signed from unsigned — the signedness lives somewhere else entirely, and finding it is a reading skill.
- 1.3`lea` is arithmeticThe most misread instruction in x86-64. It has brackets, its name says "load", and it never touches memory.
- 1.4Control flow is compare-and-jumpThere are no booleans and no if statements. There is a register of flag bits, and instructions that read them — and sometimes the branch disappears entirely.
- 1.5Reading your first functionEverything from Part 1 applied to one real function, in both instruction sets — then a mixed set that refuses to tell you which idea it is testing.
The calling contract
How code composes when nothing enforces the rules. Stack, ABI, prologues, and who is responsible for what.
- 2.1The stack is a conventionNot a data structure the CPU provides. A register, a direction, and an agreement — which is why it can be corrupted.
- 2.2The ABI is a contract nothing enforcesIdentical hardware, different rules. Which register holds your first argument is a political decision, not a physical one — and violating it fails silently.
- 2.3Prologue, epilogue, frame pointersThe bookkeeping at the top and bottom of every function — and the one flag that makes your profiler useless.
- 2.4Who saves whatCaller-saved, callee-saved, the red zone, and the alignment rule you only notice when it is broken.
- 2.5Reading a call chainReconstructing what the source said from a listing with no symbols — and noticing when the compiler deleted the calls entirely.
Reading compiler output
The central skill and the largest part. What -O2 does to your source, and how to check a performance claim instead of repeating it.
- 3.1Compiler Explorer as an instrumentThe best tool in this field, and the ways it will mislead you if you mistake it for your machine.
- 3.2`-O0` is readable, and a lieEvery tutorial teaches unoptimised output because it maps to the source. That mapping is exactly what makes it useless for understanding real code.
- 3.3The optimisations you will actually seeSix transformations account for most of the distance between your source and the output. Name them and the listing stops looking arbitrary.
- 3.4How a loop disappearsYour counter is gone, the body is duplicated four times, and the exit test compares addresses. Here is what happened and why.
- 3.5Structs, padding and layoutField order changes how big your struct is. Not by a little, and not in a way you can guess without knowing the rule.
- 3.6Verifying "zero-cost"Reading Rust told you iterator chains compile to the same loop. This is where you check, and the real answer is more interesting than yes or no.
Where the fictions break
All four fictions, dismantled. The listing is a dependency graph, a mov can cost 100x another mov, and the ISA is not the machine.
- 4.1The listing is a dependency graphFiction two, dismantled. Instructions do not execute in the order they are written, and once you stop reading them as a script the whole subject reorganises.
- 4.2Latency versus throughputThe distinction that makes instruction counting useless. One number tells you how long an operation takes; the other tells you how many you can start. They are not the same and rarely close.
- 4.3Branch predictionA predicted branch costs nothing. A mispredicted one costs a pipeline. Which is why sorting your data can make the same loop several times faster.
- 4.4The memory hierarchyFiction three. One `mov` instruction, two orders of magnitude of cost — and this is the fiction that dominates real programs.
- 4.5µops: the ISA is an interfaceFiction four, and the last one. What executes is not what you read — which settles the CISC-versus-RISC argument you may still hear people having.
SIMD and the modern ISA
Vector registers, why the compiler usually fails to use them, and how to read the output when it succeeds.
- 5.1Vector registersA second, wider register file with its own instructions. The register name tells you the width, and the width tells you the parallelism.
- 5.2Why autovectorisation usually failsThe compiler wants to vectorise your loop. It is usually blocked, always for a nameable reason, and it will tell you which one if you ask.
- 5.3Reading vectorised outputForty instructions for a three-line loop. Here is how to find the eight that matter and ignore the rest.
Speaking performance
The vocabulary, how to read a profiler, and how to stop your team hand-optimising things that do not matter.
- 6.1The lexiconA small vocabulary full of ordinary English words with narrow technical meanings — several of which mean roughly the opposite of what they sound like.
- 6.2Reading a profilerThe only tool in this course that measures rather than models — and the place where everything you have learned finally meets reality.
- 6.3Explaining performance to your teamThe last lesson. Six conversations a tech lead actually has — including the one where knowing assembly means telling someone not to use it.