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::ir::immediates::{Ieee32, Ieee64}; 10 use cranelift_codegen::ir::{self, ConstantData, Type}; 11 use std::fmt::{self, Display, Formatter}; 12 13 /// A run command appearing in a test file. 14 /// 15 /// For parsing, see 16 /// [Parser::parse_run_command](crate::parser::Parser::parse_run_command). 17 #[derive(PartialEq, Debug)] 18 pub enum RunCommand { 19 /// Invoke a function and print its result. 20 Print(Invocation), 21 /// Invoke a function and compare its result to a value sequence. 22 Run(Invocation, Comparison, Vec<DataValue>), 23 } 24 25 impl RunCommand { 26 /// Run the [RunCommand]: 27 /// - for [RunCommand::Print], print the returned values from invoking the function. 28 /// - for [RunCommand::Run], compare the returned values from the invoked function and 29 /// return an `Err` with a descriptive string if the comparison fails. 30 pub fn run<F>(&self, invoke_fn: F) -> Result<(), String> 31 where 32 F: FnOnce(&[DataValue]) -> Vec<DataValue>, 33 { 34 match self { 35 RunCommand::Print(invoke) => { 36 let actual = invoke_fn(&invoke.args); 37 println!("{} -> {}", invoke, DisplayDataValues(&actual)) 38 } 39 RunCommand::Run(invoke, compare, expected) => { 40 let actual = invoke_fn(&invoke.args); 41 let matched = match compare { 42 Comparison::Equals => *expected == actual, 43 Comparison::NotEquals => *expected != actual, 44 }; 45 if !matched { 46 let actual = DisplayDataValues(&actual); 47 return Err(format!("Failed test: {}, actual: {}", self, actual)); 48 } 49 } 50 } 51 Ok(()) 52 } 53 } 54 55 impl Display for RunCommand { 56 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 57 match self { 58 RunCommand::Print(invocation) => write!(f, "print: {}", invocation), 59 RunCommand::Run(invocation, comparison, expected) => { 60 let expected = DisplayDataValues(expected); 61 write!(f, "run: {} {} {}", invocation, comparison, expected) 62 } 63 } 64 } 65 } 66 67 /// Represent a function call; [RunCommand]s invoke a CLIF function using an [Invocation]. 68 #[derive(Debug, PartialEq)] 69 pub struct Invocation { 70 /// The name of the function to call. Note: this field is for mostly included for informational 71 /// purposes and may not always be necessary for identifying which function to call. 72 pub func: String, 73 /// The arguments to be passed to the function when invoked. 74 pub args: Vec<DataValue>, 75 } 76 77 impl Invocation { 78 pub(crate) fn new(func: &str, args: Vec<DataValue>) -> Self { 79 let func = func.to_string(); 80 Self { func, args } 81 } 82 } 83 84 impl Display for Invocation { 85 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 86 write!(f, "%{}(", self.func)?; 87 write_data_value_list(f, &self.args)?; 88 write!(f, ")") 89 } 90 } 91 92 /// Represent a data value. Where [Value] is an SSA reference, [DataValue] is the type + value 93 /// that would be referred to by a [Value]. 94 /// 95 /// [Value]: cranelift_codegen::ir::Value 96 #[allow(missing_docs)] 97 #[derive(Clone, Debug, PartialEq)] 98 pub enum DataValue { 99 B(bool), 100 I8(i8), 101 I16(i16), 102 I32(i32), 103 I64(i64), 104 F32(f32), 105 F64(f64), 106 V128([u8; 16]), 107 } 108 109 impl DataValue { 110 /// Return the Cranelift IR [Type] for this [DataValue]. 111 pub fn ty(&self) -> Type { 112 match self { 113 DataValue::B(_) => ir::types::B8, 114 DataValue::I8(_) => ir::types::I8, 115 DataValue::I16(_) => ir::types::I16, 116 DataValue::I32(_) => ir::types::I32, 117 DataValue::I64(_) => ir::types::I64, 118 DataValue::F32(_) => ir::types::F32, 119 DataValue::F64(_) => ir::types::F64, 120 DataValue::V128(_) => ir::types::I8X16, 121 } 122 } 123 124 /// Return true if the value is a vector (i.e. `DataValue::V128`). 125 pub fn is_vector(&self) -> bool { 126 match self { 127 DataValue::V128(_) => true, 128 _ => false, 129 } 130 } 131 } 132 133 /// Helper for creating [From] implementations for [DataValue] 134 macro_rules! from_data { 135 ( $ty:ty, $variant:ident ) => { 136 impl From<$ty> for DataValue { 137 fn from(data: $ty) -> Self { 138 DataValue::$variant(data) 139 } 140 } 141 }; 142 } 143 from_data!(bool, B); 144 from_data!(i8, I8); 145 from_data!(i16, I16); 146 from_data!(i32, I32); 147 from_data!(i64, I64); 148 from_data!(f32, F32); 149 from_data!(f64, F64); 150 from_data!([u8; 16], V128); 151 152 impl Display for DataValue { 153 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 154 match self { 155 DataValue::B(dv) => write!(f, "{}", dv), 156 DataValue::I8(dv) => write!(f, "{}", dv), 157 DataValue::I16(dv) => write!(f, "{}", dv), 158 DataValue::I32(dv) => write!(f, "{}", dv), 159 DataValue::I64(dv) => write!(f, "{}", dv), 160 // Use the Ieee* wrappers here to maintain a consistent syntax. 161 DataValue::F32(dv) => write!(f, "{}", Ieee32::from(*dv)), 162 DataValue::F64(dv) => write!(f, "{}", Ieee64::from(*dv)), 163 // Again, for syntax consistency, use ConstantData, which in this case displays as hex. 164 DataValue::V128(dv) => write!(f, "{}", ConstantData::from(&dv[..])), 165 } 166 } 167 } 168 169 /// Helper structure for printing bracket-enclosed vectors of [DataValue]s. 170 /// - for empty vectors, display `[]` 171 /// - for single item vectors, display `42`, e.g. 172 /// - for multiple item vectors, display `[42, 43, 44]`, e.g. 173 struct DisplayDataValues<'a>(&'a [DataValue]); 174 175 impl<'a> Display for DisplayDataValues<'a> { 176 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 177 if self.0.len() == 1 { 178 write!(f, "{}", self.0[0]) 179 } else { 180 write!(f, "[")?; 181 write_data_value_list(f, &self.0)?; 182 write!(f, "]") 183 } 184 } 185 } 186 187 /// Helper function for displaying `Vec<DataValue>`. 188 fn write_data_value_list(f: &mut Formatter<'_>, list: &[DataValue]) -> fmt::Result { 189 match list.len() { 190 0 => Ok(()), 191 1 => write!(f, "{}", list[0]), 192 _ => { 193 write!(f, "{}", list[0])?; 194 for dv in list.iter().skip(1) { 195 write!(f, ", {}", dv)?; 196 } 197 Ok(()) 198 } 199 } 200 } 201 202 /// A CLIF comparison operation; e.g. `==`. 203 #[allow(missing_docs)] 204 #[derive(Debug, PartialEq)] 205 pub enum Comparison { 206 Equals, 207 NotEquals, 208 } 209 210 impl Display for Comparison { 211 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 212 match self { 213 Comparison::Equals => write!(f, "=="), 214 Comparison::NotEquals => write!(f, "!="), 215 } 216 } 217 } 218 219 #[cfg(test)] 220 mod test { 221 use super::*; 222 use crate::parse_run_command; 223 use cranelift_codegen::ir::{types, AbiParam, Signature}; 224 use cranelift_codegen::isa::CallConv; 225 226 #[test] 227 fn run_a_command() { 228 let mut signature = Signature::new(CallConv::Fast); 229 signature.returns.push(AbiParam::new(types::I32)); 230 let command = parse_run_command(";; run: %return42() == 42 ", &signature) 231 .unwrap() 232 .unwrap(); 233 234 assert!(command.run(|_| vec![DataValue::I32(42)]).is_ok()); 235 assert!(command.run(|_| vec![DataValue::I32(43)]).is_err()); 236 } 237 } 238