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