Honest state of every frontend, backend, and tool in the MinZ compiler system. Based on
go test ./...run against v0.19.5+ (commit 4d34355).
| Package | Status | Notes |
|---|---|---|
pkg/nanz |
✅ GREEN | Nanz parser — all tests pass |
pkg/hir |
✅ GREEN | HIR + lowering — all pass |
pkg/plm |
✅ GREEN | PL/M-80 frontend — all pass |
pkg/mir2 |
✅ GREEN | 160 tests — allocator, codegen, optimizations, assertions |
pkg/mir2c |
✅ GREEN | MIR2→C99 backend — E2E roundtrip via gcc |
pkg/mir2qbe |
✅ GREEN | MIR2→QBE backend — E2E roundtrip via qbe |
pkg/qbe2mir2 |
✅ GREEN | QBE→MIR2 parser — round-trip tests |
pkg/z80asm + regression |
✅ GREEN | MZA assembler — all pass |
pkg/emulator |
✅ GREEN | MZE Z80 emulator — 1335/1335 FUSE |
pkg/z80testing |
✅ GREEN | E2E harness — all pass |
pkg/z80timing |
✅ GREEN | T-state timing tests — all pass |
pkg/disasm + analysis |
✅ GREEN | MZD disassembler — all pass |
pkg/ir, interpreter, optimizer, semantic |
✅ GREEN | Old MinZ path — all pass |
pkg/parser, parser/participle |
✅ GREEN | Old MinZ participle parser — all pass |
pkg/spectrum + formats |
✅ GREEN | MZX ZX Spectrum emulator — all pass |
pkg/mirvm, pkg/tas, pkg/trace |
✅ GREEN | MZV runner and utilities — all pass |
tests/iterator_corpus |
✅ GREEN | 11/11 iterator chain E2E — all pass |
tests/ |
🔴 2 FAIL | TestFeature_UFCS_MethodCall, TestFeature_InlineAsm |
pkg/codegen |
🔴 VET FAIL | go vet blocks test run — non-constant format strings |
cmd/repl |
🔴 BUILD FAIL | int vs uint16 type mismatch from z80asm API change |
Overall: 25 packages green, 3 red.
The main frontend. All tests pass.
- Parser: native Go (participle-based), zero external deps
- Pipeline: Nanz → HIR → MIR2 → Z80 (+ MIR2→C / MIR2→QBE)
- Features: structs, methods, UFCS, interfaces, lambdas, closures, iterators,
range(lo..hi), operator overloading, ranged typesu8<lo..hi>, LUT synthesis, dual-VM assertions,@extern,@smc, register annotations (@z80_hletc.) - Known language gaps (tracked in
docs/Open_Bugs_RCA.md):- 🔴 BUG-003:
ptr[i]inside while loop → invalid Z80 (EX DE,HL/ADD F,DE) - 🔴 BUG-006: Zero-size struct globals not emitted → undefined symbol at link time
- 🟡 BUG-004: Non-zero-lo LUT + contract opt → class mismatch (unit tests bypass it)
- 🟡 BUG-005:
applySubSwapNegmissing u16 guard (one-line fix, workaround exists)
- 🔴 BUG-003:
26/26 Intel 80 Tools source files parse. 1338 functions / 943 globals / 11661 statements lowered to HIR.
- Pipeline: PL/M-80 → HIR → MIR2 → Z80 (E2E wired in
cmd/minzc/main.go) - Preprocessor: LITERALLY macro chains,
$INCLUDEwith CP/M device designators, binary literals, escaped''quotes - Known gaps:
STRUCTUREtype (nested records)PUBLIC/EXTERNAL(multi-module linking)INPUT(port)/OUTPUT(port)hardware I/O intrinsics- 16-bit comparisons (parsed, incomplete codegen)
The original frontend. Not being developed — only maintained.
- Compiles the majority of existing
.minzexamples - 2 failing integration tests:
TestFeature_UFCS_MethodCall: typed pointer receiverfun Foo_method(self: *Foo)not in old grammarTestFeature_InlineAsm:@asm { ... }block not parsed by old grammar
- These are not regressions — features that only exist in the Nanz frontend.
Tests should be moved to a Nanz test suite or marked
t.Skip. - The old pipeline (
pkg/ir→pkg/codegen) is still available but not developed.
The active backend. 160 tests, all green.
Full optimisation pipeline (in order):
- LUTGen — pure ranged functions → compile-time lookup tables
- EliminateDeadBlocks → ReorderBlocks
- Constant pipeline to fixpoint: PropagateConstants → FoldConstants → SimplifyIdentities → ConstantCallElim
- DeadStoreElim
- BranchEquiv — VM-based proof of redundant conditional branches
- CondRetSink → hoistReorderSubBeforeCmp → CmpSubCarry
- MIR2 Verify — structural correctness check
- Phase 5b: OptimizeContracts — interprocedural calling convention DP
- PBQP allocator — cost-weighted register assignment
- Post-allocation copy coalescing
- Dual-VM assertions — MIR2 VM before codegen, Z80 emulator after
- Z80Codegen —
genMul16strength reduction, IX/IY addressing, peephole
Open bugs:
- 🔴 BUG-003:
ptr[i]in while loop —PtrAddcycle produces invalid Z80 - 🔴 BUG-006: Zero-size struct globals silently omitted →
undefined symbol - 🟡 BUG-001: GCD parallel-copy bloat (PBQP affinity edges not yet added)
- 🟡 BUG-002:
forEachconstant rematerialization in parallel-copy resolver
Translates MIR2 IR to C99. Compiled and executed via gcc.
Why it exists: semantic cross-checking. Any function that produces a wrong answer in Z80 binary but the right answer in the C translation has a codegen bug. Also useful for portability — the same MIR2 module runs on any platform with a C compiler.
E2E tests passing: abs_diff, fib, clamp, max3 (PL/M + Nanz sources).
Translates MIR2 IR to QBE intermediate representation. QBE compiles to native amd64 / arm64 / riscv64.
Why it exists:
- SSA validation: QBE is strict about SSA form. If MIR2→QBE produces valid QBE, the MIR2 module is structurally sound.
- Modern targets: The same frontend (Nanz or PL/M) can compile to x86-64 with zero extra work. Useful for testing on the host machine.
- E2E semantic testing: Run the compiled binary and check the answer — faster than the Z80 emulator.
E2E tests passing: abs_diff, clamp, gcd, UFCS dispatch, zero-cost interfaces (PLM + Nanz).
Parses QBE IR text into MIR2 data structures. Enables "QBE as a frontend" experiments (e.g. a Clang-targeting-QBE pipeline that feeds into the Z80 backend).
Contains the old backends for the .minz frontend path via pkg/ir.
go build ./pkg/codegen/ works fine. go test ./pkg/codegen/ fails on vet.
Vet errors: ~20 non-constant format string in call to emit/Fprintf in:
m6502_gen.go(6502/NES)gb.go(Game Boy)i8080.go(Intel 8080)m68k.go(Motorola 68000)
Status per backend:
| File | Target | State |
|---|---|---|
z80.go |
Z80 (old) | Partial; superseded by MIR2 path |
c.go / c_backend.go |
C99 | Partial; superseded by pkg/mir2c |
i8080.go |
Intel 8080 | Experimental, untested |
gb.go |
Game Boy (LR35902) | Experimental, untested |
m6502_gen.go |
6502/NES | Experimental |
m68k.go |
MC68000 | Experimental |
crystal.go |
Crystal lang | Stub/broken |
llvm_backend.go |
LLVM IR | Stub/broken |
wasm_backend.go |
WebAssembly | Stub/broken |
None of these are wired into the active pipeline. They are reached only via the frozen
.minz → pkg/ir → pkg/codegen path.
| Tool | Status | Notes |
|---|---|---|
MZC (cmd/minzc) |
✅ Production | Main compiler — .nanz, .plm, .minz, .hir |
MZA (pkg/z80asm) |
✅ Production | Z80 assembler, all tests green |
MZE (pkg/emulator) |
✅ Production | 1335/1335 FUSE tests |
MZX (pkg/spectrum) |
✅ Done | ZX Spectrum emulator, T-state accurate |
MZD (pkg/disasm) |
✅ Done | Disassembler + analysis engine |
MZLSP (pkg/lsp) |
✅ Done | LSP — diagnostics, hover, goto-def, completion. 0 tests |
MZV (cmd/mzv) |
✅ Done | MIR VM runner — breakpoints, tracing, PNG export |
MZRUN (cmd/mzrun) |
✅ Done | Remote runner (DZRP protocol) |
MZTAP (cmd/mztap) |
✅ Done | TAP file loader |
MZR (cmd/repl) |
🔴 Build fail | int/uint16 mismatch; also broken semantically (:run unimplemented) |
DAP (pkg/dap) |
📋 Not started | Debug Adapter Protocol |
1. cmd/repl build — trivial type cast fix
In cmd/repl/compiler.go:198 and :205:
result.EntryPoint = uint16(asmResult.Origin) // was: asmResult.Origin (int)
result.Functions[name] = uint16(addr) // was: addr (int)5-minute fix. Note: the REPL is still functionally broken after this (:run unimplemented),
but at least go test ./... won't show a build failure.
2. pkg/codegen vet errors — non-constant format strings
In m6502_gen.go, gb.go, i8080.go, m68k.go: the emit() helper takes a format string
as a variable, which go vet flags as potentially unsafe. Fix options:
- Convert
emit(str)→emit("%s", str)at the call sites (safest) - Or add
//nolint:govetto the file headers (acceptable since these backends are experimental)
3. tests/ UFCS + InlineAsm — wrong frontend, not regressions
These tests send Nanz-syntax code to the old MinZ participle parser. They test features that the old parser never supported and never will. Fix options:
- Add
t.Skip("requires Nanz frontend — use pkg/nanz tests")to both - Or delete and add equivalent tests to
pkg/nanz/nanz_test.go
| Priority | Bug | Description | Effort |
|---|---|---|---|
| 🔴 High | BUG-003 | ptr[i] in while loop → invalid Z80 |
Medium |
| 🔴 High | BUG-006 | Zero-size struct globals not emitted | Small |
| 🟡 Medium | BUG-001 | GCD parallel-copy bloat (PBQP affinity) | Medium |
| 🟡 Medium | BUG-002 | forEach constant rematerialization |
Small |
| 🟡 Low | BUG-005 | applySubSwapNeg missing u16 guard |
Tiny (one line) |
pkg/pipelinetests: The dual-VM assertion infrastructure, LUTGen wiring, and full Nanz→Z80 chain have no package-level tests. A basic smoke test would prevent silent regressions.- MZLSP tests: The LSP server has 0 tests. At minimum: parse a file with a diagnostic
and verify the
Diagnosticslist. - BUG-004 (non-zero-lo LUT + contract opt): Needs pipeline ordering fix — LUTGen must run after contract opt, or contract opt must be LUT-aware.
- Old
pkg/codegenexperimental backends (GB, 6502, M68K, Crystal, WASM, LLVM): experimental, not wired to anything, vet fix is sufficient. pkg/dap: Not started, not needed until core language is stable.pkg/hirwat,pkg/meta,pkg/metafunction: Internal utilities, no known issues.
The active pipeline (Nanz / PL/M → HIR → MIR2 → Z80) is healthy: 25 of 28 test packages are green, 160+ MIR2 tests pass, and the full optimisation chain (PBQP, LUTGen, dual-VM assertions, CondRetSink, BranchEquiv, multiply strength reduction) is wired and verified. The 3 red packages are all in legacy / experimental territory and have trivially small fixes — none touch the active pipeline.
The two blocking open bugs (BUG-003, BUG-006) are scoped to specific language patterns and do not affect the majority of programs.