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