mir-migration · git:20260816.d163901 · 2026-08-16 · sha256 f4f59e22663afecf
mir-migration git:20260816.d163901A
Immutable. This exact content never changes and is served at /api/v1/blob/f4f59e22663afecf.
--- name: mir-migration description: "Maintain and optimize dcc's completed generated-only MIR backend. Use for MIR lowering, verification, selectors, cost policy, register homes, spills, exact machine schedules, coverage censuses, or MIR performance recovery." --- # dcc generated-only MIR backend The migration is complete: - Every production function body is lowered to verified MIR. - Production Z80 comes only from a selected MIR candidate. - AST code after parsing is metadata-only: declarations, scopes/VLAs, inline temps, strings, labels, diagnostics, and debug events. - There is no body-codegen fallback, capture/replay stream, discard emitter, or legacy register-allocation retry. - Compatibility `captured_*` census columns are always `-1`. Use `dcc-project` for build, test, runtime, and baseline conventions. ## Non-negotiable rules 1. Keep `DCC_MIR_REQUIRE_COMPLETE=1` and `DCC_MIR_REQUIRE_EMIT=1` clean. 2. Never use legacy output, app/function names, padding, or performance baselines as production selection gates. 3. Identify every changed function before widening a selector or cost gate. 4. Correct output that regresses peep or nopeep remains unfinished. 5. Emit each candidate to its own temporary stream; a declined selector must not contaminate the next candidate. 6. Preserve type, volatility, aliasing, call ABI, CFG edges, PHI edge uses, VLA restoration, and callee-save state explicitly. 7. Prefer reusable range/liveness/value proofs over one-off schedules; use an exact schedule only when a general emitter cannot retain the proven win. 8. Generated reports and snapshots belong under `build/`. ## Architecture | Surface | Location | | --- | --- | | Lowering, metadata repair, verifier, CFG/liveness | `src/dcc/dcc_mir.c` | | Public/internal MIR contracts | `dcc_mir.h`, `dcc_mir_internal.h` | | Candidate selection and `mir-v1` policy | `dcc_mir_select.c` | | Shared emission and homes | `dcc_mir_emit_common.c`, `dcc_mir_homed_cfg.c` | | Spills and general CFG emission | `dcc_mir_spilled_cfg.c` | | Z80 costs/constraints/scheduling | `dcc_mir_target.c`, `dcc_mir_schedule.c` | | Exact schedule dispatch | `dcc_mir_machine_emit.c` | | Exact schedule families | `dcc_mir_machine_*.c` | All maintained C/header modules under `src/` follow the Doxygen file-header convention defined in the `dcc-project` skill: `@file`, `@brief`, explicit role, and useful entry-point/boundary notes. For MIR changes, keep the module map in `dcc_mir_internal.h` current whenever files move or responsibilities change. Never document `dcc_ast_gen*` as a production body-codegen fallback. Place new schedules in the closest family module. Keep plan/matcher state automatic and expose only that module's dispatch function: ```sh python3 scripts/audit-c-module-exports.py src/dcc/module.c \ --allow-function module_dispatch ``` ## Diagnostics | Control | Purpose | | --- | --- | | `DCC_MIR_SELECT_REPORT=1` | Selected emitter, reason, bytes, instructions, blocks, hash | | `DCC_MIR_REPORT=1` | Dump every MIR function, liveness, and allocation | | `DCC_MIR_FUNCTION=name` | Enable named MIR diagnostics; filter stderr by function | | `DCC_MIR_COVERAGE=1` | Report remaining opaque lowering | | `DCC_MIR_REQUIRE_COMPLETE=1` | Reject incomplete semantic MIR | | `DCC_MIR_REQUIRE_EMIT=1` | Reject any non-generated body | | `DCC_MIR_SELECT_FUNCTION=name` | Restrict a candidate comparison | | `DCC_MIR_SELECT_CANDIDATE=name` | Select a named generated candidate | | `DCC_MIR_EMIT_FUNCTION=name` | Diagnostic specialized-emitter isolation | | `DCC_MIR_GENERAL_FUNCTION=name` | Diagnostic general-emitter isolation | | `DCC_MIR_COST_POLICY=mir-v1-report` | Report alternatives without selecting them | Do not document or revive removed forced-legacy controls. ## Snapshot and compare Take both snapshots before changing selection: ```sh DCC_MIR_REQUIRE_COMPLETE=1 DCC_MIR_REQUIRE_EMIT=1 \ python3 scripts/mir-migration-census.py \ --output build/mir-before.tsv --jobs 24 --timeout 60 DCC_MIR_REQUIRE_COMPLETE=1 DCC_MIR_REQUIRE_EMIT=1 \ python3 scripts/mir-migration-census.py \ --output build/mir-before-stack.tsv --extra-args=-fstack-check \ --jobs 24 --timeout 60 ``` Compare after rebuilding: ```sh python3 scripts/mir-migration-census.py \ --output build/mir-after.tsv --compare build/mir-before.tsv \ --fail-on-regression python3 scripts/mir-migration-census.py \ --output build/mir-after-stack.tsv --compare build/mir-before-stack.tsv \ --fail-on-regression --extra-args=-fstack-check ``` The census prints the exact focused `runall.ps1` app list. Validate that entire list in stack and no-stack full mode. For current-vs-parent generated candidates: ```sh python3 scripts/mir-current-vs-parent.py \ --parent-compiler build/dcc-parent --apps app1,app2 \ --output-dir build/mir-parent-compare ``` `scripts/mir-migration-validate.sh <label> <baseline.tsv> <apps>` is a useful fail-fast iteration ladder. It does not replace the final strict stack and no-stack release gates. ## Efficient change loop 1. Start from a committed checkpoint and rebuild `dcc`. 2. Snapshot stack and no-stack censuses. 3. Form one falsifiable hypothesis and name the affected app/function. 4. Inspect MIR, candidate output, and linked runtime profile. 5. Make the smallest reusable change. 6. Rebuild and run the cheapest focused `-Mode full` discriminator. 7. Compare censuses with `--fail-on-regression`. 8. Run every changed app in strict stack and no-stack full mode. 9. Use ASan/UBSan for CFG, liveness, allocation, recursive proof, or ownership changes. 10. Run both strict full+extended release gates before commit/push. ## Selector and cost policy - `mir-v1` compares generated candidates only. Exact schedules retain priority when their complete structural proof succeeds. - A semantic matcher proves legality; the cost policy proves profitability. Do not merge those responsibilities. - Static assembly bytes and instruction counts are proxies. Check linked bytes and dynamic cycles. - Account for helper retention, frame bytes, spills, register moves, branches, calls, and loop frequency. - A helper with a fallback may be profitable only in a real CFG cycle or a separately proven hot exact kernel. - Preserve straight-line compact code when linking a new helper would cost more than it saves. ## Register and liveness rules - HL/DE/BC/IY homes are allocation results, not source-symbol claims. - BC/DE are caller-saved. Only IY may hold an ordinary value across calls. - IY users save/restore it and account for shifted parameter offsets. - `_setjmp`/`_longjmp` restore IY because non-local control bypasses epilogues. - PHI operands are edge uses, and call arguments remain live through their matching call-site ID. - Backedges extend live intervals; do not infer loop safety from linear text when CFG successors are available. - Unknown aliases, volatile access, opaque asm, indirect writes, and calls conservatively kill proofs. - Mark graph nodes visited when enqueuing so bounded worklists cannot overflow through duplicate predecessors. ## Exact schedules Exact schedules must prove the complete relevant semantics: - opcode/CFG shape and unique labels; - value and PHI relationships; - types, signedness, widths, storage, and volatility; - parameter offsets, array extents, strides, and aggregate sizes; - direct call prototypes, argument identities/order, and cleanup; - stack-check placement, frame size, spills, and callee saves; - all mutations, exits, and observable calls. Support source spelling variants through semantic normalization or separately proven structural fingerprints, never source/function-name tests. Add a permanent near-match or alternate-form test when a production variant caused the fix. ## Tooling and release checks ```sh pwsh ./scripts/test-mir-require-emit.ps1 DCC_MIR_REQUIRE_EMIT=1 python3 scripts/mir-extended-census.py \ --mode both --require-complete --output build/mir-extended.tsv python3 scripts/rtl-iy-safety.py python3 -m unittest discover -s scripts/tests -p 'test_*.py' git diff --check ``` Final publication requires: ```sh DCC_MIR_REQUIRE_COMPLETE=1 DCC_MIR_REQUIRE_EMIT=1 \ pwsh ./scripts/runall.ps1 -Mode full -Extended \ -RunTimeout 30 -FailuresOnly DCC_MIR_REQUIRE_COMPLETE=1 DCC_MIR_REQUIRE_EMIT=1 \ pwsh ./scripts/runall.ps1 -Mode full -Extended -NoStackCheck \ -RunTimeout 30 -FailuresOnly ``` Keep `plan.md` as the concise handoff and `mir-text-size-plan.md` as the chronological experiment/rejection log. Re-derive priorities from current census/profile data rather than continuing stale migration plans.