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