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/.
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:
| unit | callbacks | effect |
|---|---|---|
MIMIR_OUT unset, --print, a build script, a proc-macro crate | TimePassesCallbacks::default() | plain rustc |
a unit that would be dumped, but MIMIR_DUMP=workspace and CARGO_PRIMARY_PACKAGE is unset (a dependency under --workspace-only) | SummaryCallbacks | after_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_HAVE | SummaryCallbacks | a <crate>@<version>-<hash>.ring marker is written instead of a dump (src/main.rs:180-190) |
| every other lib / bin / test unit with an input | DumpCallbacks | the full walk |
DumpCallbacks (src/main.rs:57-75) implements two hooks:
config: setsconfig.override_queries = Some(walk::mir::override_queries). The override replaces the provider ofmir_drops_elaborated_and_const_checkedwith one that first clones the body'smir_promotedinto a thread-local stash, then calls the original provider (walk/mir.rs:58-75). Without it, rustc could steal a body'smir_promotedbefore the walk reads it.after_analysis(tcx): whentcx.dcx().has_errors()isNone, it callswalk::dump_guarded(tcx, inv, out)and returnsCompilation::Continue, so rustc still writes the metadata cargo expects. A panic or an error of the walk is caught and written to$MIMIR_OUT/.errors/<crate>-<hash>.txt(walk.rs:137-162); rustc's exit code is never changed.
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 calls | writes |
|---|---|---|
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 sidecars | package, version, origin per crate |
collect_tests (walk.rs:1232) | iter_local_def_id, def_kind, the #[rustc_test_marker] attribute | the test fn set (is_test) |
walk_features (walk/gates.rs:192) | sess.config (feature = "…" cfgs) + the cargo-metadata map | feature 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_data | item 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_resolve | CALLS, 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 node | cfg_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_resolve | mir_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.
| expression | callee taken from | edge |
|---|---|---|
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 resolve | CALLS from → Fn*::call* or the impl it resolves to |
| a call through a fn pointer | the 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 resolve | CALLS from → that fn |
recv.m(…) (ExprKind::MethodCall, walk.rs:1870-1876) | typeck.type_dependent_def_id(e), then resolve | CALLS from → resolved |
base.field on a struct or union, read or written | opt_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 field | WRITES_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:
| reader | rule | where |
|---|---|---|
graph lenses Graph | dyn_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_sources | crates/mimir-lens/src/lenses/mod.rs:189-213 |
| nornir room, possible system calls | a 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 names | crates/mimir-room/src/possible.rs:175-245 |
| the driver's table pass | a 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.
DefKind | kind | level |
|---|---|---|
| crate root | crate | crate |
Mod | mod | module |
Struct · Enum · Union · Trait · TraitAlias · TyAlias · ForeignTy | struct · enum · union · trait · trait_alias · type · foreign_type | item |
Fn | fn, or ffi when is_foreign_item | item |
Const · Static { nested: false } | const · static (or ffi) | item |
Macro · Impl | macro · impl | item |
AssocFn · AssocConst · AssocTy (not an RPITIT) | method · assoc_const · assoc_type | part |
Field · Variant · TyParam / ConstParam | field · variant · generic | part |
The walk adds parts that are not DefKinds:
| kind | id (suffix after the fn's moniker) | ty | written by |
|---|---|---|---|
param | (name) (_<i> for _ / unnamed) | printed param type | fn_parts, walk.rs:1412-1463 |
ret | `->`. (not written for ()) | printed return type | walk.rs:1465-1477 |
local | `name@line`., at most LOCALS_PER_FN = 48 per fn | typeck type | fn_locals, walk.rs:1390-1409 |
mir_body | mir: | the body payload (section 7) | emit_mir, walk/mir.rs:714-723 |
mir_block | bb<N>:; for a nested body `bbc<k>.<N>`: | the block payload | walk/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.
| kind | from → to | written by | sites |
|---|---|---|---|
| CONTAINS | parent → child, fn → param / ret / local / mir_body, crate → feature, scope → cfg_gated_out stub | walk.rs:1330, :1404, :1458, :1472; walk/mir.rs:723; walk/gates.rs:207 | none (count 1) |
| DEPENDS_ON | crate root → each direct extern crate root | walk.rs:1485-1493; the boundary producer adds crate → syscall map crate | none |
| CALLS | body owner → resolved callee | walk_bodies, walk.rs:1587-1588 | the call expression's line |
| TESTS | a test fn → a callee in a local crate (a copy of each such CALLS) | walk.rs:1589-1594 | as CALLS |
| READS_FIELD | body owner → struct / union field it reads | BodyVisitor::visit_expr, ExprKind::Field | the field expression's line |
| WRITES_FIELD | body owner → struct / union / variant field it writes (schema 6) | BodyVisitor::write_place and the ExprKind::Struct arm | the field expression's line (the literal field's for a struct literal) |
| IMPLEMENTS | impl → trait | impl_edges, walk.rs:1368 | none |
| FOR_TYPE | impl → the self ADT (references peeled) | walk.rs:1370-1373 | none |
| USES_TYPE | fn → each ADT in its signature; field → each ADT in its type | walk.rs:1477-1481, :1335-1339 | none |
| EXPANDS | an item whose span comes from a macro expansion → the macro | walk.rs:1345-1350 | none |
| GATED_BY | item → feature, with negated and partial | gate_site, walk/gates.rs:262-271 | the cfg's line |
| ENABLES · ENABLES_WEAK | feature → feature / crate root; crate root → each active feature | walk_features, walk/gates.rs:192-247 | none |
| TOUCHES_TABLE | fn → table node, with access | walk/tables.rs:1152 | the call's line |
| CFG_SUCC | mir_block → each normal successor block | walk/mir.rs:740-742 | none |
| INSTANCE_OF | mono_instance → generic item | mimir-binary (lib.rs:478-495), binary stream | none |
| SYSCALLS | fn / ffi → syscall node | mimir-ingest boundary producer (boundary/mod.rs:524), universe | none |
| BUILT_FROM | artifact → crate / feature / commit | mimir-ingest product.rs:134, binary stream | none |
| 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:
| column | rule | where |
|---|---|---|
file, start_line, end_line | file_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 spelling | walk.rs:1293-1300, :961-997 |
cfg | the item's own cfg predicate as text | cfg_of, walk.rs:1002 |
is_test | the fn is in the test set (#[rustc_test_marker] sibling) | walk.rs:1232-1251, :1300 |
defined_here | did.is_local(): true for the crate's own rows, false for a reference stub | base_row, walk.rs:1118 |
origin | local when CARGO_PRIMARY_PACKAGE is set or the package is a member of the cargo-metadata map; lang for a sysroot crate; else library | walk.rs:289, :294-297, :581-651 |
ty | printed 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_MIR | local crate (origin = local) | dependency |
|---|---|---|
| unset | full: mir_body + mir_block + CFG_SUCC | fingerprint: mir_body only |
all | full | full |
fingerprint | fingerprint | fingerprint |
off | none | none |
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 edges | 3,114 · 3,079 | 12,016 · 12,569 |
CFG_SUCC targets that are no row (holger_server_lib) | 95 (20 in handle) | 0 |
mir_block rows, all units | 1,707,081 | 1,735,064 (+1.6 %) |
| MIR payload bytes | 284.9 MB | 289.3 MB (+1.5 %) |
| edges, all units | 2,814,039 | 2,915,637 (+72,231 WRITES_FIELD) |
| dump size | 2,277.0 MB | 2,338.3 MB (+2.7 %) |
| dump wall | 140.2 s | 142.9 s (+1.9 %, noise level) |
| peak driver RSS | 4.53 GB | 4.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
| file | content | where |
|---|---|---|
<crate>@<version>-<hash>.nodes.arrow | code_nodes | walk.rs:414-442 |
….edges.arrow | code_edges | same |
….spans.arrow | code_spans: one row per defined node with a position, plus every positioned edge site; sorted, deduplicated | walk.rs:420-433 |
….symbols.arrow | code_symbols from walk_symbols (binary, section, addr, size null) | walk.rs:443-446 |
….stats.json | counts and timings (CrateStats), including mir_mode, mir_blocks, mir_missing, mir_max_blocks, mir_bodies_capped, mir_blocks_capped, edges_by_kind, semhash | walk.rs:476-548 |
.errors/<crate>-<hash>.txt | a walk failure | walk.rs:155-161 |
<crate>@<version>-<hash>.ring | a dependency already in the ring: not walked | src/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
- Read (
crates/mimir-lens/src/coverage/v2.rs:283-361). Every*.nodes.arrow, then every*.edges.arrow(and*.symbols.arrowwhen asked), each list sorted by path (files_with,v2.rs:145-156). Columns are read by name;countreads asmax(count, 1);defined_heredefaults to true. - Normalize (
v2.rs:250-275, applied at:346-360). The target segment`kind=name`:is cut out of an id whenkindisbin,example,benchorbuild-script, or when it istestandnameis the package's lib name (with-as_) or one of the dump's bins. Atests/*.rscrate keeps its segment. - 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 andcount = max(counts),negated = first non-null,partial = first non-null. Order is kept. - Index (
crates/mimir-lens/src/lenses/mod.rs:130-252).idx[id] = i. An edge whosefromortois not a node of the dump is skipped (it still counts inedge_kinds). CONTAINS fillschildrenand fillsparentwhen theparentcolumn did not; CALLS fillscalls,callersandcall_count[(f, t)] += max(count, 1); IMPLEMENTS fillstrait_of_implandimpls_of_trait; FOR_TYPE fillsfor_type; EXPANDS setsexpanded; CFG_SUCC fillscfg_succ; READS_FIELD and WRITES_FIELD fillreads_field/writes_field(fn → fields) and their reversesfield_readers/field_writers. Any other kind is only counted, so a dump of any schema version loads. The lists are sorted and deduplicated.dyn_targetsis the rule of section 3.2.mir_blocks_of[fn]takes a block'sparentwhen 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:
| entry | column | type | value | from |
|---|---|---|---|---|
symbols.arrow | kind | Utf8 | fn · method · trait_method (parent is a trait) · ffi (tcx.is_foreign_item) | the dump's node kind + parent (read_symbol_facts) |
symbols.arrow | trait_method | Utf8 | for an impl method: the trait method it implements | IMPLEMENTS(impl, trait) + the trait's child of the same name |
symbols.arrow | test | Boolean | test-only code | is_test, a `test= target, cfg_test_only on the item or an ancestor, a file under tests/ |
dep_nodes.arrow | proc_macro | Boolean | the package has a proc-macro target | cargo 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):
- Ordinals. The kept monikers (a node with a row, or the end of an edge
of a known kind) are sorted bytewise; the ordinal of a node is its
position (
snapshot.rs:2023-2049;Monikers,snapshot.rs:69-110). Ordinals are packed in 32 bits. - MIR blocks out. Unless
MIMIR_ROOM_MIR_BLOCKS=in,mir_blockrows andCFG_SUCCedges are dropped from the room (DROPPED,snapshot.rs:52-65,:979,:1118); a lens reads block payloads per repo from the lake (crates/mimir-room/src/mir.rs:1-13).mir_bodynodes stay. - Counts. An edge several units carry (a lib and its test harness)
keeps the larger site count, never the sum (
snapshot.rs:2330-2336). GATED_BY flags fold with AND, TOUCHES_TABLE access bits with OR (snapshot.rs:2337-2371). - Matrices. Per edge kind, the pairs are packed as
p = from·2³² + to, sorted, deduplicated and imported as one n×n boolean matrix withfrom_edge_chunks(snapshot.rs:2372-2393;crates/mimir-graph/src/arrow.rs:90-170), which packs CSR directly when the pairs are sorted and then lets GraphBLAS choose a hypersparse form (conform).
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
| loader | call | entry rule | where |
|---|---|---|---|
| dwarves | grb::pattern(nrows, ncols, &[(row, col)]) → Matrix::<bool>::from_edges | M[r][c] = true for each listed pair; duplicates collapse to one entry | crates/mimir-lens/src/lenses/grb.rs:21-30; crates/mimir-graph/src/matrix.rs:74-88 |
| nornir room | from_edge_chunks(&[(from, to)], n) | A_K[i][j] = true iff an open edge row (m_i, m_j, K) exists | snapshot.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
| step | operation | semiring as the code names it | computes | file:line |
|---|---|---|---|---|
| reach, forward | f⟨¬v, replace, struct⟩ = f · A, then v⟨f, struct⟩ = true, repeat until f is empty or max_hops | GxB_ANY_PAIR_BOOL, descriptor GrB_DESC_RSC | every node reachable from the seeds, seeds included | crates/mimir-graph/src/ops.rs:48-112 |
| reach, reverse | f⟨¬v⟩ = A · f (mxv on A) | GxB_ANY_PAIR_BOOL | every node that reaches a seed | ops.rs:76-85, :115-121 |
| many seeds at once | the same frontier step per batch of seed rows | GxB_ANY_PAIR_BOOL | one reach set per seed | ops.rs:672-710 |
| kind union | C = A ∨ B (eWiseAdd) | GrB_LOR | the walk matrix of several kinds | ops.rs:624-640 |
| scope cut | diag(s) · A · diag(s) | GxB_ANY_SECOND_BOOL, then GxB_ANY_FIRST_BOOL | A restricted to edges with both ends in s | ops.rs:409-437 |
| one step, two matrices | C = A · B or C = A · Bᵀ | GxB_ANY_PAIR_BOOL | C[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 matrix | R = L ∨ A·L ∨ A²·L ∨ … ∨ Aᵏ·L (stops early when a power is empty) | GxB_ANY_PAIR_BOOL per step, GrB_LOR union | what each row reaches in L within k hops | crates/mimir-lens/src/lenses/grb.rs:143-155 |
| weighted pair score | C⟨M⟩ = A · Bᵀ | GrB_PLUS_TIMES_SEMIRING_FP64, GrB_DESC_ST1 | C[i][j] = Σ_k A[i][k]·B[j][k] where M has an entry | crates/mimir-lens/src/lenses/grb.rs:56-81 |
| shared columns | C = A · Bᵀ | GxB_PLUS_PAIR_UINT64 | `C[i][j] = | {k : A[i][k] ∧ B[j][k]} |
| multiset overlap | C = A · Bᵀ | GxB_PLUS_MIN_UINT64 | C[i][j] = Σ_k min(A[i][k], B[j][k]) | crates/mimir-lens/src/lenses/grb.rs:119-136 |
| fewest hops | frontier relaxation | GrB_MIN_PLUS_SEMIRING_UINT64 | hop distance from the seeds | ops.rs:170-260 |
| worst value on a path | propagation | GrB_MAX_SECOND_SEMIRING_UINT64 | the largest value reaching each node | ops.rs:265-300 |
| similarity | A · Aᵀ | GxB_PLUS_PAIR_UINT64 | common out-neighbours per pair | ops.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:
| table | row |
|---|---|
code_nodes | id = 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_nodes | id = … helper().(x), kind = param, level = part, ty = u64, line 1 |
code_nodes | id = … helper().`->`., kind = ret, level = part, ty = u64, line 1 |
code_edges | crate/ → helper(). CONTAINS; helper(). → helper().(x) CONTAINS; helper(). → helper().`->`. CONTAINS |
code_edges | helper(). → <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_spans | target = edge, key = helper(). ␞ <wrapping_mul> ␞ CALLS, file = src/lib.rs, start_line = end_line = 2 |
code_edges | crate/ → 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 ):
| table | row | fixture line |
|---|---|---|
code_nodes | id = …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_nodes | id = …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_nodes | id = …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:
| i | node | i | node |
|---|---|---|---|
| 0 | anyhow … __private/format_err(). | 9 | http_handle(). |
| 1 | app crate | 10 | gate_ingest(). |
| 2 | Store# | 11 | mirror_run(). |
| 3 | Store#put(). | 12 | restore(). |
| 4 | Lonely# | 13 | t_restore(). |
| 5 | Disk# | 14 | big(). |
| 6 | impl#[Disk][Store] | 15 | Tail# |
| 7 | impl#[Disk][Store]put(). | 16 | log(). |
| 8 | fs_write(). | 20, 21 | gate_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
- The closure block id. The doc of
kind::MIR_BLOCKsays a closure's block is<fn>`c1.bb3`:(crates/mimir-schema/src/lib.rs:443), and theGraph::mir_blocks_ofdoc repeats it (crates/mimir-lens/src/lenses/mod.rs:111-112). The code writesbb+c1.3, which escapes to<fn>`bbc1.3`:(walk/mir.rs:727-731);mir_block_ownerparses that form (crates/mimir-lens/src/lenses/mod.rs:553-568). MIMIR_MIR=local. The same doc says block nodes are written for the workspace's own crates "(MIMIR_MIR=local)".Mode::of_envknows nolocalvalue; local-only full mode is the behaviour when the variable is unset (walk/mir.rs:112-120).- EXPANDS. The dwarves
Graph::expandeddoc says the driver writes EXPANDS for proc-macro output only and none for amacro_rules!expansion (crates/mimir-lens/src/lenses/mod.rs:108-110). The driver code writes EXPANDS for every item-level node whose span is from an expansion with amacro_def_id(walk.rs:1345-1350), with no check of the macro kind. Not verified against a dump here. originvalues. The dwarvesNode::origindoc namesregistry / git / lang(crates/mimir-lens/src/coverage/v2.rs:94-96). The driver writes onlylocal,libraryorlang(crates/mimir-schema/src/lib.rs,Origin).is_localonly tests forlocal, so the lenses are not affected.- Fixture shapes. The dwarves fixtures are simplified dumps, not driver
output:
crates/mimir-lens/tests/graph_lenses.rsgives the crate node the tail""instead ofcrate/, features the id#feature:xinstead of`feature=x`:,mir_blockrows noparent(the driver sets the fn,part_row,walk.rs:1378-1388), and itsladder().bb1:payload lists only the pair19>65, where the driver'sflowwould also record65>66for the call (walk/mir.rs:563-575).crates/mimir-lens/tests/mimir_v2_dump.rsuses plain paths (app::a) as ids and the valuesorigin = workspace,provenance = source.
14. Tests that pin this path
| test | pins | file:line |
|---|---|---|
mir_facts_of_an_if_a_loop_and_a_call | modes, block ids, fingerprints equal between body and blocks, CFG_SUCC = body successors, positions, stable callee | crates/mimir-driver/tests/mir_facts.rs:89-153 |
renamed_locals_give_equal_block_fingerprints | the fingerprint is name-free | crates/mimir-driver/tests/mir_facts.rs:157-173 |
body_and_block_round_trip, canonical_locals_renumber_by_first_use | the payload grammar and the renumbering | crates/mimir-schema/src/mir.rs:305-332 |
matches_rust_analyzer_scip and the fixture dump | moniker byte-identity with rust-analyzer | crates/mimir-driver/tests/fixture_dump.rs |
one_method_writes_the_flag_and_three_read_it, every_write_form_is_a_write | WRITES_FIELD / READS_FIELD per write form, the write site | crates/mimir-driver/tests/field_facts.rs |
field_nodes_carry_their_type | field 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_inside | no default cap; --mir-blocks N bounds rows and keeps CFG_SUCC inside | crates/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_own | the kind's position, the 5 → 6 re-read, the room matrix | crates/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_fix | a warm target dir never yields a silent empty dump; the consumer stays incremental | crates/mimir-driver/tests/warm_target.rs |
the_graph_indexes_calls_contains_and_the_dyn_expansion | the dwarves index and dyn_targets | crates/mimir-lens/tests/graph_lenses.rs:628 |
a_v2_dump_is_a_warehouse_root_and_yields_the_tables_the_lenses_read | the dwarves reader on a dump directory | crates/mimir-lens/tests/mimir_v2_dump.rs |
crates/mimir-graph/tests/ops.rs | the GraphBLAS operations | crates/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)