Files
wasmtime/cranelift/filetests/src/test_wasm.rs
Nick Fitzgerald f4a2d5337a Cranelift: GVN uadd_overflow_trap (#5520)
* Switch duplicate loads w/ dynamic memories test to `min_size = 0`

This test was accidentally hitting a special case for bounds checks for when we
know that `offset + access_size < min_size` and can skip some steps. This
commit changes the `min_size` of the memory to zero so that we are forced to do
fully general bounds checks.

* Cranelift: Mark `uadd_overflow_trap` as okay for GVN

Although this improves our test sequence for duplicate loads with dynamic
memories, it unfortunately doesn't have any effect on sightglass benchmarks:

```
instantiation :: instructions-retired :: benchmarks/pulldown-cmark/benchmark.wasm

  No difference in performance.

  [34448 35607.23 37158] gvn_uadd_overflow_trap.so
  [34566 35734.05 36585] main.so

instantiation :: instructions-retired :: benchmarks/spidermonkey/benchmark.wasm

  No difference in performance.

  [44101 60449.62 92712] gvn_uadd_overflow_trap.so
  [44011 60436.37 92690] main.so

instantiation :: instructions-retired :: benchmarks/bz2/benchmark.wasm

  No difference in performance.

  [35595 36675.72 38153] gvn_uadd_overflow_trap.so
  [35440 36670.42 37993] main.so

compilation :: instructions-retired :: benchmarks/bz2/benchmark.wasm

  No difference in performance.

  [17370195 17405125.62 17471222] gvn_uadd_overflow_trap.so
  [17369324 17404859.43 17470725] main.so

execution :: instructions-retired :: benchmarks/spidermonkey/benchmark.wasm

  No difference in performance.

  [7055720520 7055886880.32 7056265930] gvn_uadd_overflow_trap.so
  [7055719554 7055843809.33 7056193289] main.so

compilation :: instructions-retired :: benchmarks/spidermonkey/benchmark.wasm

  No difference in performance.

  [683589861 683767276.00 684098366] gvn_uadd_overflow_trap.so
  [683590024 683767998.02 684097885] main.so

execution :: instructions-retired :: benchmarks/pulldown-cmark/benchmark.wasm

  No difference in performance.

  [46436883 46437135.10 46437823] gvn_uadd_overflow_trap.so
  [46436883 46437087.67 46437785] main.so

compilation :: instructions-retired :: benchmarks/pulldown-cmark/benchmark.wasm

  No difference in performance.

  [126522461 126565812.58 126647044] gvn_uadd_overflow_trap.so
  [126522176 126565757.75 126647522] main.so

execution :: instructions-retired :: benchmarks/bz2/benchmark.wasm

  No difference in performance.

  [653010531 653010533.03 653010544] gvn_uadd_overflow_trap.so
  [653010531 653010533.18 653010537] main.so
```

* cranelift-codegen-meta: Rename `side_effects_okay_for_gvn` to `side_effects_idempotent`

* cranelift-filetests: Ensure there is a trailing newline for blessed Wasm tests
2023-01-04 22:03:16 -08:00

134 lines
4.4 KiB
Rust

//! Test runner for `.wat` files to exercise CLIF-to-Wasm translations.
mod config;
mod env;
use anyhow::{bail, ensure, Context, Result};
use config::TestConfig;
use env::ModuleEnv;
use similar::TextDiff;
use std::{fmt::Write, path::Path};
/// Run one `.wat` test.
pub fn run(path: &Path, wat: &str) -> Result<()> {
debug_assert_eq!(path.extension().unwrap_or_default(), "wat");
// The test config source is the leading lines of the WAT file that are
// prefixed with `;;!`.
let config_lines: Vec<_> = wat
.lines()
.take_while(|l| l.starts_with(";;!"))
.map(|l| &l[3..])
.collect();
let config_text = config_lines.join("\n");
let config: TestConfig =
toml::from_str(&config_text).context("failed to parse the test configuration")?;
config
.validate()
.context("test configuration is malformed")?;
let parsed = cranelift_reader::parse_sets_and_triple(&config.settings, &config.target)
.context("invalid ISA target or Cranelift settings")?;
let flags_or_isa = parsed.as_fisa();
ensure!(
flags_or_isa.isa.is_some(),
"Running `.wat` tests requires specifying an ISA"
);
let isa = flags_or_isa.isa.unwrap();
let mut env = ModuleEnv::new(isa, config.clone());
let wasm = wat::parse_str(wat).context("failed to parse the test WAT")?;
let mut validator = wasmparser::Validator::new_with_features(
cranelift_wasm::ModuleEnvironment::wasm_features(&env),
);
validator
.validate_all(&wasm)
.context("test WAT failed to validate")?;
cranelift_wasm::translate_module(&wasm, &mut env)
.context("failed to translate the test case into CLIF")?;
let mut actual = String::new();
for (_index, func) in env.inner.info.function_bodies.iter() {
if config.compile {
let mut ctx = cranelift_codegen::Context::for_function(func.clone());
ctx.set_disasm(true);
let code = ctx
.compile(isa)
.map_err(|e| crate::pretty_anyhow_error(&e.func, e.inner))?;
writeln!(&mut actual, "function {}:", func.name).unwrap();
writeln!(&mut actual, "{}", code.disasm.as_ref().unwrap()).unwrap();
} else if config.optimize {
let mut ctx = cranelift_codegen::Context::for_function(func.clone());
ctx.optimize(isa)
.map_err(|e| crate::pretty_anyhow_error(&ctx.func, e))?;
writeln!(&mut actual, "{}", ctx.func.display()).unwrap();
} else {
writeln!(&mut actual, "{}", func.display()).unwrap();
}
}
let actual = actual.trim();
log::debug!("=== actual ===\n{actual}");
// The test's expectation is the final comment.
let mut expected_lines: Vec<_> = wat
.lines()
.rev()
.take_while(|l| l.starts_with(";;"))
.map(|l| {
if l.starts_with(";; ") {
&l[3..]
} else {
&l[2..]
}
})
.collect();
expected_lines.reverse();
let expected = expected_lines.join("\n");
let expected = expected.trim();
log::debug!("=== expected ===\n{expected}");
if actual == expected {
return Ok(());
}
if std::env::var("CRANELIFT_TEST_BLESS").unwrap_or_default() == "1" {
let old_expectation_line_count = wat
.lines()
.rev()
.take_while(|l| l.starts_with(";;"))
.count();
let old_wat_line_count = wat.lines().count();
let new_wat_lines: Vec<_> = wat
.lines()
.take(old_wat_line_count - old_expectation_line_count)
.map(|l| l.to_string())
.chain(actual.lines().map(|l| {
if l.is_empty() {
";;".to_string()
} else {
format!(";; {l}")
}
}))
.collect();
let mut new_wat = new_wat_lines.join("\n");
new_wat.push('\n');
std::fs::write(path, new_wat)
.with_context(|| format!("failed to write file: {}", path.display()))?;
return Ok(());
}
bail!(
"Did not get the expected CLIF translation:\n\n\
{}\n\n\
Note: You can re-run with the `CRANELIFT_TEST_BLESS=1` environment\n\
variable set to update test expectations.",
TextDiff::from_lines(expected, actual)
.unified_diff()
.header("expected", "actual")
)
}