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