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::{Ieee32, Ieee64, Offset32};
4use crate::ir::{types, ConstantData, Type};
5use core::convert::TryInto;
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    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
31impl 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
67impl 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            // We don't need to worry about F32x4 / F64x2 Since we compare V128 which is already the
262            // raw bytes anyway
263            (a, b) => a == b,
264        }
265    }
266}
267
268/// Record failures to cast [DataValue].
269#[derive(Debug, PartialEq)]
270#[allow(missing_docs)]
271pub enum DataValueCastFailure {
272    TryInto(Type, Type),
273    FromInteger(i128, Type),
274}
275
276// This is manually implementing Error and Display instead of using thiserror to reduce the amount
277// of dependencies used by Cranelift.
278impl std::error::Error for DataValueCastFailure {}
279
280impl Display for DataValueCastFailure {
281    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
282        match self {
283            DataValueCastFailure::TryInto(from, to) => {
284                write!(
285                    f,
286                    "unable to cast data value of type {} to type {}",
287                    from, to
288                )
289            }
290            DataValueCastFailure::FromInteger(val, to) => {
291                write!(
292                    f,
293                    "unable to cast i64({}) to a data value of type {}",
294                    val, to
295                )
296            }
297        }
298    }
299}
300
301/// Helper for creating conversion implementations for [DataValue].
302macro_rules! build_conversion_impl {
303    ( $rust_ty:ty, $data_value_ty:ident, $cranelift_ty:ident ) => {
304        impl From<$rust_ty> for DataValue {
305            fn from(data: $rust_ty) -> Self {
306                DataValue::$data_value_ty(data)
307            }
308        }
309
310        impl TryInto<$rust_ty> for DataValue {
311            type Error = DataValueCastFailure;
312            fn try_into(self) -> Result<$rust_ty, Self::Error> {
313                if let DataValue::$data_value_ty(v) = self {
314                    Ok(v)
315                } else {
316                    Err(DataValueCastFailure::TryInto(
317                        self.ty(),
318                        types::$cranelift_ty,
319                    ))
320                }
321            }
322        }
323    };
324}
325build_conversion_impl!(i8, I8, I8);
326build_conversion_impl!(i16, I16, I16);
327build_conversion_impl!(i32, I32, I32);
328build_conversion_impl!(i64, I64, I64);
329build_conversion_impl!(i128, I128, I128);
330build_conversion_impl!(u8, U8, I8);
331build_conversion_impl!(u16, U16, I16);
332build_conversion_impl!(u32, U32, I32);
333build_conversion_impl!(u64, U64, I64);
334build_conversion_impl!(u128, U128, I128);
335build_conversion_impl!(Ieee32, F32, F32);
336build_conversion_impl!(Ieee64, F64, F64);
337build_conversion_impl!([u8; 16], V128, I8X16);
338build_conversion_impl!([u8; 8], V64, I8X8);
339impl From<Offset32> for DataValue {
340    fn from(o: Offset32) -> Self {
341        DataValue::from(Into::<i32>::into(o))
342    }
343}
344
345impl Display for DataValue {
346    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
347        match self {
348            DataValue::I8(dv) => write!(f, "{}", dv),
349            DataValue::I16(dv) => write!(f, "{}", dv),
350            DataValue::I32(dv) => write!(f, "{}", dv),
351            DataValue::I64(dv) => write!(f, "{}", dv),
352            DataValue::I128(dv) => write!(f, "{}", dv),
353            DataValue::U8(dv) => write!(f, "{}", dv),
354            DataValue::U16(dv) => write!(f, "{}", dv),
355            DataValue::U32(dv) => write!(f, "{}", dv),
356            DataValue::U64(dv) => write!(f, "{}", dv),
357            DataValue::U128(dv) => write!(f, "{}", dv),
358            // The Ieee* wrappers here print the expected syntax.
359            DataValue::F32(dv) => write!(f, "{}", dv),
360            DataValue::F64(dv) => write!(f, "{}", dv),
361            // Again, for syntax consistency, use ConstantData, which in this case displays as hex.
362            DataValue::V128(dv) => write!(f, "{}", ConstantData::from(&dv[..])),
363            DataValue::V64(dv) => write!(f, "{}", ConstantData::from(&dv[..])),
364        }
365    }
366}
367
368/// Helper structure for printing bracket-enclosed vectors of [DataValue]s.
369/// - for empty vectors, display `[]`
370/// - for single item vectors, display `42`, e.g.
371/// - for multiple item vectors, display `[42, 43, 44]`, e.g.
372pub struct DisplayDataValues<'a>(pub &'a [DataValue]);
373
374impl<'a> Display for DisplayDataValues<'a> {
375    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
376        if self.0.len() == 1 {
377            write!(f, "{}", self.0[0])
378        } else {
379            write!(f, "[")?;
380            write_data_value_list(f, &self.0)?;
381            write!(f, "]")
382        }
383    }
384}
385
386/// Helper function for displaying `Vec<DataValue>`.
387pub fn write_data_value_list(f: &mut Formatter<'_>, list: &[DataValue]) -> fmt::Result {
388    match list.len() {
389        0 => Ok(()),
390        1 => write!(f, "{}", list[0]),
391        _ => {
392            write!(f, "{}", list[0])?;
393            for dv in list.iter().skip(1) {
394                write!(f, ", {}", dv)?;
395            }
396            Ok(())
397        }
398    }
399}
400
401#[cfg(test)]
402mod test {
403    use super::*;
404
405    #[test]
406    fn type_conversions() {
407        assert_eq!(DataValue::V128([0; 16]).ty(), types::I8X16);
408        assert_eq!(
409            TryInto::<[u8; 16]>::try_into(DataValue::V128([0; 16])).unwrap(),
410            [0; 16]
411        );
412        assert_eq!(
413            TryInto::<i32>::try_into(DataValue::V128([0; 16])).unwrap_err(),
414            DataValueCastFailure::TryInto(types::I8X16, types::I32)
415        );
416    }
417}