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