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