Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/filetests/src/test_inline.rs
1691 views
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
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 {
57
fn name(&self) -> &'static str {
58
"inline"
59
}
60
61
fn is_mutating(&self) -> bool {
62
true
63
}
64
65
fn needs_isa(&self) -> bool {
66
true
67
}
68
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> {
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
160