1x: int = 42x + 2 * 31t1 = 2 * 3t1 = 62t2 = x + t1t2 = x + 6NAMEPLUSINTSTARINTOptimiser++ : intxx : int** : int22 : int33 : int1LOAD x2PUSH 63ADDxslot 0slot 1Stack machinex+2*3intt1t26LOAD xPUSH 6ADDLOAD xPUSH 6ADD4610CRustx86-64Arm
Front end: source text, tokens, syntax treeBack end: IR, optimiser, stack code; then the stack machine runs itTiles: tokens, types, IR, folded constants, instructions and values on the moveLeft, the front end: the source text, the row of tokens and the syntax tree. Right, the back end: the IR card, the optimiser and the stack code. Back right: the stack machine, its stack and the memory cell for x. The lexer cuts x + 2 * 3 into five tokens; the recursive-descent parser builds the tree leaves first; the type checker marks every node int; the tree is flattened into t1 = 2 * 3 and t2 = x + t1; the optimiser folds 2 * 3 into 6; the code generator writes LOAD x, PUSH 6, ADD, and the stack machine runs them to 10. Many compilers, LLVM among them, count the optimiser as a third, middle phase between the front end and the back end. Real back ends also choose registers and machine instructions instead of a stack. The IR is the hand-off between the two ends: the front end writes it and the back end reads it, so the figure sets the IR card at the start of the back-end plate, where the front end passes it over.

Front end

  1. A compiler is a program that turns source code into instructions a machine can run. Ours handles a tiny language: whole numbers, names, + and *. Line 1 says x is an int (a whole number) holding 4. Follow line 2 through every stage.

Back end

Front and back

What happens

;; step 1/10 · source
source code   plain text
  1  x: int = 4
  2  x + 2 * 3

the stages, in order:
  lexer        text    -> tokens
  parser       tokens  -> syntax tree
  type checker tree    -> tree with types
  IR           tree    -> three-address code
  optimiser    IR      -> faster IR
  code gen     IR      -> stack-machine code

Constant folding, constant propagation (copying the 6 into the next line) and dead-code removal (dropping t1) are three separate optimisations; the figure shows them together. CPython folds this expression too: dis shows the constant 6 and no multiply. Many compilers, LLVM among them, count the optimiser as a third, middle phase between the front end and the back end. Real back ends also choose registers and machine instructions instead of a stack. The IR is the hand-off between the two ends: the front end writes it and the back end reads it, so the figure sets the IR card at the start of the back-end plate, where the front end passes it over.