Repository navigation
Releases: mimir/mimir
Release list
MimIR 0.4
Covers everything merged since v0.3: 570 commits touching 870 files (+26,194/−16,374).
MimIR now runs in your browser: the playground is mim compiled to WebAssembly, and every Mim snippet in the docs has a run button that opens it there.
On the language side, Mim gained variants with match, nominal types via nom, fully implemented singleton types, and real universe-level inference; option.Opt is now a proper nominal variant.
Under the hood, annexes moved into a lazily imported library World, every RWPhase coerces, n-ary extract/insert subsumes tensor.get/tensor.set, absl is gone, and a round of review passes made the compiler ~7% faster.
Highlights
- Playground.
mimir.github.io/playground runsmimas WebAssembly in a Worker, with tabs for the Mim dump, LLVM IR, the log, and a pan/zoom graph view that can pop out into its own window.
Programs are shared via a compressed#z=link fragment.
This required building MimIR with statically linked plugins (MIM_STATIC_PLUGINS). - Variants and
match.
| Nil | Cons: [Nat, List]is a positional sum type; unlike∪, nothing is sorted, deduplicated, or flattened.
T#Cons/T#1select a case,match l with | Nil => … | Cons (h, t) => …eliminates it, and missing or unreachable arms are diagnosed.rec List = | Nil | Cons: [Nat, List]; lam len (l: List): Nat = match l with | Nil => 0 | Cons (_, t) => core.nat.add (1, len t); - Nominal types.
nom Meter = I32;declares a type that is distinct from every othernomover the same representation, the one place where Mim is not structurally typed.
e inj Meterwraps and prefix#unwraps;nom Tree (T: *) = | Leaf | Node: [Tree T, T, Tree T]declares a type constructor, and named constructors/fields work directly on anom.
The representation is as visible as thenom: aprivnomexported viapub let I = I;is an abstract type.
noms are erased right before codegen. option.Optis a nominal variant| None | Some: T, soOpt []no longer collapses to[]andOpt (Opt T)no longer flattens.
Tis implicit:Opt#Some 23andOpt#Nonereplaceoption.some/option.none.- Singleton types are complete:
«e»is the type whose sole inhabitant ise,‹e›introduces and#eliminates it, and eliminators resolve singletons on construction.
wrap/unwrapwere renamed tonarrow/widen. - Universe levels.
Univis nowLevel, with real level inference:x + k = lis solved,maxconstraints are postponed and solved per declaration, and remaining level holes default to0. - N-ary extract and insert.
Arrays and packs fuse —«2; «3; T»»is«2, 3; T»— andt#(i, j)≡t#i#j.
tensor.get/tensor.setand thelower_get_setphase are gone; uset#(i, j)andt#(i, j) ← von atensor.buf. - Dependent
main.
argvis nowmem.Ptr0 «core.idx2nat argc; mem.Ptr0 I8», so its length is tied toargcand themem.assumecasts go away.
Newcore.idx2nat(a never-truncatingIdx s → Nat) andmem.cstr, which views a C string as a length-indexed array.
The effect token now lives incoreascore.M(re-exported asmem.M), socoreno longer depends onmem.
Language & frontend
mutual … endgroups mutually recursiverec/nom/lam/con/fundeclarations (as in Lean) and replacesrec … and; modifiers onmutualapply to every member.- Modules are written
mod I … endinstead ofmod I { … }. - Arrays without ⊤: the ⊤ wildcard in array types and
idx_unsafeare gone; C interop goes throughmem.decay/mem.assume. - Let-binding type annotations are actually checked now, and so are all members of an identifier group:
let (a b: Nat) = (tt, 2)used to be accepted. - The AST got dedicated nodes for non-pattern binders (
«i: n; …»,return,matcharms) instead of abusingIdPtrn, andAxmDeclis represented faithfully without parser-side expansion. ruledeclarations track visibility modifiers.--pe-fuelturns potentially endless partial evaluation into an error instead of a stack overflow.- The compiler throws proper errors in many places that used to crash or assert.
Core IR & compiler infrastructure
- Lazy annex library.
optimize()moves the elaboratedWorldintoDriver::library()and imports only the externals back;World::anneximports an annex on first use.
Phases walk only the program from its externals — no more bootstrapping or annex walks.
Axioms and non-Lammutable annexes are opaque;anx lams are rewritten like ordinary code. - Coercion in every
RWPhase.
CoercePhaseis gone; each rewrite coerces arguments, tuple ops, and bodies to their rewritten expected types, at zero cost when nothing was reshaped.
Checker→CheckwithCheck::coerce; annotated tuples are always type-checked. - Towards an immutable IR: mutables are set only once;
InplaceRWPhaseis gone again. - New phases: contification, aggressive dead-code elimination (removes unused vars even in cycles), and SCCP moved in-tree;
BetaRedalso inlines Lams that merely shuffle vars or forward constants;BranchNormalize→DispatchNormalize. - η-conversion: singleton domains are η-reducible via the new
World::inhabitant, and η-chains are reduced at once (with cycle detection). - SEO: captured slots in direct-style calls,
leasupport, liveness tracking, and pinning only the slots a call actually uses;heap2stacknow sees through direct-style calls andmem.free. - Scalarize is type-directed and handles variants and value telescopes.
- Proxies no longer contribute to free variables, which killed the costly
SEO::find_unknowns. - Performance:
Vars/Mutson Patricia trees, absl replaced by ankerl::unordered_dense, tuned hash functions, a fixed zonker memo, and fewer locals merges on hash-cons hits (hlo −5.9%, vgg16 −7.1% instructions). - Round-trip: both
--output-mimand--output-astdumps re-parse to a byte-identical fixpoint over the whole lit suite, checked in CI (test-roundtrip).
--dump-local→--mim-local; let patterns drop their types unless--mim-typed-let. - The dangerous single-argument
Def::proj(i)is gone;cat_tuple/cat_sigma→Tuple::cat/Sigma::cat. - Three review passes fixed a long list of folding bugs in
core,math, andll(e.g.bit2truth table,Idxarithmetic on non-power-of-two sizes, IEEE min/max signed zeros).
Plugins
tensor:generate,reduce, numerically stablesoftmax,conv_grouped(scheduled per group), new reshape/broadcast/transpose normalizers,reassocsees through transposes, and the schedule travels as oneSchedrecord.btensor: destination-passing style (ops write into a caller-allocateddst),buffer.free, and afree_localsphase that frees local buffers after their last use — several tests now run leak-free under LeakSanitizer.
map_reduce_postlowering is expressed through a Mim higher-order function.affine: index folding (affine.lower_index) proves divisions indelinearize(linearize(…))redundant via range analysis, and peels carry-free low parts off the rest.
Together withll.novechints and conv output-channel blocking, the 25 supported KernelBench level-3 models got 1.75× faster (geomean); ShuffleNet went from 17.3 s to 2.3 s.math:inf,neg_inf,nanconstants.core:trait.sizefolds arrays with symbolic element sizes.ll: fixed SIMD extract/insert and vector returns of non-constant lanes.ll_nvptx→nvptx.sexprmoved out of tree to ashiven/sexpr; the old CRTPEmitteris gone.- Generated C++/Python bindings escape target-language keywords (annexes may be called
and,or, …) and export constructor/field indices.
Tooling, build & CI
- Builds to WebAssembly via Emscripten with static plugins; the playground is deployed from
master. - Plugin search paths reworked; Windows (Visual Studio) DLL output path fixed.
- Python binding generation simplified.
Docs
- Docs pages run their Mim snippets in the playground; new
playground.md. langref.mdcovers variants, nominal newtypes, singletons,mutual, and universe levels;dev.mddocuments the newShapehandling.
Contributors
@leissa, @HarithF, @hsqStephenZhang, @ashiven, @fodinabor, @amaebel, and @asindarov.
New Contributors
- @asindarov made their first contribution in #612
Full Changelog: v0.3...v0.4
MimIR 0.3
Covers everything merged since v0.2: 588 commits touching 821 files (+28,335/−19,142).
This is the largest surface-syntax overhaul since v0.1: the % annex sigil is gone, Mim gained Lua/Rust-style mod/import/use and a full ladder of infix operators, and most of MimIR's utility layer moved out into fe.
On the optimizer side, InplaceRWPhase, static argument optimization, and a %tensor.reassoc matrix-chain phase are new; on the backend side, NVPTX codegen and C runtimes for the LLVM backends landed.
Highlights
- Modules.
mod I { ... }groups declarations,importbinds a file as a module,use path as Ialiases one anduse path as *splices its public members into scope.
Paths are identifier-only (a.b.c);.walks modules and never reads a field out of a value — that stays#.
Declarations carry visibility (priv/pub,privby default) that is purely lexical and independent ofextern/anx. - No more
%.
Annexes are ordinary members of their enclosing module and resolve through the same path lookup as everything else, so%tensor.map_reduceis nowtensor.map_reduce.
Ananxmodifier marks a declaration as an annex;axmis implicitlyanx. - Infix operators.
== != < <= > >= << >> + - * / %and the insert arrow←/<-, with a precedence ladder shared by parser and dumper.
Mim gives them no meaning of its own:a op bis sugar for`op (a, b), solet `+ = core.nat.addis what makes2 + 3an addition.
core.opsandmath.opsship ready-made bindings.
Two consequences worth knowing:f *is a multiplication (writef (*)for the application), and<-is a single token, so a comparison against a negative literal needsx < (-1). - Insert is an operator.
t#i ← vreplaces the oldinsertaxiom and extends recursively through a#-path:t#i#j#k ← vdenotes the outer aggregatet. - NVPTX. New
nvptx(PTX intrinsics) andll_nvptx(LLVM backend targeting NVIDIA GPUs) plugins.
ll_nvptxsplits the lowered program into a host and a device world, emits<world>.lland<world>_dev.ll, and on Linux compiles and embeds the device binary into the host module by default. - C runtimes for the backends.
llshipsmim_rt.candll_nvptxshipsmim_cuda_rt.c; runtime modules get their own search path (<root>/lib/mim/rt), separate from plugins and imports. - Utility layer moved to
fe.hash,sets,span,util,vector,log,profile,sys,dl, andlink_cut_treeall leftmim/util; onlydbg.h,gid.h, andtypes.hremain.
The CLI switched from lyra tofe::cli, diagnostics tofe::Diag, andVector<bool>tofe::Bitset.
Language & frontend
import/pluginare first-class declarations and acceptas I/as *.recstarts a recursive declaration group extended byand; recursive functions uselam/con/fun, whose declared name is in scope inside their own body.- Patterns and telescopes were pinned down:
()is a pattern and[]a telescope, position decides which, and[]-style patterns for lambdas were removed. axm nat.(add, sub): ...declares several axioms at once as members of a module of that name; each tag may carry extra names via= I.- Quote and angle-bracket alternatives nobody was using were dropped from the lexer.
- Implicits got several rounds of work, and the tensor/matrix plugins were rewritten to lean on type inference instead of spelling sizes out.
option.none's type parameter is now explicit;vec.first/vec.lastreturn anoption.- Diagnostics were reworked end to end:
--no-snippet,--loc-style={full,rowcol,row,msvc},std::format-based parser messages, and delimiter recovery through fe's anchoring.
Plugins
matrix→btensor, realigned withtensorso every op is thebuffer.Bufcounterpart of itstensor.*sibling, taking the same groups in the same order and threadingmem.Mon top.tensor: newsplat,gather,scatter,map_reduce_post(epilogue fusion),if_static,fastest_axis, andshape;product_2d/transpose_2dgeneralized toproduct_/transpose_; frontend tiling code, symbolic contraction indot_product, and a first-class loop-nest chooser operand carrying the schedule.%tensor.reassoc: a new phase that reassociates chains ofproduct_with the classic matrix-chain-order DP, but costed in vector lanes rather than multiplications (-X tensor:reassoc-vec=<n>).
Extents need not be literal — a cost is a polynomial in the symbolic extents ordered coefficient-wise, which is only a partial order, so up to 4 matrices (-X tensor:reassoc-max=<n>) the surviving bracketings are emitted side by side behind a run-time cost comparison and only the winner's thunk runs.
Common sub-products are hash-consed across alternatives, keeping code growth well belowCatalan(n − 1).gpu: GPU lowering ofmap_reduce, mergedgpu.{auto_}initoperations in the backend, HLO work.core: matches on known injections now reduce.ll:vec.zipinput count and lane count are distinguished; silent assumptions in the LLVM backend were replaced by proper errors (fixes #9).- Plugin arguments via
-X <plugin>:<arg>, repeatable, keyed by plugin name and read by a phase throughPhase::args; only the first:separates the two halves.
Plugins declare their arguments and theirPluginEnvenvironment variables next to the code that reads them, and the CLI help tables are generated from those declarations.
Core IR & compiler infrastructure
InplaceRWPhase, sharing a newRWBasewithRWPhase, for rewrites that don't need a fresh world.- Static argument optimization replaces
TailRecElim: it seeds on the rightmostPidomain, freezes the site class, and pools the rest, with a per-site wrapper fallback. Unloadphase nukes a plugin together with its annexes, supersedingprefix_cleanup.sizeof(Def)shrunk by movingDef::dbg_into a side table;rebuild,stub, andrewritenow work withDbgKeydirectly.Def::rebuildandDef::refineremoved.mem.add_memrebuilds mem-placeholder tuples at every occurrence.- The world is zonked before optimizing.
- Performance work on
Checker, on the set/optimizemachinery, and a general round of profiling fixes. - Closure conversion fixes: higher-order returns (#117) and
clos-mem. mem.mslot/tensor lowering use mutable continuations to get fresh, distinct memory and avoid buffer aliasing.
Tooling, build & CI
- Google Test is gone; unit tests moved from
gtest/totest/and run on doctest (pulled in viafe).googletestandlyrasubmodules removed. MIM_DEBUG_OPTIMIZE(on by default) compilesDebugbuilds with-Oginstead of-O0; assertions andMIM_ENABLE_CHECKSare unaffected.- Three separate search paths for plugins (
-P), imports (-I), and runtimes, plus prefix roots via--prefix-path/-R;mim -lprints all three fully resolved.
Aplugin <name>;directive takes its.mimfrom the directorylibmim_<name>was loaded from, so the two halves can never be paired across directories. - Python bindings are generated automatically.
- Stack size raised to 16 MiB at link time; lit timeout raised from 30 s to 120 s; CMake and the CI workflows simplified, and CI wall time cut.
Docs
- Mim and EBNF syntax highlighting in the generated docs, with fenced-block filtering in
docs/fences.py. langref.mdsubstantially rewritten: new Files and Imports, Paths and Modules, Infix Operators, and a Precedence table covering all 16 levels including thePi/Bot/Errpseudo levels.phases.mdextended withRWBase,InplaceRWPhase, sparse fixed-point iteration, and an SSA without Dominance discussion.- New
why-cpp.mdon MimIR's implementation-language choice. - Doxygen switched to MathJax.
Contributors
Roland Leißa, Friedrich Tschirpke, Joachim Meyer, Joel Schröder, and hsqStephenZhang.
Full Changelog: v0.2...v0.3
MimIR 0.2
Changelog
All notable changes to MimIR are documented here.
v0.2
Covers everything merged since v0.1: 1104 commits touching 686 files (+19,800/−8,838). Highlights are Python bindings, a much larger tensor plugin, a new ll backend plugin, GPU codegen support, and a reworked phase/analysis infrastructure (sparse fixpoint iteration, SEO, scalarize).
Highlights
- Python bindings (
py/): brand new nanobind-based bindings (ported from an initial pybind11 prototype) exposingWorld,Def,Lam,Driver,Flags,Log, the ASTParser,Sym, andTuple. A higher-levelmimpackage addsjit/callablehelpers for calling compiled Mim functions directly from Python, plus bindings for theregexplugin. Comes with its own pytest suite wired intotest-allastest-py, gated by theMIM_BUILD_PYTHONCMake option (on by default). - Tensor plugin grew substantially:
dot_general,map_reduce(and an affine_affvariant),broadcast_in_dim,get/set,reshape,repeat(tiling),slice/flip,pad/concat,shape, 2Dconv(+ shape inference), 2D max/sum pooling, and batched matrix multiplication (bmm). Lowering was split into staged phases, backed by a new bufferization pass for constants, padding, and concat. - New
bufferplugin providing the abstract-matrix-memory/bufferization layer used by tensor lowering. - New
llplugin: the textual/LLVM-ish backend that used to live insidecore'sbe/ll.cppis now its own plugin, following the removal of the hardcoded backend hook from the driver's plugin architecture. (Known limitation carried over: runtime-sized packs still crash this backend's emitter.) directplugin renamed and rewritten ascps, with a new CPS-conversion implementation.- GPU plugin: block/async tags, a unified GPU-launch mechanism, machine-state/sync-usage checks, double-sync removal, and host/device world splitting for backend codegen.
- S-expression export: new
--output-sexpr/--output-sexpr-slotted(with--sexpr-include-types) CLI outputs, formatted for interop with the externalslotted-egraphsequality-saturation library.
Core IR & compiler infrastructure
Var::mutrenamed toVar::binder(binder mutable now lives inDef's union slot).- New SEO (symbolic expression optimization) (sea-of-nodes-style) phase for SSA construction over
%memslots + GVN + SCCP in one combined phase. - Scalarize phase reworked to flatten nested tuples type-directed by each
imm Cn Pi, rather than perLam. %mem.mslotis now continuation-based instead of direct.- Sparse fixpoint iteration for
Analysis: split into read (lattice) vs. write (lattice_mut) access, withlattice_forcestreamlined; writer-dirt tracking plus SEO-driven caller taints and a certifying full round. %optplugin reorganized;World::sym2annexremoved in favor of a more direct symbol/annex mapping.World::appiteration reworked (world-app-iter).- Internal code migrated to
std::views(range-basedVector::append_range/insert_range) as part of the move to C++23. pack#indexnow always reduces (previously left un-normalized in some cases).
Bug fixes
- Fixed argument evaluation order in
Rewriter::rewrite_imm_App(rewrite_order). nat.subnow saturates at 0 in thellbackend instead of underflowing.- Fixed correctness issues in the new SEO phase.
- Fixed a lambda-back-not-closed issue caught by
%refly.var. - Fixed a tuple-pattern parsing issue.
- Windows CI re-enabled after breakage from a GitHub Windows runner image update (also touched the regex automaton and CLI entry point); Windows venv/pip warnings fixed via
ensurepip --upgrade.
Tooling, build & CI
- Baseline language standard raised to C++23.
FileCheckis now built/vendored via theMIM_FILECHECKCMake option instead of assumed to be onPATH, later pinned to a specific release; on Windows, its MinGW runtime DLLs are staged alongside it and its PE header is restamped so msysbashcan execute it.- All test suites (lit, gtest, and now the Python suite) consolidated under one
test-alltarget. - Python bindings switched from
pybind11tonanobind.
Docs
- Developer docs (
docs/phases.mdand related) updated to describe the currentPhase/RWPhase/PhaseManpipeline, reflecting the phase-infrastructure andVar/binder work above.
Pull Requests Merged
- bump to C++23 by @leissa in #403
- MIM_FILECHECK as cmake variable by @leissa in #404
- Feature/eqsat by @ashiven in #395
- Version bump by @leissa in #405
- Bugfix/add_mim_plugin by @ashiven in #406
- Refactor/printf by @leissa in #408
- introduce test-all target by @leissa in #410
- Bugfix/parse tuple ptrn by @leissa in #412
- port to from pybind11 to nanobind by @leissa in #413
- Python bridge w/ nanobind by @leissa in #300
- more python bindings + docs by @leissa in #416
- [Tensor] Use Lowering Phase Instead of Normalizers by @fodinabor in #401
- Move broadcast_in_dim into lowering phase as well. by @HarithF in #418
- Refactor/search path2 by @leissa in #421
- Always reduce pack#index by @amaebel in #426
- Add axioms to inspect closedness of expressions to refly by @amaebel in #428
- Rewrite Pack var with Array var when building type by @amaebel in #425
- added proper setter for Lam by @leissa in #430
- refactor ensurepip by @leissa in #431
- Add %tuple.head and %tuple.tail by @amaebel in #427
- Refactor/folding by @leissa in #424
- Refactor/sexpr by @ashiven in #415
- fix venv warning during build by @leissa in #432
- Feature/tensor by @leissa in #312
- Add assertion to check if all ops of a Def are in the current world. by @fodinabor in #433
- Refactor/sexpr by @ashiven in #434
- more idiomatic use of std::views by @leissa in #435
- [core] Implement identity normalization for double conversion in normalize_conv by @fodinabor in #437
- Add affine maps to model affine expressions and showcase in map_reduce_aff by @fodinabor in #438
- Bugfix/rewrite order by @leissa in #440
- Refactor/sexpr by @ashiven in #439
- Revert "Add axioms to inspect closedness of expressions to refly" by @amaebel in #444
- Refactor/opt by @leissa in #443
- Refactor/sym2annex by @leissa in #448
- Refactor/imm by @leissa in #455
- Refactor/sexpr by @ashiven in #457
- Add profiling capabilities with summary, tree, and trace outputs by @fodinabor in #454
- fix incorrect indentations in lambda bodies by @ashiven in #462
- Respect scalarize threshold for nested arrays by @fodinabor in #458
- World::app rework + iter.mim test by @leissa in #464
- feat(tensor): batch mm by @hsqStephenZhang in #459
- GPU Axioms v2 by @ftschirpke in #460
- Refactor/docs by @leissa in #467
- feat: core.nat.div by @hsqStephenZhang in #465
- GPU Phases by @ftschirpke in #466
- Refactor/cps by @leissa in #468
- bug fix + test case: nat.sub saturates at 0 by @leissa in #469
- feat(tensor): split tensor.get/set lowering into a separate lower_get_set by @hsqStephenZhang in #461
- Refactor/eta exp by @leissa in #470
- Feature/ssa4 by @leissa in #471
- Windows not working anymore after GitHub updated the image by @leissa in #476
- ci: pin FileCheck release; fix stale comment by @leissa in #478
- only update during FP what is dirty by @leissa in #479
- Phase additional tests and features by @leissa in #480
- Refactor/lattice api by @leissa in #482
- Add support for encoding decimal float literals with correct bit width. by @fodinabor in #484
- Refactor/lattice api2 by @leissa in #483
- fix(tensor): calculate output tensor's shape for %tensor.conv by @hsqStephenZhang in #472
- fix: use c++17 for hlo tests by @hsqStephenZhang in #490
- wip: fixing probls in SEO by @leissa in #489
- [Py] Add bindings for the profiler CLI args. by @fodinabor i...
MimIR 0.1
MimIR is a pure, graph-based, higher-order intermediate representation rooted in the Calculus of Constructions.
About This Release
Version 0.1 marks the first official release of MimIR, a compiler IR evolved from the Thorin project. This release represents the stabilization of the core IR, type system, plugin architecture, and tooling after years of development.
Key Features
- Higher-order IR: First-class functions, type operators, and parametric polymorphism with dependent types
- Extensible Plugin System: Domain-specific axioms, types, normalizers, and code generation backends
- Scopeless SSA: A novel IR design based on free-variable nesting instead of dominance, well-suited for higher-order programs
- Sea-of-Nodes Style: On-the-fly normalization, type checking, and partial evaluation
- Multi-Platform: Builds and tests on Linux, macOS, and Windows
Ideal For
- DSL compilers
- Tensor compilers
- Automatic differentiation systems
- Regex engines and other systems requiring high-performance code from high-level abstractions
Documentation
Full documentation is available at mimir.github.io, including the Command-Line Reference, Language Reference, Plugin Guide, and Developer Guide.
