diff --git a/cranelift/codegen/src/data_value.rs b/cranelift/codegen/src/data_value.rs new file mode 100644 index 0000000000..05379196d4 --- /dev/null +++ b/cranelift/codegen/src/data_value.rs @@ -0,0 +1,180 @@ +//! This module gives users to instantiate values that Cranelift understands. These values are used, +//! for example, during interpretation and for wrapping immediates. +use crate::ir::immediates::{Ieee32, Ieee64, Imm64}; +use crate::ir::{types, ConstantData, Type}; +use core::convert::TryInto; +use core::fmt::{self, Display, Formatter}; +use thiserror::Error; + +/// Represent a data value. Where [Value] is an SSA reference, [DataValue] is the type + value +/// that would be referred to by a [Value]. +/// +/// [Value]: crate::ir::Value +#[allow(missing_docs)] +#[derive(Clone, Debug, PartialEq)] +pub enum DataValue { + B(bool), + I8(i8), + I16(i16), + I32(i32), + I64(i64), + F32(f32), + F64(f64), + V128([u8; 16]), +} + +impl DataValue { + /// Try to cast an immediate integer ([Imm64]) to the given Cranelift [Type]. + pub fn from_integer(imm: Imm64, ty: Type) -> Result { + 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(_) => types::B8, // A default type. + DataValue::I8(_) => types::I8, + DataValue::I16(_) => types::I16, + DataValue::I32(_) => types::I32, + DataValue::I64(_) => types::I64, + DataValue::F32(_) => types::F32, + DataValue::F64(_) => types::F64, + DataValue::V128(_) => types::I8X16, // A default type. + } + } + + /// Return true if the value is a vector (i.e. `DataValue::V128`). + pub fn is_vector(&self) -> bool { + match self { + DataValue::V128(_) => true, + _ => false, + } + } +} + +/// 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, + )) + } + } + } + }; +} +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 { + match self { + DataValue::B(dv) => write!(f, "{}", dv), + DataValue::I8(dv) => write!(f, "{}", dv), + DataValue::I16(dv) => write!(f, "{}", dv), + DataValue::I32(dv) => write!(f, "{}", dv), + DataValue::I64(dv) => write!(f, "{}", dv), + // Use the Ieee* wrappers here to maintain a consistent syntax. + DataValue::F32(dv) => write!(f, "{}", Ieee32::from(*dv)), + DataValue::F64(dv) => write!(f, "{}", Ieee64::from(*dv)), + // Again, for syntax consistency, use ConstantData, which in this case displays as hex. + DataValue::V128(dv) => write!(f, "{}", ConstantData::from(&dv[..])), + } + } +} + +/// Helper structure for printing bracket-enclosed vectors of [DataValue]s. +/// - for empty vectors, display `[]` +/// - for single item vectors, display `42`, e.g. +/// - for multiple item vectors, display `[42, 43, 44]`, e.g. +pub struct DisplayDataValues<'a>(pub &'a [DataValue]); + +impl<'a> Display for DisplayDataValues<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if self.0.len() == 1 { + write!(f, "{}", self.0[0]) + } else { + write!(f, "[")?; + write_data_value_list(f, &self.0)?; + write!(f, "]") + } + } +} + +/// Helper function for displaying `Vec`. +pub fn write_data_value_list(f: &mut Formatter<'_>, list: &[DataValue]) -> fmt::Result { + match list.len() { + 0 => Ok(()), + 1 => write!(f, "{}", list[0]), + _ => { + write!(f, "{}", list[0])?; + for dv in list.iter().skip(1) { + write!(f, ", {}", dv)?; + } + Ok(()) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn type_conversions() { + assert_eq!(DataValue::B(true).ty(), types::B8); + assert_eq!( + TryInto::::try_into(DataValue::B(false)).unwrap(), + false + ); + assert_eq!( + TryInto::::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::::try_into(DataValue::V128([0; 16])).unwrap_err(), + DataValueCastFailure::TryInto(types::I8X16, types::I32) + ); + } +} diff --git a/cranelift/codegen/src/lib.rs b/cranelift/codegen/src/lib.rs index 053d7b979c..1c5fd6c18b 100644 --- a/cranelift/codegen/src/lib.rs +++ b/cranelift/codegen/src/lib.rs @@ -71,6 +71,7 @@ pub use cranelift_entity as entity; pub mod binemit; pub mod cfg_printer; pub mod cursor; +pub mod data_value; pub mod dbg; pub mod dominator_tree; pub mod flowgraph; diff --git a/cranelift/filetests/src/function_runner.rs b/cranelift/filetests/src/function_runner.rs index 8eb801de1e..0f8931dce7 100644 --- a/cranelift/filetests/src/function_runner.rs +++ b/cranelift/filetests/src/function_runner.rs @@ -1,12 +1,12 @@ //! Provides functionality for compiling and running CLIF IR for `run` tests. use core::{mem, ptr}; use cranelift_codegen::binemit::{NullRelocSink, NullStackMapSink, NullTrapSink}; +use cranelift_codegen::data_value::DataValue; use cranelift_codegen::ir::{condcodes::IntCC, Function, InstBuilder, Signature, Type}; use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::{ir, settings, CodegenError, Context}; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_native::builder as host_isa_builder; -use cranelift_reader::DataValue; use log::trace; use memmap::{Mmap, MmapMut}; use std::cmp::max; @@ -126,7 +126,8 @@ impl Trampoline { /// /// ``` /// use cranelift_filetests::SingleFunctionCompiler; -/// use cranelift_reader::{parse_functions, DataValue}; +/// use cranelift_reader::parse_functions; +/// use cranelift_codegen::data_value::DataValue; /// /// let code = "test run \n function %add(i32, i32) -> i32 { block0(v0:i32, v1:i32): v2 = iadd v0, v1 return v2 }".into(); /// let func = parse_functions(code).unwrap().into_iter().nth(0).unwrap(); diff --git a/cranelift/interpreter/src/frame.rs b/cranelift/interpreter/src/frame.rs index 367c797c2f..1e87ab2da3 100644 --- a/cranelift/interpreter/src/frame.rs +++ b/cranelift/interpreter/src/frame.rs @@ -1,7 +1,7 @@ //! Implements a call frame (activation record) for the Cranelift interpreter. +use cranelift_codegen::data_value::DataValue; use cranelift_codegen::ir::{Function, Value as ValueRef}; -use cranelift_reader::DataValue; use log::trace; use std::collections::HashMap; @@ -78,9 +78,9 @@ impl<'a> Frame<'a> { #[cfg(test)] mod tests { use super::*; + use cranelift_codegen::data_value::DataValue; use cranelift_codegen::ir::InstBuilder; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; - use cranelift_reader::DataValue; /// Build an empty function with a single return. fn empty_function() -> Function { diff --git a/cranelift/interpreter/src/interpreter.rs b/cranelift/interpreter/src/interpreter.rs index 5485713d7e..06cc7fd271 100644 --- a/cranelift/interpreter/src/interpreter.rs +++ b/cranelift/interpreter/src/interpreter.rs @@ -5,12 +5,12 @@ use crate::environment::Environment; use crate::frame::Frame; use crate::interpreter::Trap::InvalidType; +use cranelift_codegen::data_value::{DataValue, DataValueCastFailure}; use cranelift_codegen::ir::condcodes::IntCC; use cranelift_codegen::ir::{ Block, FuncRef, Function, Inst, InstructionData, InstructionData::*, Opcode, Opcode::*, Type, Value as ValueRef, ValueList, }; -use cranelift_reader::{DataValue, DataValueCastFailure}; use log::trace; use std::ops::{Add, Div, Mul, Sub}; use thiserror::Error; diff --git a/cranelift/reader/src/lib.rs b/cranelift/reader/src/lib.rs index 14e0a84b5d..0e3d77e1e5 100644 --- a/cranelift/reader/src/lib.rs +++ b/cranelift/reader/src/lib.rs @@ -29,7 +29,7 @@ pub use crate::error::{Location, ParseError, ParseResult}; pub use crate::isaspec::{parse_options, IsaSpec, ParseOptionError}; pub use crate::parser::{parse_functions, parse_run_command, parse_test, ParseOptions}; -pub use crate::run_command::{Comparison, DataValue, DataValueCastFailure, Invocation, RunCommand}; +pub use crate::run_command::{Comparison, Invocation, RunCommand}; pub use crate::sourcemap::SourceMap; pub use crate::testcommand::{TestCommand, TestOption}; pub use crate::testfile::{Comment, Details, Feature, TestFile}; diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs index 3d6f3da1e5..a67dcfe019 100644 --- a/cranelift/reader/src/parser.rs +++ b/cranelift/reader/src/parser.rs @@ -3,10 +3,11 @@ use crate::error::{Location, ParseError, ParseResult}; use crate::isaspec; use crate::lexer::{LexError, Lexer, LocatedError, LocatedToken, Token}; -use crate::run_command::{Comparison, DataValue, Invocation, RunCommand}; +use crate::run_command::{Comparison, Invocation, RunCommand}; use crate::sourcemap::SourceMap; use crate::testcommand::TestCommand; use crate::testfile::{Comment, Details, Feature, TestFile}; +use cranelift_codegen::data_value::DataValue; use cranelift_codegen::entity::EntityRef; use cranelift_codegen::ir; use cranelift_codegen::ir::entities::AnyEntity; diff --git a/cranelift/reader/src/run_command.rs b/cranelift/reader/src/run_command.rs index fea5164d12..99f57e6a03 100644 --- a/cranelift/reader/src/run_command.rs +++ b/cranelift/reader/src/run_command.rs @@ -6,11 +6,8 @@ //! - `; 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, Imm64}; -use cranelift_codegen::ir::{self, types, ConstantData, Type}; -use std::convert::TryInto; +use cranelift_codegen::data_value::{self, DataValue, DisplayDataValues}; use std::fmt::{self, Display, Formatter}; -use thiserror::Error; /// A run command appearing in a test file. /// @@ -88,156 +85,11 @@ impl Invocation { impl Display for Invocation { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "%{}(", self.func)?; - write_data_value_list(f, &self.args)?; + data_value::write_data_value_list(f, &self.args)?; write!(f, ")") } } -/// Represent a data value. Where [Value] is an SSA reference, [DataValue] is the type + value -/// that would be referred to by a [Value]. -/// -/// [Value]: cranelift_codegen::ir::Value -#[allow(missing_docs)] -#[derive(Clone, Debug, PartialEq)] -pub enum DataValue { - B(bool), - I8(i8), - I16(i16), - I32(i32), - I64(i64), - F32(f32), - F64(f64), - V128([u8; 16]), -} - -impl DataValue { - /// Try to cast an immediate integer ([Imm64]) to the given Cranelift [Type]. - pub fn from_integer(imm: Imm64, ty: Type) -> Result { - 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, // 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, // A default type. - } - } - - /// Return true if the value is a vector (i.e. `DataValue::V128`). - pub fn is_vector(&self) -> bool { - match self { - DataValue::V128(_) => true, - _ => false, - } - } -} - -/// 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, - )) - } - } - } - }; -} -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 { - match self { - DataValue::B(dv) => write!(f, "{}", dv), - DataValue::I8(dv) => write!(f, "{}", dv), - DataValue::I16(dv) => write!(f, "{}", dv), - DataValue::I32(dv) => write!(f, "{}", dv), - DataValue::I64(dv) => write!(f, "{}", dv), - // Use the Ieee* wrappers here to maintain a consistent syntax. - DataValue::F32(dv) => write!(f, "{}", Ieee32::from(*dv)), - DataValue::F64(dv) => write!(f, "{}", Ieee64::from(*dv)), - // Again, for syntax consistency, use ConstantData, which in this case displays as hex. - DataValue::V128(dv) => write!(f, "{}", ConstantData::from(&dv[..])), - } - } -} - -/// Helper structure for printing bracket-enclosed vectors of [DataValue]s. -/// - for empty vectors, display `[]` -/// - for single item vectors, display `42`, e.g. -/// - for multiple item vectors, display `[42, 43, 44]`, e.g. -struct DisplayDataValues<'a>(&'a [DataValue]); - -impl<'a> Display for DisplayDataValues<'a> { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if self.0.len() == 1 { - write!(f, "{}", self.0[0]) - } else { - write!(f, "[")?; - write_data_value_list(f, &self.0)?; - write!(f, "]") - } - } -} - -/// Helper function for displaying `Vec`. -fn write_data_value_list(f: &mut Formatter<'_>, list: &[DataValue]) -> fmt::Result { - match list.len() { - 0 => Ok(()), - 1 => write!(f, "{}", list[0]), - _ => { - write!(f, "{}", list[0])?; - for dv in list.iter().skip(1) { - write!(f, ", {}", dv)?; - } - Ok(()) - } - } -} - /// A CLIF comparison operation; e.g. `==`. #[allow(missing_docs)] #[derive(Debug, PartialEq)] @@ -273,27 +125,4 @@ mod test { 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::::try_into(DataValue::B(false)).unwrap(), - false - ); - assert_eq!( - TryInto::::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::::try_into(DataValue::V128([0; 16])).unwrap_err(), - DataValueCastFailure::TryInto(types::I8X16, types::I32) - ); - } }