2IC30 · Topic 07
Compilers & System Architecture
From parse trees to code, and the computer beyond the core: buses, memory, caches and concurrency.
Compilers, parsing & syntax-directed translation
An interpreter executes a program directly; a compiler translates it into the target machine's language first. Mixed forms exist (compile to bytecode, then interpret).
Compilation steps: a parser reads the source and builds a parse tree following a context-free grammar (written in BNF — Backus-Naur Form). The grammar also fixes precedence (so x+y*z parses correctly). Code is then generated by walking the tree and emitting a template per construct. Syntax-directed translation (result of an expression left on the stack): ⟦constant⟧ = LOAD R0 constant ; STOR R0 [--SP] ⟦variable⟧ = LOAD R0 [SP+pos(v)] ; STOR R0 [--SP] ⟦e1*e2⟧ = ⟦e1⟧ ; ⟦e2⟧(pos+1) ; BRS mul ; ADD SP 1 ⟦v := e⟧ = ⟦e⟧ ; LOAD R0 [SP++] ; STOR R0 [SP+pos(v)] ⟦if e then B1 else B2⟧ = ⟦e⟧ ; LOAD R0 [SP++] ; BEQ else ; ⟦B1⟧ ; BRA endif ; else: ⟦B2⟧ ; endif: ⟦while e do B⟧ = loop: ⟦e⟧ ; LOAD R0 [SP++] ; BEQ end ; ⟦B⟧ ; BRA loop ; end: ⟦B1;B2⟧ = ⟦B1⟧ ; ⟦B2⟧
Translation is defined recursively on the parse tree and combined — define it abstractly first, then refine. Non-optimising compilers are fast (used in development); optimising compilers are slower but emit better code (constant folding, register allocation, dead-code elimination, function inlining, instruction reordering when sequential semantics allow) — up to 10× speed-ups. Intermediate languages (LLVM IR, JVM bytecode) decouple front-ends (Java, C++, Fortran) from back-ends (ARM, x86, PowerPC).
This is the spine of gcc/LLVM/javac; understanding templates explains why certain code compiles to tight loops and why optimisation flags matter.
Compilers, parsing & syntax-directed translation worked examples
1 questions
Common mistakes
- Forgetting the convention that expression results sit on the stack.
- Mismatching pos offsets when the stack grows during evaluation (pos+1).
- Confusing interpreter (runs directly) with compiler (translates).
Exam tips
- Be able to write the if/while/assignment templates — Q6 or Q5 may ask for one.
- Mention intermediate languages and one optimisation for theory marks.
Memory aids
- Parse → tree → emit a template per node.
- Interpreter runs it; compiler translates it.
Compilers, parsing & syntax-directed translation practice
2 questions
System architecture: buses & memory
A computer is more than the CPU: memory, I/O controllers, and the buses that connect them. A bus has address, data and control lines.
Processor pins group into three buses: Address (M pins → 2ᴹ locations), Data (N parallel bits), Control (read/write, timing, interrupts…). A master initiates transfers; a slave responds (CPU↔memory, CPU↔I/O, DMA controller↔memory). Memory types: SRAM & ROM/flash connect simply. DRAM stores bits as capacitor charge — dense but needs periodic refresh and split row/column (RAS/CAS) addressing via a controller (DDR2/3/4 transfer 4/8/16 words per cycle).
Synchronous buses time everything to a clock (simple, but everything takes a clock multiple); asynchronous buses use handshaking (e.g. 4-phase) so transfers happen as soon as possible. Bus standards proliferate (PCI, USB, SATA…); serial buses now dominate (fewer wires, no skew). A 'chip-set' wires CPU, memory bus, PCI, USB, IDE/SATA together.
DDR generations and PCIe lanes are exactly these concepts; memory bandwidth and latency now dominate performance more than raw clock speed.
System architecture: buses & memory worked examples
1 questions
Common mistakes
- Confusing address-bus width (capacity) with data-bus width (per-transfer size).
- Thinking DRAM needs no maintenance (it must be refreshed).
- Assuming synchronous buses are always faster (async can be quicker per transfer).
Exam tips
- 'Why was X introduced' Q6 questions: tie DRAM/caches to the CPU-memory speed gap.
- Know master/slave and sync vs async handshaking in one line each.
Memory aids
- Address pins = how MANY; data pins = how WIDE.
- DRAM = capacitor + refresh; SRAM = latch, no refresh.
System architecture: buses & memory practice
2 questions
Caches, out-of-order execution & concurrency
CPUs got much faster than memory, so a small fast cache keeps copies of recently used data close to the core.
Multiple cache levels (L1 fastest/smallest, L2, L3 shared). A replacement policy decides what to evict (FIFO, LRU). With several cores each having caches, the cache-coherency problem arises (cores must see a consistent value). Hyperthreading hides memory latency by running another thread while one stalls.
Modern CPUs do out-of-order execution : instructions run as soon as their data is ready, respecting only true (sequential) dependencies. This breaks naive concurrent code (e.g. Peterson's mutual-exclusion algorithm relies on ordering it doesn't enforce), so memory barriers and acquire/release semantics are needed (release: finish all earlier ops first; acquire: do this op before later ones). The consensus problem : with only read/write to shared memory, equal parties cannot always agree — so hardware adds atomic primitives: test_and_set , compare_and_swap , and ARM's LDREX/STREX (load/store exclusive). These are essential for locks and lock-free data structures.
Cache misses and false sharing dominate real-world performance; memory-ordering bugs are among the hardest concurrency defects, which is why languages define memory models.
Caches, out-of-order execution & concurrency worked examples
1 questions
Common mistakes
- Thinking out-of-order execution preserves all orderings (it preserves only data dependencies).
- Assuming a single cache has no coherency issues — multicore does.
- Believing read/write alone suffices for mutual exclusion.
Exam tips
- Q6 'why were caches introduced?': processor speed outran external memory speed.
- Know one atomic primitive (test_and_set or compare_and_swap) and why it's needed.
Memory aids
- Cache = small fast copy near the core. LRU evicts the least-recently-used.
- Atomic primitives (CAS, LDREX/STREX) make locks possible.
Caches, out-of-order execution & concurrency practice
2 questions