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 8 /// Represent a data value. Where [Value] is an SSA reference, [DataValue] is the type + value 9 /// that would be referred to by a [Value]. 10 /// 11 /// [Value]: crate::ir::Value 12 #[allow(missing_docs)] 13 #[derive(Clone, Debug, PartialOrd)] 14 pub enum DataValue { 15 I8(i8), 16 I16(i16), 17 I32(i32), 18 I64(i64), 19 I128(i128), 20 F32(Ieee32), 21 F64(Ieee64), 22 V128([u8; 16]), 23 V64([u8; 8]), 24 } 25 26 impl PartialEq for DataValue { 27 fn eq(&self, other: &Self) -> bool { 28 use DataValue::*; 29 match (self, other) { 30 (I8(l), I8(r)) => l == r, 31 (I8(_), _) => false, 32 (I16(l), I16(r)) => l == r, 33 (I16(_), _) => false, 34 (I32(l), I32(r)) => l == r, 35 (I32(_), _) => false, 36 (I64(l), I64(r)) => l == r, 37 (I64(_), _) => false, 38 (I128(l), I128(r)) => l == r, 39 (I128(_), _) => false, 40 (F32(l), F32(r)) => l.as_f32() == r.as_f32(), 41 (F32(_), _) => false, 42 (F64(l), F64(r)) => l.as_f64() == r.as_f64(), 43 (F64(_), _) => false, 44 (V128(l), V128(r)) => l == r, 45 (V128(_), _) => false, 46 (V64(l), V64(r)) => l == r, 47 (V64(_), _) => false, 48 } 49 } 50 } 51 52 impl DataValue { 53 /// Try to cast an immediate integer (a wrapped `i64` on most Cranelift instructions) to the 54 /// given Cranelift [Type]. 55 pub fn from_integer(imm: i128, ty: Type) -> Result<DataValue, DataValueCastFailure> { 56 match ty { 57 types::I8 => Ok(DataValue::I8(imm as i8)), 58 types::I16 => Ok(DataValue::I16(imm as i16)), 59 types::I32 => Ok(DataValue::I32(imm as i32)), 60 types::I64 => Ok(DataValue::I64(imm as i64)), 61 types::I128 => Ok(DataValue::I128(imm)), 62 _ => Err(DataValueCastFailure::FromInteger(imm, ty)), 63 } 64 } 65 66 /// Return the Cranelift IR [Type] for this [DataValue]. 67 pub fn ty(&self) -> Type { 68 match self { 69 DataValue::I8(_) => types::I8, 70 DataValue::I16(_) => types::I16, 71 DataValue::I32(_) => types::I32, 72 DataValue::I64(_) => types::I64, 73 DataValue::I128(_) => types::I128, 74 DataValue::F32(_) => types::F32, 75 DataValue::F64(_) => types::F64, 76 DataValue::V128(_) => types::I8X16, // A default type. 77 DataValue::V64(_) => types::I8X8, // A default type. 78 } 79 } 80 81 /// Return true if the value is a vector (i.e. `DataValue::V128`). 82 pub fn is_vector(&self) -> bool { 83 match self { 84 DataValue::V128(_) | DataValue::V64(_) => true, 85 _ => false, 86 } 87 } 88 89 fn swap_bytes(self) -> Self { 90 match self { 91 DataValue::I8(i) => DataValue::I8(i.swap_bytes()), 92 DataValue::I16(i) => DataValue::I16(i.swap_bytes()), 93 DataValue::I32(i) => DataValue::I32(i.swap_bytes()), 94 DataValue::I64(i) => DataValue::I64(i.swap_bytes()), 95 DataValue::I128(i) => DataValue::I128(i.swap_bytes()), 96 DataValue::F32(f) => DataValue::F32(Ieee32::with_bits(f.bits().swap_bytes())), 97 DataValue::F64(f) => DataValue::F64(Ieee64::with_bits(f.bits().swap_bytes())), 98 DataValue::V128(mut v) => { 99 v.reverse(); 100 DataValue::V128(v) 101 } 102 DataValue::V64(mut v) => { 103 v.reverse(); 104 DataValue::V64(v) 105 } 106 } 107 } 108 109 /// Converts `self` to big endian from target's endianness. 110 pub fn to_be(self) -> Self { 111 if cfg!(target_endian = "big") { 112 self 113 } else { 114 self.swap_bytes() 115 } 116 } 117 118 /// Converts `self` to little endian from target's endianness. 119 pub fn to_le(self) -> Self { 120 if cfg!(target_endian = "little") { 121 self 122 } else { 123 self.swap_bytes() 124 } 125 } 126 127 /// Write a [DataValue] to a slice in native-endian byte order. 128 /// 129 /// # Panics: 130 /// 131 /// Panics if the slice does not have enough space to accommodate the [DataValue] 132 pub fn write_to_slice_ne(&self, dst: &mut [u8]) { 133 match self { 134 DataValue::I8(i) => dst[..1].copy_from_slice(&i.to_ne_bytes()[..]), 135 DataValue::I16(i) => dst[..2].copy_from_slice(&i.to_ne_bytes()[..]), 136 DataValue::I32(i) => dst[..4].copy_from_slice(&i.to_ne_bytes()[..]), 137 DataValue::I64(i) => dst[..8].copy_from_slice(&i.to_ne_bytes()[..]), 138 DataValue::I128(i) => dst[..16].copy_from_slice(&i.to_ne_bytes()[..]), 139 DataValue::F32(f) => dst[..4].copy_from_slice(&f.bits().to_ne_bytes()[..]), 140 DataValue::F64(f) => dst[..8].copy_from_slice(&f.bits().to_ne_bytes()[..]), 141 DataValue::V128(v) => dst[..16].copy_from_slice(&v[..]), 142 DataValue::V64(v) => dst[..8].copy_from_slice(&v[..]), 143 }; 144 } 145 146 /// Write a [DataValue] to a slice in big-endian byte order. 147 /// 148 /// # Panics: 149 /// 150 /// Panics if the slice does not have enough space to accommodate the [DataValue] 151 pub fn write_to_slice_be(&self, dst: &mut [u8]) { 152 self.clone().to_be().write_to_slice_ne(dst); 153 } 154 155 /// Write a [DataValue] to a slice in little-endian byte order. 156 /// 157 /// # Panics: 158 /// 159 /// Panics if the slice does not have enough space to accommodate the [DataValue] 160 pub fn write_to_slice_le(&self, dst: &mut [u8]) { 161 self.clone().to_le().write_to_slice_ne(dst); 162 } 163 164 /// Read a [DataValue] from a slice using a given [Type] with native-endian byte order. 165 /// 166 /// # Panics: 167 /// 168 /// Panics if the slice does not have enough space to accommodate the [DataValue] 169 pub fn read_from_slice_ne(src: &[u8], ty: Type) -> Self { 170 match ty { 171 types::I8 => DataValue::I8(i8::from_ne_bytes(src[..1].try_into().unwrap())), 172 types::I16 => DataValue::I16(i16::from_ne_bytes(src[..2].try_into().unwrap())), 173 types::I32 => DataValue::I32(i32::from_ne_bytes(src[..4].try_into().unwrap())), 174 types::I64 => DataValue::I64(i64::from_ne_bytes(src[..8].try_into().unwrap())), 175 types::I128 => DataValue::I128(i128::from_ne_bytes(src[..16].try_into().unwrap())), 176 types::F32 => DataValue::F32(Ieee32::with_bits(u32::from_ne_bytes( 177 src[..4].try_into().unwrap(), 178 ))), 179 types::F64 => DataValue::F64(Ieee64::with_bits(u64::from_ne_bytes( 180 src[..8].try_into().unwrap(), 181 ))), 182 _ if ty.is_vector() => { 183 if ty.bytes() == 16 { 184 DataValue::V128(src[..16].try_into().unwrap()) 185 } else if ty.bytes() == 8 { 186 DataValue::V64(src[..8].try_into().unwrap()) 187 } else { 188 unimplemented!() 189 } 190 } 191 _ => unimplemented!(), 192 } 193 } 194 195 /// Read a [DataValue] from a slice using a given [Type] in big-endian byte order. 196 /// 197 /// # Panics: 198 /// 199 /// Panics if the slice does not have enough space to accommodate the [DataValue] 200 pub fn read_from_slice_be(src: &[u8], ty: Type) -> Self { 201 DataValue::read_from_slice_ne(src, ty).to_be() 202 } 203 204 /// Read a [DataValue] from a slice using a given [Type] in little-endian byte order. 205 /// 206 /// # Panics: 207 /// 208 /// Panics if the slice does not have enough space to accommodate the [DataValue] 209 pub fn read_from_slice_le(src: &[u8], ty: Type) -> Self { 210 DataValue::read_from_slice_ne(src, ty).to_le() 211 } 212 213 /// Write a [DataValue] to a memory location in native-endian byte order. 214 pub unsafe fn write_value_to(&self, p: *mut u128) { 215 let size = self.ty().bytes() as usize; 216 self.write_to_slice_ne(std::slice::from_raw_parts_mut(p as *mut u8, size)); 217 } 218 219 /// Read a [DataValue] from a memory location using a given [Type] in native-endian byte order. 220 pub unsafe fn read_value_from(p: *const u128, ty: Type) -> Self { 221 DataValue::read_from_slice_ne( 222 std::slice::from_raw_parts(p as *const u8, ty.bytes() as usize), 223 ty, 224 ) 225 } 226 227 /// Performs a bitwise comparison over the contents of [DataValue]. 228 /// 229 /// Returns true if all bits are equal. 230 /// 231 /// This behaviour is different from PartialEq for NaN floats. 232 pub fn bitwise_eq(&self, other: &DataValue) -> bool { 233 match (self, other) { 234 // We need to bit compare the floats to ensure that we produce the correct values 235 // on NaN's. The test suite expects to assert the precise bit pattern on NaN's or 236 // works around it in the tests themselves. 237 (DataValue::F32(a), DataValue::F32(b)) => a.bits() == b.bits(), 238 (DataValue::F64(a), DataValue::F64(b)) => a.bits() == b.bits(), 239 240 // We don't need to worry about F32x4 / F64x2 Since we compare V128 which is already the 241 // raw bytes anyway 242 (a, b) => a == b, 243 } 244 } 245 } 246 247 /// Record failures to cast [DataValue]. 248 #[derive(Debug, PartialEq)] 249 #[allow(missing_docs)] 250 pub enum DataValueCastFailure { 251 TryInto(Type, Type), 252 FromInteger(i128, Type), 253 } 254 255 // This is manually implementing Error and Display instead of using thiserror to reduce the amount 256 // of dependencies used by Cranelift. 257 impl std::error::Error for DataValueCastFailure {} 258 259 impl Display for DataValueCastFailure { 260 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 261 match self { 262 DataValueCastFailure::TryInto(from, to) => { 263 write!( 264 f, 265 "unable to cast data value of type {} to type {}", 266 from, to 267 ) 268 } 269 DataValueCastFailure::FromInteger(val, to) => { 270 write!( 271 f, 272 "unable to cast i64({}) to a data value of type {}", 273 val, to 274 ) 275 } 276 } 277 } 278 } 279 280 /// Helper for creating conversion implementations for [DataValue]. 281 macro_rules! build_conversion_impl { 282 ( $rust_ty:ty, $data_value_ty:ident, $cranelift_ty:ident ) => { 283 impl From<$rust_ty> for DataValue { 284 fn from(data: $rust_ty) -> Self { 285 DataValue::$data_value_ty(data) 286 } 287 } 288 289 impl TryInto<$rust_ty> for DataValue { 290 type Error = DataValueCastFailure; 291 fn try_into(self) -> Result<$rust_ty, Self::Error> { 292 if let DataValue::$data_value_ty(v) = self { 293 Ok(v) 294 } else { 295 Err(DataValueCastFailure::TryInto( 296 self.ty(), 297 types::$cranelift_ty, 298 )) 299 } 300 } 301 } 302 }; 303 } 304 build_conversion_impl!(i8, I8, I8); 305 build_conversion_impl!(i16, I16, I16); 306 build_conversion_impl!(i32, I32, I32); 307 build_conversion_impl!(i64, I64, I64); 308 build_conversion_impl!(i128, I128, I128); 309 build_conversion_impl!(Ieee32, F32, F32); 310 build_conversion_impl!(Ieee64, F64, F64); 311 build_conversion_impl!([u8; 16], V128, I8X16); 312 build_conversion_impl!([u8; 8], V64, I8X8); 313 impl From<Offset32> for DataValue { 314 fn from(o: Offset32) -> Self { 315 DataValue::from(Into::<i32>::into(o)) 316 } 317 } 318 319 impl Display for DataValue { 320 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 321 match self { 322 DataValue::I8(dv) => write!(f, "{}", dv), 323 DataValue::I16(dv) => write!(f, "{}", dv), 324 DataValue::I32(dv) => write!(f, "{}", dv), 325 DataValue::I64(dv) => write!(f, "{}", dv), 326 DataValue::I128(dv) => write!(f, "{}", dv), 327 // The Ieee* wrappers here print the expected syntax. 328 DataValue::F32(dv) => write!(f, "{}", dv), 329 DataValue::F64(dv) => write!(f, "{}", dv), 330 // Again, for syntax consistency, use ConstantData, which in this case displays as hex. 331 DataValue::V128(dv) => write!(f, "{}", ConstantData::from(&dv[..])), 332 DataValue::V64(dv) => write!(f, "{}", ConstantData::from(&dv[..])), 333 } 334 } 335 } 336 337 /// Helper structure for printing bracket-enclosed vectors of [DataValue]s. 338 /// - for empty vectors, display `[]` 339 /// - for single item vectors, display `42`, e.g. 340 /// - for multiple item vectors, display `[42, 43, 44]`, e.g. 341 pub struct DisplayDataValues<'a>(pub &'a [DataValue]); 342 343 impl<'a> Display for DisplayDataValues<'a> { 344 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 345 if self.0.len() == 1 { 346 write!(f, "{}", self.0[0]) 347 } else { 348 write!(f, "[")?; 349 write_data_value_list(f, &self.0)?; 350 write!(f, "]") 351 } 352 } 353 } 354 355 /// Helper function for displaying `Vec<DataValue>`. 356 pub fn write_data_value_list(f: &mut Formatter<'_>, list: &[DataValue]) -> fmt::Result { 357 match list.len() { 358 0 => Ok(()), 359 1 => write!(f, "{}", list[0]), 360 _ => { 361 write!(f, "{}", list[0])?; 362 for dv in list.iter().skip(1) { 363 write!(f, ", {}", dv)?; 364 } 365 Ok(()) 366 } 367 } 368 } 369 370 #[cfg(test)] 371 mod test { 372 use super::*; 373 374 #[test] 375 fn type_conversions() { 376 assert_eq!(DataValue::V128([0; 16]).ty(), types::I8X16); 377 assert_eq!( 378 TryInto::<[u8; 16]>::try_into(DataValue::V128([0; 16])).unwrap(), 379 [0; 16] 380 ); 381 assert_eq!( 382 TryInto::<i32>::try_into(DataValue::V128([0; 16])).unwrap_err(), 383 DataValueCastFailure::TryInto(types::I8X16, types::I32) 384 ); 385 } 386 } 387