use std::process::Command;
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
fn run(engine: &str, src: &str, func: &str) -> String {
let out = ilo()
.args([src, engine, func])
.output()
.expect("failed to run ilo");
assert!(
out.status.success(),
"ilo {engine} on `{func}` failed: stderr={}",
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8_lossy(&out.stdout).trim().to_string()
}
fn record_source(n: usize, probe: usize) -> String {
let type_fields: Vec<String> = (0..n).map(|i| format!("f{i}:n")).collect();
let inits: Vec<String> = (0..n).map(|i| format!("f{i}:{i}")).collect();
format!(
"type big{{{}}}\ngo>n;r=big {};r.f{}",
type_fields.join(";"),
inits.join(" "),
probe,
)
}
fn record_leading_locals_source() -> String {
let locals: Vec<String> = (0..180).map(|i| format!("a{i}=0")).collect();
let n = 60;
let type_fields: Vec<String> = (0..n).map(|i| format!("f{i}:n")).collect();
let inits: Vec<String> = (0..n).map(|i| format!("f{i}:{i}")).collect();
format!(
"type big{{{}}}\ngo>n;{};r=big {};r.f50",
type_fields.join(";"),
locals.join(";"),
inits.join(" "),
)
}
fn with_source(n: usize, probe: usize) -> String {
let type_fields: Vec<String> = (0..n).map(|i| format!("f{i}:n")).collect();
let zeroes: Vec<String> = (0..n).map(|i| format!("f{i}:0")).collect();
let updates: Vec<String> = (0..n).map(|i| format!("f{i}:{i}")).collect();
format!(
"type big{{{}}}\ngo>n;r=big {};q=r with {};q.f{}",
type_fields.join(";"),
zeroes.join(" "),
updates.join(" "),
probe,
)
}
fn with_leading_locals_source() -> String {
let locals: Vec<String> = (0..180).map(|i| format!("a{i}=0")).collect();
let n = 60;
let type_fields: Vec<String> = (0..n).map(|i| format!("f{i}:n")).collect();
let zeroes: Vec<String> = (0..n).map(|i| format!("f{i}:0")).collect();
let updates: Vec<String> = (0..n).map(|i| format!("f{i}:{i}")).collect();
format!(
"type big{{{}}}\ngo>n;{};r=big {};q=r with {};q.f50",
type_fields.join(";"),
locals.join(";"),
zeroes.join(" "),
updates.join(" "),
)
}
fn check_record(engine: &str, n: usize, probe: usize) {
let src = record_source(n, probe);
let out = run(engine, &src, "go");
assert_eq!(
out,
probe.to_string(),
"{engine} record n={n} probe={probe}: expected `{probe}`, got {out:?}"
);
}
fn check_with(engine: &str, n: usize, probe: usize) {
let src = with_source(n, probe);
let out = run(engine, &src, "go");
assert_eq!(
out,
probe.to_string(),
"{engine} with n={n} probe={probe}: expected `{probe}`, got {out:?}"
);
}
fn check_record_leading_locals(engine: &str) {
let src = record_leading_locals_source();
let out = run(engine, &src, "go");
assert_eq!(
out, "50",
"{engine}: expected `50` from 180-locals + 60-field record literal, got {out:?}"
);
}
fn check_with_leading_locals(engine: &str) {
let src = with_leading_locals_source();
let out = run(engine, &src, "go");
assert_eq!(
out, "50",
"{engine}: expected `50` from 180-locals + 60-field `with`, got {out:?}"
);
}
const SIZES: &[usize] = &[1, 16, 60, 150, 220];
#[test]
fn record_size_sweep_tree() {
for &n in SIZES {
let probe = n - 1;
check_record("--run-tree", n, probe);
}
}
#[test]
fn record_size_sweep_vm() {
for &n in SIZES {
let probe = n - 1;
check_record("--run-vm", n, probe);
}
}
#[test]
#[cfg(feature = "cranelift")]
fn record_size_sweep_cranelift() {
for &n in SIZES {
let probe = n - 1;
check_record("--run-cranelift", n, probe);
}
}
#[test]
fn with_size_sweep_tree() {
for &n in SIZES {
let probe = n - 1;
check_with("--run-tree", n, probe);
}
}
#[test]
fn with_size_sweep_vm() {
for &n in SIZES {
let probe = n - 1;
check_with("--run-vm", n, probe);
}
}
#[test]
#[cfg(feature = "cranelift")]
fn with_size_sweep_cranelift() {
for &n in SIZES {
let probe = n - 1;
check_with("--run-cranelift", n, probe);
}
}
#[test]
fn record_leading_locals_tree() {
check_record_leading_locals("--run-tree");
}
#[test]
fn record_leading_locals_vm() {
check_record_leading_locals("--run-vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn record_leading_locals_cranelift() {
check_record_leading_locals("--run-cranelift");
}
#[test]
fn with_leading_locals_tree() {
check_with_leading_locals("--run-tree");
}
#[test]
fn with_leading_locals_vm() {
check_with_leading_locals("--run-vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn with_leading_locals_cranelift() {
check_with_leading_locals("--run-cranelift");
}
#[test]
fn with_preserves_untouched_fields_vm() {
let n = 150;
let type_fields: Vec<String> = (0..n).map(|i| format!("f{i}:n")).collect();
let inits: Vec<String> = (0..n).map(|i| format!("f{i}:{i}")).collect();
let src = format!(
"type big{{{}}}\ngo>n;r=big {};q=r with f0:999;q.f140",
type_fields.join(";"),
inits.join(" "),
);
let out = run("--run-vm", &src, "go");
assert_eq!(out, "140");
}
#[test]
#[cfg(feature = "cranelift")]
fn with_preserves_untouched_fields_cranelift() {
let n = 150;
let type_fields: Vec<String> = (0..n).map(|i| format!("f{i}:n")).collect();
let inits: Vec<String> = (0..n).map(|i| format!("f{i}:{i}")).collect();
let src = format!(
"type big{{{}}}\ngo>n;r=big {};q=r with f0:999;q.f140",
type_fields.join(";"),
inits.join(" "),
);
let out = run("--run-cranelift", &src, "go");
assert_eq!(out, "140");
}
#[test]
fn with_does_not_mutate_original_vm() {
let n = 150;
let type_fields: Vec<String> = (0..n).map(|i| format!("f{i}:n")).collect();
let zeroes: Vec<String> = (0..n).map(|i| format!("f{i}:0")).collect();
let updates: Vec<String> = (0..n).map(|i| format!("f{i}:{i}")).collect();
let src = format!(
"type big{{{}}}\ngo>n;r=big {};q=r with {};r.f140",
type_fields.join(";"),
zeroes.join(" "),
updates.join(" "),
);
let out = run("--run-vm", &src, "go");
assert_eq!(out, "0", "original record should be untouched after with");
}
#[test]
#[cfg(feature = "cranelift")]
fn with_does_not_mutate_original_cranelift() {
let n = 150;
let type_fields: Vec<String> = (0..n).map(|i| format!("f{i}:n")).collect();
let zeroes: Vec<String> = (0..n).map(|i| format!("f{i}:0")).collect();
let updates: Vec<String> = (0..n).map(|i| format!("f{i}:{i}")).collect();
let src = format!(
"type big{{{}}}\ngo>n;r=big {};q=r with {};r.f140",
type_fields.join(";"),
zeroes.join(" "),
updates.join(" "),
);
let out = run("--run-cranelift", &src, "go");
assert_eq!(out, "0", "original record should be untouched after with");
}
#[test]
fn record_with_string_fields_vm() {
let n = 150;
let type_fields: Vec<String> = (0..n)
.map(|i| {
if i % 2 == 0 {
format!("f{i}:t")
} else {
format!("f{i}:n")
}
})
.collect();
let inits: Vec<String> = (0..n)
.map(|i| {
if i % 2 == 0 {
format!("f{i}:\"s{i}\"")
} else {
format!("f{i}:{i}")
}
})
.collect();
let src = format!(
"type big{{{}}}\ngo>t;r=big {};r.f140",
type_fields.join(";"),
inits.join(" "),
);
let out = run("--run-vm", &src, "go");
assert_eq!(out, "s140");
}
#[test]
#[cfg(feature = "cranelift")]
fn record_with_string_fields_cranelift() {
let n = 150;
let type_fields: Vec<String> = (0..n)
.map(|i| {
if i % 2 == 0 {
format!("f{i}:t")
} else {
format!("f{i}:n")
}
})
.collect();
let inits: Vec<String> = (0..n)
.map(|i| {
if i % 2 == 0 {
format!("f{i}:\"s{i}\"")
} else {
format!("f{i}:{i}")
}
})
.collect();
let src = format!(
"type big{{{}}}\ngo>t;r=big {};r.f140",
type_fields.join(";"),
inits.join(" "),
);
let out = run("--run-cranelift", &src, "go");
assert_eq!(out, "s140");
}