1 use cranelift::codegen::data_value::DataValue;
2 use cranelift::codegen::ir::Function;
3 use cranelift::prelude::settings::SettingKind;
4 use cranelift::prelude::*;
5 use std::fmt;
6 
7 use crate::TestCaseInput;
8 
9 #[derive(Debug)]
10 enum TestCaseKind {
11     Compile,
12     Run,
13 }
14 
15 /// Provides a way to format a `TestCase` in the .clif format.
16 pub struct PrintableTestCase<'a> {
17     kind: TestCaseKind,
18     isa: &'a isa::OwnedTargetIsa,
19     functions: &'a [Function],
20     // Only applicable for run test cases
21     inputs: &'a [TestCaseInput],
22 }
23 
24 impl<'a> PrintableTestCase<'a> {
25     /// Emits a `test compile` test case.
compile(isa: &'a isa::OwnedTargetIsa, functions: &'a [Function]) -> Self26     pub fn compile(isa: &'a isa::OwnedTargetIsa, functions: &'a [Function]) -> Self {
27         Self {
28             kind: TestCaseKind::Compile,
29             isa,
30             functions,
31             inputs: &[],
32         }
33     }
34 
35     /// Emits a `test run` test case. These also include a `test interpret`.
36     ///
37     /// By convention the first function in `functions` will be considered the main function.
run( isa: &'a isa::OwnedTargetIsa, functions: &'a [Function], inputs: &'a [TestCaseInput], ) -> Self38     pub fn run(
39         isa: &'a isa::OwnedTargetIsa,
40         functions: &'a [Function],
41         inputs: &'a [TestCaseInput],
42     ) -> Self {
43         Self {
44             kind: TestCaseKind::Run,
45             isa,
46             functions,
47             inputs,
48         }
49     }
50 
51     /// Returns the main function of this test case.
main(&self) -> &Function52     pub fn main(&self) -> &Function {
53         &self.functions[0]
54     }
55 }
56 
57 impl<'a> fmt::Debug for PrintableTestCase<'a> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result58     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59         match self.kind {
60             TestCaseKind::Compile => {
61                 writeln!(f, ";; Compile test case\n")?;
62                 writeln!(f, "test compile")?;
63             }
64             TestCaseKind::Run => {
65                 writeln!(f, ";; Run test case\n")?;
66                 writeln!(f, "test interpret")?;
67                 writeln!(f, "test run")?;
68             }
69         };
70 
71         write_non_default_flags(f, self.isa.flags())?;
72 
73         write!(f, "target {} ", self.isa.triple().architecture)?;
74         write_non_default_isa_flags(f, &self.isa)?;
75         write!(f, "\n\n")?;
76 
77         // Print the functions backwards, so that the main function is printed last
78         // and near the test inputs for run test cases.
79         for func in self.functions.iter().rev() {
80             writeln!(f, "{func}\n")?;
81         }
82 
83         if !self.inputs.is_empty() {
84             writeln!(
85                 f,
86                 "; Note: the results in the below test cases are simply a placeholder and probably will be wrong\n"
87             )?;
88         }
89 
90         for input in self.inputs.iter() {
91             // TODO: We don't know the expected outputs, maybe we can run the interpreter
92             // here to figure them out? Should work, however we need to be careful to catch
93             // panics in case its the interpreter that is failing.
94             // For now create a placeholder output consisting of the zero value for the type
95             let returns = &self.main().signature.returns;
96             let placeholder_output = returns
97                 .iter()
98                 .map(|param| DataValue::read_from_slice_ne(&[0; 16][..], param.value_type))
99                 .map(|val| format!("{val}"))
100                 .collect::<Vec<_>>()
101                 .join(", ");
102 
103             // If we have no output, we don't need the == condition
104             let test_condition = match returns.len() {
105                 0 => String::new(),
106                 1 => format!(" == {placeholder_output}"),
107                 _ => format!(" == [{placeholder_output}]"),
108             };
109 
110             let args = input
111                 .iter()
112                 .map(|val| format!("{val}"))
113                 .collect::<Vec<_>>()
114                 .join(", ");
115 
116             writeln!(f, "; run: {}({}){}", self.main().name, args, test_condition)?;
117         }
118 
119         Ok(())
120     }
121 }
122 
123 /// Print only non default flags.
write_non_default_flags(f: &mut fmt::Formatter<'_>, flags: &settings::Flags) -> fmt::Result124 fn write_non_default_flags(f: &mut fmt::Formatter<'_>, flags: &settings::Flags) -> fmt::Result {
125     let default_flags = settings::Flags::new(settings::builder());
126     for (default, flag) in default_flags.iter().zip(flags.iter()) {
127         assert_eq!(default.name, flag.name);
128 
129         if default.value_string() != flag.value_string() {
130             writeln!(f, "set {}={}", flag.name, flag.value_string())?;
131         }
132     }
133 
134     Ok(())
135 }
136 
137 /// Print non default ISA flags in a single line, as used in `target` declarations.
write_non_default_isa_flags( f: &mut fmt::Formatter<'_>, isa: &isa::OwnedTargetIsa, ) -> fmt::Result138 fn write_non_default_isa_flags(
139     f: &mut fmt::Formatter<'_>,
140     isa: &isa::OwnedTargetIsa,
141 ) -> fmt::Result {
142     let default_isa = isa::lookup(isa.triple().clone())
143         .unwrap()
144         .finish(isa.flags().clone())
145         .unwrap();
146 
147     for (default, flag) in default_isa.isa_flags().iter().zip(isa.isa_flags()) {
148         assert_eq!(default.name, flag.name);
149 
150         // Skip default flags, putting them all out there is too verbose.
151         if default.value_string() == flag.value_string() {
152             continue;
153         }
154 
155         // On boolean flags we can use the shorthand syntax instead of just specifying the flag name.
156         // This is slightly neater than the full syntax.
157         if flag.kind() == SettingKind::Bool && flag.value_string() == "true" {
158             write!(f, "{} ", flag.name)?;
159         } else {
160             write!(f, "{}={} ", flag.name, flag.value_string())?;
161         }
162     }
163 
164     Ok(())
165 }
166