Skip to main content

vyre_primitives/graph/exploded/
encoding.rs

1/// Bits reserved for each component of the packed node id.
2pub const PROC_BITS: u32 = 12;
3/// Bits reserved for the basic-block component of the packed node id.
4pub const BLOCK_BITS: u32 = 10;
5/// Bits reserved for the fact component of the packed node id.
6pub const FACT_BITS: u32 = 10;
7const _SANITY: () = assert!(PROC_BITS + BLOCK_BITS + FACT_BITS == 32);
8
9/// Max values for each component  -  one less than the available
10/// space because zero is a valid id.
11pub const MAX_PROC_ID: u32 = (1 << PROC_BITS) - 1;
12/// Maximum encodable basic-block id.
13pub const MAX_BLOCK_ID: u32 = (1 << BLOCK_BITS) - 1;
14/// Maximum encodable fact id.
15pub const MAX_FACT_ID: u32 = (1 << FACT_BITS) - 1;
16
17/// Number of facts per workgroup lane. A 32-lane subgroup x
18/// 32 bits = 1024 facts; wider subgroup layouts preserve the same budget.
19/// Matches the
20/// NFA window sizing in `nfa::subgroup_nfa` so both subsystems
21/// share occupancy budget.
22pub const FACTS_PER_WORKGROUP: usize = 1024;
23const BLOCK_SHIFT: u32 = FACT_BITS;
24const PROC_SHIFT: u32 = FACT_BITS + BLOCK_BITS;
25const FACT_MASK: u32 = MAX_FACT_ID;
26const BLOCK_MASK: u32 = MAX_BLOCK_ID;
27const PROC_MASK: u32 = MAX_PROC_ID;
28/// Pack a `(proc_id, block_id, fact_id)` triple into a 32-bit
29/// node id.
30///
31/// Invalid triples have no non-aliasing `u32` representation, so the
32/// failure is explicit instead of silently clamping or masking.
33#[must_use]
34pub fn encode_node(proc_id: u32, block_id: u32, fact_id: u32) -> Option<u32> {
35    fits(proc_id, block_id, fact_id)
36        .then_some((proc_id << PROC_SHIFT) | (block_id << BLOCK_SHIFT) | fact_id)
37}
38
39/// Unpack a node id back into `(proc_id, block_id, fact_id)`.
40#[must_use]
41pub fn decode_node(node_id: u32) -> (u32, u32, u32) {
42    let proc_id = (node_id >> PROC_SHIFT) & PROC_MASK;
43    let block_id = (node_id >> BLOCK_SHIFT) & BLOCK_MASK;
44    let fact_id = node_id & FACT_MASK;
45    (proc_id, block_id, fact_id)
46}
47
48/// Whether a `(proc, block, fact)` triple fits in the packed
49/// 32-bit representation. Callers on the production path should
50/// verify this before calling [`encode_node`].
51#[must_use]
52pub fn fits(proc_id: u32, block_id: u32, fact_id: u32) -> bool {
53    proc_id <= MAX_PROC_ID && block_id <= MAX_BLOCK_ID && fact_id <= MAX_FACT_ID
54}
55/// Convert a dense `(proc, block, fact)` index  -  the space
56/// `build_cpu_reference` (requires the `cpu-parity` feature) operates in  -  into the packed
57/// [`encode_node`] form for reporting or cross-subsystem handoff.
58#[must_use]
59pub fn dense_to_encoded(dense: u32, blocks_per_proc: u32, facts_per_proc: u32) -> Option<u32> {
60    let slots_per_proc = blocks_per_proc.checked_mul(facts_per_proc)?;
61    if slots_per_proc == 0 {
62        return None;
63    }
64    let p = dense / slots_per_proc;
65    let within_proc = dense % slots_per_proc;
66    let b = within_proc / facts_per_proc;
67    let f = within_proc % facts_per_proc;
68    encode_node(p, b, f)
69}
70
71/// Inverse of [`dense_to_encoded`].
72#[must_use]
73pub fn encoded_to_dense(node_id: u32, blocks_per_proc: u32, facts_per_proc: u32) -> Option<u32> {
74    let (p, b, f) = decode_node(node_id);
75    let proc_span = blocks_per_proc.checked_mul(facts_per_proc)?;
76    let proc_offset = p.checked_mul(proc_span)?;
77    let block_offset = b.checked_mul(facts_per_proc)?;
78    proc_offset.checked_add(block_offset)?.checked_add(f)
79}