Step 03 · Dialects worth knowing
A dialect is a namespace of operations + types + attributes. Some upstream ones you'll meet constantly:
| Dialect | Purpose |
|---|---|
builtin | module, func.func (in older MLIR builtin.module) |
func | func.func, func.call, func.return |
arith | Pure integer/float math: arith.addi, arith.cmpi, ... |
cf | Unstructured control flow: cf.br, cf.cond_br |
scf | Structured control flow: scf.for, scf.if, scf.while |
memref | Memory references with shape/layout |
tensor | Immutable value tensors |
linalg | High-level array/linear-algebra ops |
vector | Explicit SIMD vectors |
affine | Polyhedral loops, ideal for analyses |
gpu, nvvm, rocdl, spirv | Device backends |
llvm | Mirror of LLVM IR; the terminal target |
The point: write your compiler as a sequence
mydialect → linalg → memref → scf → cf → llvm, each step removing
abstraction you no longer need.
Defining a dialect (in C++)
You declare ops in TableGen (.td), which mlir-tblgen expands into
C++ classes. A typical workflow:
MinilangOps.td— declare ops, types, attributes.- Register the dialect with
MLIRContext::loadDialect. - Implement
verify,canonicalize,foldper op. - Write a
MinilangToLLVMconversion pass (mlir::ConversionTargetRewritePatterns).
cp-18's capstone leaves the dialect implementation as a guided exercise; the heavy lifting is mostly mechanical TableGen + pattern boilerplate.