#![allow(deprecated)]
use super::*;
fn assert_dominates(dom: &[u32], words: usize, dominator: u32, dominated: u32) {
let row = &dom[dominated as usize * words..(dominated as usize + 1) * words];
let bit = dominator as usize;
assert!(
row[bit / 32] & (1u32 << (bit % 32)) != 0,
"expected {dominator} to dominate {dominated}"
);
}
fn assert_not_dominates(dom: &[u32], words: usize, dominator: u32, dominated: u32) {
let row = &dom[dominated as usize * words..(dominated as usize + 1) * words];
let bit = dominator as usize;
assert!(
row[bit / 32] & (1u32 << (bit % 32)) == 0,
"expected {dominator} to NOT dominate {dominated}"
);
}
#[test]
fn straight_line_chain() {
let succ = vec![vec![1], vec![2], vec![3], vec![]];
let dom = compute_cpu(4, &succ, 0);
let words = crate::graph_layout::LinearDomain::new(4).bitset_words() as usize;
for n in 0..=3 {
assert_dominates(&dom, words, 0, n);
}
assert_dominates(&dom, words, 1, 1);
assert_dominates(&dom, words, 1, 2);
assert_dominates(&dom, words, 1, 3);
assert_dominates(&dom, words, 2, 2);
assert_dominates(&dom, words, 2, 3);
assert_dominates(&dom, words, 3, 3);
}
#[test]
fn diamond_does_not_make_branch_dominate() {
let succ = vec![vec![1, 2], vec![3], vec![3], vec![]];
let dom = compute_cpu(4, &succ, 0);
let words = crate::graph_layout::LinearDomain::new(4).bitset_words() as usize;
for n in 0..=3 {
assert_dominates(&dom, words, 0, n);
}
let row3 = &dom[3 * words..4 * words];
assert!(
row3[0] & (1u32 << 1) == 0,
"1 must NOT dominate 3 in a diamond"
);
assert!(
row3[0] & (1u32 << 2) == 0,
"2 must NOT dominate 3 in a diamond"
);
}
#[test]
fn unreachable_node_keeps_entry_only() {
let succ = vec![vec![], vec![]];
let dom = compute_cpu(2, &succ, 0);
let words = crate::graph_layout::LinearDomain::new(2).bitset_words() as usize;
assert_dominates(&dom, words, 0, 0);
assert_dominates(&dom, words, 0, 1);
assert_not_dominates(&dom, words, 1, 0);
assert_not_dominates(&dom, words, 1, 1);
}
#[test]
fn malformed_cfg_shape_panics_in_cpu_oracle() {
let short_successor_table = std::panic::catch_unwind(|| {
let _ = compute_cpu(2, &[vec![1]], 0);
});
assert!(
short_successor_table.is_err(),
"successor table length must match node_count instead of silently treating missing rows as empty"
);
let invalid_entry = std::panic::catch_unwind(|| {
let _ = compute_cpu(2, &[vec![1], vec![]], 7);
});
assert!(
invalid_entry.is_err(),
"invalid entry must fail loudly instead of returning an all-zero dominator matrix"
);
let invalid_edge = std::panic::catch_unwind(|| {
let _ = compute_cpu(2, &[vec![2], vec![]], 0);
});
assert!(
invalid_edge.is_err(),
"out-of-range CFG edge must fail loudly instead of being dropped"
);
}
#[test]
fn compute_via_rejects_out_of_domain_output_tail_bits() {
let dispatch = |_: &vyre::ir::Program, _: &[Vec<u8>], _: Option<[u32; 3]>| {
Ok(vec![vyre_primitives::wire::pack_u32_slice(&[0u32, 0b100])])
};
let err = compute_via(&dispatch, 2, &[vec![1], vec![]], 0, 1)
.expect_err("dominator matrix output tail bit outside node_count must be rejected");
assert!(
err.contains("outside the declared domain"),
"unexpected diagnostic: {err}"
);
}
#[test]
fn compute_borrowed_into_reuses_matrix_output_slot() {
use std::cell::Cell;
let node_count = 3u32;
let words = crate::graph_layout::LinearDomain::new(node_count).bitset_words() as usize;
let mut outputs = vec![Vec::with_capacity(node_count as usize * words * 4)];
let outputs_addr = outputs.as_ptr() as usize;
let slot_addr = outputs[0].as_ptr() as usize;
let calls = Cell::new(0usize);
let out = compute_borrowed_into_via(
&|_, inputs, grid, outputs| {
assert_eq!(grid, Some([3, 1, 1]));
assert_eq!(inputs.len(), 4);
assert_eq!(outputs.len(), 1);
assert_eq!(outputs.as_ptr() as usize, outputs_addr);
assert_eq!(outputs[0].as_ptr() as usize, slot_addr);
let pred_offsets = unpack_words(inputs[0]);
let pred_targets = unpack_words(inputs[1]);
let current = unpack_words(inputs[2]);
assert_eq!(pred_offsets, vec![0, 0, 1, 3]);
assert_eq!(pred_targets, vec![0, 0, 1]);
assert_eq!(current.len(), 3);
let mut next = vec![0u32; 3];
for node in 0..3usize {
let start = pred_offsets[node] as usize;
let end = pred_offsets[node + 1] as usize;
next[node] = if node == 0 {
0b001
} else if start == end {
0b001
} else {
let mut acc = 0b111;
for &pred in &pred_targets[start..end] {
acc &= current[pred as usize];
}
acc | (1u32 << node)
};
}
outputs[0].clear();
for word in next {
outputs[0].extend_from_slice(&word.to_le_bytes());
}
assert_eq!(outputs[0].as_ptr() as usize, slot_addr);
calls.set(calls.get() + 1);
Ok(())
},
node_count,
&[vec![1, 2], vec![2], vec![]],
0,
8,
&mut outputs,
)
.expect("caller-owned matrix output scratch should decode");
assert_eq!(calls.get(), 2);
assert_eq!(out, compute_cpu(3, &[vec![1, 2], vec![2], vec![]], 0));
assert_eq!(outputs.len(), 1);
assert_eq!(outputs.as_ptr() as usize, outputs_addr);
assert_eq!(outputs[0].as_ptr() as usize, slot_addr);
}
fn unpack_words(bytes: &[u8]) -> Vec<u32> {
vyre_primitives::wire::decode_u32_le_bytes_all(bytes)
}