|
|
@ -6,9 +6,11 @@ |
|
|
|
//! - `; run`: this assumes the function has a signature like `() -> b*`.
|
|
|
|
//! - `; run: %fn(42, 4.2) == false`: this syntax specifies the parameters and return values.
|
|
|
|
|
|
|
|
use cranelift_codegen::ir::immediates::{Ieee32, Ieee64}; |
|
|
|
use cranelift_codegen::ir::{self, ConstantData, Type}; |
|
|
|
use cranelift_codegen::ir::immediates::{Ieee32, Ieee64, Imm64}; |
|
|
|
use cranelift_codegen::ir::{self, types, ConstantData, Type}; |
|
|
|
use std::convert::TryInto; |
|
|
|
use std::fmt::{self, Display, Formatter}; |
|
|
|
use thiserror::Error; |
|
|
|
|
|
|
|
/// A run command appearing in a test file.
|
|
|
|
///
|
|
|
@ -27,17 +29,20 @@ impl RunCommand { |
|
|
|
/// - for [RunCommand::Print], print the returned values from invoking the function.
|
|
|
|
/// - for [RunCommand::Run], compare the returned values from the invoked function and
|
|
|
|
/// return an `Err` with a descriptive string if the comparison fails.
|
|
|
|
///
|
|
|
|
/// Accepts a function used for invoking the actual execution of the command. This function,
|
|
|
|
/// `invoked_fn`, is passed the _function name_ and _function arguments_ of the [Invocation].
|
|
|
|
pub fn run<F>(&self, invoke_fn: F) -> Result<(), String> |
|
|
|
where |
|
|
|
F: FnOnce(&[DataValue]) -> Vec<DataValue>, |
|
|
|
F: FnOnce(&str, &[DataValue]) -> Result<Vec<DataValue>, String>, |
|
|
|
{ |
|
|
|
match self { |
|
|
|
RunCommand::Print(invoke) => { |
|
|
|
let actual = invoke_fn(&invoke.args); |
|
|
|
let actual = invoke_fn(&invoke.func, &invoke.args)?; |
|
|
|
println!("{} -> {}", invoke, DisplayDataValues(&actual)) |
|
|
|
} |
|
|
|
RunCommand::Run(invoke, compare, expected) => { |
|
|
|
let actual = invoke_fn(&invoke.args); |
|
|
|
let actual = invoke_fn(&invoke.func, &invoke.args)?; |
|
|
|
let matched = match compare { |
|
|
|
Comparison::Equals => *expected == actual, |
|
|
|
Comparison::NotEquals => *expected != actual, |
|
|
@ -107,17 +112,28 @@ pub enum DataValue { |
|
|
|
} |
|
|
|
|
|
|
|
impl DataValue { |
|
|
|
/// Try to cast an immediate integer ([Imm64]) to the given Cranelift [Type].
|
|
|
|
pub fn from_integer(imm: Imm64, ty: Type) -> Result<DataValue, DataValueCastFailure> { |
|
|
|
match ty { |
|
|
|
types::I8 => Ok(DataValue::I8(imm.bits() as i8)), |
|
|
|
types::I16 => Ok(DataValue::I16(imm.bits() as i16)), |
|
|
|
types::I32 => Ok(DataValue::I32(imm.bits() as i32)), |
|
|
|
types::I64 => Ok(DataValue::I64(imm.bits())), |
|
|
|
_ => Err(DataValueCastFailure::FromImm64(imm, ty)), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// Return the Cranelift IR [Type] for this [DataValue].
|
|
|
|
pub fn ty(&self) -> Type { |
|
|
|
match self { |
|
|
|
DataValue::B(_) => ir::types::B8, |
|
|
|
DataValue::B(_) => ir::types::B8, // A default type.
|
|
|
|
DataValue::I8(_) => ir::types::I8, |
|
|
|
DataValue::I16(_) => ir::types::I16, |
|
|
|
DataValue::I32(_) => ir::types::I32, |
|
|
|
DataValue::I64(_) => ir::types::I64, |
|
|
|
DataValue::F32(_) => ir::types::F32, |
|
|
|
DataValue::F64(_) => ir::types::F64, |
|
|
|
DataValue::V128(_) => ir::types::I8X16, |
|
|
|
DataValue::V128(_) => ir::types::I8X16, // A default type.
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -130,24 +146,48 @@ impl DataValue { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// Helper for creating [From] implementations for [DataValue]
|
|
|
|
macro_rules! from_data { |
|
|
|
( $ty:ty, $variant:ident ) => { |
|
|
|
impl From<$ty> for DataValue { |
|
|
|
fn from(data: $ty) -> Self { |
|
|
|
DataValue::$variant(data) |
|
|
|
/// Record failures to cast [DataValue].
|
|
|
|
#[derive(Error, Debug, PartialEq)] |
|
|
|
#[allow(missing_docs)] |
|
|
|
pub enum DataValueCastFailure { |
|
|
|
#[error("unable to cast data value of type {0} to type {1}")] |
|
|
|
TryInto(Type, Type), |
|
|
|
#[error("unable to cast Imm64({0}) to a data value of type {1}")] |
|
|
|
FromImm64(Imm64, Type), |
|
|
|
} |
|
|
|
|
|
|
|
/// Helper for creating conversion implementations for [DataValue].
|
|
|
|
macro_rules! build_conversion_impl { |
|
|
|
( $rust_ty:ty, $data_value_ty:ident, $cranelift_ty:ident ) => { |
|
|
|
impl From<$rust_ty> for DataValue { |
|
|
|
fn from(data: $rust_ty) -> Self { |
|
|
|
DataValue::$data_value_ty(data) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl TryInto<$rust_ty> for DataValue { |
|
|
|
type Error = DataValueCastFailure; |
|
|
|
fn try_into(self) -> Result<$rust_ty, Self::Error> { |
|
|
|
if let DataValue::$data_value_ty(v) = self { |
|
|
|
Ok(v) |
|
|
|
} else { |
|
|
|
Err(DataValueCastFailure::TryInto( |
|
|
|
self.ty(), |
|
|
|
types::$cranelift_ty, |
|
|
|
)) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
} |
|
|
|
from_data!(bool, B); |
|
|
|
from_data!(i8, I8); |
|
|
|
from_data!(i16, I16); |
|
|
|
from_data!(i32, I32); |
|
|
|
from_data!(i64, I64); |
|
|
|
from_data!(f32, F32); |
|
|
|
from_data!(f64, F64); |
|
|
|
from_data!([u8; 16], V128); |
|
|
|
build_conversion_impl!(bool, B, B8); |
|
|
|
build_conversion_impl!(i8, I8, I8); |
|
|
|
build_conversion_impl!(i16, I16, I16); |
|
|
|
build_conversion_impl!(i32, I32, I32); |
|
|
|
build_conversion_impl!(i64, I64, I64); |
|
|
|
build_conversion_impl!(f32, F32, F32); |
|
|
|
build_conversion_impl!(f64, F64, F64); |
|
|
|
build_conversion_impl!([u8; 16], V128, I8X16); |
|
|
|
|
|
|
|
impl Display for DataValue { |
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
|
|
@ -231,7 +271,30 @@ mod test { |
|
|
|
.unwrap() |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
assert!(command.run(|_| vec![DataValue::I32(42)]).is_ok()); |
|
|
|
assert!(command.run(|_| vec![DataValue::I32(43)]).is_err()); |
|
|
|
assert!(command.run(|_, _| Ok(vec![DataValue::I32(42)])).is_ok()); |
|
|
|
assert!(command.run(|_, _| Ok(vec![DataValue::I32(43)])).is_err()); |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn type_conversions() { |
|
|
|
assert_eq!(DataValue::B(true).ty(), types::B8); |
|
|
|
assert_eq!( |
|
|
|
TryInto::<bool>::try_into(DataValue::B(false)).unwrap(), |
|
|
|
false |
|
|
|
); |
|
|
|
assert_eq!( |
|
|
|
TryInto::<i32>::try_into(DataValue::B(false)).unwrap_err(), |
|
|
|
DataValueCastFailure::TryInto(types::B8, types::I32) |
|
|
|
); |
|
|
|
|
|
|
|
assert_eq!(DataValue::V128([0; 16]).ty(), types::I8X16); |
|
|
|
assert_eq!( |
|
|
|
TryInto::<[u8; 16]>::try_into(DataValue::V128([0; 16])).unwrap(), |
|
|
|
[0; 16] |
|
|
|
); |
|
|
|
assert_eq!( |
|
|
|
TryInto::<i32>::try_into(DataValue::V128([0; 16])).unwrap_err(), |
|
|
|
DataValueCastFailure::TryInto(types::I8X16, types::I32) |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|