cranelift_codegen/ir/
constant.rs

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
11use crate::ir::immediates::{Ieee128, IntoBytes, V128Imm};
12use crate::ir::Constant;
13use alloc::collections::BTreeMap;
14use alloc::vec::Vec;
15use core::fmt;
16use core::slice::Iter;
17use core::str::{from_utf8, FromStr};
18use cranelift_entity::EntityRef;
19
20#[cfg(feature = "enable-serde")]
21use 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))]
30pub struct ConstantData(Vec<u8>);
31
32impl 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
39impl From<Vec<u8>> for ConstantData {
40    fn from(v: Vec<u8>) -> Self {
41        Self(v)
42    }
43}
44
45impl From<&[u8]> for ConstantData {
46    fn from(v: &[u8]) -> Self {
47        Self(v.to_vec())
48    }
49}
50
51impl From<V128Imm> for ConstantData {
52    fn from(v: V128Imm) -> Self {
53        Self(v.to_vec())
54    }
55}
56
57impl From<Ieee128> for ConstantData {
58    fn from(v: Ieee128) -> Self {
59        Self(v.into_bytes())
60    }
61}
62
63impl TryFrom<&ConstantData> for Ieee128 {
64    type Error = <[u8; 16] as TryFrom<&'static [u8]>>::Error;
65
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
73impl ConstantData {
74    /// Return the number of bytes in the constant.
75    pub fn len(&self) -> usize {
76        self.0.len()
77    }
78
79    /// Check if the constant contains any bytes.
80    pub fn is_empty(&self) -> bool {
81        self.0.is_empty()
82    }
83
84    /// Return the data as a slice.
85    pub fn as_slice(&self) -> &[u8] {
86        self.0.as_slice()
87    }
88
89    /// Convert the data to a vector.
90    pub fn into_vec(self) -> Vec<u8> {
91        self.0
92    }
93
94    /// Iterate over the constant's bytes.
95    pub fn iter(&self) -> Iter<u8> {
96        self.0.iter()
97    }
98
99    /// Add new bytes to the constant data.
100    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.
108    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
117impl 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    /// ```
127    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
138impl 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    /// ```
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))]
185pub 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
199impl ConstantPool {
200    /// Create a new constant pool instance.
201    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.
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.
217    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.
228    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.
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.
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.
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.
262    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.
267    pub fn byte_size(&self) -> usize {
268        self.handles_to_values.values().map(|c| c.len()).sum()
269    }
270}
271
272#[cfg(test)]
273mod tests {
274    use super::*;
275    use std::string::ToString;
276
277    #[test]
278    fn empty() {
279        let sut = ConstantPool::new();
280        assert_eq!(sut.len(), 0);
281    }
282
283    #[test]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
397    fn extend_constant_data_to_invalid_length() {
398        ConstantData::from([1, 2].as_ref()).expand_to(1);
399    }
400
401    #[test]
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]
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]
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]
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            &[0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0]
482        );
483        assert_eq!(Ieee128::try_from(&constant).unwrap().bits(), value.bits());
484    }
485}