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, PartialEq, PartialOrd)]
14 pub enum DataValue {
15     B(bool),
16     I8(i8),
17     I16(i16),
18     I32(i32),
19     I64(i64),
20     I128(i128),
21     U8(u8),
22     U16(u16),
23     U32(u32),
24     U64(u64),
25     U128(u128),
26     F32(Ieee32),
27     F64(Ieee64),
28     V128([u8; 16]),
29 }
30 
31 impl DataValue {
32     /// Try to cast an immediate integer (a wrapped `i64` on most Cranelift instructions) to the
33     /// given Cranelift [Type].
34     pub fn from_integer(imm: i128, ty: Type) -> Result<DataValue, DataValueCastFailure> {
35         match ty {
36             types::I8 => Ok(DataValue::I8(imm as i8)),
37             types::I16 => Ok(DataValue::I16(imm as i16)),
38             types::I32 => Ok(DataValue::I32(imm as i32)),
39             types::I64 => Ok(DataValue::I64(imm as i64)),
40             types::I128 => Ok(DataValue::I128(imm)),
41             _ => Err(DataValueCastFailure::FromInteger(imm, ty)),
42         }
43     }
44 
45     /// Return the Cranelift IR [Type] for this [DataValue].
46     pub fn ty(&self) -> Type {
47         match self {
48             DataValue::B(_) => types::B8, // A default type.
49             DataValue::I8(_) | DataValue::U8(_) => types::I8,
50             DataValue::I16(_) | DataValue::U16(_) => types::I16,
51             DataValue::I32(_) | DataValue::U32(_) => types::I32,
52             DataValue::I64(_) | DataValue::U64(_) => types::I64,
53             DataValue::I128(_) | DataValue::U128(_) => types::I128,
54             DataValue::F32(_) => types::F32,
55             DataValue::F64(_) => types::F64,
56             DataValue::V128(_) => types::I8X16, // A default type.
57         }
58     }
59 
60     /// Return true if the value is a vector (i.e. `DataValue::V128`).
61     pub fn is_vector(&self) -> bool {
62         match self {
63             DataValue::V128(_) => true,
64             _ => false,
65         }
66     }
67 
68     /// Return true if the value is a bool (i.e. `DataValue::B`).
69     pub fn is_bool(&self) -> bool {
70         match self {
71             DataValue::B(_) => true,
72             _ => false,
73         }
74     }
75 
76     /// Write a [DataValue] to a slice.
77     ///
78     /// # Panics:
79     ///
80     /// Panics if the slice does not have enough space to accommodate the [DataValue]
81     pub fn write_to_slice(&self, dst: &mut [u8]) {
82         match self {
83             DataValue::B(true) => dst[..16].copy_from_slice(&[u8::MAX; 16][..]),
84             DataValue::B(false) => dst[..16].copy_from_slice(&[0; 16][..]),
85             DataValue::I8(i) => dst[..1].copy_from_slice(&i.to_le_bytes()[..]),
86             DataValue::I16(i) => dst[..2].copy_from_slice(&i.to_le_bytes()[..]),
87             DataValue::I32(i) => dst[..4].copy_from_slice(&i.to_le_bytes()[..]),
88             DataValue::I64(i) => dst[..8].copy_from_slice(&i.to_le_bytes()[..]),
89             DataValue::F32(f) => dst[..4].copy_from_slice(&f.bits().to_le_bytes()[..]),
90             DataValue::F64(f) => dst[..8].copy_from_slice(&f.bits().to_le_bytes()[..]),
91             DataValue::V128(v) => dst[..16].copy_from_slice(&v[..]),
92             _ => unimplemented!(),
93         };
94     }
95 
96     /// Read a [DataValue] from a slice using a given [Type].
97     ///
98     /// # Panics:
99     ///
100     /// Panics if the slice does not have enough space to accommodate the [DataValue]
101     pub fn read_from_slice(src: &[u8], ty: Type) -> Self {
102         match ty {
103             types::I8 => DataValue::I8(i8::from_le_bytes(src[..1].try_into().unwrap())),
104             types::I16 => DataValue::I16(i16::from_le_bytes(src[..2].try_into().unwrap())),
105             types::I32 => DataValue::I32(i32::from_le_bytes(src[..4].try_into().unwrap())),
106             types::I64 => DataValue::I64(i64::from_le_bytes(src[..8].try_into().unwrap())),
107             types::F32 => DataValue::F32(Ieee32::with_bits(u32::from_le_bytes(
108                 src[..4].try_into().unwrap(),
109             ))),
110             types::F64 => DataValue::F64(Ieee64::with_bits(u64::from_le_bytes(
111                 src[..8].try_into().unwrap(),
112             ))),
113             _ if ty.is_bool() => {
114                 // Only `ty.bytes()` are guaranteed to be written
115                 // so we can only test the first n bytes of `src`
116 
117                 let size = ty.bytes() as usize;
118                 DataValue::B(src[..size].iter().any(|&i| i != 0))
119             }
120             _ if ty.is_vector() && ty.bytes() == 16 => {
121                 DataValue::V128(src[..16].try_into().unwrap())
122             }
123             _ => unimplemented!(),
124         }
125     }
126 
127     /// Write a [DataValue] to a memory location.
128     pub unsafe fn write_value_to(&self, p: *mut u128) {
129         // Since `DataValue` does not have type info for bools we always
130         // write out a full 16 byte slot.
131         let size = match self.ty() {
132             ty if ty.is_bool() => 16,
133             ty => ty.bytes() as usize,
134         };
135 
136         self.write_to_slice(std::slice::from_raw_parts_mut(p as *mut u8, size));
137     }
138 
139     /// Read a [DataValue] from a memory location using a given [Type].
140     pub unsafe fn read_value_from(p: *const u128, ty: Type) -> Self {
141         DataValue::read_from_slice(
142             std::slice::from_raw_parts(p as *const u8, ty.bytes() as usize),
143             ty,
144         )
145     }
146 }
147 
148 /// Record failures to cast [DataValue].
149 #[derive(Debug, PartialEq)]
150 #[allow(missing_docs)]
151 pub enum DataValueCastFailure {
152     TryInto(Type, Type),
153     FromInteger(i128, Type),
154 }
155 
156 // This is manually implementing Error and Display instead of using thiserror to reduce the amount
157 // of dependencies used by Cranelift.
158 impl std::error::Error for DataValueCastFailure {}
159 
160 impl Display for DataValueCastFailure {
161     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
162         match self {
163             DataValueCastFailure::TryInto(from, to) => {
164                 write!(
165                     f,
166                     "unable to cast data value of type {} to type {}",
167                     from, to
168                 )
169             }
170             DataValueCastFailure::FromInteger(val, to) => {
171                 write!(
172                     f,
173                     "unable to cast i64({}) to a data value of type {}",
174                     val, to
175                 )
176             }
177         }
178     }
179 }
180 
181 /// Helper for creating conversion implementations for [DataValue].
182 macro_rules! build_conversion_impl {
183     ( $rust_ty:ty, $data_value_ty:ident, $cranelift_ty:ident ) => {
184         impl From<$rust_ty> for DataValue {
185             fn from(data: $rust_ty) -> Self {
186                 DataValue::$data_value_ty(data)
187             }
188         }
189 
190         impl TryInto<$rust_ty> for DataValue {
191             type Error = DataValueCastFailure;
192             fn try_into(self) -> Result<$rust_ty, Self::Error> {
193                 if let DataValue::$data_value_ty(v) = self {
194                     Ok(v)
195                 } else {
196                     Err(DataValueCastFailure::TryInto(
197                         self.ty(),
198                         types::$cranelift_ty,
199                     ))
200                 }
201             }
202         }
203     };
204 }
205 build_conversion_impl!(bool, B, B8);
206 build_conversion_impl!(i8, I8, I8);
207 build_conversion_impl!(i16, I16, I16);
208 build_conversion_impl!(i32, I32, I32);
209 build_conversion_impl!(i64, I64, I64);
210 build_conversion_impl!(i128, I128, I128);
211 build_conversion_impl!(u8, U8, I8);
212 build_conversion_impl!(u16, U16, I16);
213 build_conversion_impl!(u32, U32, I32);
214 build_conversion_impl!(u64, U64, I64);
215 build_conversion_impl!(u128, U128, I128);
216 build_conversion_impl!(Ieee32, F32, F32);
217 build_conversion_impl!(Ieee64, F64, F64);
218 build_conversion_impl!([u8; 16], V128, I8X16);
219 impl From<Offset32> for DataValue {
220     fn from(o: Offset32) -> Self {
221         DataValue::from(Into::<i32>::into(o))
222     }
223 }
224 
225 impl Display for DataValue {
226     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
227         match self {
228             DataValue::B(dv) => write!(f, "{}", dv),
229             DataValue::I8(dv) => write!(f, "{}", dv),
230             DataValue::I16(dv) => write!(f, "{}", dv),
231             DataValue::I32(dv) => write!(f, "{}", dv),
232             DataValue::I64(dv) => write!(f, "{}", dv),
233             DataValue::I128(dv) => write!(f, "{}", dv),
234             DataValue::U8(dv) => write!(f, "{}", dv),
235             DataValue::U16(dv) => write!(f, "{}", dv),
236             DataValue::U32(dv) => write!(f, "{}", dv),
237             DataValue::U64(dv) => write!(f, "{}", dv),
238             DataValue::U128(dv) => write!(f, "{}", dv),
239             // The Ieee* wrappers here print the expected syntax.
240             DataValue::F32(dv) => write!(f, "{}", dv),
241             DataValue::F64(dv) => write!(f, "{}", dv),
242             // Again, for syntax consistency, use ConstantData, which in this case displays as hex.
243             DataValue::V128(dv) => write!(f, "{}", ConstantData::from(&dv[..])),
244         }
245     }
246 }
247 
248 /// Helper structure for printing bracket-enclosed vectors of [DataValue]s.
249 /// - for empty vectors, display `[]`
250 /// - for single item vectors, display `42`, e.g.
251 /// - for multiple item vectors, display `[42, 43, 44]`, e.g.
252 pub struct DisplayDataValues<'a>(pub &'a [DataValue]);
253 
254 impl<'a> Display for DisplayDataValues<'a> {
255     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
256         if self.0.len() == 1 {
257             write!(f, "{}", self.0[0])
258         } else {
259             write!(f, "[")?;
260             write_data_value_list(f, &self.0)?;
261             write!(f, "]")
262         }
263     }
264 }
265 
266 /// Helper function for displaying `Vec<DataValue>`.
267 pub fn write_data_value_list(f: &mut Formatter<'_>, list: &[DataValue]) -> fmt::Result {
268     match list.len() {
269         0 => Ok(()),
270         1 => write!(f, "{}", list[0]),
271         _ => {
272             write!(f, "{}", list[0])?;
273             for dv in list.iter().skip(1) {
274                 write!(f, ", {}", dv)?;
275             }
276             Ok(())
277         }
278     }
279 }
280 
281 #[cfg(test)]
282 mod test {
283     use super::*;
284 
285     #[test]
286     fn type_conversions() {
287         assert_eq!(DataValue::B(true).ty(), types::B8);
288         assert_eq!(
289             TryInto::<bool>::try_into(DataValue::B(false)).unwrap(),
290             false
291         );
292         assert_eq!(
293             TryInto::<i32>::try_into(DataValue::B(false)).unwrap_err(),
294             DataValueCastFailure::TryInto(types::B8, types::I32)
295         );
296 
297         assert_eq!(DataValue::V128([0; 16]).ty(), types::I8X16);
298         assert_eq!(
299             TryInto::<[u8; 16]>::try_into(DataValue::V128([0; 16])).unwrap(),
300             [0; 16]
301         );
302         assert_eq!(
303             TryInto::<i32>::try_into(DataValue::V128([0; 16])).unwrap_err(),
304             DataValueCastFailure::TryInto(types::I8X16, types::I32)
305         );
306     }
307 }
308