the middle end
Suppose the front end has handed you a correct but naive description of a program — every multiply, every redundant load, exactly as written. There is usually a much faster way to compute the same answer. The middle end is the part of the compiler whose whole job is to find those faster-but-equivalent versions. It is, in plain terms, the optimizer.
The middle end works entirely on the intermediate representation, never on source text or machine instructions. It is organized as a sequence of passes, where each pass is a self-contained transformation: one folds constants, one deletes dead code, one hoists work out of loops, one inlines small functions. Passes run in a pipeline and feed each other — inlining a function exposes new constants to fold, folding constants exposes new dead code to delete — so the optimizer loops over its passes to keep finding opportunities. The whole effort is bounded by one rule: every rewrite must preserve the program's observable behavior. The optimizer may change how the answer is computed, never the answer (the as-if rule).
It matters because this is where most of your speed comes from at -O2 and where the surprising behavior lives: the middle end is what 'deletes' code, 'reorders' it, and aggressively exploits the front end's promise that your program has no undefined behavior. Because it is language- and target-neutral, one good middle end (like LLVM's) serves many languages and many chips. A caveat: more passes are not always faster code — the optimizer estimates costs with heuristics, and it can guess wrong, which is exactly why measuring beats assuming.
// before the middle end: // after a few passes: int n = 10; int total = 36; // n*n folded, loop int total = 0; // turned into the answer for (int i = 0; i < 4; i++) total += i + n;
The optimizer can compute a constant loop's answer at compile time, so no loop runs at execution time.
The optimizer is bounded by the as-if rule, not by your intentions: it only must keep observable behavior, so anything it can prove is dead, redundant, or unreachable (including code that is only unreachable because reaching it would be undefined behavior) is fair game to delete.