Coverage Report

Created: 2023-04-25 07:07

/src/wasmtime/cranelift/filetests/src/test_wasm.rs
Line
Count
Source (jump to first uncovered line)
1
//! Test runner for `.wat` files to exercise CLIF-to-Wasm translations.
2
3
mod config;
4
mod env;
5
6
use anyhow::{bail, ensure, Context, Result};
7
use config::TestConfig;
8
use env::ModuleEnv;
9
use similar::TextDiff;
10
use std::{fmt::Write, path::Path};
11
12
/// Run one `.wat` test.
13
0
pub fn run(path: &Path, wat: &str) -> Result<()> {
14
0
    debug_assert_eq!(path.extension().unwrap_or_default(), "wat");
15
16
    // The test config source is the leading lines of the WAT file that are
17
    // prefixed with `;;!`.
18
0
    let config_lines: Vec<_> = wat
19
0
        .lines()
20
0
        .take_while(|l| l.starts_with(";;!"))
21
0
        .map(|l| &l[3..])
22
0
        .collect();
23
0
    let config_text = config_lines.join("\n");
24
25
0
    let config: TestConfig =
26
0
        toml::from_str(&config_text).context("failed to parse the test configuration")?;
27
0
    log::debug!("Wasm test config = {config:#?}");
28
29
0
    config
30
0
        .validate()
31
0
        .context("test configuration is malformed")?;
32
33
0
    let parsed = cranelift_reader::parse_sets_and_triple(&config.settings, &config.target)
34
0
        .context("invalid ISA target or Cranelift settings")?;
35
0
    let flags_or_isa = parsed.as_fisa();
36
0
    ensure!(
37
0
        flags_or_isa.isa.is_some(),
38
0
        "Running `.wat` tests requires specifying an ISA"
39
    );
40
0
    let isa = flags_or_isa.isa.unwrap();
41
0
42
0
    let mut env = ModuleEnv::new(isa, config.clone());
43
44
0
    let wasm = wat::parse_str(wat).context("failed to parse the test WAT")?;
45
0
    let mut validator = wasmparser::Validator::new_with_features(
46
0
        cranelift_wasm::ModuleEnvironment::wasm_features(&env),
47
0
    );
48
0
    validator
49
0
        .validate_all(&wasm)
50
0
        .context("test WAT failed to validate")?;
51
52
0
    cranelift_wasm::translate_module(&wasm, &mut env)
53
0
        .context("failed to translate the test case into CLIF")?;
54
55
0
    let mut actual = String::new();
56
0
    for (_index, func) in env.inner.info.function_bodies.iter() {
57
0
        if config.compile {
58
0
            let mut ctx = cranelift_codegen::Context::for_function(func.clone());
59
0
            ctx.set_disasm(true);
60
0
            let code = ctx
61
0
                .compile(isa, &mut Default::default())
62
0
                .map_err(|e| crate::pretty_anyhow_error(&e.func, e.inner))?;
63
0
            writeln!(&mut actual, "function {}:", func.name).unwrap();
64
0
            writeln!(&mut actual, "{}", code.vcode.as_ref().unwrap()).unwrap();
65
0
        } else if config.optimize {
66
0
            let mut ctx = cranelift_codegen::Context::for_function(func.clone());
67
0
            ctx.optimize(isa)
68
0
                .map_err(|e| crate::pretty_anyhow_error(&ctx.func, e))?;
69
0
            writeln!(&mut actual, "{}", ctx.func.display()).unwrap();
70
0
        } else {
71
0
            writeln!(&mut actual, "{}", func.display()).unwrap();
72
0
        }
73
    }
74
0
    let actual = actual.trim();
75
0
    log::debug!("=== actual ===\n{actual}");
76
77
    // The test's expectation is the final comment.
78
0
    let mut expected_lines: Vec<_> = wat
79
0
        .lines()
80
0
        .rev()
81
0
        .take_while(|l| l.starts_with(";;"))
82
0
        .map(|l| {
83
0
            if l.starts_with(";; ") {
84
0
                &l[3..]
85
            } else {
86
0
                &l[2..]
87
            }
88
0
        })
89
0
        .collect();
90
0
    expected_lines.reverse();
91
0
    let expected = expected_lines.join("\n");
92
0
    let expected = expected.trim();
93
0
    log::debug!("=== expected ===\n{expected}");
94
95
0
    if actual == expected {
96
0
        return Ok(());
97
0
    }
98
0
99
0
    if std::env::var("CRANELIFT_TEST_BLESS").unwrap_or_default() == "1" {
100
0
        let old_expectation_line_count = wat
101
0
            .lines()
102
0
            .rev()
103
0
            .take_while(|l| l.starts_with(";;"))
104
0
            .count();
105
0
        let old_wat_line_count = wat.lines().count();
106
0
        let new_wat_lines: Vec<_> = wat
107
0
            .lines()
108
0
            .take(old_wat_line_count - old_expectation_line_count)
109
0
            .map(|l| l.to_string())
110
0
            .chain(actual.lines().map(|l| {
111
0
                if l.is_empty() {
112
0
                    ";;".to_string()
113
                } else {
114
0
                    format!(";; {l}")
115
                }
116
0
            }))
117
0
            .collect();
118
0
        let mut new_wat = new_wat_lines.join("\n");
119
0
        new_wat.push('\n');
120
0
        std::fs::write(path, new_wat)
121
0
            .with_context(|| format!("failed to write file: {}", path.display()))?;
122
0
        return Ok(());
123
0
    }
124
0
125
0
    bail!(
126
0
        "Did not get the expected CLIF translation:\n\n\
127
0
         {}\n\n\
128
0
         Note: You can re-run with the `CRANELIFT_TEST_BLESS=1` environment\n\
129
0
         variable set to update test expectations.",
130
0
        TextDiff::from_lines(expected, actual)
131
0
            .unified_diff()
132
0
            .header("expected", "actual")
133
0
    )
134
0
}