1 //! Constants
2 //!
3 //! The constant pool defined here allows Cranelift to avoid emitting the same constant multiple
4 //! times. As constants are inserted in the pool, a handle is returned; the handle is a Cranelift
5 //! Entity. Inserting the same data multiple times will always return the same handle.
6 //!
7 //! Future work could include:
8 //! - ensuring alignment of constants within the pool,
9 //! - bucketing constants by size.
10 
11 use crate::ir::immediates::{IntoBytes, V128Imm};
12 use crate::ir::Constant;
13 use alloc::collections::BTreeMap;
14 use alloc::vec::Vec;
15 use core::fmt;
16 use core::iter::FromIterator;
17 use core::slice::Iter;
18 use core::str::{from_utf8, FromStr};
19 use cranelift_entity::EntityRef;
20 
21 #[cfg(feature = "enable-serde")]
22 use serde::{Deserialize, Serialize};
23 
24 /// This type describes the actual constant data. Note that the bytes stored in this structure are
25 /// expected to be in little-endian order; this is due to ease-of-use when interacting with
26 /// WebAssembly values, which are [little-endian by design].
27 ///
28 /// [little-endian by design]: https://github.com/WebAssembly/design/blob/master/Portability.md
29 #[derive(Clone, Hash, Eq, PartialEq, Debug, Default, PartialOrd, Ord)]
30 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
31 pub struct ConstantData(Vec<u8>);
32 
33 impl FromIterator<u8> for ConstantData {
34     fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
35         let v = iter.into_iter().collect();
36         Self(v)
37     }
38 }
39 
40 impl From<Vec<u8>> for ConstantData {
41     fn from(v: Vec<u8>) -> Self {
42         Self(v)
43     }
44 }
45 
46 impl From<&[u8]> for ConstantData {
47     fn from(v: &[u8]) -> Self {
48         Self(v.to_vec())
49     }
50 }
51 
52 impl From<V128Imm> for ConstantData {
53     fn from(v: V128Imm) -> Self {
54         Self(v.to_vec())
55     }
56 }
57 
58 impl ConstantData {
59     /// Return the number of bytes in the constant.
60     pub fn len(&self) -> usize {
61         self.0.len()
62     }
63 
64     /// Check if the constant contains any bytes.
65     pub fn is_empty(&self) -> bool {
66         self.0.is_empty()
67     }
68 
69     /// Return the data as a slice.
70     pub fn as_slice(&self) -> &[u8] {
71         self.0.as_slice()
72     }
73 
74     /// Convert the data to a vector.
75     pub fn into_vec(self) -> Vec<u8> {
76         self.0
77     }
78 
79     /// Iterate over the constant's bytes.
80     pub fn iter(&self) -> Iter<u8> {
81         self.0.iter()
82     }
83 
84     /// Add new bytes to the constant data.
85     pub fn append(mut self, bytes: impl IntoBytes) -> Self {
86         let mut to_add = bytes.into_bytes();
87         self.0.append(&mut to_add);
88         self
89     }
90 
91     /// Expand the size of the constant data to `expected_size` number of bytes by adding zeroes
92     /// in the high-order byte slots.
93     pub fn expand_to(mut self, expected_size: usize) -> Self {
94         if self.len() > expected_size {
95             panic!(
96                 "The constant data is already expanded beyond {} bytes",
97                 expected_size
98             )
99         }
100         self.0.resize(expected_size, 0);
101         self
102     }
103 }
104 
105 impl fmt::Display for ConstantData {
106     /// Print the constant data in hexadecimal format, e.g. 0x000102030405060708090a0b0c0d0e0f.
107     /// This function will flip the stored order of bytes--little-endian--to the more readable
108     /// big-endian ordering.
109     ///
110     /// ```
111     /// use cranelift_codegen::ir::ConstantData;
112     /// let data = ConstantData::from([3, 2, 1, 0, 0].as_ref()); // note the little-endian order
113     /// assert_eq!(data.to_string(), "0x0000010203");
114     /// ```
115     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116         if !self.is_empty() {
117             write!(f, "0x")?;
118             for b in self.0.iter().rev() {
119                 write!(f, "{:02x}", b)?;
120             }
121         }
122         Ok(())
123     }
124 }
125 
126 impl FromStr for ConstantData {
127     type Err = &'static str;
128 
129     /// Parse a hexadecimal string to `ConstantData`. This is the inverse of `Display::fmt`.
130     ///
131     /// ```
132     /// use cranelift_codegen::ir::ConstantData;
133     /// let c: ConstantData = "0x000102".parse().unwrap();
134     /// assert_eq!(c.into_vec(), [2, 1, 0]);
135     /// ```
136     fn from_str(s: &str) -> Result<Self, &'static str> {
137         if s.len() <= 2 || &s[0..2] != "0x" {
138             return Err("Expected a hexadecimal string, e.g. 0x1234");
139         }
140 
141         // clean and check the string
142         let cleaned: Vec<u8> = s[2..]
143             .as_bytes()
144             .iter()
145             .filter(|&&b| b as char != '_')
146             .cloned()
147             .collect(); // remove 0x prefix and any intervening _ characters
148 
149         if cleaned.is_empty() {
150             Err("Hexadecimal string must have some digits")
151         } else if cleaned.len() % 2 != 0 {
152             Err("Hexadecimal string must have an even number of digits")
153         } else if cleaned.len() > 32 {
154             Err("Hexadecimal string has too many digits to fit in a 128-bit vector")
155         } else {
156             let mut buffer = Vec::with_capacity((s.len() - 2) / 2);
157             for i in (0..cleaned.len()).step_by(2) {
158                 let pair = from_utf8(&cleaned[i..i + 2])
159                     .or_else(|_| Err("Unable to parse hexadecimal pair as UTF-8"))?;
160                 let byte = u8::from_str_radix(pair, 16)
161                     .or_else(|_| Err("Unable to parse as hexadecimal"))?;
162                 buffer.insert(0, byte);
163             }
164             Ok(Self(buffer))
165         }
166     }
167 }
168 
169 /// Maintains the mapping between a constant handle (i.e.  [`Constant`](crate::ir::Constant)) and
170 /// its constant data (i.e.  [`ConstantData`](crate::ir::ConstantData)).
171 #[derive(Clone, PartialEq, Hash)]
172 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
173 pub struct ConstantPool {
174     /// This mapping maintains the insertion order as long as Constants are created with
175     /// sequentially increasing integers.
176     ///
177     /// It is important that, by construction, no entry in that list gets removed. If that ever
178     /// need to happen, don't forget to update the `Constant` generation scheme.
179     handles_to_values: BTreeMap<Constant, ConstantData>,
180 
181     /// Mapping of hashed `ConstantData` to the index into the other hashmap.
182     ///
183     /// This allows for deduplication of entries into the `handles_to_values` mapping.
184     values_to_handles: BTreeMap<ConstantData, Constant>,
185 }
186 
187 impl ConstantPool {
188     /// Create a new constant pool instance.
189     pub fn new() -> Self {
190         Self {
191             handles_to_values: BTreeMap::new(),
192             values_to_handles: BTreeMap::new(),
193         }
194     }
195 
196     /// Empty the constant pool of all data.
197     pub fn clear(&mut self) {
198         self.handles_to_values.clear();
199         self.values_to_handles.clear();
200     }
201 
202     /// Insert constant data into the pool, returning a handle for later referencing; when constant
203     /// data is inserted that is a duplicate of previous constant data, the existing handle will be
204     /// returned.
205     pub fn insert(&mut self, constant_value: ConstantData) -> Constant {
206         if let Some(cst) = self.values_to_handles.get(&constant_value) {
207             return *cst;
208         }
209 
210         let constant_handle = Constant::new(self.len());
211         self.set(constant_handle, constant_value);
212         constant_handle
213     }
214 
215     /// Retrieve the constant data given a handle.
216     pub fn get(&self, constant_handle: Constant) -> &ConstantData {
217         assert!(self.handles_to_values.contains_key(&constant_handle));
218         self.handles_to_values.get(&constant_handle).unwrap()
219     }
220 
221     /// Link a constant handle to its value. This does not de-duplicate data but does avoid
222     /// replacing any existing constant values. use `set` to tie a specific `const42` to its value;
223     /// use `insert` to add a value and return the next available `const` entity.
224     pub fn set(&mut self, constant_handle: Constant, constant_value: ConstantData) {
225         let replaced = self
226             .handles_to_values
227             .insert(constant_handle, constant_value.clone());
228         assert!(
229             replaced.is_none(),
230             "attempted to overwrite an existing constant {:?}: {:?} => {:?}",
231             constant_handle,
232             &constant_value,
233             replaced.unwrap()
234         );
235         self.values_to_handles
236             .insert(constant_value, constant_handle);
237     }
238 
239     /// Iterate over the constants in insertion order.
240     pub fn iter(&self) -> impl Iterator<Item = (&Constant, &ConstantData)> {
241         self.handles_to_values.iter()
242     }
243 
244     /// Iterate over mutable entries in the constant pool in insertion order.
245     pub fn entries_mut(&mut self) -> impl Iterator<Item = &mut ConstantData> {
246         self.handles_to_values.values_mut()
247     }
248 
249     /// Return the number of constants in the pool.
250     pub fn len(&self) -> usize {
251         self.handles_to_values.len()
252     }
253 
254     /// Return the combined size of all of the constant values in the pool.
255     pub fn byte_size(&self) -> usize {
256         self.handles_to_values.values().map(|c| c.len()).sum()
257     }
258 }
259 
260 #[cfg(test)]
261 mod tests {
262     use super::*;
263     use std::string::ToString;
264 
265     #[test]
266     fn empty() {
267         let sut = ConstantPool::new();
268         assert_eq!(sut.len(), 0);
269     }
270 
271     #[test]
272     fn insert() {
273         let mut sut = ConstantPool::new();
274         sut.insert(vec![1, 2, 3].into());
275         sut.insert(vec![4, 5, 6].into());
276         assert_eq!(sut.len(), 2);
277     }
278 
279     #[test]
280     fn insert_duplicate() {
281         let mut sut = ConstantPool::new();
282         let a = sut.insert(vec![1, 2, 3].into());
283         sut.insert(vec![4, 5, 6].into());
284         let b = sut.insert(vec![1, 2, 3].into());
285         assert_eq!(a, b);
286     }
287 
288     #[test]
289     fn clear() {
290         let mut sut = ConstantPool::new();
291         sut.insert(vec![1, 2, 3].into());
292         assert_eq!(sut.len(), 1);
293 
294         sut.clear();
295         assert_eq!(sut.len(), 0);
296     }
297 
298     #[test]
299     fn iteration_order() {
300         let mut sut = ConstantPool::new();
301         sut.insert(vec![1, 2, 3].into());
302         sut.insert(vec![4, 5, 6].into());
303         sut.insert(vec![1, 2, 3].into());
304         let data = sut.iter().map(|(_, v)| v).collect::<Vec<&ConstantData>>();
305         assert_eq!(data, vec![&vec![1, 2, 3].into(), &vec![4, 5, 6].into()]);
306     }
307 
308     #[test]
309     fn get() {
310         let mut sut = ConstantPool::new();
311         let data = vec![1, 2, 3];
312         let handle = sut.insert(data.clone().into());
313         assert_eq!(sut.get(handle), &data.into());
314     }
315 
316     #[test]
317     fn set() {
318         let mut sut = ConstantPool::new();
319         let handle = Constant::with_number(42).unwrap();
320         let data = vec![1, 2, 3];
321         sut.set(handle, data.clone().into());
322         assert_eq!(sut.get(handle), &data.into());
323     }
324 
325     #[test]
326     #[should_panic]
327     fn disallow_overwriting_constant() {
328         let mut sut = ConstantPool::new();
329         let handle = Constant::with_number(42).unwrap();
330         sut.set(handle, vec![].into());
331         sut.set(handle, vec![1].into());
332     }
333 
334     #[test]
335     #[should_panic]
336     fn get_nonexistent_constant() {
337         let sut = ConstantPool::new();
338         let a = Constant::with_number(42).unwrap();
339         sut.get(a); // panics, only use constants returned by ConstantPool
340     }
341 
342     #[test]
343     fn display_constant_data() {
344         assert_eq!(ConstantData::from([0].as_ref()).to_string(), "0x00");
345         assert_eq!(ConstantData::from([42].as_ref()).to_string(), "0x2a");
346         assert_eq!(
347             ConstantData::from([3, 2, 1, 0].as_ref()).to_string(),
348             "0x00010203"
349         );
350         assert_eq!(
351             ConstantData::from(3735928559u32.to_le_bytes().as_ref()).to_string(),
352             "0xdeadbeef"
353         );
354         assert_eq!(
355             ConstantData::from(0x0102030405060708u64.to_le_bytes().as_ref()).to_string(),
356             "0x0102030405060708"
357         );
358     }
359 
360     #[test]
361     fn iterate_over_constant_data() {
362         let c = ConstantData::from([1, 2, 3].as_ref());
363         let mut iter = c.iter();
364         assert_eq!(iter.next(), Some(&1));
365         assert_eq!(iter.next(), Some(&2));
366         assert_eq!(iter.next(), Some(&3));
367         assert_eq!(iter.next(), None);
368     }
369 
370     #[test]
371     fn add_to_constant_data() {
372         let d = ConstantData::from([1, 2].as_ref());
373         let e = d.append(i16::from(3u8));
374         assert_eq!(e.into_vec(), vec![1, 2, 3, 0])
375     }
376 
377     #[test]
378     fn extend_constant_data() {
379         let d = ConstantData::from([1, 2].as_ref());
380         assert_eq!(d.expand_to(4).into_vec(), vec![1, 2, 0, 0])
381     }
382 
383     #[test]
384     #[should_panic]
385     fn extend_constant_data_to_invalid_length() {
386         ConstantData::from([1, 2].as_ref()).expand_to(1);
387     }
388 
389     #[test]
390     fn parse_constant_data_and_restringify() {
391         // Verify that parsing of `from` succeeds and stringifies to `to`.
392         fn parse_ok(from: &str, to: &str) {
393             let parsed = from.parse::<ConstantData>().unwrap();
394             assert_eq!(parsed.to_string(), to);
395         }
396 
397         // Verify that parsing of `from` fails with `error_msg`.
398         fn parse_err(from: &str, error_msg: &str) {
399             let parsed = from.parse::<ConstantData>();
400             assert!(
401                 parsed.is_err(),
402                 "Expected a parse error but parsing succeeded: {}",
403                 from
404             );
405             assert_eq!(parsed.err().unwrap(), error_msg);
406         }
407 
408         parse_ok("0x00", "0x00");
409         parse_ok("0x00000042", "0x00000042");
410         parse_ok(
411             "0x0102030405060708090a0b0c0d0e0f00",
412             "0x0102030405060708090a0b0c0d0e0f00",
413         );
414         parse_ok("0x_0000_0043_21", "0x0000004321");
415 
416         parse_err("", "Expected a hexadecimal string, e.g. 0x1234");
417         parse_err("0x", "Expected a hexadecimal string, e.g. 0x1234");
418         parse_err(
419             "0x042",
420             "Hexadecimal string must have an even number of digits",
421         );
422         parse_err(
423             "0x00000000000000000000000000000000000000000000000000",
424             "Hexadecimal string has too many digits to fit in a 128-bit vector",
425         );
426         parse_err("0xrstu", "Unable to parse as hexadecimal");
427         parse_err("0x__", "Hexadecimal string must have some digits");
428     }
429 
430     #[test]
431     fn verify_stored_bytes_in_constant_data() {
432         assert_eq!("0x01".parse::<ConstantData>().unwrap().into_vec(), [1]);
433         assert_eq!(ConstantData::from([1, 0].as_ref()).0, [1, 0]);
434         assert_eq!(ConstantData::from(vec![1, 0, 0, 0]).0, [1, 0, 0, 0]);
435     }
436 
437     #[test]
438     fn check_constant_data_endianness_as_uimm128() {
439         fn parse_to_uimm128(from: &str) -> Vec<u8> {
440             from.parse::<ConstantData>()
441                 .unwrap()
442                 .expand_to(16)
443                 .into_vec()
444         }
445 
446         assert_eq!(
447             parse_to_uimm128("0x42"),
448             [0x42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
449         );
450         assert_eq!(
451             parse_to_uimm128("0x00"),
452             [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
453         );
454         assert_eq!(
455             parse_to_uimm128("0x12345678"),
456             [0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
457         );
458         assert_eq!(
459             parse_to_uimm128("0x1234_5678"),
460             [0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
461         );
462     }
463 }
464