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     /// Write a [DataValue] to a slice.
105     ///
106     /// # Panics:
107     ///
108     /// Panics if the slice does not have enough space to accommodate the [DataValue]
109     pub fn write_to_slice(&self, dst: &mut [u8]) {
110         match self {
111             DataValue::I8(i) => dst[..1].copy_from_slice(&i.to_ne_bytes()[..]),
112             DataValue::I16(i) => dst[..2].copy_from_slice(&i.to_ne_bytes()[..]),
113             DataValue::I32(i) => dst[..4].copy_from_slice(&i.to_ne_bytes()[..]),
114             DataValue::I64(i) => dst[..8].copy_from_slice(&i.to_ne_bytes()[..]),
115             DataValue::I128(i) => dst[..16].copy_from_slice(&i.to_ne_bytes()[..]),
116             DataValue::F32(f) => dst[..4].copy_from_slice(&f.bits().to_ne_bytes()[..]),
117             DataValue::F64(f) => dst[..8].copy_from_slice(&f.bits().to_ne_bytes()[..]),
118             DataValue::V128(v) => dst[..16].copy_from_slice(&v[..]),
119             DataValue::V64(v) => dst[..8].copy_from_slice(&v[..]),
120             _ => unimplemented!(),
121         };
122     }
123 
124     /// Read a [DataValue] from a slice using a given [Type].
125     ///
126     /// # Panics:
127     ///
128     /// Panics if the slice does not have enough space to accommodate the [DataValue]
129     pub fn read_from_slice(src: &[u8], ty: Type) -> Self {
130         match ty {
131             types::I8 => DataValue::I8(i8::from_ne_bytes(src[..1].try_into().unwrap())),
132             types::I16 => DataValue::I16(i16::from_ne_bytes(src[..2].try_into().unwrap())),
133             types::I32 => DataValue::I32(i32::from_ne_bytes(src[..4].try_into().unwrap())),
134             types::I64 => DataValue::I64(i64::from_ne_bytes(src[..8].try_into().unwrap())),
135             types::I128 => DataValue::I128(i128::from_ne_bytes(src[..16].try_into().unwrap())),
136             types::F32 => DataValue::F32(Ieee32::with_bits(u32::from_ne_bytes(
137                 src[..4].try_into().unwrap(),
138             ))),
139             types::F64 => DataValue::F64(Ieee64::with_bits(u64::from_ne_bytes(
140                 src[..8].try_into().unwrap(),
141             ))),
142             _ if ty.is_vector() => {
143                 if ty.bytes() == 16 {
144                     DataValue::V128(src[..16].try_into().unwrap())
145                 } else if ty.bytes() == 8 {
146                     DataValue::V64(src[..8].try_into().unwrap())
147                 } else {
148                     unimplemented!()
149                 }
150             }
151             _ => unimplemented!(),
152         }
153     }
154 
155     /// Write a [DataValue] to a memory location.
156     pub unsafe fn write_value_to(&self, p: *mut u128) {
157         let size = self.ty().bytes() as usize;
158         self.write_to_slice(std::slice::from_raw_parts_mut(p as *mut u8, size));
159     }
160 
161     /// Read a [DataValue] from a memory location using a given [Type].
162     pub unsafe fn read_value_from(p: *const u128, ty: Type) -> Self {
163         DataValue::read_from_slice(
164             std::slice::from_raw_parts(p as *const u8, ty.bytes() as usize),
165             ty,
166         )
167     }
168 
169     /// Performs a bitwise comparison over the contents of [DataValue].
170     ///
171     /// Returns true if all bits are equal.
172     ///
173     /// This behaviour is different from PartialEq for NaN floats.
174     pub fn bitwise_eq(&self, other: &DataValue) -> bool {
175         match (self, other) {
176             // We need to bit compare the floats to ensure that we produce the correct values
177             // on NaN's. The test suite expects to assert the precise bit pattern on NaN's or
178             // works around it in the tests themselves.
179             (DataValue::F32(a), DataValue::F32(b)) => a.bits() == b.bits(),
180             (DataValue::F64(a), DataValue::F64(b)) => a.bits() == b.bits(),
181 
182             // We don't need to worry about F32x4 / F64x2 Since we compare V128 which is already the
183             // raw bytes anyway
184             (a, b) => a == b,
185         }
186     }
187 }
188 
189 /// Record failures to cast [DataValue].
190 #[derive(Debug, PartialEq)]
191 #[allow(missing_docs)]
192 pub enum DataValueCastFailure {
193     TryInto(Type, Type),
194     FromInteger(i128, Type),
195 }
196 
197 // This is manually implementing Error and Display instead of using thiserror to reduce the amount
198 // of dependencies used by Cranelift.
199 impl std::error::Error for DataValueCastFailure {}
200 
201 impl Display for DataValueCastFailure {
202     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
203         match self {
204             DataValueCastFailure::TryInto(from, to) => {
205                 write!(
206                     f,
207                     "unable to cast data value of type {} to type {}",
208                     from, to
209                 )
210             }
211             DataValueCastFailure::FromInteger(val, to) => {
212                 write!(
213                     f,
214                     "unable to cast i64({}) to a data value of type {}",
215                     val, to
216                 )
217             }
218         }
219     }
220 }
221 
222 /// Helper for creating conversion implementations for [DataValue].
223 macro_rules! build_conversion_impl {
224     ( $rust_ty:ty, $data_value_ty:ident, $cranelift_ty:ident ) => {
225         impl From<$rust_ty> for DataValue {
226             fn from(data: $rust_ty) -> Self {
227                 DataValue::$data_value_ty(data)
228             }
229         }
230 
231         impl TryInto<$rust_ty> for DataValue {
232             type Error = DataValueCastFailure;
233             fn try_into(self) -> Result<$rust_ty, Self::Error> {
234                 if let DataValue::$data_value_ty(v) = self {
235                     Ok(v)
236                 } else {
237                     Err(DataValueCastFailure::TryInto(
238                         self.ty(),
239                         types::$cranelift_ty,
240                     ))
241                 }
242             }
243         }
244     };
245 }
246 build_conversion_impl!(i8, I8, I8);
247 build_conversion_impl!(i16, I16, I16);
248 build_conversion_impl!(i32, I32, I32);
249 build_conversion_impl!(i64, I64, I64);
250 build_conversion_impl!(i128, I128, I128);
251 build_conversion_impl!(u8, U8, I8);
252 build_conversion_impl!(u16, U16, I16);
253 build_conversion_impl!(u32, U32, I32);
254 build_conversion_impl!(u64, U64, I64);
255 build_conversion_impl!(u128, U128, I128);
256 build_conversion_impl!(Ieee32, F32, F32);
257 build_conversion_impl!(Ieee64, F64, F64);
258 build_conversion_impl!([u8; 16], V128, I8X16);
259 build_conversion_impl!([u8; 8], V64, I8X8);
260 impl From<Offset32> for DataValue {
261     fn from(o: Offset32) -> Self {
262         DataValue::from(Into::<i32>::into(o))
263     }
264 }
265 
266 impl Display for DataValue {
267     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
268         match self {
269             DataValue::I8(dv) => write!(f, "{}", dv),
270             DataValue::I16(dv) => write!(f, "{}", dv),
271             DataValue::I32(dv) => write!(f, "{}", dv),
272             DataValue::I64(dv) => write!(f, "{}", dv),
273             DataValue::I128(dv) => write!(f, "{}", dv),
274             DataValue::U8(dv) => write!(f, "{}", dv),
275             DataValue::U16(dv) => write!(f, "{}", dv),
276             DataValue::U32(dv) => write!(f, "{}", dv),
277             DataValue::U64(dv) => write!(f, "{}", dv),
278             DataValue::U128(dv) => write!(f, "{}", dv),
279             // The Ieee* wrappers here print the expected syntax.
280             DataValue::F32(dv) => write!(f, "{}", dv),
281             DataValue::F64(dv) => write!(f, "{}", dv),
282             // Again, for syntax consistency, use ConstantData, which in this case displays as hex.
283             DataValue::V128(dv) => write!(f, "{}", ConstantData::from(&dv[..])),
284             DataValue::V64(dv) => write!(f, "{}", ConstantData::from(&dv[..])),
285         }
286     }
287 }
288 
289 /// Helper structure for printing bracket-enclosed vectors of [DataValue]s.
290 /// - for empty vectors, display `[]`
291 /// - for single item vectors, display `42`, e.g.
292 /// - for multiple item vectors, display `[42, 43, 44]`, e.g.
293 pub struct DisplayDataValues<'a>(pub &'a [DataValue]);
294 
295 impl<'a> Display for DisplayDataValues<'a> {
296     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
297         if self.0.len() == 1 {
298             write!(f, "{}", self.0[0])
299         } else {
300             write!(f, "[")?;
301             write_data_value_list(f, &self.0)?;
302             write!(f, "]")
303         }
304     }
305 }
306 
307 /// Helper function for displaying `Vec<DataValue>`.
308 pub fn write_data_value_list(f: &mut Formatter<'_>, list: &[DataValue]) -> fmt::Result {
309     match list.len() {
310         0 => Ok(()),
311         1 => write!(f, "{}", list[0]),
312         _ => {
313             write!(f, "{}", list[0])?;
314             for dv in list.iter().skip(1) {
315                 write!(f, ", {}", dv)?;
316             }
317             Ok(())
318         }
319     }
320 }
321 
322 #[cfg(test)]
323 mod test {
324     use super::*;
325 
326     #[test]
327     fn type_conversions() {
328         assert_eq!(DataValue::V128([0; 16]).ty(), types::I8X16);
329         assert_eq!(
330             TryInto::<[u8; 16]>::try_into(DataValue::V128([0; 16])).unwrap(),
331             [0; 16]
332         );
333         assert_eq!(
334             TryInto::<i32>::try_into(DataValue::V128([0; 16])).unwrap_err(),
335             DataValueCastFailure::TryInto(types::I8X16, types::I32)
336         );
337     }
338 }
339