Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/nornir

docs/v2/mimir/compiler-to-graph.md

Mímir v2: from the compiler to the graph

This chapter follows one fact from the place rustc knows it to the place a lens reads it as a matrix entry. It is the path

rustc ─► mimir-driver ─► per-unit Arrow files ─► loader ─► Graph / room matrices ─► GraphBLAS products ─► lens output

checked against the code on main. It does not repeat the table columns: those are in tables.md. The producer, the ids, the lake and the room are introduced in README.md §3 to §6. The lenses themselves are in lenses.md.

Path convention: a bare path is in nornir (for example crates/mimir-driver/src/walk.rs:1777); the graph lenses are nornir's crates/mimir-lens/ (for example crates/mimir-lens/src/lenses/mod.rs:130, moved from dwarves on 2026-11-05 with their line numbers); a path starting dw: is in dwarves under crates/dwarves/.

From the compiler to the graph

1. The whole path in one picture

 cargo mimir-dump  (crates/mimir-driver/src/bin/cargo-mimir-dump.rs)
   1. cargo metadata ─────────────────────────────► <out>/crate-versions.json  (MIMIR_CRATE_VERSIONS)
   2. cargo check|build --workspace --all-targets
        RUSTC_WRAPPER=mimir-driver   MIMIR_OUT  MIMIR_REPO  MIMIR_SHA  [MIMIR_DUMP=workspace]
        │
        ▼  one rustc invocation per unit (lib, bin, test harness, dependency)
 mimir-driver  (src/main.rs)
   Callbacks::config          → override mir_drops_elaborated_and_const_checked (stash mir_promoted)
   Callbacks::after_analysis  → walk::dump_guarded(tcx)   (only when tcx.dcx().has_errors() is none)
        │  walk passes, in order (walk.rs:377-404):
        │    init_tables  collect_tests  walk_features  walk_defs  walk_crate_deps
        │    walk_bodies  walk_tables   walk_gated_out walk_mir   walk_symbols
        ▼
 <crate>@<version>-<hash>.{nodes,edges,spans,symbols}.arrow + .stats.json
        │                                   │                                │
        │ dwarves                           │ nornir                         │ --build only
        ▼                                   ▼                                ▼
 coverage::v2::read_with              mimir-ingest read_out_dir          mimir-binary
   sort files, normalize_id,            → delta vs open set               symbols × artifact
   dedupe (max count)                   → +/- events (Parquet lake)       → mono_instance,
 lenses::Graph::from_dump             mimir-room fold                       ffi_import, INSTANCE_OF
   index = row position                 ordinal = sorted moniker position
   calls/callers/children lists         one bool GrB_Matrix per EdgeKind
   dyn_targets (IMPLEMENTS+CONTAINS)    unions, transposes on first use
        │                                   │
        ▼                                   ▼
 grb::pattern(n, n, edges)            Graphs::get(mask, transposed)
   ANY.PAIR reach, PLUS.TIMES,          ops::reach (ANY.PAIR), MIN.PLUS,
   PLUS.PAIR, PLUS.MIN products         MAX.SECOND, PLUS.PAIR, …
        │                                   │
        ▼                                   ▼
 dwarves lens <name>  (text, --json)  CodeRoom.Answer + Evidence (gRPC :7878)

A side road: cargo lens graph-package reads only the *.edges.arrow files, keeps the CALLS rows, joins them with cargo metadata and writes target/dwarves/graph-package.znippy (section 9.2).

2. Stage 1: how the driver hooks rustc

2.1 The wrapper

cargo mimir-dump sets RUSTC_WRAPPER=mimir-driver (or RUSTC_WORKSPACE_WRAPPER with --workspace-wrapper) and runs cargo check --workspace --all-targets, or cargo build with --build (cargo-mimir-dump.rs:215-243). Cargo then starts mimir-driver <path/to/rustc> <args…> for every unit. The driver drops argv[1] when its file stem is rustc and compiles in process with the nightly it is linked against (src/main.rs:104-111), so every crate of the build comes from one compiler.

Cargo starts the wrapper only for units it recompiles, so a warm target dir would leave a new --out empty. cargo mimir-dump keeps a stamp of the last dump in <target-dir>/mimir-dump.stamp:

stamp        = (out, driver path + size + mtime, workspace_only, build, std, MIMIR_MIR, MIMIR_MIR_BLOCKS)
redo         ⇔ stamp ≠ the stored stamp  ∨  out holds no *.nodes.arrow / *.ring
redo ⇒ drop every cargo fingerprint under <target-dir>             (all units dirty; artifacts stay)
exit 4       ⇔ cargo succeeded ∧ out holds no dump, with a message naming the fix
MIMIR_INCREMENTAL=1 ⇒ no stamp, no redo, no exit 4

The same --out with the same options stays incremental. The consumer sets MIMIR_INCREMENTAL=1: it writes one --out per commit on a shared target dir, and its mirror covers the units cargo leaves fresh (section 10.1).

For a compile of the workspace's own sources the driver adds --cfg mimir, --check-cfg cfg(mimir) and registers the mimir tool (src/main.rs:120-127), so #[cfg_attr(mimir, mimir::table(…))] exists only under this driver.

2.2 Which callbacks run for which unit

src/main.rs:164-202 picks one of three rustc_driver::Callbacks:

unitcallbackseffect
MIMIR_OUT unset, --print, a build script, a proc-macro crateTimePassesCallbacks::default()plain rustc
a unit that would be dumped, but MIMIR_DUMP=workspace and CARGO_PRIMARY_PACKAGE is unset (a dependency under --workspace-only)SummaryCallbacksafter_analysis writes only the table summaries (walk::table_summaries_guarded), no dump
a dependency whose semantic hash is already in the ring list MIMIR_RING_HAVESummaryCallbacksa <crate>@<version>-<hash>.ring marker is written instead of a dump (src/main.rs:180-190)
every other lib / bin / test unit with an inputDumpCallbacksthe full walk

DumpCallbacks (src/main.rs:57-75) implements two hooks:

2.3 The compiler queries the walk calls

Every query below is called in the code named beside it. Nothing else of rustc is read.

pass (order in walk.rs:377-404)tcx callswrites
crate identity (walker, walk.rs:279-368; compute_crate_info, walk.rs:584-654)crate_name, used_crate_source, sess.opts.sysroot, the per-artifact version sidecarspackage, version, origin per crate
collect_tests (walk.rs:1232)iter_local_def_id, def_kind, the #[rustc_test_marker] attributethe test fn set (is_test)
walk_features (walk/gates.rs:192)sess.config (feature = "…" cfgs) + the cargo-metadata mapfeature nodes, ENABLES, ENABLES_WEAK, CONTAINS
walk_defs (walk.rs:1280-1353)iter_local_def_id, def_kind, opt_item_name, source_span, hir_get_module, type_of, visibility, effective_visibilities, hir_attrs, is_doc_hidden, def_key (disambiguators), impl_trait_ref, impl_opt_trait_id, fn_sig, fn_arg_idents, hir_fn_decl_by_hir_id, span outer_expn_dataitem nodes, param / ret parts, CONTAINS, IMPLEMENTS, FOR_TYPE, USES_TYPE, EXPANDS, GATED_BY (own cfg)
walk_crate_deps (walk.rs:1485-1493)crates(()), extern_crate(cnum).is_direct()DEPENDS_ON crate → crate
walk_bodies (walk.rs:1554-1594)hir_body_owners, is_typeck_child, typeck(owner), hir_body_owned_by, TypingEnv::post_analysis, then per expression qpath_res, type_dependent_def_id, node_args_opt, expr_ty_adjusted_opt, expr_ty_opt, expr_adjustments, opt_field_index, node_type_opt, Instance::try_resolveCALLS, READS_FIELD, WRITES_FIELD, TESTS, local parts, edge sites
walk_tables (walk/tables.rs)typeck results and const evaluation of table-name arguments (the module doc of walk/tables.rs:1-50)table nodes, TOUCHES_TABLE with access
walk_gated_out (walk/gates.rs:285)none: a token scan of the source (mimir_driver::cfg_scan), once per real file; a file named by include_str! / include_bytes! is scanned only if it also holds a crate or module nodecfg_gated_out stubs, CONTAINS, GATED_BY
walk_mir (walk/mir.rs:633-712)hir_body_owners, typeck_root_def_id_local, mir_promoted (or the stash), source_map().lookup_char_pos, Operand::const_fn_def, Instance::try_resolvemir_body and mir_block parts, CONTAINS fn → body, CFG_SUCC
walk_symbols (walk.rs:890-941)generics_of(d).requires_monomorphization, symbol_name(Instance::mono(tcx, d))code_symbols rows with binary = null

Two facts follow from this table. The driver never reads optimized_mir: the MIR facts come from mir_promoted, which does not depend on -C opt-level (walk/mir.rs:4-11). And the call graph does not come from MIR at all: CALLS edges are written from the HIR body walk with typeck results; the MIR pass only names the callee inside the mir_block payload.

3. How calls are resolved

3.1 The HIR body walk (BodyVisitor, walk.rs:1707-1907)

One BodyVisitor runs per typeck root (closures and inline consts are walked inside their root; visit_nested_body, walk.rs:1804-1812). The from of every edge is the nearest node of the body owner (node_or_ancestor), so a call inside a closure is an edge of the enclosing fn.

expressioncallee taken fromedge
f(x) with a path callee (ExprKind::Call, walk.rs:1830-1843)typeck.qpath_res(qpath) = Res::Def(Fn | AssocFn, did), then resolve(did)CALLS from → resolved
a call through a value (a closure, Box<dyn Fn…>, F: Fn…; walk.rs:1844-1858)typeck.type_dependent_def_id(e) (the Fn* trait method), then resolveCALLS from → Fn*::call* or the impl it resolves to
a call through a fn pointerthe first associated item of lang_items().fn_trait()CALLS from → Fn::call
a fn named as a value, not called (.map(Self::parse); walk.rs:1864-1869)qpath_res, then resolveCALLS from → that fn
recv.m(…) (ExprKind::MethodCall, walk.rs:1870-1876)typeck.type_dependent_def_id(e), then resolveCALLS from → resolved
base.field on a struct or union, read or writtenopt_field_index + the adjusted base type (field_of)READS_FIELD from → field
an overloaded operator (a + b on an Add impl, a[i] on Index, a Deref)no arm of the visitor handles it (walk.rs:1828-1906)no CALLS edge; the MIR call terminator of that block still names the impl
p = v, p op= v, &mut p, &raw mut p, a p auto-borrowed &mut (expr_adjustments: a Borrow to &mut with no Deref)every .f on the path of p, outside in, up to the first deref (write_place)WRITES_FIELD from → each such field
S { f: v, ..base } / E::V { f: v }expr_ty_opt (the ADT), qpath_res (the variant), opt_field_index of each named fieldWRITES_FIELD from → each named field (not the ..base ones)

The two field kinds as formulas (BodyVisitor doc, walk.rs):

READS_FIELD(b, f)   ⇔ ∃ e = `x.f` in b, x a struct / union          (a mention: read or written)
WRITES_FIELD(b, f)  ⇔ ∃ place p in b : f ∈ written(p), p assigned / op-assigned / borrowed &mut (explicit or auto-ref)
                    ∨ ∃ literal `S { f: v, .. }` in b
written(p)          = { g : `.g` on p's path from the outside in, stopping after the first projection whose base derefs }
reads_only(b, f)    = READS_FIELD(b, f) ∧ ¬WRITES_FIELD(b, f)

So self.open = true is a WRITES_FIELD and (the field expression) a READS_FIELD; the three guards if !self.open are READS_FIELD only, which is what reads_only picks out. self.log.push(s) (push(&mut self)) writes log; g.inner.depth = 3 writes depth and inner; r.f = v with r: &mut S writes f only; Point { x: 0, y: 0 } writes x and y and reads nothing. READS_FIELD keeps its schema-5 meaning, so every lens that reads it is unchanged.

resolve(did, hir_id) (walk.rs:1777-1797) is the whole monomorphic versus generic rule:

resolve(did) = did                               if did is not a trait's associated item
             = did                               if args = node_args(hir_id) has a non-region param,
                                                    an inference var, an error or escaping bound vars
             = d                                 if Instance::try_resolve(tcx, typing_env, did,
                                                    erase_and_anonymize_regions(args)) = Ok(Some(inst))
                                                    and inst.def = InstanceKind::Item(d)
             = did                               otherwise (a shim, a virtual call, no instance)

So a trait method called on a concrete type becomes an edge to the impl's method rustc would pick. A call on dyn Trait, on a generic T: Trait, or one that resolves to a shim stays an edge to the trait's own method (Trait#m().). The driver does not expand it.

3.2 Where the dyn and generic sites are widened

The driver writes IMPLEMENTS (impl → trait) and FOR_TYPE (impl → self ADT) for every impl (impl_edges, walk.rs:1365-1374), and CONTAINS impl → method through the parent chain. The widening of an unresolved trait call is done by the readers, three times, each in its own way:

readerrulewhere
graph lenses Graphdyn_targets[t] = { m : tr --CONTAINS--> t, im --IMPLEMENTS--> tr, im --CONTAINS--> m, kind(t), kind(m) ∈ {fn, method}, name(m) = name(t) }, plus the reverse dyn_sourcescrates/mimir-lens/src/lenses/mod.rs:189-213
nornir room, possible system callsa call target that is a method of a trait (parent kind trait) widens to every same-named fn or method contained in an impl of that trait (widen); a Fn::call family target widens to its impls and to every fn a static or const initializer namescrates/mimir-room/src/possible.rs:175-245
the driver's table passa call through a trait with no body takes the union of the summaries of the trait method's impls (this crate and the crates it loads)walk/tables.rs module doc, lines 45-48

The plain room walks (Room::reach, dependents) do not widen: a reach over CALLS stops at Trait#m().. Room::dependents adds the IMPLEMENTS and FOR_TYPE kinds and a step down CONTAINS from every reached impl (the Jikes matrix, section 10.3).

3.3 The MIR callee (callee_of, walk/mir.rs:354-367)

The MIR pass applies the same rule to a Call / TailCall terminator: func.const_fn_def() gives (did, args); a trait method with fully concrete args is resolved with Instance::try_resolve; anything else keeps did. A call through a pointer or a local has no const_fn_def and its operand text is written instead. The callee is written by its stable moniker (version *, section 5.3), so a dependency bump does not change a block's text or fingerprint (walk/mir.rs:690-699).

4. What becomes a node and what becomes an edge

4.1 Node kinds

node_kind (walk.rs:819-864) maps a DefKind to a kind and a level. A DefKind with no row in this table is transparent: its children hang under its parent.

DefKindkindlevel
crate rootcratecrate
Modmodmodule
Struct · Enum · Union · Trait · TraitAlias · TyAlias · ForeignTystruct · enum · union · trait · trait_alias · type · foreign_typeitem
Fnfn, or ffi when is_foreign_itemitem
Const · Static { nested: false }const · static (or ffi)item
Macro · Implmacro · implitem
AssocFn · AssocConst · AssocTy (not an RPITIT)method · assoc_const · assoc_typepart
Field · Variant · TyParam / ConstParamfield · variant · genericpart

The walk adds parts that are not DefKinds:

kindid (suffix after the fn's moniker)tywritten by
param(name) (_<i> for _ / unnamed)printed param typefn_parts, walk.rs:1412-1463
ret`->`. (not written for ())printed return typewalk.rs:1465-1477
local`name@line`., at most LOCALS_PER_FN = 48 per fntypeck typefn_locals, walk.rs:1390-1409
mir_bodymir:the body payload (section 7)emit_mir, walk/mir.rs:714-723
mir_blockbb<N>:; for a nested body `bbc<k>.<N>`:the block payloadwalk/mir.rs:727-743

And nodes from other passes: feature (`feature=<name>`: under the package's moniker, moniker.rs:233), table (rust-analyzer cargo @warehouse . `` table=: ``, moniker.rs:252), cfg_gated_out stubs (walk/gates.rs:565-590). The binary layer adds mono_instanceandffi_import (crates/mimir-binary/src/lib.rs:1-50); the boundary producer adds syscall nodes (moniker.rs:286`).

A target in another crate gets a reference stub: edge_to calls stub (walk.rs:1191-1228), which writes one row with defined_here = false, the target's kind, file and lines. Only the target is stubbed, not its parents.

4.2 Edge kinds and their writers

push_edge (walk.rs:1154-1189) keeps one row per (from, to, kind). Its count is the number of distinct sites:

count(from, to, kind) = | { (file, line) : a site of this edge } |

where a site without a position counts once as (None, None). Every site with a position also becomes a code_spans row with target = edge, key = from ␞ to ␞ KIND and start_line = end_line = line.

kindfrom → towritten bysites
CONTAINSparent → child, fn → param / ret / local / mir_body, crate → feature, scope → cfg_gated_out stubwalk.rs:1330, :1404, :1458, :1472; walk/mir.rs:723; walk/gates.rs:207none (count 1)
DEPENDS_ONcrate root → each direct extern crate rootwalk.rs:1485-1493; the boundary producer adds crate → syscall map cratenone
CALLSbody owner → resolved calleewalk_bodies, walk.rs:1587-1588the call expression's line
TESTSa test fn → a callee in a local crate (a copy of each such CALLS)walk.rs:1589-1594as CALLS
READS_FIELDbody owner → struct / union field it readsBodyVisitor::visit_expr, ExprKind::Fieldthe field expression's line
WRITES_FIELDbody owner → struct / union / variant field it writes (schema 6)BodyVisitor::write_place and the ExprKind::Struct armthe field expression's line (the literal field's for a struct literal)
IMPLEMENTSimpl → traitimpl_edges, walk.rs:1368none
FOR_TYPEimpl → the self ADT (references peeled)walk.rs:1370-1373none
USES_TYPEfn → each ADT in its signature; field → each ADT in its typewalk.rs:1477-1481, :1335-1339none
EXPANDSan item whose span comes from a macro expansion → the macrowalk.rs:1345-1350none
GATED_BYitem → feature, with negated and partialgate_site, walk/gates.rs:262-271the cfg's line
ENABLES · ENABLES_WEAKfeature → feature / crate root; crate root → each active featurewalk_features, walk/gates.rs:192-247none
TOUCHES_TABLEfn → table node, with accesswalk/tables.rs:1152the call's line
CFG_SUCCmir_block → each normal successor blockwalk/mir.rs:740-742none
INSTANCE_OFmono_instance → generic itemmimir-binary (lib.rs:478-495), binary streamnone
SYSCALLSfn / ffi → syscall nodemimir-ingest boundary producer (boundary/mod.rs:524), universenone
BUILT_FROMartifact → crate / feature / commitmimir-ingest product.rs:134, binary streamnone
COMPATIBLE(no writer)nothing writes it; the room's evidence says "belongs to another producer; this log has none" (crates/mimir-room/src/query.rs:217)—

4.3 The node columns the walk fills

The column list is in tables.md §1. How the walk fills the ones a lens leans on:

columnrulewhere
file, start_line, end_linefile_lines(span) of source_span(did), or the module's inner span for a mod; paths relative to the workspace root (members) or the manifest dir (dependencies), with . and a/.. taken out (clean_path), so one file has one spellingwalk.rs:1293-1300, :961-997
cfgthe item's own cfg predicate as textcfg_of, walk.rs:1002
is_testthe fn is in the test set (#[rustc_test_marker] sibling)walk.rs:1232-1251, :1300
defined_heredid.is_local(): true for the crate's own rows, false for a reference stubbase_row, walk.rs:1118
originlocal when CARGO_PRIMARY_PACKAGE is set or the package is a member of the cargo-metadata map; lang for a sysroot crate; else librarywalk.rs:289, :294-297, :581-651
typrinted type for field, const, static, type alias, assoc const, param, ret, local; the MIR payload for mir_body / mir_block. Types print with short paths (OnceLock<Option<String>>, Arc<OnceLock<Arc<IntakeChain>>>), so a lens matches the head name; a reference stub has no ty (tests/field_facts.rs field_nodes_carry_their_type)printed_ty, walk.rs:1352-1360; part_row, walk.rs:1375-1385

--workspace-only sets MIMIR_DUMP=workspace (cargo-mimir-dump.rs:236). Then only primary-package units are dumped, and every row of the dump has origin = local, apart from the reference stubs of dependency items. A full dump (the default) also holds one dump per dependency unit, whose rows are library or lang.

5. Ids

5.1 The moniker grammar

The grammar and the descriptor table are in README.md §4. In one line:

moniker     = "rust-analyzer" SP "cargo" SP package SP version SP descriptors
descriptors = [ target-prefix ] { descriptor }
descriptor  = name "/" | name "#" | name "." | name "()." | "[" name "]" | "(" name ")" | name ":" | name "!"
name        = a simple identifier ( [A-Za-z0-9_+$-]+ ), else "`" name-with-`-doubled "`"

(moniker.rs:69-121, :177-191). The descriptor chain is built from the DefId's parent chain (compute_desc, walk.rs:687-768); impl scopes are impl#[Self] or impl#[Self][Trait]; a non-zero DefPath disambiguator appends $n; a closure or anon const adds `$n`:.

Real ids from the fixture of crates/mimir-driver/tests/mir_facts.rs:29-30 and tests/fixture-mir/app/src/lib.rs:1:

rust-analyzer cargo app 0.1.0 crate/              the crate root
rust-analyzer cargo app 0.1.0 helper().           fn helper
rust-analyzer cargo app 0.1.0 helper().(x)        its param x            (rule of walk.rs:1421-1427)
rust-analyzer cargo app 0.1.0 helper().`->`.      its ret                (rule of walk.rs:1467-1469)
rust-analyzer cargo app 0.1.0 helper().mir:       its mir_body           (walk/mir.rs:718-720)
rust-analyzer cargo app 0.1.0 helper().bb0:       its block 0            (walk/mir.rs:727-731)
rust-analyzer cargo app 0.1.0 `feature=x`:        a cargo feature x      (moniker.rs:233)

The first two are asserted by the test; the rest follow from the code lines named.

5.2 Ids per target

A lib target (and its --test harness) has no prefix. Every other target gets one leading `<kind>=<crate_name>`: descriptor (moniker::target_prefix, moniker.rs:204-209). Because cargo check --all-targets compiles both the lib and the lib's test harness, the same item is dumped twice with the same id; both loaders fold those rows (sections 9 and 10).

5.3 The stable form

moniker::stable(id) replaces the version field by * (moniker.rs:391-404). The lake keys its delta by it (crates/mimir-ingest/src/delta.rs:552-570), and the MIR payload names callees by it. The rows themselves keep the real version, and the room's ordinals are taken over the real monikers.

6. The MIR facts

6.1 Which MIR, which crates

The pass reads mir_promoted (built MIR after SimplifyCfg::Initial and promotion, before any optimisation), from the stash or from the query (with_body, walk/mir.rs:88-100). A body found neither way is counted in mir_missing. The mode comes from MIMIR_MIR (Mode::of_env, walk/mir.rs:112-120):

MIMIR_MIRlocal crate (origin = local)dependency
unsetfull: mir_body + mir_block + CFG_SUCCfingerprint: mir_body only
allfullfull
fingerprintfingerprintfingerprint
offnonenone

Bodies are grouped by typeck root: the fn's own body has block prefix "", its k-th nested closure or coroutine body has prefix c<k>. (walk/mir.rs:641-674). Cleanup blocks are skipped. Every non-cleanup block of a body is written (schema 6); at most STMTS_PER_BLOCK = 128 statements are written per block (mir.rs:62); the fingerprint covers all statements.

The block cap. Up to schema 5 the pass took the first BLOCKS_PER_BODY = 1024 non-cleanup blocks of each body and dropped the rest, while the kept blocks' CFG_SUCC edges still pointed at the dropped ones. On Holger (aa5f0adc, --workspace-only) that cut 3 bodies of holger_server_lib: the async coroutine of exposed/http/handle() (the 4,071-line request router; 9,926 blocks), impl#[Holger]start() and the derived Deserialize visit_map. Of handle the lenses saw 3,114 of its 12,016 blocks (1,024 of the coroutine's 9,926), and 20 of its CFG_SUCC targets were no rows. Since 6:

cap        = MIMIR_MIR_BLOCKS  (cargo mimir-dump --mir-blocks N)    unset / 0 = ∞
blocks(b)  = the first min(cap, n_b) non-cleanup blocks of body b, in index order
succ'(x)   = succ(x) ∩ names(blocks of the fn)      only when some body of the fn was capped
stats      mir_max_blocks = max n_b,  mir_bodies_capped = |{b : n_b > cap}|,  mir_blocks_capped = Σ (n_b − cap)⁺

The cost of no cap, measured on Holger aa5f0adc (--workspace-only, build-1, 24 cores, fresh target dir each; the "after" driver also writes WRITES_FIELD):

schema 5 driver (cap 1,024)schema 6 driver (no cap)
handle() blocks · CFG_SUCC edges3,114 · 3,07912,016 · 12,569
CFG_SUCC targets that are no row (holger_server_lib)95 (20 in handle)0
mir_block rows, all units1,707,0811,735,064 (+1.6 %)
MIR payload bytes284.9 MB289.3 MB (+1.5 %)
edges, all units2,814,0392,915,637 (+72,231 WRITES_FIELD)
dump size2,277.0 MB2,338.3 MB (+2.7 %)
dump wall140.2 s142.9 s (+1.9 %, noise level)
peak driver RSS4.53 GB4.60 GB (+1.5 %)

Rows are bounded per block (one mir_block row each), so a long body costs rows, not one huge row; the largest body's mir_body payload grows by about 30 bytes per block. Segmenting was not needed, and a cap stays available.

6.2 The payload grammar

␞ is U+001E, ␟ is U+001F, ⟨ is U+27E8 and ⟩ is U+27E9 (crates/mimir-schema/src/mir.rs:53-55, :207-209).

mir_body.ty  = "1" ␞ entry { ␟ entry }
entry        = block "=" fp16 ":" term-kind ">" [ succ { "+" succ } ]
block        = N | "c" k "." N

mir_block.ty = fp16 ␞ terminator ␞ [ stmt { ␟ stmt } ] ␞ [ local ":" type { ␟ local ":" type } ] ␞ [ a ">" b { "," a ">" b } ]
stmt         = text [ "@" dline ":" col [ "m" ] ]           (no "@…" when the span is in another file)
operand      = "_" L  (copy)  |  "^_" L  (move)  |  "k:" type  (a constant: its type, never its value)
place        = "_" L { "*" | "." F | "[_" L "]" | "[" ["-"] off "]" | "[" a ".." ["-"] b "]" | "~" V }
call term    = "call " ( stable-callee-moniker | operand ) ⟨ operand { "," operand } ⟩ "->" place
term-kind    = goto | switch | return | unreachable | drop | call | tailcall | assert | yield
             | coroutine_drop | false_edge | false_unwind | asm | resume | terminate

(mir.rs:16-44, encode_body :96-112, encode_block :212-239; the texts are produced in walk/mir.rs:188-561). dline is the line relative to the fn's first line; col is 1-based; m marks a macro call site. succ lists only normal successors (cleanup blocks are removed, walk/mir.rs:547). The locals table lists every local the block touches, sorted by number, with its type. A pair a>b means local a is read in a statement or terminator that writes local b (flow, walk/mir.rs:563-575; a write through a deref counts as a read of the pointer, dest, walk/mir.rs:218-228).

6.3 The fingerprint

canon(block) = Σ_{s ∈ statements}  C(text(s)) ‖ "\n"
             ‖ C(text(terminator)) ‖ "\n"
             ‖ Σ_{l ∈ order}  ty(l) ‖ ";"
C(t)         = t with every local "_L" renamed "$i", i = rank of L in order of first use in the block
fp_raw       = FNV1a64(canon)            h₀ = 0xcbf29ce484222325,  h ← (h ⊕ byte) · 0x100000001b3  mod 2⁶⁴
fp           = fp_raw                                           if the terminator has no resolved callee
             = FNV1a64( hex16(fp_raw) ‖ stable(callee moniker) ) otherwise

(walk/mir.rs:377-545, :690-699; canonical_locals mir.rs:269-299; fnv64 mir.rs:70-72). The text used for C has no positions (they are appended later, full mode only), and the callee is the placeholder byte U+0000 at that point. So two blocks that do the same thing to the same types have one fingerprint, whatever their locals are called or numbered (asserted in crates/mimir-driver/tests/mir_facts.rs:157-173).

7. How the files are written

filecontentwhere
<crate>@<version>-<hash>.nodes.arrowcode_nodeswalk.rs:414-442
….edges.arrowcode_edgessame
….spans.arrowcode_spans: one row per defined node with a position, plus every positioned edge site; sorted, deduplicatedwalk.rs:420-433
….symbols.arrowcode_symbols from walk_symbols (binary, section, addr, size null)walk.rs:443-446
….stats.jsoncounts and timings (CrateStats), including mir_mode, mir_blocks, mir_missing, mir_max_blocks, mir_bodies_capped, mir_blocks_capped, edges_by_kind, semhashwalk.rs:476-548
.errors/<crate>-<hash>.txta walk failurewalk.rs:155-161
<crate>@<version>-<hash>.ringa dependency already in the ring: not walkedsrc/main.rs:185-188

<version> is lang for a toolchain crate and has / and : replaced by _ otherwise (walk.rs:414-416). <hash> is Invocation::short_hash: cargo's -C extra-filename without the dash, else m<-C metadata>, else nohash.<pid> (crates/mimir-driver/src/lib.rs:252-260). The format is the Arrow IPC file format, one RecordBatch per file, written to <name>.arrow.tmp and renamed (write_ipc, walk.rs:560-571). The schema metadata keys are in README.md §3.4.

The driver itself writes no znippy. The znippy packaging is dwarves' graph-package.znippy (section 9.2).

8. The binary layer (--build)

With --build, cargo builds instead of checks, so the linked artifacts come out of the same compile as the dump. mimir-binary then joins an artifact's symbol table with the driver's .symbols.arrow (exact symbol name first, then v0 demangling and moniker candidates), and writes mono_instance / ffi_import / unmatched nodes, INSTANCE_OF edges and code_symbols rows into its own stream (<stem>.binary.symbols.arrow, crates/mimir-binary/src/lib.rs:1-50, :1023). Provenance (source_only / binary_only / both) is derived at read time by derive_provenance (crates/mimir-schema/src/lib.rs), never stored by the binary layer.

9. Loader A: dwarves

9.1 coverage::v2::read_with and Graph::from_dump

dump dir ──files_with(suffix), sorted──► rows in file order, row order
         ──normalize_id──────────────► ids without a bin / own-harness target segment
         ──dedupe────────────────────► one node per id, one edge per (from, to, kind)
         ──Graph::from_dump──────────► index i = position in dump.nodes
                                        parent, children, calls, callers, call_count,
                                        trait_of_impl, impls_of_trait, for_type,
                                        expanded, dyn_targets, dyn_sources,
                                        mir_blocks_of, cfg_succ
  1. Read (crates/mimir-lens/src/coverage/v2.rs:283-361). Every *.nodes.arrow, then every *.edges.arrow (and *.symbols.arrow when asked), each list sorted by path (files_with, v2.rs:145-156). Columns are read by name; count reads as max(count, 1); defined_here defaults to true.
  2. Normalize (v2.rs:250-275, applied at :346-360). The target segment `kind=name`: is cut out of an id when kind is bin, example, bench or build-script, or when it is test and name is the package's lib name (with - as _) or one of the dump's bins. A tests/*.rs crate keeps its segment.
  3. Dedupe (v2.rs:389-445). Per id the first defining row wins over any reference stub; per (from, to, kind) the first row keeps the slot and count = max(counts), negated = first non-null, partial = first non-null. Order is kept.
  4. Index (crates/mimir-lens/src/lenses/mod.rs:130-252). idx[id] = i. An edge whose from or to is not a node of the dump is skipped (it still counts in edge_kinds). CONTAINS fills children and fills parent when the parent column did not; CALLS fills calls, callers and call_count[(f, t)] += max(count, 1); IMPLEMENTS fills trait_of_impl and impls_of_trait; FOR_TYPE fills for_type; EXPANDS sets expanded; CFG_SUCC fills cfg_succ; READS_FIELD and WRITES_FIELD fill reads_field / writes_field (fn → fields) and their reverses field_readers / field_writers. Any other kind is only counted, so a dump of any schema version loads. The lists are sorted and deduplicated. dyn_targets is the rule of section 3.2. mir_blocks_of[fn] takes a block's parent when it is a fn or method, else the owner parsed from the id (mir_block_owner, mod.rs:547-559).

9.2 graph-package.znippy

cargo lens graph-package (crates/mimir-lens/src/lens_graph.rs) runs cargo-mimir-dump with --out <target>/dwarves/mimir --target-dir <target>/dwarves/mimir-target (lens_graph.rs:669-713), or reads a given dump, and keeps from it only the CALLS rows of *.edges.arrow, one per (from, to) with count = max(count) (read_call_edges, lens_graph.rs:351-388). It joins each callee's package (parsed from the moniker) with cargo metadata and Cargo.lock, and packs five entries (call_edges.arrow, graph-package.json, dep_edges.arrow, dep_nodes.arrow, symbols.arrow) with znippy_common::create_archive_to_vec(&entries, 3) into target/dwarves/graph-package.znippy (lens_graph.rs:760-769).

Additive columns (schema "3", lane LENS-POLISH 2026-11-05; Holger's reader reads by column name and checks only dwarves.graph_package.schema ∈ {"2", "3"}). All are nullable, so an older reader ignores them and an older package lacks them:

entrycolumntypevaluefrom
symbols.arrowkindUtf8fn · method · trait_method (parent is a trait) · ffi (tcx.is_foreign_item)the dump's node kind + parent (read_symbol_facts)
symbols.arrowtrait_methodUtf8for an impl method: the trait method it implementsIMPLEMENTS(impl, trait) + the trait's child of the same name
symbols.arrowtestBooleantest-only codeis_test, a `test= target, cfg_test_only on the item or an ancestor, a file under tests/
dep_nodes.arrowproc_macroBooleanthe package has a proc-macro targetcargo metadata target kinds
trait_method(m) = tm  ⇔  ∃ im, tr : parent(m) = im ∧ IMPLEMENTS(im, tr) ∧ parent(tm) = tr ∧ name(tm) = name(m)
test(f)           ⇔  is_test(f) ∨ "`test=" ⊂ id(f) ∨ ∃ a ∈ ancestors(f) ∪ {f} : cfg_test_only(cfg(a)) ∨ tests/ ∈ dir(file(f))

With them mimir_lens::reach::ReachGraph::from_graph_package expands dyn calls exactly (dyn_source = "trait_method") and leaves test code out as the dump does; the test reach_api_answers_the_same_from_a_dump_and_from_its_graph_package builds both from one fixture (lens_graph::package_from_dump) and compares every answer. An older package falls back to the moniker rule (…/Trait#m(). ↔ …impl#[T][Trait]m().).

Schema "4" (branch mimir/schema7-refers-fn, not on main until Holger's reader takes "4"): call_edges.site. The driver writes a fn named as a VALUE (.map(Self::parse), each_addr(addr, TcpStream::connect), a stored fn pointer) as a CALLS edge, as before, and now ALSO as a REFERS_FN edge of the same site (Mimir dump schema 7, appended edge kind, no column; walk.rs, the hir::ExprKind::Path arm). The package derives:

site(e) = dyn     if kind(to) = trait_method                 (dyn or generic dispatch)
        = fn_ptr  if to = core Fn / FnMut / FnOnce :: call*   (a call through a fn pointer)
        = fn_ref  if refs(from, to) ≥ count(e)                (every site only names the fn)
        = static  otherwise
refs(from, to) = max site count of REFERS_FN(from, to) over the units (as CALLS counts)

A lake stamped 6 is not re-read for 7 (REREAD_SCHEMA stays 6): a dependency dumped before 7 has no REFERS_FN rows until it is dumped again, so its edges read static. Tests: driver fixture_dump (REFERS_FN by_value -> a tiny(), none for a direct call; RED before the driver change), mimir-schema schema_7_appends_refers_fn, lens reach_api_answers_the_same_from_a_dump_and_from_its_graph_package (fn_ref, dyn, static read back from call_edges.site).

10. Loader B: nornir consumer, lake, room

10.1 Consumer to lake

The consumer runs the dump with --keep-going --message-format=json (crates/mimir-ingest/src/driver.rs:1419-1421), then read_out_dir (driver.rs:2333-2380) reads, per *.stats.json, the unit's nodes, edges and spans files. A nohash stem (a build script's probe) is skipped. Each dump must match exactly one cargo unit, by hash or by target key, and its origin must agree with cargo's view of the unit; otherwise the run fails. The delta against the open set and the atomic release are in README.md §5.

10.2 The fold to a snapshot

mimir-room folds the event tables at a scope (snapshot.rs:1572, build):

10.3 Derived matrices (Graphs, snapshot.rs:378-495)

A_K           = the matrix of edge kind K                        (bool, n × n)
A_{K1∪…∪Km}   = A_K1 ∨ … ∨ A_Km        GrB_Matrix_eWiseAdd with GrB_LOR    (ops.rs:624-640)
A_Sᵀ          = transpose(A_S), built on first use and cached
J             = A_Jᵀ ∨ diag(impls) · A_CONTAINS                 J = CALLS ∪ USES_TYPE ∪ IMPLEMENTS ∪ FOR_TYPE
                                                                  (query.rs:18, snapshot.rs:465-480)

Room::reach walks A_S forward; dependents_by walks the cached A_Sᵀ forward (the same BFS, not reverse_reach); dependents walks J (crates/mimir-room/src/query.rs:332-352, :482-545).

11. From lists and matrices to GraphBLAS products

11.1 Building a matrix

loadercallentry rulewhere
dwarvesgrb::pattern(nrows, ncols, &[(row, col)]) → Matrix::<bool>::from_edgesM[r][c] = true for each listed pair; duplicates collapse to one entrycrates/mimir-lens/src/lenses/grb.rs:21-30; crates/mimir-graph/src/matrix.rs:74-88
nornir roomfrom_edge_chunks(&[(from, to)], n)A_K[i][j] = true iff an open edge row (m_i, m_j, K) existssnapshot.rs:2388; arrow.rs:90

A boolean matrix carries no count. The site counts live beside it (Graph::call_count in dwarves, edge_counts and the site table in the room).

11.2 The GraphBLAS steps

stepoperationsemiring as the code names itcomputesfile:line
reach, forwardf⟨¬v, replace, struct⟩ = f · A, then v⟨f, struct⟩ = true, repeat until f is empty or max_hopsGxB_ANY_PAIR_BOOL, descriptor GrB_DESC_RSCevery node reachable from the seeds, seeds includedcrates/mimir-graph/src/ops.rs:48-112
reach, reversef⟨¬v⟩ = A · f (mxv on A)GxB_ANY_PAIR_BOOLevery node that reaches a seedops.rs:76-85, :115-121
many seeds at oncethe same frontier step per batch of seed rowsGxB_ANY_PAIR_BOOLone reach set per seedops.rs:672-710
kind unionC = A ∨ B (eWiseAdd)GrB_LORthe walk matrix of several kindsops.rs:624-640
scope cutdiag(s) · A · diag(s)GxB_ANY_SECOND_BOOL, then GxB_ANY_FIRST_BOOLA restricted to edges with both ends in sops.rs:409-437
one step, two matricesC = A · B or C = A · BᵀGxB_ANY_PAIR_BOOLC[i][j] = ∃k A[i][k] ∧ B[k][j] (or B[j][k])crates/mimir-lens/src/lenses/grb.rs:33-54
bounded reach into a label matrixR = L ∨ A·L ∨ A²·L ∨ … ∨ Aᵏ·L (stops early when a power is empty)GxB_ANY_PAIR_BOOL per step, GrB_LOR unionwhat each row reaches in L within k hopscrates/mimir-lens/src/lenses/grb.rs:143-155
weighted pair scoreC⟨M⟩ = A · BᵀGrB_PLUS_TIMES_SEMIRING_FP64, GrB_DESC_ST1C[i][j] = Σ_k A[i][k]·B[j][k] where M has an entrycrates/mimir-lens/src/lenses/grb.rs:56-81
shared columnsC = A · BᵀGxB_PLUS_PAIR_UINT64`C[i][j] ={k : A[i][k] ∧ B[j][k]}
multiset overlapC = A · BᵀGxB_PLUS_MIN_UINT64C[i][j] = Σ_k min(A[i][k], B[j][k])crates/mimir-lens/src/lenses/grb.rs:119-136
fewest hopsfrontier relaxationGrB_MIN_PLUS_SEMIRING_UINT64hop distance from the seedsops.rs:170-260
worst value on a pathpropagationGrB_MAX_SECOND_SEMIRING_UINT64the largest value reaching each nodeops.rs:265-300
similarityA · AᵀGxB_PLUS_PAIR_UINT64common out-neighbours per pairops.rs:471-485

ANY.PAIR on booleans is the structural OR-AND (LOR.LAND) product: an entry of the result exists exactly when some k joins the two operands, and its value is always true.

Not every walk is GraphBLAS. gate-bypass walks callers ∪ dyn_sources with a breadth-first queue over the dwarves lists (reverse_bfs, crates/mimir-lens/src/lenses/gate_bypass.rs:82-110). The possible-syscalls walk widens call sites in plain Rust before it reaches (possible.rs:222-245).

12. Worked example

Two fixtures, because no single fixture in the repos records every layer. Part A starts from real source that the driver compiles in a test. Part B starts from a dump written by a dwarves test and goes down to matrix entries.

12.A Real source through the driver: helper

Source (crates/mimir-driver/tests/fixture-mir/app/src/lib.rs:1-3, package app 0.1.0, which depends on the path crate dep 0.2.0):

pub fn helper(x: u64) -> u64 {
    x.wrapping_mul(3)
}

What the test asserts about this fixture (crates/mimir-driver/tests/mir_facts.rs:89-153): the local crate runs in full mode and the dependency in fingerprint mode; block ids are <fn>bb<N>: with name bb<N>; each block's fp equals the fingerprint the body lists; CFG_SUCC equals the body's successor lists; positions are relative to the fn's first line with 1-based columns (while i < n in sample is @3:11); a call names the resolved callee by its stable moniker.

Rows (derived from the code paths named; no recorded dump of this fixture is in the repos). walk_defs and fn_parts write:

tablerow
code_nodesid = rust-analyzer cargo app 0.1.0 helper()., kind = fn, level = item, name = helper, parent = rust-analyzer cargo app 0.1.0 crate/, file = src/lib.rs, start_line = 1, end_line = 3, origin = local, is_test = false, defined_here = true, visibility = pub
code_nodesid = … helper().(x), kind = param, level = part, ty = u64, line 1
code_nodesid = … helper().`->`., kind = ret, level = part, ty = u64, line 1
code_edgescrate/ → helper(). CONTAINS; helper(). → helper().(x) CONTAINS; helper(). → helper().`->`. CONTAINS
code_edgeshelper(). → <wrapping_mul> CALLS, count = 1 (one site, line 2), from the MethodCall arm (walk.rs:1870-1876); wrapping_mul is inherent, so resolve returns it unchanged
code_spanstarget = edge, key = helper(). ␞ <wrapping_mul> ␞ CALLS, file = src/lib.rs, start_line = end_line = 2
code_edgescrate/ → rust-analyzer cargo dep 0.2.0 crate/ DEPENDS_ON (walk.rs:1485-1493)

No USES_TYPE is written: u64 is not an ADT (adts_in, walk.rs:1677-1691). <wrapping_mul> gets a reference stub with defined_here = false.

MIR (hand-derived illustration). If mir_promoted of helper is the usual two blocks, bb0: _0 = <u64>::wrapping_mul(copy _1, const 3_u64) -> bb1 and bb1: return, the pass writes:

canon(bb0) = "call \u{0}⟨$0,k:u64⟩->$1\n" ‖ "u64;u64;"        order = [1, 0]
fp_raw(bb0) = FNV1a64(canon(bb0)) = 3ee0a6a950006b48
fp(bb0)     = FNV1a64("3ee0a6a950006b48" ‖ stable(callee))
canon(bb1)  = "return\n"
fp(bb1)     = FNV1a64("return\n") = 8f0d8ef748da876f

helper().bb0:  ty = <fp(bb0)> ␞ call <stable callee>⟨_1,k:u64⟩->_0@1:5 ␞ ␞ 0:u64␟1:u64 ␞ 1>0
helper().bb1:  ty = 8f0d8ef748da876f ␞ return@<dl>:<col> ␞ ␞ ␞
helper().mir:  ty = 1 ␞ 0=<fp(bb0)>:call>1 ␟ 1=8f0d8ef748da876f:return>
CFG_SUCC       helper().bb0: → helper().bb1:

The two FNV values are computed from the canonical strings shown, with the fnv64 of mir.rs:70-72. @1:5 is the call x.wrapping_mul(3) on line 2, column 5, relative to line 1. 1>0 is the def-use pair: _1 is read where _0 is written. The stable callee moniker (core's inherent u64 method, version *) is not spelled out here, because no dump of it is recorded; the fixture of part B shows the same shape for core's str PartialEq::eq: rust-analyzer cargo core * str/traits/impl#[str][PartialEq]eq(). (crates/mimir-lens/tests/graph_lenses.rs:551).

12.B From dump rows to matrix entries: gate_ingest

The fixture is fixture() in crates/mimir-lens/tests/graph_lenses.rs:293-626, a dump written by the test with the schema columns the reader takes (no nightly). Its module doc gives the source it stands for (crates/mimir-lens/tests/graph_lenses.rs:7-15):

trait Store { fn put(); }
struct Disk; impl Store for Disk { fn put() { fs_write() } }
fn fs_write()
fn http_handle() { gate_ingest() }             an entry
fn gate_ingest() { Store::put() }              the gate; calls the sink
fn mirror_run()  { Store::put() }              an entry that bypasses the gate
fn restore()     { Store::put() }              only a test calls it
#[test] fn t_restore() { restore() }

Rows as the fixture writes them (crate app 0.1.0; … stands for rust-analyzer cargo app 0.1.0 ):

tablerowfixture line
code_nodesid = …gate_ingest()., kind = fn, name = gate_ingest, parent = … (the crate node), file = src/lib.rs, start_line = 12, end_line = 20, origin = local, defined_here = true:323-331
code_nodesid = …gate_ingest().bb0:, kind = mir_block, name = bb0, parent = null, ty = 0000000000000010␞call rust-analyzer cargo app * Store#put().⟨_1⟩->_2@1:1␞␞1:&str␟2:bool␞1>2:383, built by blk at :130-159
code_nodesid = …gate_ingest().bb1:, kind = mir_block, ty = 0000000000000011␞return@1:1␞␞1:&str␟2:bool␞1>2:384
code_edges… → …gate_ingest(). CONTAINS (from the parent):100-103
code_edges…gate_ingest(). → …Store#put(). CALLS, count = 1:354
code_edges…gate_ingest().bb0: → …gate_ingest().bb1: CFG_SUCC:385
code_edges…impl#[Disk][Store] → …Store# IMPLEMENTS; …impl#[Disk][Store] → …Disk# FOR_TYPE:301-302

The fixture writes no mir_body node for gate_ingest. The one the driver would write from these two blocks is, by encode_body (mir.rs:96-112), 1␞0=0000000000000010:call>1␟1=0000000000000011:return> (hand-built).

Indices (Graph::from_dump). files_with sorts the node files, and anyhow@0.1.0-….nodes.arrow (one node, __private/format_err().) sorts before app@0.1.0-….nodes.arrow; inside a file the rows keep the order the fixture pushed them. So:

inodeinode
0anyhow … __private/format_err().9http_handle().
1app crate10gate_ingest().
2Store#11mirror_run().
3Store#put().12restore().
4Lonely#13t_restore().
5Disk#14big().
6impl#[Disk][Store]15Tail#
7impl#[Disk][Store]put().16log().
8fs_write().20, 21gate_ingest().bb0:, gate_ingest().bb1:

(17 to 19 are the three blocks of http_handle.)

Lists. calls[9] = [10], calls[10] = [3], calls[11] = [3, 16], calls[12] = [3, 16], calls[7] = [8], calls[13] = [12]; impls_of_trait[2] = [6], children[2] ⊇ [3], children[6] ⊇ [7], and both 3 and 7 are named put, so dyn_targets[3] = [7] and dyn_sources[7] = [3]. The test asserts exactly this dyn expansion (crates/mimir-lens/tests/graph_lenses.rs:628-634). cfg_succ[20] = [21]; mir_blocks_of[10] = [20, 21] (found from the id, since the fixture's blocks have no parent).

Matrices (hand-built illustration over those lists; gate-bypass itself walks the lists with BFS). With n = the node count of the dump:

A = grb::pattern(n, n, CALLS pairs)       entries among nodes 0..16:
      A[9][10]  A[10][3]  A[11][3]  A[12][3]  A[7][8]  A[13][12]  A[11][16]  A[12][16]
D = grb::pattern(n, n, dyn pairs)         D[3][7]
W = A ∨ D                                 (GrB_LOR)

Forward reach from the entry s = {9} on W with GxB_ANY_PAIR_BOOL (ops::reach):

hop  frontier f        visited v
0    {9}               {9}
1    {10}              {9, 10}          f = {9}·W       : W[9][10]
2    {3}               {9, 10, 3}       f = {10}·W      : W[10][3]
3    {7}               {9, 10, 3, 7}    f = {3}·W       : D[3][7]
4    {8}               {9, 10, 3, 7, 8} f = {7}·W       : W[7][8]
5    {}                stop

On A alone the walk stops at hop 2 with {9, 10, 3}: without the dyn expansion the edge into fs_write is not seen. The reverse walk from the sink {8} on W (mxv) reaches {7, 3, 10, 11, 12, 9, 13}: every caller of Store::put, which is the set gate-bypass searches for entries that do not pass the gate.

Room ordinals. The nornir room would number the same nodes by sorted moniker, not by file order. Within this crate the byte order of the descriptor tails puts upper case before lower case: `` (the crate), Disk#, Lonely#, Store#, Store#put()., …, fs_write()., gate_ingest()., http_handle()., impl#[Disk][Store], … . The dwarves index and the room ordinal of one node are different numbers.

13. Where the code and the comments disagree

14. Tests that pin this path

testpinsfile:line
mir_facts_of_an_if_a_loop_and_a_callmodes, block ids, fingerprints equal between body and blocks, CFG_SUCC = body successors, positions, stable calleecrates/mimir-driver/tests/mir_facts.rs:89-153
renamed_locals_give_equal_block_fingerprintsthe fingerprint is name-freecrates/mimir-driver/tests/mir_facts.rs:157-173
body_and_block_round_trip, canonical_locals_renumber_by_first_usethe payload grammar and the renumberingcrates/mimir-schema/src/mir.rs:305-332
matches_rust_analyzer_scip and the fixture dumpmoniker byte-identity with rust-analyzercrates/mimir-driver/tests/fixture_dump.rs
one_method_writes_the_flag_and_three_read_it, every_write_form_is_a_writeWRITES_FIELD / READS_FIELD per write form, the write sitecrates/mimir-driver/tests/field_facts.rs
field_nodes_carry_their_typefield ty (bool, OnceLock<Option<String>>)crates/mimir-driver/tests/field_facts.rs
a_body_over_1024_blocks_is_written_whole, a_block_cap_bounds_the_rows_and_keeps_the_edges_insideno default cap; --mir-blocks N bounds rows and keeps CFG_SUCC insidecrates/mimir-driver/tests/field_facts.rs
schema_6_appends_writes_field, a_schema_5_lake_is_re_read_for_the_field_writes, writes_field_is_a_matrix_of_its_ownthe kind's position, the 5 → 6 re-read, the room matrixcrates/mimir-schema/tests/schema_and_moniker.rs, crates/mimir-ingest/tests/ingest/evolve.rs, crates/mimir-room/tests/room/queries.rs
a_new_out_on_a_warm_target_is_a_whole_dump, an_incremental_run_dumps_only_what_cargo_recompiles, nothing_to_dump_is_a_failure_that_names_the_fixa warm target dir never yields a silent empty dump; the consumer stays incrementalcrates/mimir-driver/tests/warm_target.rs
the_graph_indexes_calls_contains_and_the_dyn_expansionthe dwarves index and dyn_targetscrates/mimir-lens/tests/graph_lenses.rs:628
a_v2_dump_is_a_warehouse_root_and_yields_the_tables_the_lenses_readthe dwarves reader on a dump directorycrates/mimir-lens/tests/mimir_v2_dump.rs
crates/mimir-graph/tests/ops.rsthe GraphBLAS operationscrates/mimir-graph/tests/ops.rs

How to run the producer on a workspace:

cargo mimir-dump --out <dir> [--workspace-only] [--build] [--repo NAME] [--mir-blocks N]
cargo mimir-dump --cat <dir>          # print nodes, edges and spans of a dump as TSV
cargo lens graph-package              # dump + package join → target/dwarves/graph-package.znippy
cargo lens <name> [--mimir-dump <dir>] # a graph lens over a dump (default ./target/dwarves/mimir; lenses.md §1, §4)