use std::process::Command;
use object::{Object, ObjectSection};
use sha2::{Digest, Sha256};
fn synth() -> &'static str {
env!("CARGO_BIN_EXE_synth")
}
fn fixture(name: &str) -> std::path::PathBuf {
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("scripts/repro")
.join(name)
}
fn text_sha256(wasm: &str, backend: &str, target: &str) -> (String, usize) {
let path = fixture(wasm);
let elf = format!("/tmp/frozenbytes_{backend}_{wasm}.elf");
let out = Command::new(synth())
.env_remove("SYNTH_NO_CMP_SELECT_FUSE")
.env_remove("SYNTH_NO_LOCAL_PROMOTE")
.env_remove("SYNTH_NO_IMM_SHIFT_FOLD")
.env_remove("SYNTH_NO_STACK_FWD")
.env_remove("SYNTH_SPILL_REALLOC")
.env_remove("SYNTH_CONST_CSE")
.env_remove("SYNTH_BASE_CSE")
.env_remove("SYNTH_RV_CMP_SELECT")
.env_remove("SYNTH_RV_LOCAL_PROMO")
.env_remove("SYNTH_RV_SHIFT_FOLD")
.env_remove("SYNTH_DEAD_FRAME_ELIM")
.env_remove("SYNTH_UXTH_FOLD")
.args([
"compile",
path.to_str().unwrap(),
"-o",
&elf,
"-b",
backend,
"--target",
target,
"--all-exports",
"--relocatable",
])
.output()
.expect("run synth");
assert!(
out.status.success(),
"synth compile failed for {wasm} ({backend}/{target}): {}",
String::from_utf8_lossy(&out.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("frozen fixture must have a .text section");
let data = text.data().expect("read .text");
let mut hasher = Sha256::new();
hasher.update(data);
let digest = hasher.finalize();
let hex: String = digest.iter().map(|b| format!("{b:02x}")).collect();
(hex, data.len())
}
fn assert_frozen(cases: &[(&str, &str, usize)], backend: &str, target: &str) {
for &(wasm, golden, golden_len) in cases {
let (got, len) = text_sha256(wasm, backend, target);
if std::env::var("SYNTH_REFREEZE_PRINT").is_ok() {
println!("REPIN ({backend}/{target}): (\"{wasm}\", \"{got}\", {len}),");
continue;
}
assert_eq!(
len, golden_len,
"{wasm} ({backend}): .text length changed ({len} vs locked {golden_len}) \
— codegen moved. If intentional, re-freeze the golden HERE and re-run \
scripts/repro/*_differential.py on this commit (they move together)."
);
assert_eq!(
got, golden,
"{wasm} ({backend}): .text SHA-256 changed — a frozen fixture's machine \
code drifted with no deliberate re-freeze. This is the gate working: \
either a codegen change leaked in (investigate — dep bump? flag? \
selector?), or it is intentional, in which case update the golden HERE \
and re-run the scripts/repro/*_differential.py result checks on the \
SAME commit."
);
}
}
#[test]
fn frozen_fixtures_text_is_bit_identical_oracle_001() {
let cases = [
(
"control_step.wasm",
"f962794f128223b5212fc5cc25bb8393bcae758e89b93c07eb23deb4fffe5c0d",
314usize,
),
(
"flight_seam.wasm",
"28642d60533c3fc154dfc7ab27e6a9f4ffe5b0a81adfe4c0fba493d490fc8d2c",
870,
),
(
"flight_seam_flat.wasm",
"643882379d3c25cd1acc34129f5456d54b6864b127184c1f81823daeb267401c",
1014,
),
(
"signed_div_const.wasm",
"b277453b7829a5b2c64527131298d89fa63f5641231b1d4c7336675e8cdab9b0",
34,
),
];
assert_frozen(&cases, "arm", "cortex-m4");
}
#[test]
fn frozen_fixtures_stack_fwd_escape_hatch_restores_old_bytes() {
let old = [
(
"flight_seam.wasm",
"a11ed21e5fe53cdd3bc45382e7ad4288a53abf7fbdffa42c299cf1d2bc24f1da",
938usize,
),
(
"flight_seam_flat.wasm",
"973a630e6ed687ec02dc73ce9f79cb8261455fceaca1f373c51279d23943ef8a",
1078,
),
];
for &(wasm, golden, golden_len) in &old {
let elf = format!("/tmp/frozen_nofwd_{wasm}.elf");
let out = Command::new(synth())
.env("SYNTH_NO_STACK_FWD", "1")
.env("SYNTH_SPILL_REALLOC", "0")
.env("SYNTH_CONST_CSE", "0")
.env("SYNTH_DEAD_FRAME_ELIM", "0")
.env("SYNTH_UXTH_FOLD", "0")
.env_remove("SYNTH_NO_CMP_SELECT_FUSE")
.env_remove("SYNTH_NO_LOCAL_PROMOTE")
.env_remove("SYNTH_NO_IMM_SHIFT_FOLD")
.env_remove("SYNTH_BASE_CSE")
.args([
"compile",
fixture(wasm).to_str().unwrap(),
"-o",
&elf,
"-b",
"arm",
"--target",
"cortex-m4",
"--all-exports",
"--relocatable",
])
.output()
.expect("run synth");
assert!(out.status.success(), "compile failed for {wasm}");
let bytes = std::fs::read(&elf).expect("read elf");
let obj = object::File::parse(&*bytes).expect("parse elf");
let data = obj
.section_by_name(".text")
.expect(".text")
.data()
.expect("read .text");
if std::env::var("SYNTH_REFREEZE_PRINT").is_ok() {
let hex: String = Sha256::digest(data)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
println!("REPIN: (\"{wasm}\", \"{hex}\", {}),", data.len());
continue;
}
assert_eq!(
data.len(),
golden_len,
"{wasm}: SYNTH_NO_STACK_FWD must restore the pre-flip length"
);
let hex: String = Sha256::digest(data)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
assert_eq!(
hex, golden,
"{wasm}: SYNTH_NO_STACK_FWD=1 must restore the pre-flip bytes (rollback broken)"
);
}
}
#[test]
fn frozen_fixtures_spill_realloc_escape_hatch_restores_old_bytes() {
let old = [
(
"flight_seam.wasm",
"b590aea84d62b5dd58702a1a4dd5628683615b0482f1610bdf700509f8c8e360",
902usize,
),
(
"flight_seam_flat.wasm",
"49ebf8a1b48d96f66ad4d91f401cd78ce8bcbacc9340941039301a18aba2ec83",
1046,
),
];
for &(wasm, golden, golden_len) in &old {
let elf = format!("/tmp/frozen_nospillrealloc_{wasm}.elf");
let out = Command::new(synth())
.env("SYNTH_SPILL_REALLOC", "0")
.env("SYNTH_CONST_CSE", "0")
.env("SYNTH_DEAD_FRAME_ELIM", "0")
.env("SYNTH_UXTH_FOLD", "0")
.env_remove("SYNTH_NO_CMP_SELECT_FUSE")
.env_remove("SYNTH_NO_LOCAL_PROMOTE")
.env_remove("SYNTH_NO_IMM_SHIFT_FOLD")
.env_remove("SYNTH_NO_STACK_FWD")
.env_remove("SYNTH_BASE_CSE")
.args([
"compile",
fixture(wasm).to_str().unwrap(),
"-o",
&elf,
"-b",
"arm",
"--target",
"cortex-m4",
"--all-exports",
"--relocatable",
])
.output()
.expect("run synth");
assert!(out.status.success(), "compile failed for {wasm}");
let bytes = std::fs::read(&elf).expect("read elf");
let obj = object::File::parse(&*bytes).expect("parse elf");
let data = obj
.section_by_name(".text")
.expect(".text")
.data()
.expect("read .text");
if std::env::var("SYNTH_REFREEZE_PRINT").is_ok() {
let hex: String = Sha256::digest(data)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
println!("REPIN: (\"{wasm}\", \"{hex}\", {}),", data.len());
continue;
}
assert_eq!(
data.len(),
golden_len,
"{wasm}: SYNTH_SPILL_REALLOC=0 must restore the pre-flip length"
);
let hex: String = Sha256::digest(data)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
assert_eq!(
hex, golden,
"{wasm}: SYNTH_SPILL_REALLOC=0 must restore the pre-flip bytes (rollback broken)"
);
}
}
#[test]
fn frozen_fixtures_const_cse_escape_hatch_restores_old_bytes() {
let old = [
(
"control_step.wasm",
"93ef2610ce177e30e28767182ceecfa455d4e69968429145c967f16d7b2e22f1",
324usize,
),
(
"flight_seam.wasm",
"d8af257f82594e0a41d75c2fba2aaee62041fff54863342f4a9c3aebe39e10f3",
894,
),
];
for &(wasm, golden, golden_len) in &old {
let elf = format!("/tmp/frozen_nocse_{wasm}.elf");
let out = Command::new(synth())
.env("SYNTH_CONST_CSE", "0")
.env("SYNTH_DEAD_FRAME_ELIM", "0")
.env("SYNTH_UXTH_FOLD", "0")
.env_remove("SYNTH_NO_CMP_SELECT_FUSE")
.env_remove("SYNTH_NO_LOCAL_PROMOTE")
.env_remove("SYNTH_NO_IMM_SHIFT_FOLD")
.env_remove("SYNTH_NO_STACK_FWD")
.env_remove("SYNTH_SPILL_REALLOC")
.env_remove("SYNTH_BASE_CSE")
.args([
"compile",
fixture(wasm).to_str().unwrap(),
"-o",
&elf,
"-b",
"arm",
"--target",
"cortex-m4",
"--all-exports",
"--relocatable",
])
.output()
.expect("run synth");
assert!(out.status.success(), "compile failed for {wasm}");
let bytes = std::fs::read(&elf).expect("read elf");
let obj = object::File::parse(&*bytes).expect("parse elf");
let data = obj
.section_by_name(".text")
.expect(".text")
.data()
.expect("read .text");
if std::env::var("SYNTH_REFREEZE_PRINT").is_ok() {
let hex: String = Sha256::digest(data)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
println!("REPIN: (\"{wasm}\", \"{hex}\", {}),", data.len());
continue;
}
assert_eq!(
data.len(),
golden_len,
"{wasm}: SYNTH_CONST_CSE=0 must restore the pre-flip length"
);
let hex: String = Sha256::digest(data)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
assert_eq!(
hex, golden,
"{wasm}: SYNTH_CONST_CSE=0 must restore the pre-flip bytes (rollback broken)"
);
}
}
#[test]
fn frozen_fixtures_dead_frame_elim_escape_hatch_restores_old_bytes() {
let old = [
(
"flight_seam.wasm",
"18163e0b37f5932f0e97fe573d9626f53ca1962b823c24e8f5057a34cf8c4318",
890usize,
),
(
"flight_seam_flat.wasm",
"d62bdfa3faed21aef1ad943d824817f3adb04f0f400c1dd498690a437dd2bf43",
1034,
),
];
for &(wasm, golden, golden_len) in &old {
let elf = format!("/tmp/frozen_nodfe_{wasm}.elf");
let out = Command::new(synth())
.env("SYNTH_DEAD_FRAME_ELIM", "0")
.env_remove("SYNTH_UXTH_FOLD")
.env_remove("SYNTH_NO_CMP_SELECT_FUSE")
.env_remove("SYNTH_NO_LOCAL_PROMOTE")
.env_remove("SYNTH_NO_IMM_SHIFT_FOLD")
.env_remove("SYNTH_NO_STACK_FWD")
.env_remove("SYNTH_SPILL_REALLOC")
.env_remove("SYNTH_CONST_CSE")
.env_remove("SYNTH_BASE_CSE")
.args([
"compile",
fixture(wasm).to_str().unwrap(),
"-o",
&elf,
"-b",
"arm",
"--target",
"cortex-m4",
"--all-exports",
"--relocatable",
])
.output()
.expect("run synth");
assert!(out.status.success(), "compile failed for {wasm}");
let bytes = std::fs::read(&elf).expect("read elf");
let obj = object::File::parse(&*bytes).expect("parse elf");
let data = obj
.section_by_name(".text")
.expect(".text")
.data()
.expect("read .text");
if std::env::var("SYNTH_REFREEZE_PRINT").is_ok() {
let hex: String = Sha256::digest(data)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
println!("REPIN: (\"{wasm}\", \"{hex}\", {}),", data.len());
continue;
}
assert_eq!(
data.len(),
golden_len,
"{wasm}: SYNTH_DEAD_FRAME_ELIM=0 must restore the pre-flip length"
);
let hex: String = Sha256::digest(data)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
assert_eq!(
hex, golden,
"{wasm}: SYNTH_DEAD_FRAME_ELIM=0 must restore the pre-flip bytes (rollback broken)"
);
}
}
#[test]
fn frozen_fixtures_uxth_fold_escape_hatch_restores_old_bytes() {
let old = [(
"control_step.wasm",
"fdcfbac168dcd71ae4a981103f618bb661ffb74c9fd2248c876e5c9b007a65fe",
320usize,
)];
for &(wasm, golden, golden_len) in &old {
let elf = format!("/tmp/frozen_nouxth_{wasm}.elf");
let out = Command::new(synth())
.env("SYNTH_UXTH_FOLD", "0")
.env_remove("SYNTH_DEAD_FRAME_ELIM")
.env_remove("SYNTH_NO_CMP_SELECT_FUSE")
.env_remove("SYNTH_NO_LOCAL_PROMOTE")
.env_remove("SYNTH_NO_IMM_SHIFT_FOLD")
.env_remove("SYNTH_NO_STACK_FWD")
.env_remove("SYNTH_SPILL_REALLOC")
.env_remove("SYNTH_CONST_CSE")
.env_remove("SYNTH_BASE_CSE")
.args([
"compile",
fixture(wasm).to_str().unwrap(),
"-o",
&elf,
"-b",
"arm",
"--target",
"cortex-m4",
"--all-exports",
"--relocatable",
])
.output()
.expect("run synth");
assert!(out.status.success(), "compile failed for {wasm}");
let bytes = std::fs::read(&elf).expect("read elf");
let obj = object::File::parse(&*bytes).expect("parse elf");
let data = obj
.section_by_name(".text")
.expect(".text")
.data()
.expect("read .text");
if std::env::var("SYNTH_REFREEZE_PRINT").is_ok() {
let hex: String = Sha256::digest(data)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
println!("REPIN: (\"{wasm}\", \"{hex}\", {}),", data.len());
continue;
}
assert_eq!(
data.len(),
golden_len,
"{wasm}: SYNTH_UXTH_FOLD=0 must restore the pre-flip length"
);
let hex: String = Sha256::digest(data)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
assert_eq!(
hex, golden,
"{wasm}: SYNTH_UXTH_FOLD=0 must restore the pre-flip bytes (rollback broken)"
);
}
}
#[test]
fn frozen_fixtures_rv32_text_is_bit_identical_oracle_001() {
let cases = [
(
"control_step.wasm",
"6ac5d7f94f3e17b0314aa2549e24b172b50dc0df110130c80a94e19d62c7b75b",
484usize,
),
(
"signed_div_const.wasm",
"15fa429d5ef5474f8b65fdd9c81b3da4c70176fb077df25131e2ec3988eb999e",
88,
),
];
assert_frozen(&cases, "riscv", "rv32imac");
}
#[test]
fn frozen_fixtures_rv32_shift_fold_escape_hatch_restores_old_bytes() {
let cases = [
(
"control_step.wasm",
"780e427a7ce94b54e0aaad0165e4984bebc1c0c43d25def5b5e9abf6a3929fed",
492usize,
),
(
"signed_div_const.wasm",
"15fa429d5ef5474f8b65fdd9c81b3da4c70176fb077df25131e2ec3988eb999e",
88,
),
];
for &(wasm, golden, golden_len) in &cases {
let path = fixture(wasm);
let elf = format!("/tmp/frozenbytes_rv32_shiftfold_off_{wasm}.elf");
let out = Command::new(synth())
.env("SYNTH_RV_LOCAL_PROMO", "0")
.env_remove("SYNTH_RV_CMP_SELECT")
.env("SYNTH_RV_SHIFT_FOLD", "0")
.args([
"compile",
path.to_str().unwrap(),
"-o",
&elf,
"-b",
"riscv",
"--target",
"rv32imac",
"--all-exports",
"--relocatable",
])
.output()
.expect("run synth");
assert!(out.status.success(), "compile failed for {wasm}");
let bytes = std::fs::read(&elf).expect("read elf");
let obj = object::File::parse(&*bytes).expect("parse elf");
let data = obj
.section_by_name(".text")
.expect(".text")
.data()
.expect("read .text");
if std::env::var("SYNTH_REFREEZE_PRINT").is_ok() {
let hex: String = Sha256::digest(data)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
println!("REPIN: (\"{wasm}\", \"{hex}\", {}),", data.len());
continue;
}
assert_eq!(
data.len(),
golden_len,
"{wasm}: SYNTH_RV_SHIFT_FOLD=0 must restore the pre-flip length"
);
let hex: String = Sha256::digest(data)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
assert_eq!(
hex, golden,
"{wasm}: SYNTH_RV_SHIFT_FOLD=0 must restore the pre-flip bytes (rollback broken)"
);
}
}
#[test]
fn frozen_fixtures_rv32_cmp_select_escape_hatch_restores_old_bytes() {
let cases = [
(
"control_step.wasm",
"6e734c4c67b651067a56ff2a7593015f48ea24fe068a7a20c17f1c08a4e48e83",
504usize,
),
(
"signed_div_const.wasm",
"15fa429d5ef5474f8b65fdd9c81b3da4c70176fb077df25131e2ec3988eb999e",
88,
),
];
for &(wasm, golden, golden_len) in &cases {
let path = fixture(wasm);
let elf = format!("/tmp/frozenbytes_rv32_cmpsel_off_{wasm}.elf");
let out = Command::new(synth())
.env("SYNTH_RV_LOCAL_PROMO", "0")
.env("SYNTH_RV_CMP_SELECT", "0")
.env("SYNTH_RV_SHIFT_FOLD", "0")
.args([
"compile",
path.to_str().unwrap(),
"-o",
&elf,
"-b",
"riscv",
"--target",
"rv32imac",
"--all-exports",
"--relocatable",
])
.output()
.expect("run synth");
assert!(out.status.success(), "compile failed for {wasm}");
let bytes = std::fs::read(&elf).expect("read elf");
let obj = object::File::parse(&*bytes).expect("parse elf");
let data = obj
.section_by_name(".text")
.expect(".text")
.data()
.expect("read .text");
if std::env::var("SYNTH_REFREEZE_PRINT").is_ok() {
let hex: String = Sha256::digest(data)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
println!("REPIN: (\"{wasm}\", \"{hex}\", {}),", data.len());
continue;
}
assert_eq!(
data.len(),
golden_len,
"{wasm}: SYNTH_RV_CMP_SELECT=0 must restore the pre-flip length"
);
let hex: String = Sha256::digest(data)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
assert_eq!(
hex, golden,
"{wasm}: SYNTH_RV_CMP_SELECT=0 must restore the pre-flip bytes (rollback broken)"
);
}
}
#[test]
fn frozen_fixtures_rv32_local_promo_escape_hatch_is_noop() {
let cases = [
(
"control_step.wasm",
"6ac5d7f94f3e17b0314aa2549e24b172b50dc0df110130c80a94e19d62c7b75b",
484usize,
),
(
"signed_div_const.wasm",
"15fa429d5ef5474f8b65fdd9c81b3da4c70176fb077df25131e2ec3988eb999e",
88,
),
];
for &(wasm, golden, golden_len) in &cases {
let path = fixture(wasm);
let elf = format!("/tmp/frozenbytes_rv32_promo_off_{wasm}.elf");
let out = Command::new(synth())
.env("SYNTH_RV_LOCAL_PROMO", "0")
.env_remove("SYNTH_RV_CMP_SELECT")
.env_remove("SYNTH_RV_SHIFT_FOLD")
.args([
"compile",
path.to_str().unwrap(),
"-o",
&elf,
"-b",
"riscv",
"--target",
"rv32imac",
"--all-exports",
"--relocatable",
])
.output()
.expect("run synth");
assert!(out.status.success(), "compile failed for {wasm}");
let bytes = std::fs::read(&elf).expect("read elf");
let obj = object::File::parse(&*bytes).expect("parse elf");
let data = obj
.section_by_name(".text")
.expect(".text")
.data()
.expect("read .text");
if std::env::var("SYNTH_REFREEZE_PRINT").is_ok() {
let hex: String = Sha256::digest(data)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
println!("REPIN: (\"{wasm}\", \"{hex}\", {}),", data.len());
continue;
}
assert_eq!(
data.len(),
golden_len,
"{wasm}: SYNTH_RV_LOCAL_PROMO=0 must restore the pre-flip length"
);
let hex: String = Sha256::digest(data)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
assert_eq!(
hex, golden,
"{wasm}: SYNTH_RV_LOCAL_PROMO=0 must restore the pre-flip bytes (rollback broken)"
);
}
}