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);
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",
"d0907e0206e8abd5724dec30eb261f2b9d689c6ec1a3d3b66853691fb532de82",
294usize,
),
(
"flight_seam.wasm",
"92fd68638e4074024f54c7744775f44aa88d2f5b36abbd0963d27d62b95e1e97",
706,
),
(
"flight_seam_flat.wasm",
"660c3fbc3a58820b13949412e11f79d47eccfb4de7cfa737a781abe01e4e1b96",
846,
),
(
"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",
"9e73eea3867ba085820329951e84a7d650c38a7fc78d9d03a6a83d02963f9670",
774usize,
),
(
"flight_seam_flat.wasm",
"887ea546429a4569112147fdc94b0ba90f02a6ccd2b511aa2ca48dab017dbc2c",
910,
),
];
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");
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",
"dce728b48e14aa9187774799c0b268c6ad60be6dc0fa3e7cc9458c17dc65403b",
738usize,
),
(
"flight_seam_flat.wasm",
"0665e623f33457479940046d57a4b7ec02416a61bcc6ec71ea3556ed5b3cb568",
878,
),
];
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");
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",
"1a97711cfb4754794a8577814388f08b81eff444edcba3de7d3e3d18ff435183",
304usize,
),
(
"flight_seam.wasm",
"6872d6f3f00331e9a52e176428fca375a87b9fbe0cf2b3eb0fb657c304a7372d",
730,
),
];
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");
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",
"1e1d2b75fe6c4975359c8b1163a6e082a548a0ce1e23d7a360495785bf5e54c2",
726usize,
),
(
"flight_seam_flat.wasm",
"d11849db9bef82b77280ee06fdc6f076a7df278b2e5a3213284e569cbf1eccc9",
866,
),
];
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");
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",
"158b036bc678261a6f6ee71450d0af7b23d92415d16d21679347e2a436c25bb2",
300usize,
)];
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");
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");
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");
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");
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)"
);
}
}