cranelift_codegen/
data_value.rs

1//! This module gives users to instantiate values that Cranelift understands. These values are used,
2//! for example, during interpretation and for wrapping immediates.
3use crate::ir::immediates::{Ieee128, Ieee16, Ieee32, Ieee64, Offset32};
4use crate::ir::{types, ConstantData, Type};
5use core::cmp::Ordering;
6use 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)]
14pub 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
30impl 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
64impl 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)]
286pub 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.
293impl std::error::Error for DataValueCastFailure {}
294
295impl 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].
309macro_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}
332build_conversion_impl!(i8, I8, I8);
333build_conversion_impl!(i16, I16, I16);
334build_conversion_impl!(i32, I32, I32);
335build_conversion_impl!(i64, I64, I64);
336build_conversion_impl!(i128, I128, I128);
337build_conversion_impl!(Ieee16, F16, F16);
338build_conversion_impl!(Ieee32, F32, F32);
339build_conversion_impl!(Ieee64, F64, F64);
340build_conversion_impl!(Ieee128, F128, F128);
341build_conversion_impl!([u8; 16], V128, I8X16);
342build_conversion_impl!([u8; 8], V64, I8X8);
343build_conversion_impl!([u8; 4], V32, I8X4);
344build_conversion_impl!([u8; 2], V16, I8X2);
345impl From<Offset32> for DataValue {
346    fn from(o: Offset32) -> Self {
347        DataValue::from(Into::<i32>::into(o))
348    }
349}
350
351impl 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.
377pub struct DisplayDataValues<'a>(pub &'a [DataValue]);
378
379impl<'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>`.
392pub 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)]
407mod 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}