Code Optimization
Two Principles
Safe-Only Transformations
Every optimization must be provably meaning-preserving under the language’s semantics — floating-point rearrangements, exception order, and integer overflow are the classic places where “obvious” rewrites break programs.
Measure, Then Transform
Profile on real inputs before writing a pass. Unoptimized bytecode is already fast enough for most DSL workloads; an optimizer with no measured target becomes the second system syndrome from Design Considerations.
Local Passes
Constant Folding
Compute constants at compile time: t1 = 2 * 3 becomes t1 = 6. The first pass everyone writes; also the first to be haunted by division by zero and overflow — handle them before folding.
Algebraic Simplification
x + 0, x * 1, x - x are the low-hanging fruit after folding. Each rule needs its “is this true in my semantics?” check (strings? NaN? unsigned?) — write the rules with the specification open.
Dataflow & Loops
Beyond single statements, information must flow: liveness (which values are still needed), reaching definitions (which assignment produced this value), and dominators underpin dead-code elimination, copy propagation, and loop hoisting.
Liveness in One Idea
A value is live at a point if its result is still needed later. Values that are never live lead to dead code, which is removed; liveness also feeds the register allocator from Code Generation.
Loop Basics
Hoist invariant computations out of loops and keep induction-variable strength in check. For DSLs, the honest move is usually: let LLVM (Phase 3) do these — the passes are well-understood but finicky.
The DSL Truth
Semantic Speed Wins
For a domain-specific language, the largest speedups are semantic, not syntactic: choose empowered operations (a fused matrix multiply instead of a loop), strong typing (Types & Values) so fallback paths vanish, and a good back-end target. Optimizing an awkward semantic is polishing a design error.
Example: Folding Before You Run
The whole philosophy, in a commented pass: constant expressions die at compile time.
fold.py (commented)
# fold.py — replace constant IR statements with their results
def fold(stmts):
out = []
for s in stmts:
if is_const(s.lhs) and is_const(s.rhs) and is_safe(s.op):
value = s.op.eval(s.lhs.value, s.rhs.value) # compute NOW
out.append(Set(s.dst, Const(value))) # emit the answer
else:
out.append(s) # keep it as-is
return out
How to Read It
One rule, one safety check, one rewrite — that is a whole optimizer’s unit of currency. Add rules until profiling stops asking; then stop.
Next Steps
Continue the Roadmap
Phase 2 is complete: your language can be designed, parsed, analyzed, compiled, and run. Phase 3 hands the machinery to production compilers (LLVM IR, Triton, Mojo), and Phase 4’s AI languages show the same ideas at work in the wild.