Path: blob/main/cranelift/filetests/src/test_inline.rs
1691 views
//! Test command for testing inlining.1//!2//! The `inline` test command inlines all calls, and optionally optimizes each3//! function before and after the optimization passes. It does not perform4//! lowering or regalloc. The output for filecheck purposes is the resulting5//! CLIF.6//!7//! Some legalization may be ISA-specific, so this requires an ISA8//! (for now).910use crate::subtest::{Context, SubTest, check_precise_output, run_filecheck};11use anyhow::{Context as _, Result};12use cranelift_codegen::{13inline::{Inline, InlineCommand},14ir,15print_errors::pretty_verifier_error,16};17use cranelift_control::ControlPlane;18use cranelift_reader::{TestCommand, TestOption};19use std::{20borrow::Cow,21cell::{Ref, RefCell},22collections::HashMap,23};2425#[derive(Default)]26struct TestInline {27/// Flag indicating that the text expectation, comments after the function,28/// must be a precise 100% match on the compiled output of the function.29/// This test assertion is also automatically-update-able to allow tweaking30/// the code generator and easily updating all affected tests.31precise_output: bool,3233/// Flag indicating whether to run optimizations on the function after34/// inlining.35optimize: bool,3637/// The already-defined functions we have seen, available for inlining into38/// future functions.39funcs: RefCell<HashMap<ir::UserFuncName, ir::Function>>,40}4142pub fn subtest(parsed: &TestCommand) -> Result<Box<dyn SubTest>> {43assert_eq!(parsed.command, "inline");44let mut test = TestInline::default();45for option in parsed.options.iter() {46match option {47TestOption::Flag("precise-output") => test.precise_output = true,48TestOption::Flag("optimize") => test.optimize = true,49_ => anyhow::bail!("unknown option on {}", parsed),50}51}52Ok(Box::new(test))53}5455impl SubTest for TestInline {56fn name(&self) -> &'static str {57"inline"58}5960fn is_mutating(&self) -> bool {61true62}6364fn needs_isa(&self) -> bool {65true66}6768fn run(&self, func: Cow<ir::Function>, context: &Context) -> Result<()> {69// Legalize this function.70let isa = context.isa.unwrap();71let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());72comp_ctx73.legalize(isa)74.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, e))75.context("error while legalizing")?;7677// Insert this function in our map for inlining into subsequent78// functions.79let func_name = comp_ctx.func.name.clone();80self.funcs81.borrow_mut()82.insert(func_name, comp_ctx.func.clone());8384// Run the inliner.85let inlined_any = comp_ctx.inline(Inliner(self.funcs.borrow()))?;8687// Verify that the CLIF is still valid.88comp_ctx89.verify(context.flags_or_isa())90.map_err(|errors| {91anyhow::Error::msg(pretty_verifier_error(&comp_ctx.func, None, errors))92})93.context("CLIF verification error after inlining")?;9495// If requested, run optimizations.96if self.optimize {97comp_ctx98.optimize(isa, &mut ControlPlane::default())99.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, e))100.context("error while optimizing")?;101}102103// Check the filecheck expectations.104let actual = if inlined_any {105format!("{:?}", comp_ctx.func)106} else {107format!("(no functions inlined into {})", comp_ctx.func.name)108};109log::debug!("filecheck input: {actual}");110if self.precise_output {111let actual: Vec<_> = actual.lines().collect();112check_precise_output(&actual, context)113} else {114run_filecheck(&actual, context)115}116}117}118119struct Inliner<'a>(Ref<'a, HashMap<ir::UserFuncName, ir::Function>>);120121impl<'a> Inline for Inliner<'a> {122fn inline(123&mut self,124caller: &ir::Function,125_inst: ir::Inst,126_opcode: ir::Opcode,127callee: ir::FuncRef,128_args: &[ir::Value],129) -> InlineCommand<'_> {130match &caller.dfg.ext_funcs[callee].name {131ir::ExternalName::User(name) => match caller132.params133.user_named_funcs()134.get(*name)135.and_then(|name| self.0.get(&ir::UserFuncName::User(name.clone())))136{137None => InlineCommand::KeepCall,138Some(f) => InlineCommand::Inline {139callee: Cow::Borrowed(f),140visit_callee: true,141},142},143ir::ExternalName::TestCase(name) => {144match self.0.get(&ir::UserFuncName::Testcase(name.clone())) {145None => InlineCommand::KeepCall,146Some(f) => InlineCommand::Inline {147callee: Cow::Borrowed(f),148visit_callee: true,149},150}151}152ir::ExternalName::LibCall(_) | ir::ExternalName::KnownSymbol(_) => {153InlineCommand::KeepCall154}155}156}157}158159160