Path: blob/main/cranelift/reader/src/run_command.rs
2450 views
//! Run commands.1//!2//! Functions in a `.clif` file can have *run commands* appended that control how a function is3//! invoked and tested within the `test run` context. The general syntax is:4//!5//! - `; run`: this assumes the function has a signature like `() -> b*`.6//! - `; run: %fn(42, 4.2) == false`: this syntax specifies the parameters and return values.78use cranelift_codegen::data_value::{self, DataValue, DisplayDataValues};9use std::fmt::{self, Display, Formatter};1011/// A run command appearing in a test file.12///13/// For parsing, see `Parser::parse_run_command`14#[derive(PartialEq, Debug)]15pub enum RunCommand {16/// Invoke a function and print its result.17Print(Invocation),18/// Invoke a function and compare its result to a value sequence.19Run(Invocation, Comparison, Vec<DataValue>),20}2122impl RunCommand {23/// Run the [RunCommand]:24/// - for [RunCommand::Print], print the returned values from invoking the function.25/// - for [RunCommand::Run], compare the returned values from the invoked function and26/// return an `Err` with a descriptive string if the comparison fails.27///28/// Accepts a function used for invoking the actual execution of the command. This function,29/// `invoked_fn`, is passed the _function name_ and _function arguments_ of the [Invocation].30pub fn run<F>(&self, invoke_fn: F) -> Result<(), String>31where32F: FnOnce(&str, &[DataValue]) -> Result<Vec<DataValue>, String>,33{34match self {35RunCommand::Print(invoke) => {36let actual = invoke_fn(&invoke.func, &invoke.args)?;37println!("{} -> {}", invoke, DisplayDataValues(&actual))38}39RunCommand::Run(invoke, compare, expected) => {40let actual = invoke_fn(&invoke.func, &invoke.args)?;41let matched = Self::compare_results(compare, &actual, expected);42if !matched {43let actual = DisplayDataValues(&actual);44return Err(format!("Failed test: {self}, actual: {actual}"));45}46}47}48Ok(())49}5051fn compare_results(52compare: &Comparison,53actual: &Vec<DataValue>,54expected: &Vec<DataValue>,55) -> bool {56let are_equal = actual.len() == expected.len()57&& actual58.into_iter()59.zip(expected)60.all(|(a, b)| a.bitwise_eq(b));6162match compare {63Comparison::Equals => are_equal,64Comparison::NotEquals => !are_equal,65}66}67}6869impl Display for RunCommand {70fn fmt(&self, f: &mut Formatter) -> fmt::Result {71match self {72RunCommand::Print(invocation) => write!(f, "print: {invocation}"),73RunCommand::Run(invocation, comparison, expected) => {74let expected = DisplayDataValues(expected);75write!(f, "run: {invocation} {comparison} {expected}")76}77}78}79}8081/// Represent a function call; [RunCommand]s invoke a CLIF function using an [Invocation].82#[derive(Debug, PartialEq)]83pub struct Invocation {84/// The name of the function to call. Note: this field is for mostly included for informational85/// purposes and may not always be necessary for identifying which function to call.86pub func: String,87/// The arguments to be passed to the function when invoked.88pub args: Vec<DataValue>,89}9091impl Invocation {92pub(crate) fn new(func: &str, args: Vec<DataValue>) -> Self {93let func = func.to_string();94Self { func, args }95}96}9798impl Display for Invocation {99fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {100write!(f, "%{}(", self.func)?;101data_value::write_data_value_list(f, &self.args)?;102write!(f, ")")103}104}105106/// A CLIF comparison operation; e.g. `==`.107#[expect(missing_docs, reason = "self-describing variants")]108#[derive(Debug, PartialEq)]109pub enum Comparison {110Equals,111NotEquals,112}113114impl Display for Comparison {115fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {116match self {117Comparison::Equals => write!(f, "=="),118Comparison::NotEquals => write!(f, "!="),119}120}121}122123#[cfg(test)]124mod test {125use super::*;126use crate::parse_run_command;127use cranelift_codegen::ir::{AbiParam, Signature, types};128use cranelift_codegen::isa::CallConv;129130#[test]131fn run_a_command() {132let mut signature = Signature::new(CallConv::Fast);133signature.returns.push(AbiParam::new(types::I32));134let command = parse_run_command(";; run: %return42() == 42 ", &signature)135.unwrap()136.unwrap();137138assert!(command.run(|_, _| Ok(vec![DataValue::I32(42)])).is_ok());139assert!(command.run(|_, _| Ok(vec![DataValue::I32(43)])).is_err());140}141}142143144