use super::*;
#[test]
fn fixed_point_batch_reuses_prepared_reaching_plan() {
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 plan = batch
.prepare_reaching_plan(graph)
.expect("batch must prepare reaching plan once");
let retained = plan.retained_graph_bytes();
let mut into = Vec::with_capacity(1);
let into_ptr = into.as_ptr();
let first = batch
.reaching_plan(&plan, &[0b01], 4)
.expect("first planned reaching run must converge");
batch
.reaching_plan_into(&plan, &[0b01], 4, &mut into)
.expect("planned reaching_into run must converge");
let second = batch
.reaching_plan(&plan, &[0b10], 4)
.expect("second planned reaching run must converge");
assert_eq!(first, vec![0b11]);
assert_eq!(into, vec![0b11]);
assert_eq!(
into.as_ptr(),
into_ptr,
"reaching_plan_into must reuse caller result allocation"
);
assert_eq!(second, vec![0b11]);
assert_eq!(plan.retained_graph_bytes(), retained);
assert!(!plan.program().entry().is_empty());
}
#[test]
fn fixed_point_batch_repacks_prepared_plan_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 plan = batch
.prepare_reaching_plan(first_graph)
.expect("initial reaching plan must prepare");
let pg_nodes_ptr = plan.graph().pg_nodes_bytes.as_ptr() as usize;
let edge_offsets_ptr = plan.graph().edge_offsets_bytes.as_ptr() as usize;
let edge_targets_ptr = plan.graph().edge_targets_bytes.as_ptr() as usize;
let edge_kind_ptr = plan.graph().edge_kind_mask_bytes.as_ptr() as usize;
let pg_tags_ptr = plan.graph().pg_node_tags_bytes.as_ptr() as usize;
let retained = plan.retained_graph_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_plan_into(second_graph, &mut plan)
.expect("batch must repack reaching plan in place");
assert_eq!(
plan.kind(),
crate::fixed_point_graph::FixedPointAnalysisKind::Reaching
);
assert_eq!(plan.graph().node_count(), 4);
assert_eq!(plan.graph().edge_count(), 2);
assert_eq!(plan.graph().pg_nodes_bytes.as_ptr() as usize, pg_nodes_ptr);
assert_eq!(
plan.graph().edge_offsets_bytes.as_ptr() as usize,
edge_offsets_ptr
);
assert_eq!(
plan.graph().edge_targets_bytes.as_ptr() as usize,
edge_targets_ptr
);
assert_eq!(
plan.graph().edge_kind_mask_bytes.as_ptr() as usize,
edge_kind_ptr
);
assert_eq!(
plan.graph().pg_node_tags_bytes.as_ptr() as usize,
pg_tags_ptr
);
assert_eq!(plan.retained_graph_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);
assert!(!plan.program().entry().is_empty());
}
#[test]
fn fixed_point_batch_reuses_live_and_points_to_plans() {
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 control_graph = CsrGraph::new(
2,
&[0, 1, 1],
&[1],
&[vyre_primitives::predicate::edge_kind::CONTROL],
);
let points_to_graph = CsrGraph::new(
2,
&[0, 1, 1],
&[1],
&[vyre_primitives::predicate::edge_kind::ASSIGNMENT],
);
let mut batch = FixedPointBatch::new(&dispatch);
let live_plan = batch
.prepare_live_plan(control_graph)
.expect("batch must prepare live plan once");
let points_to_plan = batch
.prepare_points_to_subset_plan(points_to_graph)
.expect("batch must prepare points-to plan once");
let mut live_into = Vec::with_capacity(1);
let live_into_ptr = live_into.as_ptr();
let mut points_to_into = Vec::with_capacity(1);
let points_to_into_ptr = points_to_into.as_ptr();
let live = batch
.live_plan(&live_plan, &[0b10], 4)
.expect("planned live run must converge");
batch
.live_plan_into(&live_plan, &[0b10], 4, &mut live_into)
.expect("planned live_into run must converge");
let points_to = batch
.points_to_subset_plan(&points_to_plan, &[0b01], 4)
.expect("planned points-to run must converge");
batch
.points_to_subset_plan_into(&points_to_plan, &[0b01], 4, &mut points_to_into)
.expect("planned points-to_into run must converge");
assert_eq!(live, vec![0b11]);
assert_eq!(live_into, vec![0b11]);
assert_eq!(
live_into.as_ptr(),
live_into_ptr,
"live_plan_into must reuse caller result allocation"
);
assert_eq!(points_to, vec![0b11]);
assert_eq!(points_to_into, vec![0b11]);
assert_eq!(
points_to_into.as_ptr(),
points_to_into_ptr,
"points_to_subset_plan_into must reuse caller result allocation"
);
assert_eq!(
live_plan.kind(),
crate::fixed_point_graph::FixedPointAnalysisKind::Live
);
assert_eq!(
points_to_plan.kind(),
crate::fixed_point_graph::FixedPointAnalysisKind::PointsToSubset
);
assert!(!live_plan.program().entry().is_empty());
assert!(!points_to_plan.program().entry().is_empty());
}
#[test]
fn fixed_point_batch_reuses_slice_plan() {
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 plan = batch
.prepare_slice_plan(graph)
.expect("batch must prepare slice plan once");
let retained = plan.retained_graph_bytes();
let first = batch
.slice_plan(&plan, &[0b10], 4)
.expect("first planned slice run must converge");
let second = batch
.slice_plan(&plan, &[0b01], 4)
.expect("second planned slice run must converge");
assert_eq!(first, vec![0b11]);
assert_eq!(second, vec![0b11]);
assert_eq!(
plan.kind(),
crate::fixed_point_graph::FixedPointAnalysisKind::Slice
);
assert_eq!(plan.retained_graph_bytes(), retained);
assert!(!plan.program().entry().is_empty());
let mut result = Vec::with_capacity(1);
let result_ptr = result.as_ptr();
let result_capacity = result.capacity();
batch
.slice_plan_into(&plan, &[0b10], 4, &mut result)
.expect("planned slice into run must fill caller-owned result");
assert_eq!(result, vec![0b11]);
assert_eq!(result.as_ptr(), result_ptr);
assert_eq!(result.capacity(), result_capacity);
}