use std::collections::HashMap;
use std::path::Path;
use std::process::Command;
fn synth() -> &'static str {
env!("CARGO_BIN_EXE_synth")
}
fn fixture(rel: &str) -> std::path::PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join(rel)
}
fn compile(rel: &str, out: &str, flag: bool) -> (Vec<u8>, String) {
let mut cmd = Command::new(synth());
if flag {
cmd.env_remove("SYNTH_SPILL_REALLOC");
} else {
cmd.env("SYNTH_SPILL_REALLOC", "0");
}
let output = cmd
.env("SYNTH_SPILL_REPORT", "1")
.env("SYNTH_FUSE_STATS", "1")
.args([
"compile",
fixture(rel).to_str().unwrap(),
"-o",
out,
"-b",
"arm",
"--target",
"cortex-m4",
"--all-exports",
])
.output()
.expect("run synth compile");
assert!(output.status.success(), "synth compile failed ({rel})");
(
std::fs::read(out).expect("read ELF"),
String::from_utf8_lossy(&output.stderr).into_owned(),
)
}
fn func_sizes(elf: &[u8]) -> HashMap<String, usize> {
use object::{Object, ObjectSymbol, SymbolKind};
let obj = object::read::elf::ElfFile32::<object::Endianness>::parse(elf).expect("parse ELF");
let mut out = HashMap::new();
for sym in obj.symbols() {
if sym.kind() == SymbolKind::Text
&& sym.size() > 0
&& let Ok(name) = sym.name()
{
out.insert(name.to_string(), sym.size() as usize);
}
}
out
}
#[derive(Debug)]
struct SegReport {
peak: usize,
actual_reloads: usize,
actual_stores: usize,
belady_reloads: usize,
belady_stores: usize,
}
fn parse_reports(stderr: &str) -> Vec<SegReport> {
fn lead_digits(s: &str) -> usize {
let d: String = s.chars().take_while(|c| c.is_ascii_digit()).collect();
d.parse().expect("leading digits")
}
fn after<'a>(line: &'a str, field: &str) -> &'a str {
line.split(field)
.nth(1)
.unwrap_or_else(|| panic!("cannot parse `{field}` from: {line}"))
}
stderr
.lines()
.filter(|l| l.contains("[spill-report]"))
.map(|l| {
let actual = after(l, "actual=");
let belady = after(l, "belady(k=9)=");
SegReport {
peak: lead_digits(after(l, "peak=")),
actual_reloads: lead_digits(actual),
actual_stores: lead_digits(after(actual, "ld+")),
belady_reloads: lead_digits(belady),
belady_stores: lead_digits(after(belady, "ld+")),
}
})
.collect()
}
fn forwarded_total(stderr: &str) -> usize {
stderr
.lines()
.filter(|l| l.contains("[spill-realloc]"))
.map(|l| {
l.split("[spill-realloc] ")
.nth(1)
.and_then(|r| {
let d: String = r.chars().take_while(|c| c.is_ascii_digit()).collect();
d.parse::<usize>().ok()
})
.unwrap_or_else(|| panic!("cannot parse [spill-realloc] line: {l}"))
})
.sum()
}
#[test]
fn spill_realloc_no_grow_and_fires_on_flight_seam_242() {
let (off_elf, _) = compile(
"scripts/repro/flight_seam.wat",
"/tmp/sr242_fs_off.elf",
false,
);
let (on_elf, on_err) = compile(
"scripts/repro/flight_seam.wat",
"/tmp/sr242_fs_on.elf",
true,
);
let (off, on) = (func_sizes(&off_elf), func_sizes(&on_elf));
for (name, &o) in &off {
let n = *on.get(name).unwrap_or(&o);
assert!(
n <= o,
"no function may grow flag-on: {name} off={o}B on={n}B"
);
}
assert!(
on["flight_algo"] < off["flight_algo"],
"the spike must shrink flight_seam::flight_algo (measured 306→300 B): off={}B on={}B",
off["flight_algo"],
on["flight_algo"]
);
let fired = forwarded_total(&on_err);
assert!(
fired >= 3,
"expected ≥3 reloads forwarded on flight_seam (measured 3); got {fired} — the pass went inert"
);
eprintln!(
"[spill-realloc-242] flight_seam::flight_algo off={}B on={}B, {} reload(s) forwarded",
off["flight_algo"], on["flight_algo"], fired
);
let (ff_off_elf, ff_off_err) = compile(
"scripts/repro/flat_flight/flat_flight.loom.wasm",
"/tmp/sr242_ff_off.elf",
false,
);
let (ff_on_elf, ff_on_err) = compile(
"scripts/repro/flat_flight/flat_flight.loom.wasm",
"/tmp/sr242_ff_on.elf",
true,
);
let (ff_off, ff_on) = (func_sizes(&ff_off_elf), func_sizes(&ff_on_elf));
assert!(
ff_on["flat_flight"] < ff_off["flat_flight"],
"the Belady re-choice must shrink flat_flight (measured 412→388 B): \
off={}B on={}B",
ff_off["flat_flight"],
ff_on["flat_flight"]
);
assert!(
ff_off["flat_flight"] - ff_on["flat_flight"] >= 20,
"the three dissolved 4-byte reloads AND the two whole-function-dead \
4-byte stores must be gone: off={}B on={}B",
ff_off["flat_flight"],
ff_on["flat_flight"]
);
let ff_fired = forwarded_total(&ff_on_err);
assert!(
ff_fired >= 3,
"all 3 flat_flight reloads must dissolve via re-choice; got {ff_fired}"
);
let traffic = |stderr: &str| {
parse_reports(stderr).iter().fold((0, 0), |acc, r| {
(acc.0 + r.actual_reloads, acc.1 + r.actual_stores)
})
};
assert_eq!(
traffic(&ff_off_err),
(3, 3),
"flag-off flat_flight baseline frame traffic moved — re-measure the claim"
);
assert_eq!(
traffic(&ff_on_err),
(0, 0),
"flag-on flat_flight frame traffic: every reload must dissolve and \
every provably-unread store must drop (full Belady contract)"
);
eprintln!(
"[spill-rechoice-242] flat_flight off={}B on={}B, traffic {:?} -> {:?}, \
{} reload(s) dissolved",
ff_off["flat_flight"],
ff_on["flat_flight"],
traffic(&ff_off_err),
traffic(&ff_on_err),
ff_fired
);
}
#[test]
fn spill_report_shows_flat_flight_headroom_242() {
let (_, stderr) = compile(
"scripts/repro/flat_flight/flat_flight.loom.wasm",
"/tmp/sr242_ff_rep.elf",
false,
);
let reports = parse_reports(&stderr);
assert!(
!reports.is_empty(),
"no [spill-report] lines — the measure-only instrumentation went dead"
);
let hot = reports
.iter()
.find(|r| r.peak > 9)
.expect("flat_flight must report a peak>9 (pressure-saturated) segment");
let (actual, ideal) = (
hot.actual_reloads + hot.actual_stores,
hot.belady_reloads + hot.belady_stores,
);
assert!(
ideal < actual,
"the pressure-11 segment must show recovery headroom: actual {actual} vs belady {ideal}"
);
eprintln!(
"[spill-report-242] flat_flight hot segment: peak={} actual={}ld+{}st \
belady(k=9)={}ld+{}st — the VCR-RA-001 recovery target",
hot.peak, hot.actual_reloads, hot.actual_stores, hot.belady_reloads, hot.belady_stores
);
}