midenc_hir/
constants.rs

1use std::{collections::BTreeMap, fmt, str::FromStr, sync::Arc};
2
3use cranelift_entity::{entity_impl, EntityRef};
4
5pub trait IntoBytes {
6    fn into_bytes(self) -> Vec<u8>;
7}
8impl IntoBytes for Vec<u8> {
9    #[inline(always)]
10    fn into_bytes(self) -> Vec<u8> {
11        self
12    }
13}
14impl IntoBytes for i8 {
15    #[inline]
16    fn into_bytes(self) -> Vec<u8> {
17        vec![self as u8]
18    }
19}
20impl IntoBytes for i16 {
21    #[inline]
22    fn into_bytes(self) -> Vec<u8> {
23        self.to_le_bytes().to_vec()
24    }
25}
26
27/// A handle to a constant
28#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub struct Constant(u32);
30entity_impl!(Constant, "const");
31
32/// This type represents the raw data of a constant.
33///
34/// The data is expected to be in little-endian order.
35#[derive(Debug, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37pub struct ConstantData(#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] Vec<u8>);
38impl ConstantData {
39    /// Return the number of bytes in the constant.
40    pub fn len(&self) -> usize {
41        self.0.len()
42    }
43
44    /// Check if the constant contains any bytes.
45    pub fn is_empty(&self) -> bool {
46        self.0.is_empty()
47    }
48
49    /// Return the data as a slice.
50    pub fn as_slice(&self) -> &[u8] {
51        self.0.as_slice()
52    }
53
54    /// Append bytes to this constant
55    pub fn append(mut self, bytes: impl IntoBytes) -> Self {
56        let mut bytes = bytes.into_bytes();
57        self.0.append(&mut bytes);
58        self
59    }
60
61    /// Grow the size of the constant data in bytes to `expected_size`, zero-extending
62    /// the data by writing zeroes to the newly-added high-order bytes.
63    pub fn zext(mut self, expected_size: usize) -> Self {
64        assert!(
65            self.len() <= expected_size,
66            "the constant is already larger than {} bytes",
67            expected_size
68        );
69        self.0.resize(expected_size, 0);
70        self
71    }
72
73    /// Attempt to convert this constant data to a `u32` value
74    pub fn as_u32(&self) -> Option<u32> {
75        let bytes = self.as_slice();
76        if bytes.len() != 4 {
77            return None;
78        }
79        let bytes = bytes.as_ptr() as *const [u8; 4];
80        Some(u32::from_le_bytes(unsafe { bytes.read() }))
81    }
82}
83impl From<ConstantData> for Vec<u8> {
84    #[inline(always)]
85    fn from(data: ConstantData) -> Self {
86        data.0
87    }
88}
89impl FromIterator<u8> for ConstantData {
90    fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
91        Self(iter.into_iter().collect())
92    }
93}
94impl From<Vec<u8>> for ConstantData {
95    fn from(v: Vec<u8>) -> Self {
96        Self(v)
97    }
98}
99impl<const N: usize> From<[u8; N]> for ConstantData {
100    fn from(v: [u8; N]) -> Self {
101        Self(v.to_vec())
102    }
103}
104impl From<&[u8]> for ConstantData {
105    fn from(v: &[u8]) -> Self {
106        Self(v.to_vec())
107    }
108}
109impl fmt::Display for ConstantData {
110    /// Print the constant data in hexadecimal format, e.g. 0x000102030405060708090a0b0c0d0e0f.
111    ///
112    /// The printed form of the constant renders the bytes in big-endian order, for readability.
113    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114        fmt::LowerHex::fmt(self, f)
115    }
116}
117impl fmt::LowerHex for ConstantData {
118    /// Print the constant data in hexadecimal format, e.g. 0x000102030405060708090a0b0c0d0e0f.
119    ///
120    /// The printed form of the constant renders the bytes in the same order as the data.
121    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122        if !self.is_empty() {
123            if f.alternate() {
124                f.write_str("0x")?;
125            }
126            for byte in self.0.iter().rev() {
127                write!(f, "{byte:02x}")?;
128            }
129        }
130        Ok(())
131    }
132}
133impl FromStr for ConstantData {
134    type Err = ();
135
136    #[inline]
137    fn from_str(s: &str) -> Result<Self, Self::Err> {
138        Self::from_str_be(s).map_err(|_| ())
139    }
140}
141impl ConstantData {
142    pub fn from_str_be(s: &str) -> Result<Self, &'static str> {
143        const NOT_EVEN: &str = "invalid hex-encoded data: expected an even number of hex digits";
144        const NOT_HEX: &str = "invalid hex-encoded data: contains invalid hex digits";
145
146        let s = s.strip_prefix("0x").unwrap_or(s);
147        let len = s.len();
148        if len % 2 != 0 {
149            return Err(NOT_EVEN);
150        }
151        // Parse big-endian
152        let pairs = len / 2;
153        let mut data = Vec::with_capacity(pairs);
154        let mut chars = s.chars();
155        while let Some(a) = chars.next() {
156            let a = a.to_digit(16).ok_or(NOT_HEX)?;
157            let b = chars.next().unwrap().to_digit(16).ok_or(NOT_HEX)?;
158            data.push(((a << 4) + b) as u8);
159        }
160
161        Ok(Self(data))
162    }
163
164    pub fn from_str_le(s: &str) -> Result<Self, &'static str> {
165        let mut data = Self::from_str_be(s)?;
166        // Make little-endian
167        data.0.reverse();
168        Ok(data)
169    }
170}
171
172/// This maintains the storage for constants used within a function
173#[derive(Default)]
174pub struct ConstantPool {
175    /// This mapping maintains the insertion order as long as Constants are created with
176    /// sequentially increasing integers.
177    ///
178    /// It is important that, by construction, no entry in that list gets removed. If that ever
179    /// need to happen, don't forget to update the `Constant` generation scheme.
180    constants: BTreeMap<Constant, Arc<ConstantData>>,
181
182    /// Mapping of hashed `ConstantData` to the index into the other hashmap.
183    ///
184    /// This allows for deduplication of entries into the `handles_to_values` mapping.
185    cache: BTreeMap<Arc<ConstantData>, Constant>,
186}
187impl ConstantPool {
188    /// Returns true if the pool is empty
189    pub fn is_empty(&self) -> bool {
190        self.constants.is_empty()
191    }
192
193    /// Returns the number of constants in this pool
194    pub fn len(&self) -> usize {
195        self.constants.len()
196    }
197
198    /// Retrieve the constant data as a reference-counted pointer, given a handle.
199    pub fn get(&self, id: Constant) -> Arc<ConstantData> {
200        Arc::clone(&self.constants[&id])
201    }
202
203    /// Retrieve the constant data by reference given a handle.
204    pub fn get_by_ref(&self, id: Constant) -> &ConstantData {
205        self.constants[&id].as_ref()
206    }
207
208    /// Returns true if this pool contains the given constant data
209    pub fn contains(&self, data: &ConstantData) -> bool {
210        self.cache.contains_key(data)
211    }
212
213    /// Insert constant data into the pool, returning a handle for later referencing; when constant
214    /// data is inserted that is a duplicate of previous constant data, the existing handle will be
215    /// returned.
216    pub fn insert(&mut self, data: ConstantData) -> Constant {
217        if let Some(cst) = self.cache.get(&data) {
218            return *cst;
219        }
220
221        let data = Arc::new(data);
222        let id = Constant::new(self.len());
223        self.constants.insert(id, Arc::clone(&data));
224        self.cache.insert(data, id);
225        id
226    }
227
228    /// Same as [ConstantPool::insert], but for data already allocated in an [Arc].
229    pub fn insert_arc(&mut self, data: Arc<ConstantData>) -> Constant {
230        if let Some(cst) = self.cache.get(data.as_ref()) {
231            return *cst;
232        }
233
234        let id = Constant::new(self.len());
235        self.constants.insert(id, Arc::clone(&data));
236        self.cache.insert(data, id);
237        id
238    }
239
240    /// Traverse the contents of the pool
241    #[inline]
242    pub fn iter(&self) -> impl Iterator<Item = (Constant, Arc<ConstantData>)> + '_ {
243        self.constants.iter().map(|(k, v)| (*k, Arc::clone(v)))
244    }
245}