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