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, PartialEq, PartialOrd)]
14 pub enum DataValue {
15     B(bool),
16     I8(i8),
17     I16(i16),
18     I32(i32),
19     I64(i64),
20     I128(i128),
21     U8(u8),
22     U16(u16),
23     U32(u32),
24     U64(u64),
25     U128(u128),
26     F32(Ieee32),
27     F64(Ieee64),
28     V128([u8; 16]),
29     V64([u8; 8]),
30 }
31 
32 impl DataValue {
33     /// Try to cast an immediate integer (a wrapped `i64` on most Cranelift instructions) to the
34     /// given Cranelift [Type].
35     pub fn from_integer(imm: i128, ty: Type) -> Result<DataValue, DataValueCastFailure> {
36         match ty {
37             types::I8 => Ok(DataValue::I8(imm as i8)),
38             types::I16 => Ok(DataValue::I16(imm as i16)),
39             types::I32 => Ok(DataValue::I32(imm as i32)),
40             types::I64 => Ok(DataValue::I64(imm as i64)),
41             types::I128 => Ok(DataValue::I128(imm)),
42             _ => Err(DataValueCastFailure::FromInteger(imm, ty)),
43         }
44     }
45 
46     /// Return the Cranelift IR [Type] for this [DataValue].
47     pub fn ty(&self) -> Type {
48         match self {
49             DataValue::B(_) => types::B8, // A default type.
50             DataValue::I8(_) | DataValue::U8(_) => types::I8,
51             DataValue::I16(_) | DataValue::U16(_) => types::I16,
52             DataValue::I32(_) | DataValue::U32(_) => types::I32,
53             DataValue::I64(_) | DataValue::U64(_) => types::I64,
54             DataValue::I128(_) | DataValue::U128(_) => types::I128,
55             DataValue::F32(_) => types::F32,
56             DataValue::F64(_) => types::F64,
57             DataValue::V128(_) => types::I8X16, // A default type.
58             DataValue::V64(_) => types::I8X8,   // A default type.
59         }
60     }
61 
62     /// Return true if the value is a vector (i.e. `DataValue::V128`).
63     pub fn is_vector(&self) -> bool {
64         match self {
65             DataValue::V128(_) | DataValue::V64(_) => true,
66             _ => false,
67         }
68     }
69 
70     /// Return true if the value is a bool (i.e. `DataValue::B`).
71     pub fn is_bool(&self) -> bool {
72         match self {
73             DataValue::B(_) => true,
74             _ => false,
75         }
76     }
77 
78     /// Write a [DataValue] to a slice.
79     ///
80     /// # Panics:
81     ///
82     /// Panics if the slice does not have enough space to accommodate the [DataValue]
83     pub fn write_to_slice(&self, dst: &mut [u8]) {
84         match self {
85             DataValue::B(true) => dst[..16].copy_from_slice(&[u8::MAX; 16][..]),
86             DataValue::B(false) => dst[..16].copy_from_slice(&[0; 16][..]),
87             DataValue::I8(i) => dst[..1].copy_from_slice(&i.to_ne_bytes()[..]),
88             DataValue::I16(i) => dst[..2].copy_from_slice(&i.to_ne_bytes()[..]),
89             DataValue::I32(i) => dst[..4].copy_from_slice(&i.to_ne_bytes()[..]),
90             DataValue::I64(i) => dst[..8].copy_from_slice(&i.to_ne_bytes()[..]),
91             DataValue::I128(i) => dst[..16].copy_from_slice(&i.to_ne_bytes()[..]),
92             DataValue::F32(f) => dst[..4].copy_from_slice(&f.bits().to_ne_bytes()[..]),
93             DataValue::F64(f) => dst[..8].copy_from_slice(&f.bits().to_ne_bytes()[..]),
94             DataValue::V128(v) => dst[..16].copy_from_slice(&u128::from_le_bytes(*v).to_ne_bytes()),
95             DataValue::V64(v) => dst[..8].copy_from_slice(&u64::from_le_bytes(*v).to_ne_bytes()),
96             _ => unimplemented!(),
97         };
98     }
99 
100     /// Read a [DataValue] from a slice using a given [Type].
101     ///
102     /// # Panics:
103     ///
104     /// Panics if the slice does not have enough space to accommodate the [DataValue]
105     pub fn read_from_slice(src: &[u8], ty: Type) -> Self {
106         match ty {
107             types::I8 => DataValue::I8(i8::from_ne_bytes(src[..1].try_into().unwrap())),
108             types::I16 => DataValue::I16(i16::from_ne_bytes(src[..2].try_into().unwrap())),
109             types::I32 => DataValue::I32(i32::from_ne_bytes(src[..4].try_into().unwrap())),
110             types::I64 => DataValue::I64(i64::from_ne_bytes(src[..8].try_into().unwrap())),
111             types::I128 => DataValue::I128(i128::from_ne_bytes(src[..16].try_into().unwrap())),
112             types::F32 => DataValue::F32(Ieee32::with_bits(u32::from_ne_bytes(
113                 src[..4].try_into().unwrap(),
114             ))),
115             types::F64 => DataValue::F64(Ieee64::with_bits(u64::from_ne_bytes(
116                 src[..8].try_into().unwrap(),
117             ))),
118             _ if ty.is_bool() => {
119                 // Only `ty.bytes()` are guaranteed to be written
120                 // so we can only test the first n bytes of `src`
121 
122                 let size = ty.bytes() as usize;
123                 DataValue::B(src[..size].iter().any(|&i| i != 0))
124             }
125             _ if ty.is_vector() => {
126                 if ty.bytes() == 16 {
127                     DataValue::V128(
128                         u128::from_ne_bytes(src[..16].try_into().unwrap()).to_le_bytes(),
129                     )
130                 } else if ty.bytes() == 8 {
131                     DataValue::V64(u64::from_ne_bytes(src[..8].try_into().unwrap()).to_le_bytes())
132                 } else {
133                     unimplemented!()
134                 }
135             }
136             _ => unimplemented!(),
137         }
138     }
139 
140     /// Write a [DataValue] to a memory location.
141     pub unsafe fn write_value_to(&self, p: *mut u128) {
142         // Since `DataValue` does not have type info for bools we always
143         // write out a full 16 byte slot.
144         let size = match self.ty() {
145             ty if ty.is_bool() => 16,
146             ty => ty.bytes() as usize,
147         };
148 
149         self.write_to_slice(std::slice::from_raw_parts_mut(p as *mut u8, size));
150     }
151 
152     /// Read a [DataValue] from a memory location using a given [Type].
153     pub unsafe fn read_value_from(p: *const u128, ty: Type) -> Self {
154         DataValue::read_from_slice(
155             std::slice::from_raw_parts(p as *const u8, ty.bytes() as usize),
156             ty,
157         )
158     }
159 }
160 
161 /// Record failures to cast [DataValue].
162 #[derive(Debug, PartialEq)]
163 #[allow(missing_docs)]
164 pub enum DataValueCastFailure {
165     TryInto(Type, Type),
166     FromInteger(i128, Type),
167 }
168 
169 // This is manually implementing Error and Display instead of using thiserror to reduce the amount
170 // of dependencies used by Cranelift.
171 impl std::error::Error for DataValueCastFailure {}
172 
173 impl Display for DataValueCastFailure {
174     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
175         match self {
176             DataValueCastFailure::TryInto(from, to) => {
177                 write!(
178                     f,
179                     "unable to cast data value of type {} to type {}",
180                     from, to
181                 )
182             }
183             DataValueCastFailure::FromInteger(val, to) => {
184                 write!(
185                     f,
186                     "unable to cast i64({}) to a data value of type {}",
187                     val, to
188                 )
189             }
190         }
191     }
192 }
193 
194 /// Helper for creating conversion implementations for [DataValue].
195 macro_rules! build_conversion_impl {
196     ( $rust_ty:ty, $data_value_ty:ident, $cranelift_ty:ident ) => {
197         impl From<$rust_ty> for DataValue {
198             fn from(data: $rust_ty) -> Self {
199                 DataValue::$data_value_ty(data)
200             }
201         }
202 
203         impl TryInto<$rust_ty> for DataValue {
204             type Error = DataValueCastFailure;
205             fn try_into(self) -> Result<$rust_ty, Self::Error> {
206                 if let DataValue::$data_value_ty(v) = self {
207                     Ok(v)
208                 } else {
209                     Err(DataValueCastFailure::TryInto(
210                         self.ty(),
211                         types::$cranelift_ty,
212                     ))
213                 }
214             }
215         }
216     };
217 }
218 build_conversion_impl!(bool, B, B8);
219 build_conversion_impl!(i8, I8, I8);
220 build_conversion_impl!(i16, I16, I16);
221 build_conversion_impl!(i32, I32, I32);
222 build_conversion_impl!(i64, I64, I64);
223 build_conversion_impl!(i128, I128, I128);
224 build_conversion_impl!(u8, U8, I8);
225 build_conversion_impl!(u16, U16, I16);
226 build_conversion_impl!(u32, U32, I32);
227 build_conversion_impl!(u64, U64, I64);
228 build_conversion_impl!(u128, U128, I128);
229 build_conversion_impl!(Ieee32, F32, F32);
230 build_conversion_impl!(Ieee64, F64, F64);
231 build_conversion_impl!([u8; 16], V128, I8X16);
232 build_conversion_impl!([u8; 8], V64, I8X8);
233 impl From<Offset32> for DataValue {
234     fn from(o: Offset32) -> Self {
235         DataValue::from(Into::<i32>::into(o))
236     }
237 }
238 
239 impl Display for DataValue {
240     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
241         match self {
242             DataValue::B(dv) => write!(f, "{}", dv),
243             DataValue::I8(dv) => write!(f, "{}", dv),
244             DataValue::I16(dv) => write!(f, "{}", dv),
245             DataValue::I32(dv) => write!(f, "{}", dv),
246             DataValue::I64(dv) => write!(f, "{}", dv),
247             DataValue::I128(dv) => write!(f, "{}", dv),
248             DataValue::U8(dv) => write!(f, "{}", dv),
249             DataValue::U16(dv) => write!(f, "{}", dv),
250             DataValue::U32(dv) => write!(f, "{}", dv),
251             DataValue::U64(dv) => write!(f, "{}", dv),
252             DataValue::U128(dv) => write!(f, "{}", dv),
253             // The Ieee* wrappers here print the expected syntax.
254             DataValue::F32(dv) => write!(f, "{}", dv),
255             DataValue::F64(dv) => write!(f, "{}", dv),
256             // Again, for syntax consistency, use ConstantData, which in this case displays as hex.
257             DataValue::V128(dv) => write!(f, "{}", ConstantData::from(&dv[..])),
258             DataValue::V64(dv) => write!(f, "{}", ConstantData::from(&dv[..])),
259         }
260     }
261 }
262 
263 /// Helper structure for printing bracket-enclosed vectors of [DataValue]s.
264 /// - for empty vectors, display `[]`
265 /// - for single item vectors, display `42`, e.g.
266 /// - for multiple item vectors, display `[42, 43, 44]`, e.g.
267 pub struct DisplayDataValues<'a>(pub &'a [DataValue]);
268 
269 impl<'a> Display for DisplayDataValues<'a> {
270     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
271         if self.0.len() == 1 {
272             write!(f, "{}", self.0[0])
273         } else {
274             write!(f, "[")?;
275             write_data_value_list(f, &self.0)?;
276             write!(f, "]")
277         }
278     }
279 }
280 
281 /// Helper function for displaying `Vec<DataValue>`.
282 pub fn write_data_value_list(f: &mut Formatter<'_>, list: &[DataValue]) -> fmt::Result {
283     match list.len() {
284         0 => Ok(()),
285         1 => write!(f, "{}", list[0]),
286         _ => {
287             write!(f, "{}", list[0])?;
288             for dv in list.iter().skip(1) {
289                 write!(f, ", {}", dv)?;
290             }
291             Ok(())
292         }
293     }
294 }
295 
296 #[cfg(test)]
297 mod test {
298     use super::*;
299 
300     #[test]
301     fn type_conversions() {
302         assert_eq!(DataValue::B(true).ty(), types::B8);
303         assert_eq!(
304             TryInto::<bool>::try_into(DataValue::B(false)).unwrap(),
305             false
306         );
307         assert_eq!(
308             TryInto::<i32>::try_into(DataValue::B(false)).unwrap_err(),
309             DataValueCastFailure::TryInto(types::B8, types::I32)
310         );
311 
312         assert_eq!(DataValue::V128([0; 16]).ty(), types::I8X16);
313         assert_eq!(
314             TryInto::<[u8; 16]>::try_into(DataValue::V128([0; 16])).unwrap(),
315             [0; 16]
316         );
317         assert_eq!(
318             TryInto::<i32>::try_into(DataValue::V128([0; 16])).unwrap_err(),
319             DataValueCastFailure::TryInto(types::I8X16, types::I32)
320         );
321     }
322 }
323