1 //! This module gives users to instantiate values that Cranelift understands. These values are used, 2 //! for example, during interpretation and for wrapping immediates. 3 use crate::ir::immediates::{Ieee32, Ieee64, Offset32}; 4 use crate::ir::{types, ConstantData, Type}; 5 use core::convert::TryInto; 6 use core::fmt::{self, Display, Formatter}; 7 use core::ptr; 8 9 /// Represent a data value. Where [Value] is an SSA reference, [DataValue] is the type + value 10 /// that would be referred to by a [Value]. 11 /// 12 /// [Value]: crate::ir::Value 13 #[allow(missing_docs)] 14 #[derive(Clone, Debug, PartialEq, PartialOrd)] 15 pub enum DataValue { 16 B(bool), 17 I8(i8), 18 I16(i16), 19 I32(i32), 20 I64(i64), 21 I128(i128), 22 U8(u8), 23 U16(u16), 24 U32(u32), 25 U64(u64), 26 U128(u128), 27 F32(Ieee32), 28 F64(Ieee64), 29 V128([u8; 16]), 30 } 31 32 impl DataValue { 33 /// Try to cast an immediate integer (a wrapped `i128` on most Cranelift instructions) to the 34 /// given Cranelift [Type]. 35 pub fn from_integer(imm: i128, ty: Type) -> Result<DataValue, DataValueCastFailure> { 36 match ty { 37 types::I8 => Ok(DataValue::I8(imm as i8)), 38 types::I16 => Ok(DataValue::I16(imm as i16)), 39 types::I32 => Ok(DataValue::I32(imm as i32)), 40 types::I64 => Ok(DataValue::I64(imm as i64)), 41 types::I128 => Ok(DataValue::I128(imm)), 42 _ => Err(DataValueCastFailure::FromInteger(imm, ty)), 43 } 44 } 45 46 /// Return the Cranelift IR [Type] for this [DataValue]. 47 pub fn ty(&self) -> Type { 48 match self { 49 DataValue::B(_) => types::B8, // A default type. 50 DataValue::I8(_) | DataValue::U8(_) => types::I8, 51 DataValue::I16(_) | DataValue::U16(_) => types::I16, 52 DataValue::I32(_) | DataValue::U32(_) => types::I32, 53 DataValue::I64(_) | DataValue::U64(_) => types::I64, 54 DataValue::I128(_) | DataValue::U128(_) => types::I128, 55 DataValue::F32(_) => types::F32, 56 DataValue::F64(_) => types::F64, 57 DataValue::V128(_) => types::I8X16, // A default type. 58 } 59 } 60 61 /// Return true if the value is a vector (i.e. `DataValue::V128`). 62 pub fn is_vector(&self) -> bool { 63 match self { 64 DataValue::V128(_) => true, 65 _ => false, 66 } 67 } 68 69 /// Write a [DataValue] to a memory location. 70 pub unsafe fn write_value_to(&self, p: *mut u128) { 71 match self { 72 DataValue::B(b) => ptr::write(p as *mut bool, *b), 73 DataValue::I8(i) => ptr::write(p as *mut i8, *i), 74 DataValue::I16(i) => ptr::write(p as *mut i16, *i), 75 DataValue::I32(i) => ptr::write(p as *mut i32, *i), 76 DataValue::I64(i) => ptr::write(p as *mut i64, *i), 77 DataValue::F32(f) => ptr::write(p as *mut Ieee32, *f), 78 DataValue::F64(f) => ptr::write(p as *mut Ieee64, *f), 79 DataValue::V128(b) => ptr::write(p as *mut [u8; 16], *b), 80 _ => unimplemented!(), 81 } 82 } 83 84 /// Read a [DataValue] from a memory location using a given [Type]. 85 pub unsafe fn read_value_from(p: *const u128, ty: Type) -> Self { 86 match ty { 87 types::I8 => DataValue::I8(ptr::read(p as *const i8)), 88 types::I16 => DataValue::I16(ptr::read(p as *const i16)), 89 types::I32 => DataValue::I32(ptr::read(p as *const i32)), 90 types::I64 => DataValue::I64(ptr::read(p as *const i64)), 91 types::F32 => DataValue::F32(ptr::read(p as *const Ieee32)), 92 types::F64 => DataValue::F64(ptr::read(p as *const Ieee64)), 93 _ if ty.is_bool() => DataValue::B(ptr::read(p as *const bool)), 94 _ if ty.is_vector() && ty.bytes() == 16 => { 95 DataValue::V128(ptr::read(p as *const [u8; 16])) 96 } 97 _ => unimplemented!(), 98 } 99 } 100 } 101 102 /// Record failures to cast [DataValue]. 103 #[derive(Debug, PartialEq)] 104 #[allow(missing_docs)] 105 pub enum DataValueCastFailure { 106 TryInto(Type, Type), 107 FromInteger(i128, Type), 108 } 109 110 // This is manually implementing Error and Display instead of using thiserror to reduce the amount 111 // of dependencies used by Cranelift. 112 impl std::error::Error for DataValueCastFailure {} 113 114 impl Display for DataValueCastFailure { 115 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 116 match self { 117 DataValueCastFailure::TryInto(from, to) => { 118 write!( 119 f, 120 "unable to cast data value of type {} to type {}", 121 from, to 122 ) 123 } 124 DataValueCastFailure::FromInteger(val, to) => { 125 write!( 126 f, 127 "unable to cast i64({}) to a data value of type {}", 128 val, to 129 ) 130 } 131 } 132 } 133 } 134 135 /// Helper for creating conversion implementations for [DataValue]. 136 macro_rules! build_conversion_impl { 137 ( $rust_ty:ty, $data_value_ty:ident, $cranelift_ty:ident ) => { 138 impl From<$rust_ty> for DataValue { 139 fn from(data: $rust_ty) -> Self { 140 DataValue::$data_value_ty(data) 141 } 142 } 143 144 impl TryInto<$rust_ty> for DataValue { 145 type Error = DataValueCastFailure; 146 fn try_into(self) -> Result<$rust_ty, Self::Error> { 147 if let DataValue::$data_value_ty(v) = self { 148 Ok(v) 149 } else { 150 Err(DataValueCastFailure::TryInto( 151 self.ty(), 152 types::$cranelift_ty, 153 )) 154 } 155 } 156 } 157 }; 158 } 159 build_conversion_impl!(bool, B, B8); 160 build_conversion_impl!(i8, I8, I8); 161 build_conversion_impl!(i16, I16, I16); 162 build_conversion_impl!(i32, I32, I32); 163 build_conversion_impl!(i64, I64, I64); 164 build_conversion_impl!(i128, I128, I128); 165 build_conversion_impl!(u8, U8, I8); 166 build_conversion_impl!(u16, U16, I16); 167 build_conversion_impl!(u32, U32, I32); 168 build_conversion_impl!(u64, U64, I64); 169 build_conversion_impl!(u128, U128, I128); 170 build_conversion_impl!(Ieee32, F32, F32); 171 build_conversion_impl!(Ieee64, F64, F64); 172 build_conversion_impl!([u8; 16], V128, I8X16); 173 impl From<Offset32> for DataValue { 174 fn from(o: Offset32) -> Self { 175 DataValue::from(Into::<i32>::into(o)) 176 } 177 } 178 179 impl Display for DataValue { 180 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 181 match self { 182 DataValue::B(dv) => write!(f, "{}", dv), 183 DataValue::I8(dv) => write!(f, "{}", dv), 184 DataValue::I16(dv) => write!(f, "{}", dv), 185 DataValue::I32(dv) => write!(f, "{}", dv), 186 DataValue::I64(dv) => write!(f, "{}", dv), 187 DataValue::I128(dv) => write!(f, "{}", dv), 188 DataValue::U8(dv) => write!(f, "{}", dv), 189 DataValue::U16(dv) => write!(f, "{}", dv), 190 DataValue::U32(dv) => write!(f, "{}", dv), 191 DataValue::U64(dv) => write!(f, "{}", dv), 192 DataValue::U128(dv) => write!(f, "{}", dv), 193 // The Ieee* wrappers here print the expected syntax. 194 DataValue::F32(dv) => write!(f, "{}", dv), 195 DataValue::F64(dv) => write!(f, "{}", dv), 196 // Again, for syntax consistency, use ConstantData, which in this case displays as hex. 197 DataValue::V128(dv) => write!(f, "{}", ConstantData::from(&dv[..])), 198 } 199 } 200 } 201 202 /// Helper structure for printing bracket-enclosed vectors of [DataValue]s. 203 /// - for empty vectors, display `[]` 204 /// - for single item vectors, display `42`, e.g. 205 /// - for multiple item vectors, display `[42, 43, 44]`, e.g. 206 pub struct DisplayDataValues<'a>(pub &'a [DataValue]); 207 208 impl<'a> Display for DisplayDataValues<'a> { 209 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 210 if self.0.len() == 1 { 211 write!(f, "{}", self.0[0]) 212 } else { 213 write!(f, "[")?; 214 write_data_value_list(f, &self.0)?; 215 write!(f, "]") 216 } 217 } 218 } 219 220 /// Helper function for displaying `Vec<DataValue>`. 221 pub fn write_data_value_list(f: &mut Formatter<'_>, list: &[DataValue]) -> fmt::Result { 222 match list.len() { 223 0 => Ok(()), 224 1 => write!(f, "{}", list[0]), 225 _ => { 226 write!(f, "{}", list[0])?; 227 for dv in list.iter().skip(1) { 228 write!(f, ", {}", dv)?; 229 } 230 Ok(()) 231 } 232 } 233 } 234 235 #[cfg(test)] 236 mod test { 237 use super::*; 238 239 #[test] 240 fn type_conversions() { 241 assert_eq!(DataValue::B(true).ty(), types::B8); 242 assert_eq!( 243 TryInto::<bool>::try_into(DataValue::B(false)).unwrap(), 244 false 245 ); 246 assert_eq!( 247 TryInto::<i32>::try_into(DataValue::B(false)).unwrap_err(), 248 DataValueCastFailure::TryInto(types::B8, types::I32) 249 ); 250 251 assert_eq!(DataValue::V128([0; 16]).ty(), types::I8X16); 252 assert_eq!( 253 TryInto::<[u8; 16]>::try_into(DataValue::V128([0; 16])).unwrap(), 254 [0; 16] 255 ); 256 assert_eq!( 257 TryInto::<i32>::try_into(DataValue::V128([0; 16])).unwrap_err(), 258 DataValueCastFailure::TryInto(types::I8X16, types::I32) 259 ); 260 } 261 } 262