Intermediate representation and compiler optimisation
In short: Modern compilers translate the syntax tree into an intermediate representation (IR), optimise it independently of language and target processor and only then generate machine code.
In more detail: An IR such as LLVM IR is close to the machine but processor-independent. For m languages and n processors only m front ends and n back ends must be written instead of m·n compilers. In SSA form each variable is assigned exactly once, which simplifies many optimisations.
In Depth
- Optimisations: Constant folding, inlining, dead-code elimination, loop optimisations, vectorisation.
- Inlining: Replace a function call with the function body. Saves call costs, opens further optimisations.
- Register allocation: Map variables onto the few CPU registers.
- Back end: Instruction selection and scheduling for the target processor.
- Profile-guided optimisation: Optimises using real measurement data.
- JIT compiler: Compiles at run time and uses knowledge of actual behaviour (for example V8, HotSpot).
- Ahead-of-time: Everything beforehand (C, Rust, Go).
See also: Lexer, parser and syntax tree, Linker and loader