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