1 //! Test command for testing inlining.
2 //!
3 //! The `inline` test command inlines all calls, and optionally optimizes each
4 //! function before and after the optimization passes. It does not perform
5 //! lowering or regalloc. The output for filecheck purposes is the resulting
6 //! CLIF.
7 //!
8 //! Some legalization may be ISA-specific, so this requires an ISA
9 //! (for now).
10 
11 use crate::subtest::{Context, SubTest, check_precise_output, run_filecheck};
12 use anyhow::{Context as _, Result};
13 use cranelift_codegen::{
14     inline::{Inline, InlineCommand},
15     ir,
16     print_errors::pretty_verifier_error,
17 };
18 use cranelift_control::ControlPlane;
19 use cranelift_reader::{TestCommand, TestOption};
20 use std::{
21     borrow::Cow,
22     cell::{Ref, RefCell},
23     collections::HashMap,
24 };
25 
26 #[derive(Default)]
27 struct TestInline {
28     /// Flag indicating that the text expectation, comments after the function,
29     /// must be a precise 100% match on the compiled output of the function.
30     /// This test assertion is also automatically-update-able to allow tweaking
31     /// the code generator and easily updating all affected tests.
32     precise_output: bool,
33 
34     /// Flag indicating whether to run optimizations on the function after
35     /// inlining.
36     optimize: bool,
37 
38     /// The already-defined functions we have seen, available for inlining into
39     /// future functions.
40     funcs: RefCell<HashMap<ir::UserFuncName, ir::Function>>,
41 }
42 
subtest(parsed: &TestCommand) -> Result<Box<dyn SubTest>>43 pub fn subtest(parsed: &TestCommand) -> Result<Box<dyn SubTest>> {
44     assert_eq!(parsed.command, "inline");
45     let mut test = TestInline::default();
46     for option in parsed.options.iter() {
47         match option {
48             TestOption::Flag("precise-output") => test.precise_output = true,
49             TestOption::Flag("optimize") => test.optimize = true,
50             _ => anyhow::bail!("unknown option on {parsed}"),
51         }
52     }
53     Ok(Box::new(test))
54 }
55 
56 impl SubTest for TestInline {
name(&self) -> &'static str57     fn name(&self) -> &'static str {
58         "inline"
59     }
60 
is_mutating(&self) -> bool61     fn is_mutating(&self) -> bool {
62         true
63     }
64 
needs_isa(&self) -> bool65     fn needs_isa(&self) -> bool {
66         true
67     }
68 
run(&self, func: Cow<ir::Function>, context: &Context) -> Result<()>69     fn run(&self, func: Cow<ir::Function>, context: &Context) -> Result<()> {
70         // Legalize this function.
71         let isa = context.isa.unwrap();
72         let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
73         comp_ctx
74             .legalize(isa)
75             .map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, e))
76             .context("error while legalizing")?;
77 
78         // Insert this function in our map for inlining into subsequent
79         // functions.
80         let func_name = comp_ctx.func.name.clone();
81         self.funcs
82             .borrow_mut()
83             .insert(func_name, comp_ctx.func.clone());
84 
85         // Run the inliner.
86         let inlined_any = comp_ctx.inline(Inliner(self.funcs.borrow()))?;
87 
88         // Verify that the CLIF is still valid.
89         comp_ctx
90             .verify(context.flags_or_isa())
91             .map_err(|errors| {
92                 anyhow::Error::msg(pretty_verifier_error(&comp_ctx.func, None, errors))
93             })
94             .context("CLIF verification error after inlining")?;
95 
96         // If requested, run optimizations.
97         if self.optimize {
98             comp_ctx
99                 .optimize(isa, &mut ControlPlane::default())
100                 .map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, e))
101                 .context("error while optimizing")?;
102         }
103 
104         // Check the filecheck expectations.
105         let actual = if inlined_any {
106             format!("{:?}", comp_ctx.func)
107         } else {
108             format!("(no functions inlined into {})", comp_ctx.func.name)
109         };
110         log::debug!("filecheck input: {actual}");
111         if self.precise_output {
112             let actual: Vec<_> = actual.lines().collect();
113             check_precise_output(&actual, context)
114         } else {
115             run_filecheck(&actual, context)
116         }
117     }
118 }
119 
120 struct Inliner<'a>(Ref<'a, HashMap<ir::UserFuncName, ir::Function>>);
121 
122 impl<'a> Inline for Inliner<'a> {
inline( &mut self, caller: &ir::Function, _inst: ir::Inst, _opcode: ir::Opcode, callee: ir::FuncRef, _args: &[ir::Value], ) -> InlineCommand<'_>123     fn inline(
124         &mut self,
125         caller: &ir::Function,
126         _inst: ir::Inst,
127         _opcode: ir::Opcode,
128         callee: ir::FuncRef,
129         _args: &[ir::Value],
130     ) -> InlineCommand<'_> {
131         match &caller.dfg.ext_funcs[callee].name {
132             ir::ExternalName::User(name) => match caller
133                 .params
134                 .user_named_funcs()
135                 .get(*name)
136                 .and_then(|name| self.0.get(&ir::UserFuncName::User(name.clone())))
137             {
138                 None => InlineCommand::KeepCall,
139                 Some(f) => InlineCommand::Inline {
140                     callee: Cow::Borrowed(f),
141                     visit_callee: true,
142                 },
143             },
144             ir::ExternalName::TestCase(name) => {
145                 match self.0.get(&ir::UserFuncName::Testcase(name.clone())) {
146                     None => InlineCommand::KeepCall,
147                     Some(f) => InlineCommand::Inline {
148                         callee: Cow::Borrowed(f),
149                         visit_callee: true,
150                     },
151                 }
152             }
153             ir::ExternalName::LibCall(_) | ir::ExternalName::KnownSymbol(_) => {
154                 InlineCommand::KeepCall
155             }
156         }
157     }
158 }
159