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