use std::collections::HashMap;
use std::path::Path;
use std::process::Command;
use object::{Object, ObjectSymbol, SymbolKind};
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, out: &str, flag: &str, default_on: bool, backend_args: &[&str]) -> Vec<u8> {
let mut cmd = Command::new(synth());
cmd.env_remove("SYNTH_RV_LOCAL_PROMO");
if default_on {
cmd.env_remove(flag);
} else {
cmd.env(flag, "0");
}
let out_status = cmd
.args(["compile", fixture(rel).to_str().unwrap(), "-o", out])
.args(backend_args)
.args(["--all-exports"])
.output()
.expect("run synth compile");
assert!(
out_status.status.success(),
"synth compile failed ({rel}, {flag} default_on={default_on}): {}",
String::from_utf8_lossy(&out_status.stderr)
);
std::fs::read(out).expect("read ELF")
}
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!("/tmp/ffw242_{flag}_{tag}_{mode}_off.o"),
flag,
false,
args,
));
let on = func_sizes(&compile(
rel,
&format!("/tmp/ffw242_{flag}_{tag}_{mode}_on.o"),
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"];
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,
);
}