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