use super::*;
#[test]
fn fixed_point_batch_reuses_prepared_graph_buffers() {
let dispatch = |_: &vyre::ir::Program,
inputs: &[&[u8]],
_: Option<[u32; 3]>,
outputs: &mut Vec<Vec<u8>>| {
if inputs.len() < 6 {
return crate::fixed_point_closure::fallback_bitset_equal_dispatch(inputs, outputs);
}
let frontier = u32::from_le_bytes(inputs[5][..4].try_into().unwrap());
let next = frontier | 0b11;
if outputs.is_empty() {
outputs.push(Vec::new());
}
outputs[0].clear();
outputs[0].extend_from_slice(&next.to_le_bytes());
Ok(())
};
let graph = CsrGraph::new(
2,
&[0, 1, 1],
&[1],
&[vyre_primitives::predicate::edge_kind::CONTROL],
);
let mut batch = FixedPointBatch::new(&dispatch);
let prepared = batch
.prepare_reaching(graph)
.expect("batch must prepare reaching graph once");
let retained = prepared.retained_bytes();
let mut into = Vec::with_capacity(1);
let into_ptr = into.as_ptr();
let first = batch
.reaching_prepared(&prepared, &[0b01], 4)
.expect("first prepared reaching run must converge");
batch
.reaching_prepared_into(&prepared, &[0b01], 4, &mut into)
.expect("prepared reaching_into run must converge");
let second = batch
.reaching_prepared(&prepared, &[0b10], 4)
.expect("second prepared reaching run must converge");
assert_eq!(first, vec![0b11]);
assert_eq!(into, vec![0b11]);
assert_eq!(
into.as_ptr(),
into_ptr,
"reaching_prepared_into must reuse caller result allocation"
);
assert_eq!(second, vec![0b11]);
assert_eq!(prepared.retained_bytes(), retained);
}
#[test]
fn fixed_point_batch_repacks_prepared_graph_in_place() {
let dispatch = |_: &vyre::ir::Program,
inputs: &[&[u8]],
_: Option<[u32; 3]>,
outputs: &mut Vec<Vec<u8>>| {
if inputs.len() < 6 {
return crate::fixed_point_closure::fallback_bitset_equal_dispatch(inputs, outputs);
}
outputs.clear();
outputs.push(inputs[5].to_vec());
Ok(())
};
let first_graph = CsrGraph::new(
4,
&[0, 2, 3, 3, 3],
&[1, 2, 3],
&[
vyre_primitives::predicate::edge_kind::CONTROL,
vyre_primitives::predicate::edge_kind::ASSIGNMENT,
vyre_primitives::predicate::edge_kind::CONTROL,
],
);
let second_graph = CsrGraph::new(
4,
&[0, 1, 1, 2, 2],
&[2, 3],
&[
vyre_primitives::predicate::edge_kind::CONTROL,
vyre_primitives::predicate::edge_kind::CONTROL,
],
);
let mut batch = FixedPointBatch::new(&dispatch);
let mut prepared = batch
.prepare_reaching(first_graph)
.expect("initial reaching graph must prepare");
let pg_nodes_ptr = prepared.pg_nodes_bytes.as_ptr() as usize;
let edge_offsets_ptr = prepared.edge_offsets_bytes.as_ptr() as usize;
let edge_targets_ptr = prepared.edge_targets_bytes.as_ptr() as usize;
let edge_kind_ptr = prepared.edge_kind_mask_bytes.as_ptr() as usize;
let pg_tags_ptr = prepared.pg_node_tags_bytes.as_ptr() as usize;
let retained = prepared.retained_bytes();
let mask_scratch_ptr = batch.scratch().edge_kind_masks.as_ptr() as usize;
let mask_scratch_capacity = batch.scratch().edge_kind_mask_capacity();
batch
.prepare_reaching_into(second_graph, &mut prepared)
.expect("batch must repack reaching graph in place");
assert_eq!(prepared.node_count(), 4);
assert_eq!(prepared.edge_count(), 2);
assert_eq!(prepared.pg_nodes_bytes.as_ptr() as usize, pg_nodes_ptr);
assert_eq!(
prepared.edge_offsets_bytes.as_ptr() as usize,
edge_offsets_ptr
);
assert_eq!(
prepared.edge_targets_bytes.as_ptr() as usize,
edge_targets_ptr
);
assert_eq!(
prepared.edge_kind_mask_bytes.as_ptr() as usize,
edge_kind_ptr
);
assert_eq!(prepared.pg_node_tags_bytes.as_ptr() as usize, pg_tags_ptr);
assert_eq!(prepared.retained_bytes(), retained);
assert_eq!(
batch.scratch().edge_kind_masks.as_ptr() as usize,
mask_scratch_ptr
);
assert!(batch.scratch().edge_kind_mask_capacity() >= mask_scratch_capacity);
}
#[test]
fn fixed_point_batch_reuses_prepared_live_graph_buffers() {
let dispatch = |_: &vyre::ir::Program,
inputs: &[&[u8]],
_: Option<[u32; 3]>,
outputs: &mut Vec<Vec<u8>>| {
if inputs.len() < 6 {
return crate::fixed_point_closure::fallback_bitset_equal_dispatch(inputs, outputs);
}
let frontier = u32::from_le_bytes(inputs[5][..4].try_into().unwrap());
let next = frontier | 0b11;
if outputs.is_empty() {
outputs.push(Vec::new());
}
outputs[0].clear();
outputs[0].extend_from_slice(&next.to_le_bytes());
Ok(())
};
let graph = CsrGraph::new(
2,
&[0, 1, 1],
&[1],
&[vyre_primitives::predicate::edge_kind::CONTROL],
);
let mut batch = FixedPointBatch::new(&dispatch);
let prepared = batch
.prepare_live(graph)
.expect("batch must prepare live graph once");
let retained = prepared.retained_bytes();
let reverse_offsets_ptr = batch.scratch().reverse_offsets.as_ptr() as usize;
let reverse_targets_ptr = batch.scratch().reverse_targets.as_ptr() as usize;
let reverse_offsets_capacity = batch.scratch().reverse_offset_capacity();
let reverse_targets_capacity = batch.scratch().reverse_target_capacity();
let mut into = Vec::with_capacity(1);
let into_ptr = into.as_ptr();
let first = batch
.live_prepared(&prepared, &[0b10], 4)
.expect("first prepared live run must converge");
batch
.live_prepared_into(&prepared, &[0b10], 4, &mut into)
.expect("prepared live_into run must converge");
let second = batch
.live_prepared(&prepared, &[0b01], 4)
.expect("second prepared live run must converge");
assert_eq!(first, vec![0b11]);
assert_eq!(into, vec![0b11]);
assert_eq!(
into.as_ptr(),
into_ptr,
"live_prepared_into must reuse caller result allocation"
);
assert_eq!(second, vec![0b11]);
assert_eq!(prepared.retained_bytes(), retained);
assert_eq!(
batch.scratch().reverse_offsets.as_ptr() as usize,
reverse_offsets_ptr
);
assert_eq!(
batch.scratch().reverse_targets.as_ptr() as usize,
reverse_targets_ptr
);
assert!(batch.scratch().reverse_offset_capacity() >= reverse_offsets_capacity);
assert!(batch.scratch().reverse_target_capacity() >= reverse_targets_capacity);
}