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::HashMap;
13 use alloc::collections::BTreeMap;
14 use alloc::vec::Vec;
15 use cranelift_entity::EntityRef;
16 
17 /// This type describes the actual constant data.
18 pub type ConstantData = Vec<u8>;
19 
20 /// This type describes an offset in bytes within a constant pool.
21 pub type ConstantOffset = u32;
22 
23 /// Inner type for storing data and offset together in the constant pool. The offset is optional
24 /// because it must be set relative to the function code size (i.e. constants are emitted after the
25 /// function body); because the function is not yet compiled when constants are inserted,
26 /// [`set_offset`](crate::ir::ConstantPool::set_offset) must be called once a constant's offset
27 /// from the beginning of the function is known (see
28 /// [`relaxation.rs`](crate::binemit::relaxation)).
29 #[derive(Clone)]
30 pub struct ConstantPoolEntry {
31     data: ConstantData,
32     offset: Option<ConstantOffset>,
33 }
34 
35 impl ConstantPoolEntry {
36     fn new(data: ConstantData) -> Self {
37         Self { data, offset: None }
38     }
39 
40     /// Return the size of the constant at this entry.
41     pub fn len(&self) -> usize {
42         self.data.len()
43     }
44 
45     /// Assign a new offset to the constant at this entry.
46     pub fn set_offset(&mut self, offset: ConstantOffset) {
47         self.offset = Some(offset)
48     }
49 }
50 
51 /// Maintains the mapping between a constant handle (i.e.  [`Constant`](crate::ir::Constant)) and
52 /// its constant data (i.e.  [`ConstantData`](crate::ir::ConstantData)).
53 #[derive(Clone)]
54 pub struct ConstantPool {
55     /// This mapping maintains the insertion order as long as Constants are created with
56     /// sequentially increasing integers.
57     handles_to_values: BTreeMap<Constant, ConstantPoolEntry>,
58 
59     /// This mapping is unordered (no need for lexicographic ordering) but allows us to map
60     /// constant data back to handles.
61     values_to_handles: HashMap<ConstantData, Constant>,
62 }
63 
64 impl ConstantPool {
65     /// Create a new constant pool instance.
66     pub fn new() -> Self {
67         Self {
68             handles_to_values: BTreeMap::new(),
69             values_to_handles: HashMap::new(),
70         }
71     }
72 
73     /// Empty the constant pool of all data.
74     pub fn clear(&mut self) {
75         self.handles_to_values.clear();
76         self.values_to_handles.clear();
77     }
78 
79     /// Insert constant data into the pool, returning a handle for later referencing; when constant
80     /// data is inserted that is a duplicate of previous constant data, the existing handle will be
81     /// returned.
82     pub fn insert(&mut self, constant_value: ConstantData) -> Constant {
83         if self.values_to_handles.contains_key(&constant_value) {
84             self.values_to_handles.get(&constant_value).unwrap().clone()
85         } else {
86             let constant_handle = Constant::new(self.len());
87             self.values_to_handles
88                 .insert(constant_value.clone(), constant_handle.clone());
89             self.handles_to_values.insert(
90                 constant_handle.clone(),
91                 ConstantPoolEntry::new(constant_value),
92             );
93             constant_handle
94         }
95     }
96 
97     /// Retrieve the constant data given a handle.
98     pub fn get(&self, constant_handle: Constant) -> &ConstantData {
99         assert!(self.handles_to_values.contains_key(&constant_handle));
100         &self.handles_to_values.get(&constant_handle).unwrap().data
101     }
102 
103     /// Assign an offset to a given constant, where the offset is the number of bytes from the
104     /// beginning of the function to the beginning of the constant data inside the pool.
105     pub fn set_offset(&mut self, constant_handle: Constant, constant_offset: ConstantOffset) {
106         assert!(
107             self.handles_to_values.contains_key(&constant_handle),
108             "A constant handle must have already been inserted into the pool; perhaps a \
109              constant pool was created outside of the pool?"
110         );
111         self.handles_to_values
112             .entry(constant_handle)
113             .and_modify(|e| e.offset = Some(constant_offset));
114     }
115 
116     /// Retrieve the offset of a given constant, where the offset is the number of bytes from the
117     /// beginning of the function to the beginning of the constant data inside the pool.
118     pub fn get_offset(&self, constant_handle: Constant) -> ConstantOffset {
119         self.handles_to_values
120             .get(&constant_handle)
121             .expect(
122                 "A constant handle must have a corresponding constant value; was a constant \
123                  handle created outside of the pool?",
124             )
125             .offset
126             .expect(
127                 "A constant offset has not yet been set; verify that `set_offset` has been \
128                  called before this point",
129             )
130     }
131 
132     /// Iterate over the constants in insertion order.
133     pub fn iter(&self) -> impl Iterator<Item = (&Constant, &ConstantData)> {
134         self.handles_to_values.iter().map(|(h, e)| (h, &e.data))
135     }
136 
137     /// Iterate over mutable entries in the constant pool in insertion order.
138     pub fn entries_mut(&mut self) -> impl Iterator<Item = &mut ConstantPoolEntry> {
139         self.handles_to_values.values_mut()
140     }
141 
142     /// Return the number of constants in the pool.
143     pub fn len(&self) -> usize {
144         self.handles_to_values.len()
145     }
146 
147     /// Return the combined size of all of the constant values in the pool.
148     pub fn byte_size(&self) -> usize {
149         self.values_to_handles.keys().map(|c| c.len()).sum()
150     }
151 }
152 
153 #[cfg(test)]
154 mod tests {
155     use super::*;
156 
157     #[test]
158     fn empty() {
159         let sut = ConstantPool::new();
160         assert_eq!(sut.len(), 0);
161     }
162 
163     #[test]
164     fn insert() {
165         let mut sut = ConstantPool::new();
166         sut.insert(vec![1, 2, 3]);
167         sut.insert(vec![4, 5, 6]);
168         assert_eq!(sut.len(), 2);
169     }
170 
171     #[test]
172     fn insert_duplicate() {
173         let mut sut = ConstantPool::new();
174         let a = sut.insert(vec![1, 2, 3]);
175         sut.insert(vec![4, 5, 6]);
176         let b = sut.insert(vec![1, 2, 3]);
177         assert_eq!(a, b);
178     }
179 
180     #[test]
181     fn clear() {
182         let mut sut = ConstantPool::new();
183         sut.insert(vec![1, 2, 3]);
184         assert_eq!(sut.len(), 1);
185 
186         sut.clear();
187         assert_eq!(sut.len(), 0);
188     }
189 
190     #[test]
191     fn iteration_order() {
192         let mut sut = ConstantPool::new();
193         sut.insert(vec![1, 2, 3]);
194         sut.insert(vec![4, 5, 6]);
195         sut.insert(vec![1, 2, 3]);
196         let data = sut.iter().map(|(_, v)| v).collect::<Vec<&ConstantData>>();
197         assert_eq!(data, vec![&vec![1, 2, 3], &vec![4, 5, 6]]);
198     }
199 
200     #[test]
201     fn get() {
202         let mut sut = ConstantPool::new();
203         let data = vec![1, 2, 3];
204         let handle = sut.insert(data.clone());
205         assert_eq!(sut.get(handle), &data);
206     }
207 
208     #[test]
209     #[should_panic]
210     fn get_nonexistent_constant() {
211         let sut = ConstantPool::new();
212         let a = Constant::with_number(42).unwrap();
213         sut.get(a); // panics, only use constants returned by ConstantPool
214     }
215 
216     #[test]
217     fn get_offset() {
218         let mut sut = ConstantPool::new();
219         let a = sut.insert(vec![1]);
220         sut.set_offset(a, 42);
221         assert_eq!(sut.get_offset(a), 42)
222     }
223 
224     #[test]
225     #[should_panic]
226     fn get_nonexistent_offset() {
227         let mut sut = ConstantPool::new();
228         let a = sut.insert(vec![1]);
229         sut.get_offset(a); // panics, set_offset should have been called
230     }
231 }
232