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