1 Introduction
1.1 What is a compiler?
1.2 The phases of a compiler
1.3 Interpreters
1.4 Why learn about compilers?
1.5 The structure of this book
1.6 To the lecturer
1.7 Acknowledgements
1.8 Permission to use
2 Lexical Analysis
2.1 Introduction
2.2 Regular expressions
2.2.1 Shorthands
2.2.2 Examples
2.3 Nondeterministic nite automata
2.4 Converting a regular expression to an NFA
2.4.1 Optimisations
2.5 Deterministic nite automata
2.6 Converting an NFA to a DFA
2.6.1 Solving set equations
2.6.2 The subset construction
2.7 Size versus speed
2.8 Minimisation of DFAs
2.8.1 Example
2.8.2 Dead states
2.9 Lexers and lexer generators
2.9.1 Lexer generators
2.10 Properties of regular languages
2.10.1 Relative expressive power
2.10.2 Limits to expressive power
2.10.3 Closure properties
2.11 Further reading
Exercises
3 Syntax Analysis
3.1 Introduction
3.2 Context-free grammars
3.2.1 How to write context free grammars
3.3 Derivation
3.3.1 Syntax trees and ambiguity
3.4 Operator precedence
3.4.1 Rewriting ambiguous expression grammars
3.5 Other sources of ambiguity
3.6 Syntax analysis
3.7 Predictive parsing
3.8 Nullable and FIRST
3.9 Predictive parsing revisited
3.10 FOLLOW
3.11 LL(1) parsing
3.11.1 Recursive descent
3.11.2 Table-driven LL(1) parsing
3.11.3 Conicts
3.12 Rewriting a grammar for LL(1) parsing
3.12.1 Eliminating left-recursion
3.12.2 Left-factorisation
3.12.3 Construction of LL(1) parsers summarized
3.13 SLR parsing
3.14 Constructing SLR parse tables
3.14.1 Conicts in SLR parse-tables
3.15 Using precedence rules in LR parse tables
3.16 Using LR-parser generators
3.16.1 Declarations and actions
3.16.2 Abstract syntax
3.16.3 Conict handling in parser generators
3.17 Properties of context-free languages
3.18 Further reading
Exercises
4 Symbol Tables
4.1 Introduction
4.2 Symbol tables
4.2.1 Implementation of symbol tables
4.2.2 Simple persistent symbol tables
4.2.3 A simple imperative symbol table
4.2.4 Efficiency issues
4.2.5 Shared or separate name spaces
4.3 Further reading
Exercises
5 Type Checking
5.1 Introduction
5.2 Attributes
5.3 A small example language
5.4 Environments for type checking
5.5 Type checking expressions
5.6 Type checking of function declarations
5.7 Type checking a program
5.8 Advanced type checking
5.9 Further reading
Exercises
6 Intermediate-Code Generation
6.1 Introduction
6.2 Choosing an intermediate language
6.3 The intermediate language
6.4 Generating code from expressions
6.4.1 Examples of translation
6.5 Translating statements
6.6 Logical operators
6.6.1 Sequential logical operators
6.7 Advanced control statements
6.8 Translating structured data
6.8.1 Floating-point values
6.8.2 Arrays
6.8.3 Strings
6.8.4 Records/structs and unions
6.9 Translating declarations
6.9.1 Example: Simple local declarations
6.10 Further reading
Exercises
7 Machine-Code Generation
7.1 Introduction
7.2 Conditional jumps
7.3 Constants
7.4 Exploiting complex instructions
7.4.1 Two-address instructions
7.5 Optimisations
7.6 Further reading
Exercises
8 Register Allocation
8.1 Introduction
8.2 Liveness
8.3 Liveness analysis
8.4 Interference
8.5 Register allocation by graph colouring
8.6 Spilling
8.7 Heuristics
8.7.1 Removing redundant moves
8.8 Further reading
Exercises
9 Function calls
9.1 Introduction
9.1.1 The call stack
9.2 Activation records
9.3 Prologues, epilogues and call-sequences
9.4 Caller-saves versus callee-saves
9.5 Using registers to pass parameters
9.6 Interaction with the register allocator
9.7 Accessing non-local variables
9.7.1 Global variables
9.7.2 Call-by-reference parameters
9.7.3 Nested scopes
9.8 Variants
9.8.1 Variable-sized frames
9.8.2 Variable number of parameters
9.8.3 Direction of stack-growth and position of FP
9.8.4 Register stacks
9.9 Further reading
Exercises
10 Analysis and optimisation
10.1 Data-flow analysis
10.2 Common subexpression elimination
10.2.1 Available assignments
10.2.2 Example of available-assignments analysis
elimination
10.3 Jump-to-jump elimination
10.4 Index-check elimination
10.5 Limitations of data-flow analyses
10.6 Loop optimisations
10.6.1 code hoisting
10.6.2 Memory prefetching
10.7 Optimisations for function calls
10.7.1 Inlining
10.7.2 Tail call optimisation
10.8 Specialisation
10.9 Further reading
Exercises
11 Bootstrapping a compiler
11.1 Introduction
11.2 Notation
11.3 Compiling compilers
11.3.1 Full bootstrap
11.4 Further reading
Exercises