#[allow(deprecated)]
use super::*;
use vyre::ir::Node;
fn write_words(out: &mut Vec<u8>, words: &[u32]) {
out.clear();
for word in words {
out.extend_from_slice(&word.to_le_bytes());
}
}
fn read_word(bytes: &[u8], index: usize) -> u32 {
let start = index * 4;
let mut word = [0; 4];
word.copy_from_slice(&bytes[start..start + 4]);
u32::from_le_bytes(word)
}
#[test]
fn loop_summarize_emits_program_with_op_id() {
let p = loop_summarize_with_count("cfg", "ranges", "summary", 4);
let has_region = p.entry().iter().any(|n| {
matches!(
n,
Node::Region { generator, .. } if generator.as_str() == OP_ID
)
});
assert!(
has_region,
"weir::loop_sum: Program body must contain a Region tagged `{OP_ID}`"
);
}
#[test]
fn loop_summarize_buffer_count_matches_var_pairs() {
let p = loop_summarize_with_count("cfg", "ranges", "summary", 4);
for buf in p.buffers() {
assert_eq!(
buf.count(),
8,
"weir::loop_sum: buffer `{}` must have count=8 for 4 var-pairs",
buf.name()
);
}
}
#[test]
fn loop_summarize_zero_vars_is_valid() {
let p = loop_summarize_with_count("cfg", "ranges", "summary", 0);
assert!(
!p.buffers().is_empty(),
"weir::loop_sum: zero-var case must still produce a valid Program"
);
}
#[test]
fn loop_summarize_dispatch_uses_checked_program_builder() {
let dispatch_source = include_str!("../dispatch.rs");
assert!(
dispatch_source.contains("try_loop_summarize_with_count")
&& !dispatch_source.contains("let program = loop_summarize_with_count")
&& !dispatch_source.contains("use super::{loop_summarize_with_count"),
"weir::loop_sum dispatch must route through checked Program construction so oversized dimensions return errors before GPU setup."
);
let error = try_loop_summarize_with_count("cfg", "ranges", "summary", u32::MAX)
.expect_err("oversized variable count must return an actionable construction error");
assert!(
error.contains("overflows u32 buffer slots") && error.contains("shard"),
"unexpected checked-builder diagnostic: {error}"
);
}
#[test]
fn loop_summarize_soundness_is_mayover() {
use super::super::soundness::SoundnessTagged;
assert_eq!(
LoopSum.soundness(),
super::super::soundness::Soundness::MayOver,
"weir::loop_sum: Cousot widening is over-approximate"
);
}
#[test]
fn loop_summarize_into_reuses_caller_output_slot() {
use std::cell::Cell;
let mut outputs = vec![Vec::with_capacity(16)];
let outputs_addr = outputs.as_ptr() as usize;
let slot_addr = outputs[0].as_ptr() as usize;
let observed = Cell::new(false);
let out = loop_summarize_borrowed_into_via(
&|_, inputs, grid, outputs| {
assert_eq!(grid, Some([1, 1, 1]));
assert_eq!(inputs.len(), 3);
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 prev_lo = read_word(inputs[0], 0);
let prev_hi = read_word(inputs[0], 1);
let new_lo = read_word(inputs[1], 0);
let new_hi = read_word(inputs[1], 1);
let wide_lo = if new_lo < prev_lo { 0 } else { prev_lo };
let wide_hi = if prev_hi < new_hi { u32::MAX } else { prev_hi };
write_words(&mut outputs[0], &[wide_lo, wide_hi]);
assert_eq!(outputs[0].as_ptr() as usize, slot_addr);
observed.set(true);
Ok(())
},
&[10, 20],
&[9, 25],
1,
&mut outputs,
)
.expect("caller-owned output scratch should decode");
assert!(observed.get());
assert_eq!(out, vec![0, u32::MAX]);
assert_eq!(outputs.len(), 1);
assert_eq!(outputs.as_ptr() as usize, outputs_addr);
assert_eq!(outputs[0].as_ptr() as usize, slot_addr);
}
#[test]
fn loop_summarize_with_scratch_reuses_staging_and_output_slots() {
let mut scratch = LoopSummarizeScratch::default();
let dispatch = |_: &vyre::ir::Program,
inputs: &[&[u8]],
_: Option<[u32; 3]>|
-> Result<Vec<Vec<u8>>, String> {
let prev_lo = read_word(inputs[0], 0);
let prev_hi = read_word(inputs[0], 1);
let new_lo = read_word(inputs[1], 0);
let new_hi = read_word(inputs[1], 1);
let wide_lo = if new_lo < prev_lo { 0 } else { prev_lo };
let wide_hi = if prev_hi < new_hi { u32::MAX } else { prev_hi };
let mut out = Vec::with_capacity(64);
write_words(&mut out, &[wide_lo, wide_hi]);
Ok(vec![out])
};
let first =
loop_summarize_borrowed_with_scratch_via(&dispatch, &[10, 20], &[9, 25], 1, &mut scratch)
.expect("first scratch-backed loop summary should decode");
assert_eq!(first, vec![0, u32::MAX]);
let cfg_capacity = scratch.cfg_bytes.capacity();
let ranges_capacity = scratch.ranges_bytes.capacity();
let summary_capacity = scratch.summary_bytes.capacity();
let outputs_addr = scratch.outputs.as_ptr() as usize;
let slot_addr = scratch.outputs[0].as_ptr() as usize;
let second =
loop_summarize_borrowed_with_scratch_via(&dispatch, &[30, 40], &[31, 39], 1, &mut scratch)
.expect("second scratch-backed loop summary should decode");
assert_eq!(second, vec![30, 40]);
assert_eq!(scratch.cfg_bytes.capacity(), cfg_capacity);
assert_eq!(scratch.ranges_bytes.capacity(), ranges_capacity);
assert_eq!(scratch.summary_bytes.capacity(), summary_capacity);
assert_eq!(scratch.outputs.as_ptr() as usize, outputs_addr);
assert_eq!(scratch.outputs[0].as_ptr() as usize, slot_addr);
}
#[test]
fn loop_summarize_into_result_with_scratch_reuses_decoded_result() {
let mut scratch = LoopSummarizeScratch::default();
let mut result = Vec::with_capacity(8);
let result_addr = result.as_ptr() as usize;
let dispatch = |_: &vyre::ir::Program,
inputs: &[&[u8]],
_: Option<[u32; 3]>|
-> Result<Vec<Vec<u8>>, String> {
let prev_lo = read_word(inputs[0], 0);
let prev_hi = read_word(inputs[0], 1);
let new_lo = read_word(inputs[1], 0);
let new_hi = read_word(inputs[1], 1);
let wide_lo = if new_lo < prev_lo { 0 } else { prev_lo };
let wide_hi = if prev_hi < new_hi { u32::MAX } else { prev_hi };
let mut out = Vec::with_capacity(64);
write_words(&mut out, &[wide_lo, wide_hi]);
Ok(vec![out])
};
loop_summarize_borrowed_into_result_with_scratch_via(
&dispatch,
&[10, 20],
&[9, 25],
1,
&mut scratch,
&mut result,
)
.expect("first decoded-result scratch loop summary should decode");
assert_eq!(result, vec![0, u32::MAX]);
assert_eq!(result.as_ptr() as usize, result_addr);
loop_summarize_borrowed_into_result_with_scratch_via(
&dispatch,
&[30, 40],
&[31, 39],
1,
&mut scratch,
&mut result,
)
.expect("second decoded-result scratch loop summary should decode");
assert_eq!(result, vec![30, 40]);
assert_eq!(result.as_ptr() as usize, result_addr);
}