dead-code elimination
/ DCE /
If you compute a value and then never use it, computing it was a waste; if a line of code can never be reached, keeping it is pointless. Dead-code elimination is the optimization that finds and removes exactly this kind of work — computations whose results are never observed, and code that can never run.
There are two distinct things being deleted, and it is worth keeping them separate. Dead code in the dataflow sense is a computation whose result is never used on any path that observes it: int t = a * b; with t never read again is a dead store, so the multiply is removed. Unreachable code is code no control-flow path ever reaches: the body of if (0) { ... }, or anything after a return. The compiler proves these with analyses — liveness analysis tells it which values are still needed, and reachability tells it which blocks are entered. SSA form makes liveness especially easy because each value's single definition and its uses are explicit. Often DCE is the cleanup crew after other passes: constant propagation folds a condition to false, and DCE then deletes the now-unreachable branch.
It matters because DCE shrinks code and removes wasted run-time work, and it is what makes layered abstractions cheap — code paths you 'pay for' only conceptually get deleted when unused. A sharp caveat for systems programmers: the compiler may delete a store ONLY if it can prove the store is unobservable. A write to volatile memory, a memory-mapped device register, or a value other threads or a signal handler may read is observable, so it is NOT dead and is kept; this is exactly why volatile and atomics exist. And undefined behavior can make code 'dead' in a way that surprises you — if reaching a branch would be UB, the optimizer may treat it as unreachable and delete it, including a safety check.
int f(int a, int b) { int unused = a * b; // result never read -> dead store, the multiply is removed if (0) return -1; // unreachable -> deleted return a + b; }
The unused multiply and the if (0) branch both vanish; only a + b survives.
A store is only dead if it is unobservable: writes to volatile or memory-mapped registers, or values another thread or signal handler may read, are observable and kept; and UB can make a check appear 'unreachable', so DCE may silently delete it.