The Compile–Link–Load Toolchain

the compiler phases (lexing, parsing, semantic analysis, optimization, codegen)

The compiler is the brain of the toolchain: it takes one translation unit of human-readable text and turns it into low-level instructions for the machine. It does this not in one leap but in a series of inner phases, each transforming the program into a form a little closer to the metal. You never see these phases directly, but knowing them explains exactly where each kind of error message comes from.

Walking through them in order: first comes lexing (also called scanning), which chops the raw text into tokens, the smallest meaningful pieces, the way you mentally split a sentence into words; int x = 3; becomes the tokens int, x, =, 3, and the semicolon. Next parsing groups those tokens according to the grammar of the language into a tree shape that captures structure, catching things like a missing semicolon or unbalanced braces. Then semantic analysis checks the meaning: does this variable exist, do the types match, are you adding a number to a string. After that, optimization rewrites the program to do the same work faster or in less space, for example folding 2+3 into 5 at compile time or deleting code that can never run. Finally code generation (codegen) emits the actual assembly instructions for your target processor.

The first three phases together are loosely called the front end (they understand your language); the last two are the back end (they produce machine-specific output). This split is why one compiler can support many languages or many processors by mixing front ends and back ends. Practically, an error about a stray token comes from lexing or parsing; a 'use of undeclared identifier' or 'incompatible types' comes from semantic analysis; and a program that builds but misbehaves only when optimized is often a sign of undefined behavior the optimizer was allowed to exploit.

Source: total = a + b * 2; lexing -> tokens: total = a + b * 2 ; parsing -> tree: assign(total, add(a, mul(b, 2))) semantic-> checks a, b are numeric and declared optimize-> if b is const 3: total = a + 6; codegen -> imul / add machine instructions

One small line passing through every phase, each step closer to machine code.

Optimization must never change a correct program's observable behavior, but it is allowed to ASSUME your code has no undefined behavior, which is why UB can make optimized builds behave very differently from unoptimized ones.

Also called
compiler pipelinecompiler front-end and back-end編譯器流程