Step 06 · Progressive lowering — what a "real" pipeline looks like
Emitting llvm dialect directly skips MLIR's superpower:
progressive lowering. Here's the shape of a fuller pipeline you
would build once you have a custom dialect.
A hypothetical minilang dialect
module {
minilang.func @add(%a: !minilang.value, %b: !minilang.value) -> !minilang.value {
%r = minilang.add %a, %b : !minilang.value
minilang.return %r : !minilang.value
}
minilang.func @main() {
%a = minilang.const #minilang.num<40> : !minilang.value
%b = minilang.const #minilang.num<2> : !minilang.value
%c = minilang.call @add(%a, %b) : (!minilang.value, !minilang.value) -> !minilang.value
minilang.print %c : !minilang.value
minilang.return
}
}
The !minilang.value type encodes our boxed runtime value (Nil / Bool /
Number / Str / Fn).
Lowering passes
mlir-opt minilang.mlir \
--minilang-specialise-numeric # unbox numeric ops where types prove safe
--convert-minilang-to-func # minilang.func/call → func dialect
--convert-minilang-to-arith # numeric ops → arith
--convert-minilang-to-cf # control flow lowered
--convert-minilang-to-memref # boxed values → struct in memref
--convert-arith-to-llvm
--convert-cf-to-llvm
--convert-memref-to-llvm
--convert-func-to-llvm
--reconcile-unrealized-casts
| mlir-translate --mlir-to-llvmir
| lli
Each --convert-* is a RewritePattern set authored once and reused
across every MiniLang program. That's the value MLIR offers: a
ready-made pattern infrastructure plus a dozen battle-tested target
dialects.
Domain-specific optimisation
Before lowering to arith, the minilang dialect can run high-level
passes: type-specialise polymorphic numeric ops, sink GC barriers,
inline closures whose upvalues are constant. Those are nearly
impossible to do once everything is i64s and pointers.
What we lose by going straight to llvm
- No structured loops (
scf.for) so loop transformations like--affine-loop-unrolland--scf-parallel-loop-fusionare out. - No higher-level type info — every value is
i64. - No room for domain-specific peepholes.
For a numeric scripting language those losses are small; for ML or DSP workloads they're enormous.