use std::path::{Path, PathBuf};
use std::process::Command;
use object::{Object, ObjectSection, ObjectSymbol, SymbolKind};
fn synth() -> &'static str {
env!("CARGO_BIN_EXE_synth")
}
fn repro(rel: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("scripts/repro")
.join(rel)
}
fn wat_fixture(rel: &str) -> Vec<u8> {
let wat = std::fs::read(repro(rel)).expect("read fixture wat");
wat::parse_bytes(&wat)
.expect("fixture wat must parse")
.into_owned()
}
fn per_function_sizes(wasm: &[u8], tag: &str, target: &str, fact_spec: bool) -> Vec<(String, u64)> {
let dir = std::env::temp_dir().join("parity_benchmark_735");
std::fs::create_dir_all(&dir).expect("mk tempdir");
let input = dir.join(format!("{tag}.wasm"));
let elf = dir.join(format!("{tag}.o"));
std::fs::write(&input, wasm).expect("write wasm");
let mut cmd = Command::new(synth());
cmd.args([
"compile",
input.to_str().unwrap(),
"-o",
elf.to_str().unwrap(),
"-b",
"arm",
"--target",
target,
"--all-exports",
]);
if fact_spec {
cmd.env("SYNTH_FACT_SPEC", "1");
} else {
cmd.env_remove("SYNTH_FACT_SPEC");
}
let out = cmd.output().expect("run synth");
assert!(
out.status.success(),
"synth compile of '{tag}' failed: {}",
String::from_utf8_lossy(&out.stderr)
);
if fact_spec {
let stderr = String::from_utf8_lossy(&out.stderr);
assert_eq!(
stderr.matches("fact-spec: ADMIT").count(),
2,
"expected 2 ordeal-certified elisions; stderr:\n{stderr}"
);
}
let bytes = std::fs::read(&elf).expect("read elf");
let obj = object::File::parse(&*bytes).expect("parse elf");
let text = obj.section_by_name(".text").expect(".text");
let end = text.address() + text.size();
let mut starts: Vec<(u64, String)> = obj
.symbols()
.filter(|s| {
!s.name().unwrap_or("").is_empty()
&& matches!(
s.kind(),
SymbolKind::Text | SymbolKind::Label | SymbolKind::Unknown
)
&& s.address() >= text.address()
&& s.address() < end
})
.map(|s| (s.address() & !1, s.name().unwrap().to_string()))
.collect();
starts.sort();
starts.dedup_by(|a, b| a.0 == b.0);
starts
.iter()
.enumerate()
.map(|(i, (addr, name))| {
let next = starts.get(i + 1).map(|(a, _)| *a).unwrap_or(end);
(name.clone(), next - addr)
})
.collect()
}
fn assert_pinned(sizes: &[(String, u64)], name: &str, locked: u64) {
let got = sizes
.iter()
.find(|(n, _)| n == name)
.unwrap_or_else(|| panic!("function {name} not found in .text"))
.1;
assert_eq!(
got,
locked,
"{name}: {got} B (locked {locked} B, delta {:+}) — the #735 parity \
benchmark pins this MEASURED number. If a change intentionally moved \
it, repin here AND regenerate the report: \
SYNTH=$CARGO_TARGET_DIR/debug/synth \
python3 scripts/repro/parity_benchmark/run.py --report",
got as i64 - locked as i64
);
}
#[test]
fn clamp_default_path_is_pinned_735() {
let wasm = wat_fixture("fact_spec_clamp_494.wat");
let sizes = per_function_sizes(&wasm, "clamp_default", "cortex-m4", false);
assert_pinned(&sizes, "gust_mix", 84);
}
#[test]
fn flat_flight_is_pinned_735() {
let wasm = std::fs::read(repro("flat_flight/flat_flight.loom.wasm")).expect("read fixture");
let sizes = per_function_sizes(&wasm, "flat_flight", "cortex-m4", false);
assert_pinned(&sizes, "flat_flight", 384);
}
#[test]
fn falcon_axis_is_pinned_735() {
let wasm = wat_fixture("parity_benchmark/falcon_axis.wat");
let sizes = per_function_sizes(&wasm, "falcon_axis", "cortex-m4f", false);
assert_pinned(&sizes, "falcon_axis", 72);
}
#[cfg(feature = "verify")]
#[test]
fn clamp_fact_spec_is_pinned_735() {
fn leb_u32(mut v: u32, out: &mut Vec<u8>) {
loop {
let mut b = (v & 0x7f) as u8;
v >>= 7;
if v != 0 {
b |= 0x80;
}
out.push(b);
if v == 0 {
return;
}
}
}
fn leb_s64(mut v: i64, out: &mut Vec<u8>) {
loop {
let mut b = (v & 0x7f) as u8;
v >>= 7;
let done = (v == 0 && b & 0x40 == 0) || (v == -1 && b & 0x40 != 0);
if !done {
b |= 0x80;
}
out.push(b);
if done {
return;
}
}
}
let mut wasm = wat_fixture("fact_spec_clamp_494.wat");
let mut body = Vec::new();
leb_s64(524, &mut body);
leb_s64(1524, &mut body);
let mut payload = vec![0x01]; leb_u32(1, &mut payload); payload.push(0x01); leb_u32(0, &mut payload); leb_u32(0, &mut payload); leb_u32(body.len() as u32, &mut payload);
payload.extend_from_slice(&body);
let name = b"wsc.facts";
let mut content = Vec::new();
leb_u32(name.len() as u32, &mut content);
content.extend_from_slice(name);
content.extend_from_slice(&payload);
wasm.push(0x00);
leb_u32(content.len() as u32, &mut wasm);
wasm.extend_from_slice(&content);
let sizes = per_function_sizes(&wasm, "clamp_spec", "cortex-m4", true);
assert_pinned(&sizes, "gust_mix", 14);
}