#[allow(deprecated)]
use super::*;
use vyre::ir::Program;
use vyre_primitives::predicate::edge_kind;
#[test]
fn ifds_reach_mask_excludes_dominance_and_control() {
assert_eq!(
IFDS_REACH_MASK & edge_kind::DOMINANCE,
0,
"IFDS reach must NOT include DOMINANCE - pre-fix bug"
);
assert_eq!(
IFDS_REACH_MASK & edge_kind::CONTROL,
0,
"IFDS reach must NOT include CONTROL - only data + call edges"
);
assert!(
IFDS_REACH_MASK & edge_kind::ASSIGNMENT != 0,
"IFDS reach must include ASSIGNMENT"
);
assert!(
IFDS_REACH_MASK & edge_kind::CALL_ARG != 0,
"IFDS reach must include CALL_ARG"
);
assert_ne!(
IFDS_REACH_MASK, 0xFFFF_FFFF,
"IFDS_REACH_MASK regressed to 0xFFFF_FFFF - original PHASE6_DATAFLOW bug"
);
}
#[test]
fn ifds_reach_step_exploded_emits_real_program() {
let shape = super::super::ifds_gpu::IfdsShape {
num_procs: 4,
blocks_per_proc: 4,
facts_per_proc: 8,
edge_count: 16,
};
let p = ifds_reach_step_exploded(shape, "fin", "fout")
.expect("valid IFDS exploded step fixture must build");
let names: Vec<&str> = p.buffers.iter().map(|b| b.name()).collect();
assert!(names.contains(&"fin"), "frontier_in must be declared");
assert!(names.contains(&"fout"), "frontier_out must be declared");
assert!(
!p.entry.is_empty(),
"ifds_reach_step_exploded must emit a non-empty entry - pre-fix the IFDS module never imported ifds_gpu_step at all"
);
}
#[test]
fn ifds_soundness_is_mayover_not_exact() {
use super::super::soundness::{Soundness, SoundnessTagged};
assert_eq!(
Ifds.soundness(),
Soundness::MayOver,
"IFDS without sanitizer-gating is not Exact - must be MayOver"
);
}
#[test]
fn ifds_reach_closure_into_reuses_output_storage_across_iterations() {
let calls = std::cell::Cell::new(0_u32);
let saw_reused_slot = std::cell::Cell::new(false);
let dispatch =
|_: &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 call = calls.get();
if call > 0 && outputs.len() == 1 {
saw_reused_slot.set(true);
}
calls.set(call + 1);
let frontier = u32::from_le_bytes(inputs[5][..4].try_into().unwrap());
let next = if frontier & 1 != 0 {
frontier | 0b10
} else {
frontier
};
if outputs.is_empty() {
outputs.push(Vec::new());
}
outputs[0].clear();
outputs[0].extend_from_slice(&next.to_le_bytes());
Ok(())
};
let mut scratch = crate::fixed_point_scratch::FixedPointScratch::default();
let result = ifds_reach_closure_borrowed_into_with_scratch_via(
&dispatch,
2,
&[0, 1, 1],
&[1],
&[edge_kind::ASSIGNMENT],
&[0b01],
4,
&mut scratch,
)
.expect("ifds reach closure into dispatch must converge");
assert_eq!(result, vec![0b11]);
assert_eq!(calls.get(), 2);
assert!(saw_reused_slot.get());
assert!(scratch.frontier_word_capacity() >= 1);
let mut result_into = Vec::with_capacity(1);
let result_into_ptr = result_into.as_ptr();
ifds_reach_closure_borrowed_into_result_with_scratch_via(
&dispatch,
2,
&[0, 1, 1],
&[1],
&[edge_kind::ASSIGNMENT],
&[0b01],
4,
&mut scratch,
&mut result_into,
)
.expect("ifds reach closure result_into dispatch must converge");
assert_eq!(result_into, vec![0b11]);
assert_eq!(
result_into.as_ptr(),
result_into_ptr,
"ifds result_into must reuse caller result allocation"
);
}