/src/wasmtime/cranelift/filetests/src/test_compile.rs
Line | Count | Source |
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 | | |
5 | | use crate::subtest::{Context, SubTest, check_precise_output, run_filecheck}; |
6 | | use anyhow::Result; |
7 | | use cranelift_codegen::ir; |
8 | | use cranelift_reader::{TestCommand, TestOption}; |
9 | | use log::info; |
10 | | use std::borrow::Cow; |
11 | | |
12 | | struct 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 | | |
22 | 0 | pub fn subtest(parsed: &TestCommand) -> Result<Box<dyn SubTest>> { |
23 | 0 | assert_eq!(parsed.command, "compile"); |
24 | 0 | let mut test = TestCompile { |
25 | 0 | precise_output: false, |
26 | 0 | expect_fail: false, |
27 | 0 | }; |
28 | 0 | for option in parsed.options.iter() { |
29 | 0 | match option { |
30 | 0 | TestOption::Flag("precise-output") => test.precise_output = true, |
31 | 0 | TestOption::Flag("expect-fail") => test.expect_fail = true, |
32 | 0 | _ => anyhow::bail!("unknown option on {parsed}"), |
33 | | } |
34 | | } |
35 | 0 | Ok(Box::new(test)) |
36 | 0 | } |
37 | | |
38 | | impl SubTest for TestCompile { |
39 | 0 | fn name(&self) -> &'static str { |
40 | 0 | "compile" |
41 | 0 | } |
42 | | |
43 | 0 | fn is_mutating(&self) -> bool { |
44 | 0 | true |
45 | 0 | } |
46 | | |
47 | 0 | fn needs_isa(&self) -> bool { |
48 | 0 | true |
49 | 0 | } |
50 | | |
51 | 0 | fn run(&self, func: Cow<ir::Function>, context: &Context) -> Result<()> { |
52 | 0 | let isa = context.isa.expect("compile needs an ISA"); |
53 | 0 | let params = func.params.clone(); |
54 | 0 | 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 | 0 | comp_ctx.set_disasm(true); |
58 | | |
59 | 0 | let compiled_code = comp_ctx.compile(isa, &mut Default::default()); |
60 | | |
61 | 0 | let compiled_code = if self.expect_fail { |
62 | 0 | if compiled_code.is_ok() { |
63 | 0 | anyhow::bail!("Expected compilation failure but compilation succeeded"); |
64 | 0 | } |
65 | 0 | return Ok(()); |
66 | | } else { |
67 | 0 | compiled_code.map_err(|e| crate::pretty_anyhow_error(&e.func, e.inner))? |
68 | | }; |
69 | 0 | let total_size = compiled_code.code_info().total_size; |
70 | | |
71 | 0 | let vcode = compiled_code.vcode.as_ref().unwrap(); |
72 | | |
73 | 0 | info!("Generated {total_size} bytes of code:\n{vcode}"); |
74 | | |
75 | 0 | if self.precise_output { |
76 | 0 | 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 | 0 | let mut disas = |
82 | 0 | pulley_interpreter::disas::Disassembler::new(compiled_code.buffer.data()); |
83 | 0 | disas.hexdump(false).offsets(false); |
84 | 0 | pulley_interpreter::decode::Decoder::decode_all(&mut disas)?; |
85 | 0 | disas.disas().to_string() |
86 | | } |
87 | | _ => { |
88 | 0 | let cs = isa.to_capstone().map_err(|e| anyhow::format_err!("{e}"))?; |
89 | 0 | compiled_code.disassemble(Some(¶ms), &cs)? |
90 | | } |
91 | | }; |
92 | | |
93 | 0 | let actual = Vec::from_iter( |
94 | 0 | std::iter::once("VCode:") |
95 | 0 | .chain(compiled_code.vcode.as_ref().unwrap().lines()) |
96 | 0 | .chain(["", "Disassembled:"]) |
97 | 0 | .chain(dis.lines()), |
98 | | ); |
99 | | |
100 | 0 | check_precise_output(&actual, context) |
101 | | } else { |
102 | 0 | run_filecheck(&vcode, context) |
103 | | } |
104 | 0 | } |
105 | | } |