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 `i64` 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 /// Return true if the value is a bool (i.e. `DataValue::B`). 70 pub fn is_bool(&self) -> bool { 71 match self { 72 DataValue::B(_) => true, 73 _ => false, 74 } 75 } 76 77 /// Write a [DataValue] to a memory location. 78 pub unsafe fn write_value_to(&self, p: *mut u128) { 79 match self { 80 DataValue::B(b) => ptr::write(p, if *b { -1i128 as u128 } else { 0u128 }), 81 DataValue::I8(i) => ptr::write(p as *mut i8, *i), 82 DataValue::I16(i) => ptr::write(p as *mut i16, *i), 83 DataValue::I32(i) => ptr::write(p as *mut i32, *i), 84 DataValue::I64(i) => ptr::write(p as *mut i64, *i), 85 DataValue::F32(f) => ptr::write(p as *mut Ieee32, *f), 86 DataValue::F64(f) => ptr::write(p as *mut Ieee64, *f), 87 DataValue::V128(b) => ptr::write(p as *mut [u8; 16], *b), 88 _ => unimplemented!(), 89 } 90 } 91 92 /// Read a [DataValue] from a memory location using a given [Type]. 93 pub unsafe fn read_value_from(p: *const u128, ty: Type) -> Self { 94 match ty { 95 types::I8 => DataValue::I8(ptr::read(p as *const i8)), 96 types::I16 => DataValue::I16(ptr::read(p as *const i16)), 97 types::I32 => DataValue::I32(ptr::read(p as *const i32)), 98 types::I64 => DataValue::I64(ptr::read(p as *const i64)), 99 types::F32 => DataValue::F32(ptr::read(p as *const Ieee32)), 100 types::F64 => DataValue::F64(ptr::read(p as *const Ieee64)), 101 _ if ty.is_bool() => DataValue::B(ptr::read(p) != 0), 102 _ if ty.is_vector() && ty.bytes() == 16 => { 103 DataValue::V128(ptr::read(p as *const [u8; 16])) 104 } 105 _ => unimplemented!(), 106 } 107 } 108 } 109 110 /// Record failures to cast [DataValue]. 111 #[derive(Debug, PartialEq)] 112 #[allow(missing_docs)] 113 pub enum DataValueCastFailure { 114 TryInto(Type, Type), 115 FromInteger(i128, Type), 116 } 117 118 // This is manually implementing Error and Display instead of using thiserror to reduce the amount 119 // of dependencies used by Cranelift. 120 impl std::error::Error for DataValueCastFailure {} 121 122 impl Display for DataValueCastFailure { 123 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 124 match self { 125 DataValueCastFailure::TryInto(from, to) => { 126 write!( 127 f, 128 "unable to cast data value of type {} to type {}", 129 from, to 130 ) 131 } 132 DataValueCastFailure::FromInteger(val, to) => { 133 write!( 134 f, 135 "unable to cast i64({}) to a data value of type {}", 136 val, to 137 ) 138 } 139 } 140 } 141 } 142 143 /// Helper for creating conversion implementations for [DataValue]. 144 macro_rules! build_conversion_impl { 145 ( $rust_ty:ty, $data_value_ty:ident, $cranelift_ty:ident ) => { 146 impl From<$rust_ty> for DataValue { 147 fn from(data: $rust_ty) -> Self { 148 DataValue::$data_value_ty(data) 149 } 150 } 151 152 impl TryInto<$rust_ty> for DataValue { 153 type Error = DataValueCastFailure; 154 fn try_into(self) -> Result<$rust_ty, Self::Error> { 155 if let DataValue::$data_value_ty(v) = self { 156 Ok(v) 157 } else { 158 Err(DataValueCastFailure::TryInto( 159 self.ty(), 160 types::$cranelift_ty, 161 )) 162 } 163 } 164 } 165 }; 166 } 167 build_conversion_impl!(bool, B, B8); 168 build_conversion_impl!(i8, I8, I8); 169 build_conversion_impl!(i16, I16, I16); 170 build_conversion_impl!(i32, I32, I32); 171 build_conversion_impl!(i64, I64, I64); 172 build_conversion_impl!(i128, I128, I128); 173 build_conversion_impl!(u8, U8, I8); 174 build_conversion_impl!(u16, U16, I16); 175 build_conversion_impl!(u32, U32, I32); 176 build_conversion_impl!(u64, U64, I64); 177 build_conversion_impl!(u128, U128, I128); 178 build_conversion_impl!(Ieee32, F32, F32); 179 build_conversion_impl!(Ieee64, F64, F64); 180 build_conversion_impl!([u8; 16], V128, I8X16); 181 impl From<Offset32> for DataValue { 182 fn from(o: Offset32) -> Self { 183 DataValue::from(Into::<i32>::into(o)) 184 } 185 } 186 187 impl Display for DataValue { 188 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 189 match self { 190 DataValue::B(dv) => write!(f, "{}", dv), 191 DataValue::I8(dv) => write!(f, "{}", dv), 192 DataValue::I16(dv) => write!(f, "{}", dv), 193 DataValue::I32(dv) => write!(f, "{}", dv), 194 DataValue::I64(dv) => write!(f, "{}", dv), 195 DataValue::I128(dv) => write!(f, "{}", dv), 196 DataValue::U8(dv) => write!(f, "{}", dv), 197 DataValue::U16(dv) => write!(f, "{}", dv), 198 DataValue::U32(dv) => write!(f, "{}", dv), 199 DataValue::U64(dv) => write!(f, "{}", dv), 200 DataValue::U128(dv) => write!(f, "{}", dv), 201 // The Ieee* wrappers here print the expected syntax. 202 DataValue::F32(dv) => write!(f, "{}", dv), 203 DataValue::F64(dv) => write!(f, "{}", dv), 204 // Again, for syntax consistency, use ConstantData, which in this case displays as hex. 205 DataValue::V128(dv) => write!(f, "{}", ConstantData::from(&dv[..])), 206 } 207 } 208 } 209 210 /// Helper structure for printing bracket-enclosed vectors of [DataValue]s. 211 /// - for empty vectors, display `[]` 212 /// - for single item vectors, display `42`, e.g. 213 /// - for multiple item vectors, display `[42, 43, 44]`, e.g. 214 pub struct DisplayDataValues<'a>(pub &'a [DataValue]); 215 216 impl<'a> Display for DisplayDataValues<'a> { 217 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 218 if self.0.len() == 1 { 219 write!(f, "{}", self.0[0]) 220 } else { 221 write!(f, "[")?; 222 write_data_value_list(f, &self.0)?; 223 write!(f, "]") 224 } 225 } 226 } 227 228 /// Helper function for displaying `Vec<DataValue>`. 229 pub fn write_data_value_list(f: &mut Formatter<'_>, list: &[DataValue]) -> fmt::Result { 230 match list.len() { 231 0 => Ok(()), 232 1 => write!(f, "{}", list[0]), 233 _ => { 234 write!(f, "{}", list[0])?; 235 for dv in list.iter().skip(1) { 236 write!(f, ", {}", dv)?; 237 } 238 Ok(()) 239 } 240 } 241 } 242 243 #[cfg(test)] 244 mod test { 245 use super::*; 246 247 #[test] 248 fn type_conversions() { 249 assert_eq!(DataValue::B(true).ty(), types::B8); 250 assert_eq!( 251 TryInto::<bool>::try_into(DataValue::B(false)).unwrap(), 252 false 253 ); 254 assert_eq!( 255 TryInto::<i32>::try_into(DataValue::B(false)).unwrap_err(), 256 DataValueCastFailure::TryInto(types::B8, types::I32) 257 ); 258 259 assert_eq!(DataValue::V128([0; 16]).ty(), types::I8X16); 260 assert_eq!( 261 TryInto::<[u8; 16]>::try_into(DataValue::V128([0; 16])).unwrap(), 262 [0; 16] 263 ); 264 assert_eq!( 265 TryInto::<i32>::try_into(DataValue::V128([0; 16])).unwrap_err(), 266 DataValueCastFailure::TryInto(types::I8X16, types::I32) 267 ); 268 } 269 } 270