Compiler Design and ConstructionUnit 812 min read
Code Optimization: Techniques, Flow Graphs & Intermediate Code
Unit 8 of Compiler Design and Construction covers code optimization techniques (copy propagation, constant folding, dead code elimination, strength reduction), their implementation in intermediate code (three-address code, quadruples), and how optimizations affect control flow graphs, basic blocks, and activation recor
TAKEAWAYS:
- Code optimization transforms intermediate code into efficient target code by eliminating redundant operations, simplifying expressions, and reducing runtime overhead.
- Basic blocks and control flow graphs are the foundation for identifying optimization opportunities in loops, conditionals, and arithmetic expressions.
- Three-address code (e.g., quadruples) is the standard intermediate representation where optimizations like constant folding (
3 + 5 → 8) and copy propagation (y = x; z = y → z = x) are applied. - Strength reduction replaces expensive operations (e.g.,
x * 2→x << 1) to speed up execution, while dead code elimination removes unreachable or unused computations. - Real-world systems (e.g., Ncell’s billing engine, Daraz’s order queue) use these techniques to handle millions of transactions efficiently.
- Exam questions focus on constructing flow graphs, applying optimizations step-by-step, and converting code to basic blocks—always trace variable changes visually.
1. What is Code Optimization?
Code optimization is the process of transforming intermediate code (e.g., three-address code) into an equivalent but more efficient target code without changing the program’s functionality. The goal is to:
- Reduce execution time (critical for apps like eSewa or Khalti during peak transactions).
- Minimize memory usage (important for Pathao’s driver-app updates).
- Improve code readability (helps maintainers at NTC or NEPSE).
Key Idea: Optimizations are applied after syntax/semantic analysis but before code generation, targeting intermediate representations like:
- Three-address code (e.g.,
t1 = a + b). - Quadruples/triples (e.g.,
(+, t1, a, b)). - Control flow graphs (CFGs).
2. Intermediate Code Representations (Visual)
Before optimization, code is converted to a structured form. Here’s how three-address code and quadruples represent the same arithmetic expression:
Example: x = 3; y = x; z = x + 5;
flowchart TD
A["Original Code"] --> B["Three-Address Code"]
B --> C["Quadruples"]
C --> D["Optimized Quadruples"]Three-Address Code:
t1 = 3
x = t1
y = x
t2 = x + 5
z = t2
Quadruples:
| Op | Arg1 | Arg2 | Result |
|---|---|---|---|
= |
3 | - | t1 |
= |
t1 | - | x |
= |
x | - | y |
+ |
x | 5 | t2 |
= |
t2 | - | z |
3. Optimization Techniques (With Real-World Ties)
A. Constant Folding
Definition: Evaluating constant expressions at compile time.
Example: t2 = x + 5 → If x = 3, this becomes t2 = 8 (no runtime addition needed).
Real-World Use:
- Ncell’s Billing System: Pre-calculates fixed charges (e.g.,
total = base_fee + tax) to speed up invoice generation. - Daraz Order Processing: Folds constants like
discount = 100 - (price * 0.1)intodiscount = price * 0.9for faster checkout.
Worked Example:
flowchart LR
A["Before: t2 = x + 5"] -->|"x=3"| B["After: t2 = 8"]Code Trace:
| Step | Code | Optimized Code |
|---|---|---|
| 1 | t1 = 3 |
t1 = 3 |
| 2 | x = t1 |
x = 3 |
| 3 | t2 = x + 5 |
t2 = 8 |
| 4 | z = t2 |
z = 8 |
B. Copy Propagation
Definition: Replacing variable copies with their original values to eliminate redundant loads/stores.
Example: y = x; z = y + 5 → z = x + 5.
Real-World Use:
- eSewa’s Transaction Logs: Avoids copying user IDs (
user = session.user; log(user)→log(session.user)) to reduce memory writes. - Bank Loan Calculators: Propagates
interest_rate = 0.05directly intoemi = (loan * rate) / terminstead of storingrateseparately.
Visual Trace:
flowchart TD
A["Before:\ny = x\nz = y + 5"] --> B["After:\nz = x + 5"]Code Example:
# Before optimization
x = 3
y = x
z = y + 5
# After copy propagation
x = 3
z = x + 5
C. Dead Code Elimination
Definition: Removing unreachable or unused computations. Example:
if False:
temp = a + b # Dead code (unreachable)
x = c + d # Live code
→ Compiler removes temp = a + b.
Real-World Use:
- Pathao’s Driver App: Eliminates unused GPS coordinates when the app is in idle mode.
- NEPSE Stock Alerts: Skips redundant checks (e.g.,
if (market_closed) { check_prices() }) when the market is closed.
Flow Graph Visualization:
graph TD
A["Start"] --> B["if False"]
B --> C["temp = a + b"] --> D["x = c + d"]D. Strength Reduction
Definition: Replacing expensive operations with cheaper ones. Examples:
x * 2→x << 1(bit shift is faster).x / 2→x >> 1(for integers).
Real-World Use:
- Google’s Search Index: Uses bit shifts to scale document IDs in ranking algorithms.
- WhatsApp Media Compression: Replaces division by 2 with right shifts to speed up JPEG resizing.
Code Comparison:
flowchart LR
A["Before:\nx = x * 2"] --> B["After:\nx = x << 1"]4. Control Flow Graphs (CFGs) and Basic Blocks
Optimizations rely on analyzing CFGs and basic blocks (sequences of instructions with no branches except at the end).
A. Basic Blocks
A basic block is a maximal sequence of instructions where:
- The first instruction has no predecessors.
- Every other instruction is reachable from the first.
- The last instruction is a branch or the end of the function.
Example: Convert this to basic blocks:
i = 1
sum = 0
while i <= x:
square = i * i
sum = sum + square
i = i + 1
Step-by-Step Construction:
flowchart TD
A["BB1:\ni = 1\nsum = 0"] --> B["BB2:\nwhile i <= x"]
B --> C["BB3:\nsquare = i * i\nsum = sum + square\ni = i + 1"]
C --> D["BB2"] --> E["End"]Basic Blocks:
i = 1; sum = 0(BB1)while i <= x(BB2)square = i * i; sum = sum + square; i = i + 1(BB3)
B. Control Flow Graph (CFG)
A CFG represents the flow of control between basic blocks.
Example CFG for the Loop:
graph TD
BB1["i=1; sum=0"] --> BB2["while i <= x"]
BB2 -->|"True"| BB3["square=i*i; sum+=square; i++"]
BB3 --> BB2
BB2 -->|"False"| BB4["End"]Optimization Opportunity:
- Loop-Invariant Code Motion: Move
sum = 0outside the loop if it’s initialized once. - Strength Reduction: Replace
i * iwithi*i(already optimal, but in C,i*imight compile to a faster sequence thanpow(i,2)).
5. Factors Affecting Code Optimization
| Factor | Description | Example |
|---|---|---|
| Target Architecture | Optimizations depend on CPU features (e.g., pipelining, SIMD). | x86 vs. ARM assembly. |
| Language Semantics | Some languages (e.g., Python) hide optimizations; others (C/C++) allow low-level tweaks. | for vs. while loops. |
| Intermediate Form | Three-address code is easier to optimize than abstract syntax trees (ASTs). | Quadruples vs. ASTs. |
| Aliasing | If two variables reference the same memory, optimizations like copy propagation may fail. | int *a, *b; a = b; |
| Precision Loss | Strength reduction (e.g., x / 2 → x >> 1) may lose fractional bits. |
Floating-point division. |
6. Activation Records and Optimization
Optimizations can affect activation records (stack frames) by:
- Reducing the number of temporaries (saves stack space).
- Eliminating redundant parameter passing (e.g.,
y = xin function calls).
Example: Before/After Optimization
flowchart LR
A["Before:\nfunc(x):\n y = x\n return y"] --> B["After:\nfunc(x):\n return x"]Activation Record Impact:
- Before: Stack frame includes
x,y, and return address. - After: Only
xand return address are needed.
7. In the Real World
eSewa’s Transaction Processing:
- Technique: Constant folding and copy propagation.
- How: Pre-computes fixed fees (e.g.,
total = amount + tax + service_fee) and avoids redundant variable copies in high-frequency transactions.
Ncell’s Billing Engine:
- Technique: Dead code elimination and strength reduction.
- How: Skips unused tariff checks after the user’s plan is selected and replaces
data_used * 2withdata_used << 1for faster calculations.
Daraz Order Queue:
- Technique: Loop-invariant code motion.
- How: Moves the
order_status = "processing"assignment outside the order-processing loop to avoid redundant writes.
NEPSE Stock Alerts:
- Technique: Control flow optimization.
- How: Simplifies nested
ifconditions (e.g.,if (market_open && price > threshold) { alert() }) into a single branch.
8. Worked Example: Full Optimization Pass
Original Code:
x = 3
y = x
z = x + 5
if x > 0:
t1 = x * 2
t2 = t1 + y
else:
t2 = 0
Step-by-Step Optimization:
Copy Propagation:
y = x→ Replaceywithxint2 = t1 + y.- New code:
x = 3 z = x + 5 if x > 0: t1 = x * 2 t2 = t1 + x else: t2 = 0
Constant Folding:
x = 3→ Foldz = x + 5→z = 8.- New code:
x = 3 z = 8 if x > 0: t1 = x * 2 t2 = t1 + x else: t2 = 0
Strength Reduction:
t1 = x * 2→t1 = x << 1.- Final optimized code:
x = 3 z = 8 if x > 0: t1 = x << 1 t2 = t1 + x else: t2 = 0
Quadruple Representation After Optimization:
| Op | Arg1 | Arg2 | Result |
|---|---|---|---|
= |
3 | - | x |
= |
8 | - | z |
> |
x | 0 | t3 |
<< |
x | 1 | t1 |
+ |
t1 | x | t2 |
= |
0 | - | t2 |
9. Exam Tip
Flow Graphs:
- Always label basic blocks clearly (BB1, BB2, etc.).
- Use arrows to show control flow (e.g.,
whileloops back to the condition). - Example Question: Given a loop, draw the CFG and identify loop-invariant code (code that doesn’t change per iteration).
Optimization Steps:
- Trace variable changes in a table (like the one above for
x,y,z). - Show intermediate steps: Start with the original code, then apply one optimization at a time (e.g., copy propagation → constant folding).
- Trace variable changes in a table (like the one above for
Real-World Applications:
- Link optimizations to systems you know (e.g., "How would Daraz optimize its order-processing loop?").
- Avoid vague answers: Instead of "optimization reduces runtime," say "strength reduction replaces
x * 2withx << 1, cutting CPU cycles by 30%."
Common Pitfalls:
- Aliasing: If
aandbpoint to the same memory,a = b; b = 5meansais also5. Copy propagation fails here. - Precision:
x / 2→x >> 1only works for integers. For floats, usex * 0.5.
- Aliasing: If
Past Exam Patterns:
- Construct CFGs for given code snippets (e.g., loops, conditionals).
- Apply 2–3 optimizations to a small code block (e.g.,
x = y; z = x + 1→z = y + 1). - Explain trade-offs: "Why isn’t dead code elimination always applied?" (Answer: May hide bugs if the code is theoretically reachable but never executed in tests.)
Final Visual Summary:
mindmap
root((Code Optimization))
Techniques
Constant Folding
Copy Propagation
Dead Code Elimination
Strength Reduction
Intermediate Forms
Three-Address Code
Quadruples
CFGs
Real-World
eSewa (Folding)
Ncell (Strength Reduction)
Daraz (Loop Optimization)
Exam Focus
CFG Construction
Step-by-Step Optimization
Aliasing PitfallsBased on the TU BSc CSIT syllabus for Compiler Design and Construction (CSC365), unit 8.
Discussion
Loading…