1 //! Run commands.
2 //!
3 //! Functions in a `.clif` file can have *run commands* appended that control how a function is
4 //! invoked and tested within the `test run` context. The general syntax is:
5 //!
6 //! - `; run`: this assumes the function has a signature like `() -> b*`.
7 //! - `; run: %fn(42, 4.2) == false`: this syntax specifies the parameters and return values.
8 
9 use cranelift_codegen::data_value::{self, DataValue, DisplayDataValues};
10 use std::fmt::{self, Display, Formatter};
11 
12 /// A run command appearing in a test file.
13 ///
14 /// For parsing, see `Parser::parse_run_command`
15 #[derive(PartialEq, Debug)]
16 pub enum RunCommand {
17     /// Invoke a function and print its result.
18     Print(Invocation),
19     /// Invoke a function and compare its result to a value sequence.
20     Run(Invocation, Comparison, Vec<DataValue>),
21 }
22 
23 impl RunCommand {
24     /// Run the [RunCommand]:
25     ///  - for [RunCommand::Print], print the returned values from invoking the function.
26     ///  - for [RunCommand::Run], compare the returned values from the invoked function and
27     ///    return an `Err` with a descriptive string if the comparison fails.
28     ///
29     /// Accepts a function used for invoking the actual execution of the command. This function,
30     /// `invoked_fn`, is passed the _function name_ and _function arguments_ of the [Invocation].
31     pub fn run<F>(&self, invoke_fn: F) -> Result<(), String>
32     where
33         F: FnOnce(&str, &[DataValue]) -> Result<Vec<DataValue>, String>,
34     {
35         match self {
36             RunCommand::Print(invoke) => {
37                 let actual = invoke_fn(&invoke.func, &invoke.args)?;
38                 println!("{} -> {}", invoke, DisplayDataValues(&actual))
39             }
40             RunCommand::Run(invoke, compare, expected) => {
41                 let actual = invoke_fn(&invoke.func, &invoke.args)?;
42                 let matched = Self::compare_results(compare, &actual, expected);
43                 if !matched {
44                     let actual = DisplayDataValues(&actual);
45                     return Err(format!("Failed test: {}, actual: {}", self, actual));
46                 }
47             }
48         }
49         Ok(())
50     }
51 
52     fn compare_results(
53         compare: &Comparison,
54         actual: &Vec<DataValue>,
55         expected: &Vec<DataValue>,
56     ) -> bool {
57         let are_equal = actual
58             .into_iter()
59             .zip(expected.into_iter())
60             .all(|(a, b)| match (a, b) {
61                 // We need to bit compare the floats to ensure that we produce the correct values
62                 // on NaN's. The test suite expects to assert the precise bit pattern on NaN's or
63                 // works around it in the tests themselves.
64                 (DataValue::F32(a), DataValue::F32(b)) => a.bits() == b.bits(),
65                 (DataValue::F64(a), DataValue::F64(b)) => a.bits() == b.bits(),
66 
67                 // We don't need to worry about F32x4 / F64x2 Since we compare V128 which is already the
68                 // raw bytes anyway
69                 (a, b) => a == b,
70             });
71 
72         match compare {
73             Comparison::Equals => are_equal,
74             Comparison::NotEquals => !are_equal,
75         }
76     }
77 }
78 
79 impl Display for RunCommand {
80     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
81         match self {
82             RunCommand::Print(invocation) => write!(f, "print: {}", invocation),
83             RunCommand::Run(invocation, comparison, expected) => {
84                 let expected = DisplayDataValues(expected);
85                 write!(f, "run: {} {} {}", invocation, comparison, expected)
86             }
87         }
88     }
89 }
90 
91 /// Represent a function call; [RunCommand]s invoke a CLIF function using an [Invocation].
92 #[derive(Debug, PartialEq)]
93 pub struct Invocation {
94     /// The name of the function to call. Note: this field is for mostly included for informational
95     /// purposes and may not always be necessary for identifying which function to call.
96     pub func: String,
97     /// The arguments to be passed to the function when invoked.
98     pub args: Vec<DataValue>,
99 }
100 
101 impl Invocation {
102     pub(crate) fn new(func: &str, args: Vec<DataValue>) -> Self {
103         let func = func.to_string();
104         Self { func, args }
105     }
106 }
107 
108 impl Display for Invocation {
109     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
110         write!(f, "%{}(", self.func)?;
111         data_value::write_data_value_list(f, &self.args)?;
112         write!(f, ")")
113     }
114 }
115 
116 /// A CLIF comparison operation; e.g. `==`.
117 #[allow(missing_docs)]
118 #[derive(Debug, PartialEq)]
119 pub enum Comparison {
120     Equals,
121     NotEquals,
122 }
123 
124 impl Display for Comparison {
125     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
126         match self {
127             Comparison::Equals => write!(f, "=="),
128             Comparison::NotEquals => write!(f, "!="),
129         }
130     }
131 }
132 
133 #[cfg(test)]
134 mod test {
135     use super::*;
136     use crate::parse_run_command;
137     use cranelift_codegen::ir::{types, AbiParam, Signature};
138     use cranelift_codegen::isa::CallConv;
139 
140     #[test]
141     fn run_a_command() {
142         let mut signature = Signature::new(CallConv::Fast);
143         signature.returns.push(AbiParam::new(types::I32));
144         let command = parse_run_command(";; run: %return42() == 42 ", &signature)
145             .unwrap()
146             .unwrap();
147 
148         assert!(command.run(|_, _| Ok(vec![DataValue::I32(42)])).is_ok());
149         assert!(command.run(|_, _| Ok(vec![DataValue::I32(43)])).is_err());
150     }
151 }
152