1968952abSNick Fitzgerald //! Test command for testing inlining.
2968952abSNick Fitzgerald //!
3968952abSNick Fitzgerald //! The `inline` test command inlines all calls, and optionally optimizes each
4968952abSNick Fitzgerald //! function before and after the optimization passes. It does not perform
5968952abSNick Fitzgerald //! lowering or regalloc. The output for filecheck purposes is the resulting
6968952abSNick Fitzgerald //! CLIF.
7968952abSNick Fitzgerald //!
8968952abSNick Fitzgerald //! Some legalization may be ISA-specific, so this requires an ISA
9968952abSNick Fitzgerald //! (for now).
10968952abSNick Fitzgerald 
11968952abSNick Fitzgerald use crate::subtest::{Context, SubTest, check_precise_output, run_filecheck};
12968952abSNick Fitzgerald use anyhow::{Context as _, Result};
13968952abSNick Fitzgerald use cranelift_codegen::{
14968952abSNick Fitzgerald     inline::{Inline, InlineCommand},
15968952abSNick Fitzgerald     ir,
16968952abSNick Fitzgerald     print_errors::pretty_verifier_error,
17968952abSNick Fitzgerald };
18968952abSNick Fitzgerald use cranelift_control::ControlPlane;
19968952abSNick Fitzgerald use cranelift_reader::{TestCommand, TestOption};
20968952abSNick Fitzgerald use std::{
21968952abSNick Fitzgerald     borrow::Cow,
22968952abSNick Fitzgerald     cell::{Ref, RefCell},
23968952abSNick Fitzgerald     collections::HashMap,
24968952abSNick Fitzgerald };
25968952abSNick Fitzgerald 
26968952abSNick Fitzgerald #[derive(Default)]
27968952abSNick Fitzgerald struct TestInline {
28968952abSNick Fitzgerald     /// Flag indicating that the text expectation, comments after the function,
29968952abSNick Fitzgerald     /// must be a precise 100% match on the compiled output of the function.
30968952abSNick Fitzgerald     /// This test assertion is also automatically-update-able to allow tweaking
31968952abSNick Fitzgerald     /// the code generator and easily updating all affected tests.
32968952abSNick Fitzgerald     precise_output: bool,
33968952abSNick Fitzgerald 
34968952abSNick Fitzgerald     /// Flag indicating whether to run optimizations on the function after
35968952abSNick Fitzgerald     /// inlining.
36968952abSNick Fitzgerald     optimize: bool,
37968952abSNick Fitzgerald 
38968952abSNick Fitzgerald     /// The already-defined functions we have seen, available for inlining into
39968952abSNick Fitzgerald     /// future functions.
40968952abSNick Fitzgerald     funcs: RefCell<HashMap<ir::UserFuncName, ir::Function>>,
41968952abSNick Fitzgerald }
42968952abSNick Fitzgerald 
subtest(parsed: &TestCommand) -> Result<Box<dyn SubTest>>43968952abSNick Fitzgerald pub fn subtest(parsed: &TestCommand) -> Result<Box<dyn SubTest>> {
44968952abSNick Fitzgerald     assert_eq!(parsed.command, "inline");
45968952abSNick Fitzgerald     let mut test = TestInline::default();
46968952abSNick Fitzgerald     for option in parsed.options.iter() {
47968952abSNick Fitzgerald         match option {
48968952abSNick Fitzgerald             TestOption::Flag("precise-output") => test.precise_output = true,
49968952abSNick Fitzgerald             TestOption::Flag("optimize") => test.optimize = true,
50*557cc2d6SAlex Crichton             _ => anyhow::bail!("unknown option on {parsed}"),
51968952abSNick Fitzgerald         }
52968952abSNick Fitzgerald     }
53968952abSNick Fitzgerald     Ok(Box::new(test))
54968952abSNick Fitzgerald }
55968952abSNick Fitzgerald 
56968952abSNick Fitzgerald impl SubTest for TestInline {
name(&self) -> &'static str57968952abSNick Fitzgerald     fn name(&self) -> &'static str {
58968952abSNick Fitzgerald         "inline"
59968952abSNick Fitzgerald     }
60968952abSNick Fitzgerald 
is_mutating(&self) -> bool61968952abSNick Fitzgerald     fn is_mutating(&self) -> bool {
62968952abSNick Fitzgerald         true
63968952abSNick Fitzgerald     }
64968952abSNick Fitzgerald 
needs_isa(&self) -> bool65968952abSNick Fitzgerald     fn needs_isa(&self) -> bool {
66968952abSNick Fitzgerald         true
67968952abSNick Fitzgerald     }
68968952abSNick Fitzgerald 
run(&self, func: Cow<ir::Function>, context: &Context) -> Result<()>69968952abSNick Fitzgerald     fn run(&self, func: Cow<ir::Function>, context: &Context) -> Result<()> {
70968952abSNick Fitzgerald         // Legalize this function.
71968952abSNick Fitzgerald         let isa = context.isa.unwrap();
72968952abSNick Fitzgerald         let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
73968952abSNick Fitzgerald         comp_ctx
74968952abSNick Fitzgerald             .legalize(isa)
75968952abSNick Fitzgerald             .map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, e))
76968952abSNick Fitzgerald             .context("error while legalizing")?;
77968952abSNick Fitzgerald 
78968952abSNick Fitzgerald         // Insert this function in our map for inlining into subsequent
79968952abSNick Fitzgerald         // functions.
80968952abSNick Fitzgerald         let func_name = comp_ctx.func.name.clone();
81968952abSNick Fitzgerald         self.funcs
82968952abSNick Fitzgerald             .borrow_mut()
83968952abSNick Fitzgerald             .insert(func_name, comp_ctx.func.clone());
84968952abSNick Fitzgerald 
85968952abSNick Fitzgerald         // Run the inliner.
86968952abSNick Fitzgerald         let inlined_any = comp_ctx.inline(Inliner(self.funcs.borrow()))?;
87968952abSNick Fitzgerald 
88968952abSNick Fitzgerald         // Verify that the CLIF is still valid.
89968952abSNick Fitzgerald         comp_ctx
90968952abSNick Fitzgerald             .verify(context.flags_or_isa())
91968952abSNick Fitzgerald             .map_err(|errors| {
92968952abSNick Fitzgerald                 anyhow::Error::msg(pretty_verifier_error(&comp_ctx.func, None, errors))
93968952abSNick Fitzgerald             })
94968952abSNick Fitzgerald             .context("CLIF verification error after inlining")?;
95968952abSNick Fitzgerald 
96968952abSNick Fitzgerald         // If requested, run optimizations.
97968952abSNick Fitzgerald         if self.optimize {
98968952abSNick Fitzgerald             comp_ctx
99968952abSNick Fitzgerald                 .optimize(isa, &mut ControlPlane::default())
100968952abSNick Fitzgerald                 .map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, e))
101968952abSNick Fitzgerald                 .context("error while optimizing")?;
102968952abSNick Fitzgerald         }
103968952abSNick Fitzgerald 
104968952abSNick Fitzgerald         // Check the filecheck expectations.
105968952abSNick Fitzgerald         let actual = if inlined_any {
106968952abSNick Fitzgerald             format!("{:?}", comp_ctx.func)
107968952abSNick Fitzgerald         } else {
108968952abSNick Fitzgerald             format!("(no functions inlined into {})", comp_ctx.func.name)
109968952abSNick Fitzgerald         };
110968952abSNick Fitzgerald         log::debug!("filecheck input: {actual}");
111968952abSNick Fitzgerald         if self.precise_output {
112968952abSNick Fitzgerald             let actual: Vec<_> = actual.lines().collect();
113968952abSNick Fitzgerald             check_precise_output(&actual, context)
114968952abSNick Fitzgerald         } else {
115968952abSNick Fitzgerald             run_filecheck(&actual, context)
116968952abSNick Fitzgerald         }
117968952abSNick Fitzgerald     }
118968952abSNick Fitzgerald }
119968952abSNick Fitzgerald 
120968952abSNick Fitzgerald struct Inliner<'a>(Ref<'a, HashMap<ir::UserFuncName, ir::Function>>);
121968952abSNick Fitzgerald 
122968952abSNick Fitzgerald 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<'_>123968952abSNick Fitzgerald     fn inline(
1243ecb338eSNick Fitzgerald         &mut self,
125968952abSNick Fitzgerald         caller: &ir::Function,
126968952abSNick Fitzgerald         _inst: ir::Inst,
127968952abSNick Fitzgerald         _opcode: ir::Opcode,
128968952abSNick Fitzgerald         callee: ir::FuncRef,
129968952abSNick Fitzgerald         _args: &[ir::Value],
130968952abSNick Fitzgerald     ) -> InlineCommand<'_> {
131968952abSNick Fitzgerald         match &caller.dfg.ext_funcs[callee].name {
132968952abSNick Fitzgerald             ir::ExternalName::User(name) => match caller
133968952abSNick Fitzgerald                 .params
134968952abSNick Fitzgerald                 .user_named_funcs()
135968952abSNick Fitzgerald                 .get(*name)
136968952abSNick Fitzgerald                 .and_then(|name| self.0.get(&ir::UserFuncName::User(name.clone())))
137968952abSNick Fitzgerald             {
138968952abSNick Fitzgerald                 None => InlineCommand::KeepCall,
139dcedcbf5SNick Fitzgerald                 Some(f) => InlineCommand::Inline {
140dcedcbf5SNick Fitzgerald                     callee: Cow::Borrowed(f),
141dcedcbf5SNick Fitzgerald                     visit_callee: true,
142dcedcbf5SNick Fitzgerald                 },
143968952abSNick Fitzgerald             },
144968952abSNick Fitzgerald             ir::ExternalName::TestCase(name) => {
145968952abSNick Fitzgerald                 match self.0.get(&ir::UserFuncName::Testcase(name.clone())) {
146968952abSNick Fitzgerald                     None => InlineCommand::KeepCall,
147dcedcbf5SNick Fitzgerald                     Some(f) => InlineCommand::Inline {
148dcedcbf5SNick Fitzgerald                         callee: Cow::Borrowed(f),
149dcedcbf5SNick Fitzgerald                         visit_callee: true,
150dcedcbf5SNick Fitzgerald                     },
151968952abSNick Fitzgerald                 }
152968952abSNick Fitzgerald             }
153968952abSNick Fitzgerald             ir::ExternalName::LibCall(_) | ir::ExternalName::KnownSymbol(_) => {
154968952abSNick Fitzgerald                 InlineCommand::KeepCall
155968952abSNick Fitzgerald             }
156968952abSNick Fitzgerald         }
157968952abSNick Fitzgerald     }
158968952abSNick Fitzgerald }
159