Reading — step 1 of 3
Compiler Optimizations
Optimization — Constant Folding & Dead Code
A compiler's job is not just translating code — it can also make code faster. Optimization passes transform the code (at the AST or bytecode level) to reduce instructions, eliminate waste, and improve performance.
Constant Folding
Constant folding evaluates constant expressions at compile time:
var x = 1 + 2 * 3; // before: OP_CONST 1, OP_CONST 2, OP_CONST 3, OP_MUL, OP_ADD
// after: OP_CONST 7 (computed at compile time)
If both operands of an arithmetic operation are known at compile time, we compute the result immediately and emit a single constant. This eliminates runtime work entirely.
The compiler checks: is this a Binary node where both children are Literal nodes? If so, compute the result and replace the Binary with a Literal.
Constant Propagation
If a variable is assigned a constant and never reassigned, replace all uses with the constant:
var x = 10;
var y = x + 5; // x is known to be 10 → y = 10 + 5 → y = 15
This enables further constant folding. The two optimizations work synergistically.
Dead Code Elimination
Code that can never execute is dead code. Remove it:
fun f() {
return 42;
print "unreachable"; // dead code — remove
}
if (false) {
print "never"; // dead code — remove
}
Dead code elimination makes the bytecode smaller and avoids confusing the programmer.
Strength Reduction
Replace expensive operations with cheaper ones:
x * 2 → x + x // multiplication → addition
x * 1 → x // identity
x + 0 → x // identity
x * 0 → 0 // zero
x / 1 → x // identity
These algebraic identities can save cycles, especially in tight loops.
Peephole Optimization
Examine small sequences of bytecode instructions and replace with more efficient sequences:
OP_PUSH 1, OP_ADD → OP_INCREMENT // if we add such an instruction
OP_NOT, OP_NOT → (nothing) // double negation cancels
OP_PUSH, OP_POP → (nothing) // push then immediately discard
How V8 Optimizes JavaScript
V8's TurboFan JIT compiler applies dozens of optimization passes: inlining, escape analysis, loop-invariant code motion, register allocation, instruction selection, and more. These are far beyond what we implement, but the fundamentals (constant folding, dead code elimination) are the same.
Your Task
Implement constant folding, dead code elimination after return statements, and strength reduction for arithmetic operations. Compare bytecode size before and after optimization.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…