use std::collections::HashMap;
use std::path::Path;
use std::process::Command;
use object::{Object, ObjectSymbol, SymbolKind};
mod artifact_guard;
fn synth() -> &'static str {
env!("CARGO_BIN_EXE_synth")
}
fn fixture(rel: &str) -> std::path::PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("scripts/repro")
.join(rel)
}
fn compile(rel: &str, tag: &str, flag: &str, default_on: bool, backend_args: &[&str]) -> Vec<u8> {
let out = artifact_guard::unique_artifact(tag, "o");
let mut cmd = Command::new(synth());
cmd.env("SYNTH_RV_LOCAL_PROMO", "0");
if default_on {
cmd.env_remove(flag);
} else {
cmd.env(flag, "0");
}
cmd.args([
"compile",
fixture(rel).to_str().unwrap(),
"-o",
out.to_str().unwrap(),
])
.args(backend_args)
.args(["--all-exports"]);
artifact_guard::compile_bytes_or_panic(
&mut cmd,
&out,
&format!("{rel} ({flag} default_on={default_on})"),
)
}
fn func_sizes(elf: &[u8]) -> HashMap<String, usize> {
let obj = object::File::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
}
fn assert_no_grow(flag: &str, corpus: &[&str], configs: &[(&str, &[&str])], min_fired: usize) {
let mut fired = 0usize;
for rel in corpus {
let tag = Path::new(rel).file_stem().unwrap().to_str().unwrap();
for (mode, args) in configs {
let off = func_sizes(&compile(
rel,
&format!("ffw242_{flag}_{tag}_{mode}_off"),
flag,
false,
args,
));
let on = func_sizes(&compile(
rel,
&format!("ffw242_{flag}_{tag}_{mode}_on"),
flag,
true,
args,
));
for (name, &o) in &off {
let n = *on.get(name).unwrap_or(&o);
assert!(
n <= o,
"no function may grow under default {flag}: \
{name} opt-out={o}B default={n}B ({rel} [{mode}])"
);
if n < o {
fired += 1;
}
}
}
}
assert!(
fired >= min_fired,
"non-vacuity: {flag} must shrink its firing fixtures \
(expected ≥{min_fired}, saw {fired} shrinking function(s))"
);
}
const ARM_OPT: &[&str] = &["-b", "arm", "--target", "cortex-m4"];
const ARM_RELOC: &[&str] = &[
"-b",
"arm",
"--target",
"cortex-m4",
"--relocatable",
"--embedder-data-init",
"--embedder-global-init",
];
const RV32: &[&str] = &["-b", "riscv", "--target", "rv32imac", "--relocatable"];
#[test]
fn rv32_shift_fold_no_grow_corpus_242() {
assert_no_grow(
"SYNTH_RV_SHIFT_FOLD",
&[
"control_step.wasm",
"signed_div_const.wasm",
"shift_fold.wat",
"flight_seam_flat.wasm",
"call_6_7args.wat",
"rv32_cmp_select_472.wat",
],
&[("rv32", RV32)],
2,
);
}
#[test]
fn dead_frame_elim_no_grow_corpus_242() {
assert_no_grow(
"SYNTH_DEAD_FRAME_ELIM",
&[
"control_step.wasm",
"signed_div_const.wasm",
"flight_seam.wat",
"native_pointer_shadow_stack.wat",
"leaf_caller_saved.wat",
"memcopy_dropped_359.wat",
"uxth_fold.wat",
],
&[("opt", ARM_OPT), ("reloc", ARM_RELOC)],
2,
);
}
#[test]
fn uxth_fold_no_grow_corpus_242() {
assert_no_grow(
"SYNTH_UXTH_FOLD",
&[
"control_step.wasm",
"signed_div_const.wasm",
"uxth_fold.wat",
"gust_kernel.wasm",
"flight_seam.wat",
],
&[("opt", ARM_OPT), ("reloc", ARM_RELOC)],
2,
);
}