Debug Information
Mapping optimized machine code back to what you wrote — line tables, variable locations, source maps, and why a variable reads as "optimized out".
The compiler emits a second artifact alongside the code: a map from addresses to source positions, a description of where every variable lives at every point, the shape of every type, and how to walk back up the stack. None of it is recoverable from the instructions.
The same problem as a DWARF line table, solved in JSON for pipelines that emit source rather than machine code. The two things worth understanding are the VLQ encoding that makes the mappings small, and the composition rule that makes a chain of four tools still point at your original file.
Two default configurations that bundle several independent decisions together, and the bundling is the problem. Optimization, debug information and assertions are three separate dials, and the right shipped build is very often optimized *with* full debug information, split out of the artifact.
Why a variable reads "optimized out", why the instruction pointer jumps backwards between two functions, and why a breakpoint on a line you can see never fires. Three symptoms, three specific transformations, and none of them is a bug.
Turning `0x00007f8a3c0012ef` back into `parse_header at http.c:184`, using symbols and debug information you deliberately stripped out of the shipped binary. The whole thing works or fails on one detail: whether the build ID ties the two artifacts together.
The most directly useful skill in the domain. Every question of the form "did it inline that", "did that vectorize", "is this bounds check still there" is answerable in under a minute with a flag you can memorise — and optimization remarks will tell you *why* the answer was no.