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 U8(u8), 22 U16(u16), 23 U32(u32), 24 U64(u64), 25 F32(Ieee32), 26 F64(Ieee64), 27 V128([u8; 16]), 28 } 29 30 impl DataValue { 31 /// Try to cast an immediate integer (a wrapped `i64` on most Cranelift instructions) to the 32 /// given Cranelift [Type]. 33 pub fn from_integer(imm: i64, ty: Type) -> Result<DataValue, DataValueCastFailure> { 34 match ty { 35 types::I8 => Ok(DataValue::I8(imm as i8)), 36 types::I16 => Ok(DataValue::I16(imm as i16)), 37 types::I32 => Ok(DataValue::I32(imm as i32)), 38 types::I64 => Ok(DataValue::I64(imm)), 39 _ => Err(DataValueCastFailure::FromInteger(imm, ty)), 40 } 41 } 42 43 /// Return the Cranelift IR [Type] for this [DataValue]. 44 pub fn ty(&self) -> Type { 45 match self { 46 DataValue::B(_) => types::B8, // A default type. 47 DataValue::I8(_) | DataValue::U8(_) => types::I8, 48 DataValue::I16(_) | DataValue::U16(_) => types::I16, 49 DataValue::I32(_) | DataValue::U32(_) => types::I32, 50 DataValue::I64(_) | DataValue::U64(_) => types::I64, 51 DataValue::F32(_) => types::F32, 52 DataValue::F64(_) => types::F64, 53 DataValue::V128(_) => types::I8X16, // A default type. 54 } 55 } 56 57 /// Return true if the value is a vector (i.e. `DataValue::V128`). 58 pub fn is_vector(&self) -> bool { 59 match self { 60 DataValue::V128(_) => true, 61 _ => false, 62 } 63 } 64 65 /// Return true if the value is a bool (i.e. `DataValue::B`). 66 pub fn is_bool(&self) -> bool { 67 match self { 68 DataValue::B(_) => true, 69 _ => false, 70 } 71 } 72 73 /// Write a [DataValue] to a memory location. 74 pub unsafe fn write_value_to(&self, p: *mut u128) { 75 match self { 76 DataValue::B(b) => ptr::write(p, if *b { -1i128 as u128 } else { 0u128 }), 77 DataValue::I8(i) => ptr::write(p as *mut i8, *i), 78 DataValue::I16(i) => ptr::write(p as *mut i16, *i), 79 DataValue::I32(i) => ptr::write(p as *mut i32, *i), 80 DataValue::I64(i) => ptr::write(p as *mut i64, *i), 81 DataValue::F32(f) => ptr::write(p as *mut Ieee32, *f), 82 DataValue::F64(f) => ptr::write(p as *mut Ieee64, *f), 83 DataValue::V128(b) => ptr::write(p as *mut [u8; 16], *b), 84 _ => unimplemented!(), 85 } 86 } 87 88 /// Read a [DataValue] from a memory location using a given [Type]. 89 pub unsafe fn read_value_from(p: *const u128, ty: Type) -> Self { 90 match ty { 91 types::I8 => DataValue::I8(ptr::read(p as *const i8)), 92 types::I16 => DataValue::I16(ptr::read(p as *const i16)), 93 types::I32 => DataValue::I32(ptr::read(p as *const i32)), 94 types::I64 => DataValue::I64(ptr::read(p as *const i64)), 95 types::F32 => DataValue::F32(ptr::read(p as *const Ieee32)), 96 types::F64 => DataValue::F64(ptr::read(p as *const Ieee64)), 97 _ if ty.is_bool() => DataValue::B(ptr::read(p) != 0), 98 _ if ty.is_vector() && ty.bytes() == 16 => { 99 DataValue::V128(ptr::read(p as *const [u8; 16])) 100 } 101 _ => unimplemented!(), 102 } 103 } 104 } 105 106 /// Record failures to cast [DataValue]. 107 #[derive(Debug, PartialEq)] 108 #[allow(missing_docs)] 109 pub enum DataValueCastFailure { 110 TryInto(Type, Type), 111 FromInteger(i64, Type), 112 } 113 114 // This is manually implementing Error and Display instead of using thiserror to reduce the amount 115 // of dependencies used by Cranelift. 116 impl std::error::Error for DataValueCastFailure {} 117 118 impl Display for DataValueCastFailure { 119 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 120 match self { 121 DataValueCastFailure::TryInto(from, to) => { 122 write!( 123 f, 124 "unable to cast data value of type {} to type {}", 125 from, to 126 ) 127 } 128 DataValueCastFailure::FromInteger(val, to) => { 129 write!( 130 f, 131 "unable to cast i64({}) to a data value of type {}", 132 val, to 133 ) 134 } 135 } 136 } 137 } 138 139 /// Helper for creating conversion implementations for [DataValue]. 140 macro_rules! build_conversion_impl { 141 ( $rust_ty:ty, $data_value_ty:ident, $cranelift_ty:ident ) => { 142 impl From<$rust_ty> for DataValue { 143 fn from(data: $rust_ty) -> Self { 144 DataValue::$data_value_ty(data) 145 } 146 } 147 148 impl TryInto<$rust_ty> for DataValue { 149 type Error = DataValueCastFailure; 150 fn try_into(self) -> Result<$rust_ty, Self::Error> { 151 if let DataValue::$data_value_ty(v) = self { 152 Ok(v) 153 } else { 154 Err(DataValueCastFailure::TryInto( 155 self.ty(), 156 types::$cranelift_ty, 157 )) 158 } 159 } 160 } 161 }; 162 } 163 build_conversion_impl!(bool, B, B8); 164 build_conversion_impl!(i8, I8, I8); 165 build_conversion_impl!(i16, I16, I16); 166 build_conversion_impl!(i32, I32, I32); 167 build_conversion_impl!(i64, I64, I64); 168 build_conversion_impl!(u8, U8, I8); 169 build_conversion_impl!(u16, U16, I16); 170 build_conversion_impl!(u32, U32, I32); 171 build_conversion_impl!(u64, U64, I64); 172 build_conversion_impl!(Ieee32, F32, F32); 173 build_conversion_impl!(Ieee64, F64, F64); 174 build_conversion_impl!([u8; 16], V128, I8X16); 175 impl From<Offset32> for DataValue { 176 fn from(o: Offset32) -> Self { 177 DataValue::from(Into::<i32>::into(o)) 178 } 179 } 180 181 impl Display for DataValue { 182 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 183 match self { 184 DataValue::B(dv) => write!(f, "{}", dv), 185 DataValue::I8(dv) => write!(f, "{}", dv), 186 DataValue::I16(dv) => write!(f, "{}", dv), 187 DataValue::I32(dv) => write!(f, "{}", dv), 188 DataValue::I64(dv) => write!(f, "{}", dv), 189 DataValue::U8(dv) => write!(f, "{}", dv), 190 DataValue::U16(dv) => write!(f, "{}", dv), 191 DataValue::U32(dv) => write!(f, "{}", dv), 192 DataValue::U64(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