cranelift_filetests/
test_compile.rs

1//! Test command for testing the code generator pipeline
2//!
3//! The `compile` test command runs each function through the full code generator pipeline
4
5use crate::subtest::{check_precise_output, run_filecheck, Context, SubTest};
6use anyhow::Result;
7use cranelift_codegen::ir;
8use cranelift_reader::{TestCommand, TestOption};
9use log::info;
10use std::borrow::Cow;
11
12struct TestCompile {
13    /// Flag indicating that the text expectation, comments after the function,
14    /// must be a precise 100% match on the compiled output of the function.
15    /// This test assertion is also automatically-update-able to allow tweaking
16    /// the code generator and easily updating all affected tests.
17    precise_output: bool,
18    /// Flag indicating that we expect compilation to fail, not succeed.
19    expect_fail: bool,
20}
21
22pub fn subtest(parsed: &TestCommand) -> Result<Box<dyn SubTest>> {
23    assert_eq!(parsed.command, "compile");
24    let mut test = TestCompile {
25        precise_output: false,
26        expect_fail: false,
27    };
28    for option in parsed.options.iter() {
29        match option {
30            TestOption::Flag("precise-output") => test.precise_output = true,
31            TestOption::Flag("expect-fail") => test.expect_fail = true,
32            _ => anyhow::bail!("unknown option on {}", parsed),
33        }
34    }
35    Ok(Box::new(test))
36}
37
38impl SubTest for TestCompile {
39    fn name(&self) -> &'static str {
40        "compile"
41    }
42
43    fn is_mutating(&self) -> bool {
44        true
45    }
46
47    fn needs_isa(&self) -> bool {
48        true
49    }
50
51    fn run(&self, func: Cow<ir::Function>, context: &Context) -> Result<()> {
52        let isa = context.isa.expect("compile needs an ISA");
53        let params = func.params.clone();
54        let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
55
56        // With `MachBackend`s, we need to explicitly request disassembly results.
57        comp_ctx.set_disasm(true);
58
59        let compiled_code = comp_ctx.compile(isa, &mut Default::default());
60
61        let compiled_code = if self.expect_fail {
62            if compiled_code.is_ok() {
63                anyhow::bail!("Expected compilation failure but compilation succeeded");
64            }
65            return Ok(());
66        } else {
67            compiled_code.map_err(|e| crate::pretty_anyhow_error(&e.func, e.inner))?
68        };
69        let total_size = compiled_code.code_info().total_size;
70
71        let vcode = compiled_code.vcode.as_ref().unwrap();
72
73        info!("Generated {} bytes of code:\n{}", total_size, vcode);
74
75        if self.precise_output {
76            let dis = match isa.triple().architecture {
77                target_lexicon::Architecture::Pulley32 | target_lexicon::Architecture::Pulley64 => {
78                    // Disable hexdumps/offsets to reduce the churn in these
79                    // tests as instructions are encoded differently and/or
80                    // their immediates change.
81                    let mut disas =
82                        pulley_interpreter::disas::Disassembler::new(compiled_code.buffer.data());
83                    disas.hexdump(false).offsets(false);
84                    pulley_interpreter::decode::Decoder::decode_all(&mut disas)?;
85                    disas.disas().to_string()
86                }
87                _ => {
88                    let cs = isa
89                        .to_capstone()
90                        .map_err(|e| anyhow::format_err!("{}", e))?;
91                    compiled_code.disassemble(Some(&params), &cs)?
92                }
93            };
94
95            let actual = Vec::from_iter(
96                std::iter::once("VCode:")
97                    .chain(compiled_code.vcode.as_ref().unwrap().lines())
98                    .chain(["", "Disassembled:"])
99                    .chain(dis.lines()),
100            );
101
102            check_precise_output(&actual, context)
103        } else {
104            run_filecheck(&vcode, context)
105        }
106    }
107}