Skip to main content

qcode/value/block/
cfg.rs

1// TODO: use a modified baseRef
2
3use jstd::{
4    Identifier,
5    graph::{Cfg, FxBuildHasher},
6};
7
8use crate::value::{BlockRef, QCodeView, function::FunctionRef};
9
10/// Function-local block index (indexes the owning [`FunctionBody`](crate::value::FunctionBody)'s block arena).
11#[derive(Identifier)]
12pub struct LocalBlockId(u32);
13
14crate::composite_id!(BlockId, LocalBlockId);
15
16/// Function-local CFG-edge index. A plain body-local id (stage 4): it indexes
17/// the owning [`FunctionBody`](crate::value::FunctionBody)'s edge arena directly and carries **no** function
18/// qualifier. Global addressing of an edge is the explicit pair
19/// `(FunctionId, EdgeId)`; every edge is stored in its `from` block's function,
20/// so the owning function is recoverable from either incident block.
21#[derive(Identifier)]
22pub struct EdgeId(u32);
23
24#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
25pub struct EdgeData {
26    pub from: LocalBlockId,
27    pub to: LocalBlockId,
28}
29
30// A plain body-local [`EdgeId`] no longer self-describes its owning function, so
31// the old whole-context `EdgeRef`/`EdgeMutRef` wrappers (which resolved
32// `values.edge(id)` without a function) are gone. Edges are read through
33// `FunctionBody::edge(id)` / `QCodeView::edge(func, id)` with the owning function named
34// explicitly. Strict locality (context-split ruling 2) guarantees both endpoints
35// live in the edge's own function, so `from`/`to` are bare `LocalBlockId`s;
36// qualify with the owning function (which every reader names) to get a composite
37// `BlockId`.
38
39// ---------------------------------------------------------------------------
40// A CFG rooted at a single function.
41//
42// The CFG is inherently per-function: its nodes are the function's own blocks.
43// Dominator analysis needs only the successor relation (see jstd's `Cfg`), which
44// [`BlockRef::successors`] already routes through the function's [`QCodeView`], so
45// it reads a *checked-out* function correctly inside a `FunctionPass`.
46// ---------------------------------------------------------------------------
47
48impl<'str: 'ctx, 'ctx, R> Cfg for FunctionRef<'str, 'ctx, R>
49where
50    R: QCodeView<'ctx, 'str>,
51{
52    type NodeId = BlockId;
53
54    // Fixed-seed hasher (not std's `RandomState`), so a block's incident-edge set
55    // — and thus `successors()` iteration — is deterministic across runs; per-run
56    // nondeterminism would otherwise leak into borderline mem2reg promotions and
57    // hence into the lifted IR.
58    type Hasher = FxBuildHasher;
59
60    fn successors(&self, b: BlockId) -> impl Iterator<Item = BlockId> + '_ {
61        let succs: Vec<BlockId> = BlockRef::new(self.view, b)
62            .successors()
63            .map(|(_, s)| s)
64            .collect();
65        succs.into_iter()
66    }
67}