Step 05 · mem2reg + the O2 pipeline
Our lowering is deliberately naïve: every named local becomes an
alloca i64 in the entry block, every read is a load, every write
a store. We do not try to construct SSA in the front end.
That's fine, because O2 includes mem2reg (a.k.a. PromoteMemToReg),
which:
- Finds
allocas whose only uses are direct loads/stores. - Replaces them with proper SSA values, inserting Φ-nodes where control flow joins.
After mem2reg, downstream passes can do real work:
instcombine— peephole rewrites.gvn— global value numbering deduplicates.simplifycfg— collapses trivial branches.licm— hoists loop invariants.loop-unroll,loop-vectorize,slp-vectorize— where profitable.globalopt— turns module-private mutable globals into constants when only initialised once.
Observable
let x = 7;
print x;
Pre--O:
@x = global i64 0
…
store i64 7, ptr @x
%0 = load i64, ptr @x
call i32 @printf(ptr @.fmt, i64 %0)
Post--O (test test_mem2reg_after_opt_eliminates_allocas asserts this):
call i32 @printf(ptr @.fmt, i64 7)
Lesson
Front-ends do not need a smart code generator. Emit straightforward load/store-heavy IR; let mem2reg + the rest of O2 turn it into great machine code. This is the LLVM superpower.