/src/wasmtime/cranelift/codegen/src/data_value.rs
Line | Count | Source |
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::{Ieee16, Ieee32, Ieee64, Ieee128, Offset32}; |
4 | | use crate::ir::{ConstantData, Type, types}; |
5 | | use core::cmp::Ordering; |
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 | | #[expect(missing_docs, reason = "self-describing variants")] |
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 | | 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 | | |
30 | | impl PartialEq for DataValue { |
31 | 182k | fn eq(&self, other: &Self) -> bool { |
32 | | use DataValue::*; |
33 | 182k | match (self, other) { |
34 | 29.8k | (I8(l), I8(r)) => l == r, |
35 | 0 | (I8(_), _) => false, |
36 | 14.5k | (I16(l), I16(r)) => l == r, |
37 | 0 | (I16(_), _) => false, |
38 | 6.93k | (I32(l), I32(r)) => l == r, |
39 | 0 | (I32(_), _) => false, |
40 | 78.1k | (I64(l), I64(r)) => l == r, |
41 | 0 | (I64(_), _) => false, |
42 | 5.82k | (I128(l), I128(r)) => l == r, |
43 | 0 | (I128(_), _) => false, |
44 | 0 | (F16(l), F16(r)) => l.partial_cmp(&r) == Some(Ordering::Equal), |
45 | 0 | (F16(_), _) => false, |
46 | 0 | (F32(l), F32(r)) => l.as_f32() == r.as_f32(), |
47 | 0 | (F32(_), _) => false, |
48 | 0 | (F64(l), F64(r)) => l.as_f64() == r.as_f64(), |
49 | 0 | (F64(_), _) => false, |
50 | 0 | (F128(l), F128(r)) => l.partial_cmp(&r) == Some(Ordering::Equal), |
51 | 0 | (F128(_), _) => false, |
52 | 47.2k | (V128(l), V128(r)) => l == r, |
53 | 0 | (V128(_), _) => false, |
54 | 0 | (V64(l), V64(r)) => l == r, |
55 | 0 | (V64(_), _) => false, |
56 | 0 | (V32(l), V32(r)) => l == r, |
57 | 0 | (V32(_), _) => false, |
58 | 0 | (V16(l), V16(r)) => l == r, |
59 | 0 | (V16(_), _) => false, |
60 | | } |
61 | 182k | } |
62 | | } |
63 | | |
64 | | impl DataValue { |
65 | | /// Try to cast an immediate integer (a wrapped `i64` on most Cranelift instructions) to the |
66 | | /// given Cranelift [Type]. |
67 | 1.05M | pub fn from_integer(imm: i128, ty: Type) -> Result<DataValue, DataValueCastFailure> { |
68 | 1.05M | match ty { |
69 | 185k | types::I8 => Ok(DataValue::I8(imm as i8)), |
70 | 351k | types::I16 => Ok(DataValue::I16(imm as i16)), |
71 | 167k | types::I32 => Ok(DataValue::I32(imm as i32)), |
72 | 332k | types::I64 => Ok(DataValue::I64(imm as i64)), |
73 | 16.0k | types::I128 => Ok(DataValue::I128(imm)), |
74 | 0 | _ => Err(DataValueCastFailure::FromInteger(imm, ty)), |
75 | | } |
76 | 1.05M | } |
77 | | |
78 | | /// Return the Cranelift IR [Type] for this [DataValue]. |
79 | 2.10M | pub fn ty(&self) -> Type { |
80 | 2.10M | match self { |
81 | 602k | DataValue::I8(_) => types::I8, |
82 | 232k | DataValue::I16(_) => types::I16, |
83 | 78.4k | DataValue::I32(_) => types::I32, |
84 | 611k | DataValue::I64(_) => types::I64, |
85 | 117k | DataValue::I128(_) => types::I128, |
86 | 0 | DataValue::F16(_) => types::F16, |
87 | 225k | DataValue::F32(_) => types::F32, |
88 | 69.3k | DataValue::F64(_) => types::F64, |
89 | 0 | DataValue::F128(_) => types::F128, |
90 | 171k | DataValue::V128(_) => types::I8X16, // A default type. |
91 | 0 | DataValue::V64(_) => types::I8X8, // A default type. |
92 | 0 | DataValue::V32(_) => types::I8X4, // A default type. |
93 | 0 | DataValue::V16(_) => types::I8X2, // A default type. |
94 | | } |
95 | 2.10M | } |
96 | | |
97 | | /// Return true if the value is a vector (i.e. `DataValue::V128`). |
98 | 32.6k | pub fn is_vector(&self) -> bool { |
99 | 32.6k | match self { |
100 | 32.6k | DataValue::V128(_) | DataValue::V64(_) | DataValue::V32(_) | DataValue::V16(_) => true, |
101 | 0 | _ => false, |
102 | | } |
103 | 32.6k | } |
104 | | |
105 | 0 | fn swap_bytes(self) -> Self { |
106 | 0 | match self { |
107 | 0 | DataValue::I8(i) => DataValue::I8(i.swap_bytes()), |
108 | 0 | DataValue::I16(i) => DataValue::I16(i.swap_bytes()), |
109 | 0 | DataValue::I32(i) => DataValue::I32(i.swap_bytes()), |
110 | 0 | DataValue::I64(i) => DataValue::I64(i.swap_bytes()), |
111 | 0 | DataValue::I128(i) => DataValue::I128(i.swap_bytes()), |
112 | 0 | DataValue::F16(f) => DataValue::F16(Ieee16::with_bits(f.bits().swap_bytes())), |
113 | 0 | DataValue::F32(f) => DataValue::F32(Ieee32::with_bits(f.bits().swap_bytes())), |
114 | 0 | DataValue::F64(f) => DataValue::F64(Ieee64::with_bits(f.bits().swap_bytes())), |
115 | 0 | DataValue::F128(f) => DataValue::F128(Ieee128::with_bits(f.bits().swap_bytes())), |
116 | 0 | DataValue::V128(mut v) => { |
117 | 0 | v.reverse(); |
118 | 0 | DataValue::V128(v) |
119 | | } |
120 | 0 | DataValue::V64(mut v) => { |
121 | 0 | v.reverse(); |
122 | 0 | DataValue::V64(v) |
123 | | } |
124 | 0 | DataValue::V32(mut v) => { |
125 | 0 | v.reverse(); |
126 | 0 | DataValue::V32(v) |
127 | | } |
128 | 0 | DataValue::V16(mut v) => { |
129 | 0 | v.reverse(); |
130 | 0 | DataValue::V16(v) |
131 | | } |
132 | | } |
133 | 0 | } |
134 | | |
135 | | /// Converts `self` to big endian from target's endianness. |
136 | 0 | pub fn to_be(self) -> Self { |
137 | 0 | if cfg!(target_endian = "big") { |
138 | 0 | self |
139 | | } else { |
140 | 0 | self.swap_bytes() |
141 | | } |
142 | 0 | } |
143 | | |
144 | | /// Converts `self` to little endian from target's endianness. |
145 | 169k | pub fn to_le(self) -> Self { |
146 | 169k | if cfg!(target_endian = "little") { |
147 | 169k | self |
148 | | } else { |
149 | 0 | self.swap_bytes() |
150 | | } |
151 | 169k | } |
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 | 233k | pub fn write_to_slice_ne(&self, dst: &mut [u8]) { |
159 | 233k | match self { |
160 | 37.5k | DataValue::I8(i) => dst[..1].copy_from_slice(&i.to_ne_bytes()[..]), |
161 | 21.1k | DataValue::I16(i) => dst[..2].copy_from_slice(&i.to_ne_bytes()[..]), |
162 | 18.9k | DataValue::I32(i) => dst[..4].copy_from_slice(&i.to_ne_bytes()[..]), |
163 | 14.1k | DataValue::I64(i) => dst[..8].copy_from_slice(&i.to_ne_bytes()[..]), |
164 | 74.6k | DataValue::I128(i) => dst[..16].copy_from_slice(&i.to_ne_bytes()[..]), |
165 | 0 | DataValue::F16(f) => dst[..2].copy_from_slice(&f.bits().to_ne_bytes()[..]), |
166 | 6.74k | DataValue::F32(f) => dst[..4].copy_from_slice(&f.bits().to_ne_bytes()[..]), |
167 | 7.52k | DataValue::F64(f) => dst[..8].copy_from_slice(&f.bits().to_ne_bytes()[..]), |
168 | 0 | DataValue::F128(f) => dst[..16].copy_from_slice(&f.bits().to_ne_bytes()[..]), |
169 | 52.6k | DataValue::V128(v) => dst[..16].copy_from_slice(&v[..]), |
170 | 0 | DataValue::V64(v) => dst[..8].copy_from_slice(&v[..]), |
171 | 0 | DataValue::V32(v) => dst[..4].copy_from_slice(&v[..]), |
172 | 0 | DataValue::V16(v) => dst[..2].copy_from_slice(&v[..]), |
173 | | }; |
174 | 233k | } |
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 | 0 | pub fn write_to_slice_be(&self, dst: &mut [u8]) { |
182 | 0 | self.clone().to_be().write_to_slice_ne(dst); |
183 | 0 | } |
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 | 121k | pub fn write_to_slice_le(&self, dst: &mut [u8]) { |
191 | 121k | self.clone().to_le().write_to_slice_ne(dst); |
192 | 121k | } |
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 | 145k | pub fn read_from_slice_ne(src: &[u8], ty: Type) -> Self { |
200 | 76.5k | match ty { |
201 | 31.5k | types::I8 => DataValue::I8(i8::from_ne_bytes(src[..1].try_into().unwrap())), |
202 | 9.92k | types::I16 => DataValue::I16(i16::from_ne_bytes(src[..2].try_into().unwrap())), |
203 | 10.3k | types::I32 => DataValue::I32(i32::from_ne_bytes(src[..4].try_into().unwrap())), |
204 | 3.54k | types::I64 => DataValue::I64(i64::from_ne_bytes(src[..8].try_into().unwrap())), |
205 | 4.86k | types::I128 => DataValue::I128(i128::from_ne_bytes(src[..16].try_into().unwrap())), |
206 | 0 | types::F16 => DataValue::F16(Ieee16::with_bits(u16::from_ne_bytes( |
207 | 0 | src[..2].try_into().unwrap(), |
208 | 0 | ))), |
209 | 2.26k | types::F32 => DataValue::F32(Ieee32::with_bits(u32::from_ne_bytes( |
210 | 2.26k | src[..4].try_into().unwrap(), |
211 | 2.26k | ))), |
212 | 6.28k | types::F64 => DataValue::F64(Ieee64::with_bits(u64::from_ne_bytes( |
213 | 6.28k | src[..8].try_into().unwrap(), |
214 | 6.28k | ))), |
215 | 0 | types::F128 => DataValue::F128(Ieee128::with_bits(u128::from_ne_bytes( |
216 | 0 | src[..16].try_into().unwrap(), |
217 | 0 | ))), |
218 | 76.5k | _ if ty.is_vector() => match ty.bytes() { |
219 | 76.5k | 16 => DataValue::V128(src[..16].try_into().unwrap()), |
220 | 0 | 8 => DataValue::V64(src[..8].try_into().unwrap()), |
221 | 0 | 4 => DataValue::V32(src[..4].try_into().unwrap()), |
222 | 0 | 2 => DataValue::V16(src[..2].try_into().unwrap()), |
223 | 0 | _ => unimplemented!(), |
224 | | }, |
225 | 0 | _ => unimplemented!(), |
226 | | } |
227 | 145k | } |
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 | 0 | pub fn read_from_slice_be(src: &[u8], ty: Type) -> Self { |
235 | 0 | DataValue::read_from_slice_ne(src, ty).to_be() |
236 | 0 | } |
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 | 47.8k | pub fn read_from_slice_le(src: &[u8], ty: Type) -> Self { |
244 | 47.8k | DataValue::read_from_slice_ne(src, ty).to_le() |
245 | 47.8k | } |
246 | | |
247 | | /// Write a [DataValue] to a memory location in native-endian byte order. |
248 | 111k | pub unsafe fn write_value_to(&self, p: *mut u128) { |
249 | 111k | let size = self.ty().bytes() as usize; |
250 | 111k | self.write_to_slice_ne(unsafe { core::slice::from_raw_parts_mut(p as *mut u8, size) }); |
251 | 111k | } |
252 | | |
253 | | /// Read a [DataValue] from a memory location using a given [Type] in native-endian byte order. |
254 | 94.3k | pub unsafe fn read_value_from(p: *const u128, ty: Type) -> Self { |
255 | 94.3k | DataValue::read_from_slice_ne( |
256 | 94.3k | unsafe { core::slice::from_raw_parts(p as *const u8, ty.bytes() as usize) }, |
257 | 94.3k | ty, |
258 | | ) |
259 | 94.3k | } |
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 | 99.5k | pub fn bitwise_eq(&self, other: &DataValue) -> bool { |
267 | 99.5k | 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 | 0 | (DataValue::F16(a), DataValue::F16(b)) => a.bits() == b.bits(), |
272 | 1.06k | (DataValue::F32(a), DataValue::F32(b)) => a.bits() == b.bits(), |
273 | 4.31k | (DataValue::F64(a), DataValue::F64(b)) => a.bits() == b.bits(), |
274 | 0 | (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 | 94.1k | (a, b) => a == b, |
279 | | } |
280 | 99.5k | } |
281 | | } |
282 | | |
283 | | /// Record failures to cast [DataValue]. |
284 | | #[derive(Debug, PartialEq)] |
285 | | #[expect(missing_docs, reason = "self-describing variants")] |
286 | | pub 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. |
293 | | impl core::error::Error for DataValueCastFailure {} |
294 | | |
295 | | impl Display for DataValueCastFailure { |
296 | 0 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { |
297 | 0 | match self { |
298 | 0 | DataValueCastFailure::TryInto(from, to) => { |
299 | 0 | write!(f, "unable to cast data value of type {from} to type {to}") |
300 | | } |
301 | 0 | DataValueCastFailure::FromInteger(val, to) => { |
302 | 0 | write!(f, "unable to cast i64({val}) to a data value of type {to}") |
303 | | } |
304 | | } |
305 | 0 | } |
306 | | } |
307 | | |
308 | | /// Helper for creating conversion implementations for [DataValue]. |
309 | | macro_rules! build_conversion_impl { |
310 | | ( $rust_ty:ty, $data_value_ty:ident, $cranelift_ty:ident ) => { |
311 | | impl From<$rust_ty> for DataValue { |
312 | 712k | fn from(data: $rust_ty) -> Self { |
313 | 712k | DataValue::$data_value_ty(data) |
314 | 712k | } <cranelift_codegen::data_value::DataValue as core::convert::From<i8>>::from Line | Count | Source | 312 | 6.57k | fn from(data: $rust_ty) -> Self { | 313 | 6.57k | DataValue::$data_value_ty(data) | 314 | 6.57k | } |
Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::From<i16>>::from <cranelift_codegen::data_value::DataValue as core::convert::From<i32>>::from Line | Count | Source | 312 | 342k | fn from(data: $rust_ty) -> Self { | 313 | 342k | DataValue::$data_value_ty(data) | 314 | 342k | } |
<cranelift_codegen::data_value::DataValue as core::convert::From<i64>>::from Line | Count | Source | 312 | 355k | fn from(data: $rust_ty) -> Self { | 313 | 355k | DataValue::$data_value_ty(data) | 314 | 355k | } |
Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::From<i128>>::from Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::From<cranelift_codegen::ir::immediates::Ieee16>>::from <cranelift_codegen::data_value::DataValue as core::convert::From<cranelift_codegen::ir::immediates::Ieee32>>::from Line | Count | Source | 312 | 2.46k | fn from(data: $rust_ty) -> Self { | 313 | 2.46k | DataValue::$data_value_ty(data) | 314 | 2.46k | } |
<cranelift_codegen::data_value::DataValue as core::convert::From<cranelift_codegen::ir::immediates::Ieee64>>::from Line | Count | Source | 312 | 4.20k | fn from(data: $rust_ty) -> Self { | 313 | 4.20k | DataValue::$data_value_ty(data) | 314 | 4.20k | } |
Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::From<cranelift_codegen::ir::immediates::Ieee128>>::from Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::From<[u8; 16]>>::from Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::From<[u8; 8]>>::from Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::From<[u8; 4]>>::from Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::From<[u8; 2]>>::from |
315 | | } |
316 | | |
317 | | impl TryInto<$rust_ty> for DataValue { |
318 | | type Error = DataValueCastFailure; |
319 | 0 | fn try_into(self) -> Result<$rust_ty, Self::Error> { |
320 | 0 | if let DataValue::$data_value_ty(v) = self { |
321 | 0 | Ok(v) |
322 | | } else { |
323 | 0 | Err(DataValueCastFailure::TryInto( |
324 | 0 | self.ty(), |
325 | 0 | types::$cranelift_ty, |
326 | 0 | )) |
327 | | } |
328 | 0 | } Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::TryInto<[u8; 2]>>::try_into Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::TryInto<i8>>::try_into Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::TryInto<i16>>::try_into Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::TryInto<i32>>::try_into Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::TryInto<i64>>::try_into Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::TryInto<i128>>::try_into Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::TryInto<cranelift_codegen::ir::immediates::Ieee16>>::try_into Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::TryInto<cranelift_codegen::ir::immediates::Ieee32>>::try_into Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::TryInto<cranelift_codegen::ir::immediates::Ieee64>>::try_into Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::TryInto<cranelift_codegen::ir::immediates::Ieee128>>::try_into Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::TryInto<[u8; 16]>>::try_into Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::TryInto<[u8; 8]>>::try_into Unexecuted instantiation: <cranelift_codegen::data_value::DataValue as core::convert::TryInto<[u8; 4]>>::try_into |
329 | | } |
330 | | }; |
331 | | } |
332 | | build_conversion_impl!(i8, I8, I8); |
333 | | build_conversion_impl!(i16, I16, I16); |
334 | | build_conversion_impl!(i32, I32, I32); |
335 | | build_conversion_impl!(i64, I64, I64); |
336 | | build_conversion_impl!(i128, I128, I128); |
337 | | build_conversion_impl!(Ieee16, F16, F16); |
338 | | build_conversion_impl!(Ieee32, F32, F32); |
339 | | build_conversion_impl!(Ieee64, F64, F64); |
340 | | build_conversion_impl!(Ieee128, F128, F128); |
341 | | build_conversion_impl!([u8; 16], V128, I8X16); |
342 | | build_conversion_impl!([u8; 8], V64, I8X8); |
343 | | build_conversion_impl!([u8; 4], V32, I8X4); |
344 | | build_conversion_impl!([u8; 2], V16, I8X2); |
345 | | impl From<Offset32> for DataValue { |
346 | 342k | fn from(o: Offset32) -> Self { |
347 | 342k | DataValue::from(Into::<i32>::into(o)) |
348 | 342k | } |
349 | | } |
350 | | |
351 | | impl Display for DataValue { |
352 | 0 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
353 | 0 | match self { |
354 | 0 | DataValue::I8(dv) => write!(f, "{dv}"), |
355 | 0 | DataValue::I16(dv) => write!(f, "{dv}"), |
356 | 0 | DataValue::I32(dv) => write!(f, "{dv}"), |
357 | 0 | DataValue::I64(dv) => write!(f, "{dv}"), |
358 | 0 | DataValue::I128(dv) => write!(f, "{dv}"), |
359 | | // The Ieee* wrappers here print the expected syntax. |
360 | 0 | DataValue::F16(dv) => write!(f, "{dv}"), |
361 | 0 | DataValue::F32(dv) => write!(f, "{dv}"), |
362 | 0 | DataValue::F64(dv) => write!(f, "{dv}"), |
363 | 0 | DataValue::F128(dv) => write!(f, "{dv}"), |
364 | | // Again, for syntax consistency, use ConstantData, which in this case displays as hex. |
365 | 0 | DataValue::V128(dv) => write!(f, "{}", ConstantData::from(&dv[..])), |
366 | 0 | DataValue::V64(dv) => write!(f, "{}", ConstantData::from(&dv[..])), |
367 | 0 | DataValue::V32(dv) => write!(f, "{}", ConstantData::from(&dv[..])), |
368 | 0 | DataValue::V16(dv) => write!(f, "{}", ConstantData::from(&dv[..])), |
369 | | } |
370 | 0 | } |
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. |
377 | | pub struct DisplayDataValues<'a>(pub &'a [DataValue]); |
378 | | |
379 | | impl<'a> Display for DisplayDataValues<'a> { |
380 | 0 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
381 | 0 | if self.0.len() == 1 { |
382 | 0 | write!(f, "{}", self.0[0]) |
383 | | } else { |
384 | 0 | write!(f, "[")?; |
385 | 0 | write_data_value_list(f, &self.0)?; |
386 | 0 | write!(f, "]") |
387 | | } |
388 | 0 | } |
389 | | } |
390 | | |
391 | | /// Helper function for displaying `Vec<DataValue>`. |
392 | 0 | pub fn write_data_value_list(f: &mut Formatter<'_>, list: &[DataValue]) -> fmt::Result { |
393 | 0 | match list.len() { |
394 | 0 | 0 => Ok(()), |
395 | 0 | 1 => write!(f, "{}", list[0]), |
396 | | _ => { |
397 | 0 | write!(f, "{}", list[0])?; |
398 | 0 | for dv in list.iter().skip(1) { |
399 | 0 | write!(f, ", {dv}")?; |
400 | | } |
401 | 0 | Ok(()) |
402 | | } |
403 | | } |
404 | 0 | } |
405 | | |
406 | | #[cfg(test)] |
407 | | mod 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 | | } |