Path: blob/main/cranelift/codegen/src/ir/constant.rs
1693 views
//! Constants1//!2//! The constant pool defined here allows Cranelift to avoid emitting the same constant multiple3//! times. As constants are inserted in the pool, a handle is returned; the handle is a Cranelift4//! Entity. Inserting the same data multiple times will always return the same handle.5//!6//! Future work could include:7//! - ensuring alignment of constants within the pool,8//! - bucketing constants by size.910use crate::ir::Constant;11use crate::ir::immediates::{Ieee128, IntoBytes, V128Imm};12use alloc::collections::BTreeMap;13use alloc::vec::Vec;14use core::fmt;15use core::slice::Iter;16use core::str::{FromStr, from_utf8};17use cranelift_entity::EntityRef;1819#[cfg(feature = "enable-serde")]20use serde_derive::{Deserialize, Serialize};2122/// This type describes the actual constant data. Note that the bytes stored in this structure are23/// expected to be in little-endian order; this is due to ease-of-use when interacting with24/// WebAssembly values, which are [little-endian by design].25///26/// [little-endian by design]: https://github.com/WebAssembly/design/blob/master/Portability.md27#[derive(Clone, Hash, Eq, PartialEq, Debug, Default, PartialOrd, Ord)]28#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]29pub struct ConstantData(Vec<u8>);3031impl FromIterator<u8> for ConstantData {32fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {33let v = iter.into_iter().collect();34Self(v)35}36}3738impl From<Vec<u8>> for ConstantData {39fn from(v: Vec<u8>) -> Self {40Self(v)41}42}4344impl From<&[u8]> for ConstantData {45fn from(v: &[u8]) -> Self {46Self(v.to_vec())47}48}4950impl From<V128Imm> for ConstantData {51fn from(v: V128Imm) -> Self {52Self(v.to_vec())53}54}5556impl From<Ieee128> for ConstantData {57fn from(v: Ieee128) -> Self {58Self(v.into_bytes())59}60}6162impl TryFrom<&ConstantData> for Ieee128 {63type Error = <[u8; 16] as TryFrom<&'static [u8]>>::Error;6465fn try_from(value: &ConstantData) -> Result<Self, Self::Error> {66Ok(Ieee128::with_bits(u128::from_le_bytes(67value.as_slice().try_into()?,68)))69}70}7172impl ConstantData {73/// Return the number of bytes in the constant.74pub fn len(&self) -> usize {75self.0.len()76}7778/// Check if the constant contains any bytes.79pub fn is_empty(&self) -> bool {80self.0.is_empty()81}8283/// Return the data as a slice.84pub fn as_slice(&self) -> &[u8] {85self.0.as_slice()86}8788/// Convert the data to a vector.89pub fn into_vec(self) -> Vec<u8> {90self.091}9293/// Iterate over the constant's bytes.94pub fn iter(&self) -> Iter<'_, u8> {95self.0.iter()96}9798/// Add new bytes to the constant data.99pub fn append(mut self, bytes: impl IntoBytes) -> Self {100let mut to_add = bytes.into_bytes();101self.0.append(&mut to_add);102self103}104105/// Expand the size of the constant data to `expected_size` number of bytes by adding zeroes106/// in the high-order byte slots.107pub fn expand_to(mut self, expected_size: usize) -> Self {108if self.len() > expected_size {109panic!("The constant data is already expanded beyond {expected_size} bytes")110}111self.0.resize(expected_size, 0);112self113}114}115116impl fmt::Display for ConstantData {117/// Print the constant data in hexadecimal format, e.g. 0x000102030405060708090a0b0c0d0e0f.118/// This function will flip the stored order of bytes--little-endian--to the more readable119/// big-endian ordering.120///121/// ```122/// use cranelift_codegen::ir::ConstantData;123/// let data = ConstantData::from([3, 2, 1, 0, 0].as_ref()); // note the little-endian order124/// assert_eq!(data.to_string(), "0x0000010203");125/// ```126fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {127if !self.is_empty() {128write!(f, "0x")?;129for b in self.0.iter().rev() {130write!(f, "{b:02x}")?;131}132}133Ok(())134}135}136137impl FromStr for ConstantData {138type Err = &'static str;139140/// Parse a hexadecimal string to `ConstantData`. This is the inverse of `Display::fmt`.141///142/// ```143/// use cranelift_codegen::ir::ConstantData;144/// let c: ConstantData = "0x000102".parse().unwrap();145/// assert_eq!(c.into_vec(), [2, 1, 0]);146/// ```147fn from_str(s: &str) -> Result<Self, &'static str> {148if s.len() <= 2 || &s[0..2] != "0x" {149return Err("Expected a hexadecimal string, e.g. 0x1234");150}151152// clean and check the string153let cleaned: Vec<u8> = s[2..]154.as_bytes()155.iter()156.filter(|&&b| b as char != '_')157.cloned()158.collect(); // remove 0x prefix and any intervening _ characters159160if cleaned.is_empty() {161Err("Hexadecimal string must have some digits")162} else if cleaned.len() % 2 != 0 {163Err("Hexadecimal string must have an even number of digits")164} else if cleaned.len() > 32 {165Err("Hexadecimal string has too many digits to fit in a 128-bit vector")166} else {167let mut buffer = Vec::with_capacity((s.len() - 2) / 2);168for i in (0..cleaned.len()).step_by(2) {169let pair = from_utf8(&cleaned[i..i + 2])170.or_else(|_| Err("Unable to parse hexadecimal pair as UTF-8"))?;171let byte = u8::from_str_radix(pair, 16)172.or_else(|_| Err("Unable to parse as hexadecimal"))?;173buffer.insert(0, byte);174}175Ok(Self(buffer))176}177}178}179180/// Maintains the mapping between a constant handle (i.e. [`Constant`]) and181/// its constant data (i.e. [`ConstantData`]).182#[derive(Clone, PartialEq, Hash)]183#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]184pub struct ConstantPool {185/// This mapping maintains the insertion order as long as Constants are created with186/// sequentially increasing integers.187///188/// It is important that, by construction, no entry in that list gets removed. If that ever189/// need to happen, don't forget to update the `Constant` generation scheme.190handles_to_values: BTreeMap<Constant, ConstantData>,191192/// Mapping of hashed `ConstantData` to the index into the other hashmap.193///194/// This allows for deduplication of entries into the `handles_to_values` mapping.195values_to_handles: BTreeMap<ConstantData, Constant>,196}197198impl ConstantPool {199/// Create a new constant pool instance.200pub fn new() -> Self {201Self {202handles_to_values: BTreeMap::new(),203values_to_handles: BTreeMap::new(),204}205}206207/// Empty the constant pool of all data.208pub fn clear(&mut self) {209self.handles_to_values.clear();210self.values_to_handles.clear();211}212213/// Insert constant data into the pool, returning a handle for later referencing; when constant214/// data is inserted that is a duplicate of previous constant data, the existing handle will be215/// returned.216pub fn insert(&mut self, constant_value: ConstantData) -> Constant {217if let Some(cst) = self.values_to_handles.get(&constant_value) {218return *cst;219}220221let constant_handle = Constant::new(self.len());222self.set(constant_handle, constant_value);223constant_handle224}225226/// Retrieve the constant data given a handle.227pub fn get(&self, constant_handle: Constant) -> &ConstantData {228assert!(self.handles_to_values.contains_key(&constant_handle));229self.handles_to_values.get(&constant_handle).unwrap()230}231232/// Link a constant handle to its value. This does not de-duplicate data but does avoid233/// replacing any existing constant values. use `set` to tie a specific `const42` to its value;234/// use `insert` to add a value and return the next available `const` entity.235pub fn set(&mut self, constant_handle: Constant, constant_value: ConstantData) {236let replaced = self237.handles_to_values238.insert(constant_handle, constant_value.clone());239assert!(240replaced.is_none(),241"attempted to overwrite an existing constant {:?}: {:?} => {:?}",242constant_handle,243&constant_value,244replaced.unwrap()245);246self.values_to_handles247.insert(constant_value, constant_handle);248}249250/// Iterate over the constants in insertion order.251pub fn iter(&self) -> impl Iterator<Item = (&Constant, &ConstantData)> {252self.handles_to_values.iter()253}254255/// Iterate over mutable entries in the constant pool in insertion order.256pub fn entries_mut(&mut self) -> impl Iterator<Item = &mut ConstantData> {257self.handles_to_values.values_mut()258}259260/// Return the number of constants in the pool.261pub fn len(&self) -> usize {262self.handles_to_values.len()263}264265/// Return the combined size of all of the constant values in the pool.266pub fn byte_size(&self) -> usize {267self.handles_to_values.values().map(|c| c.len()).sum()268}269}270271#[cfg(test)]272mod tests {273use super::*;274use std::string::ToString;275276#[test]277fn empty() {278let sut = ConstantPool::new();279assert_eq!(sut.len(), 0);280}281282#[test]283fn insert() {284let mut sut = ConstantPool::new();285sut.insert(vec![1, 2, 3].into());286sut.insert(vec![4, 5, 6].into());287assert_eq!(sut.len(), 2);288}289290#[test]291fn insert_duplicate() {292let mut sut = ConstantPool::new();293let a = sut.insert(vec![1, 2, 3].into());294sut.insert(vec![4, 5, 6].into());295let b = sut.insert(vec![1, 2, 3].into());296assert_eq!(a, b);297}298299#[test]300fn clear() {301let mut sut = ConstantPool::new();302sut.insert(vec![1, 2, 3].into());303assert_eq!(sut.len(), 1);304305sut.clear();306assert_eq!(sut.len(), 0);307}308309#[test]310fn iteration_order() {311let mut sut = ConstantPool::new();312sut.insert(vec![1, 2, 3].into());313sut.insert(vec![4, 5, 6].into());314sut.insert(vec![1, 2, 3].into());315let data = sut.iter().map(|(_, v)| v).collect::<Vec<&ConstantData>>();316assert_eq!(data, vec![&vec![1, 2, 3].into(), &vec![4, 5, 6].into()]);317}318319#[test]320fn get() {321let mut sut = ConstantPool::new();322let data = vec![1, 2, 3];323let handle = sut.insert(data.clone().into());324assert_eq!(sut.get(handle), &data.into());325}326327#[test]328fn set() {329let mut sut = ConstantPool::new();330let handle = Constant::with_number(42).unwrap();331let data = vec![1, 2, 3];332sut.set(handle, data.clone().into());333assert_eq!(sut.get(handle), &data.into());334}335336#[test]337#[should_panic]338fn disallow_overwriting_constant() {339let mut sut = ConstantPool::new();340let handle = Constant::with_number(42).unwrap();341sut.set(handle, vec![].into());342sut.set(handle, vec![1].into());343}344345#[test]346#[should_panic]347fn get_nonexistent_constant() {348let sut = ConstantPool::new();349let a = Constant::with_number(42).unwrap();350sut.get(a); // panics, only use constants returned by ConstantPool351}352353#[test]354fn display_constant_data() {355assert_eq!(ConstantData::from([0].as_ref()).to_string(), "0x00");356assert_eq!(ConstantData::from([42].as_ref()).to_string(), "0x2a");357assert_eq!(358ConstantData::from([3, 2, 1, 0].as_ref()).to_string(),359"0x00010203"360);361assert_eq!(362ConstantData::from(3735928559u32.to_le_bytes().as_ref()).to_string(),363"0xdeadbeef"364);365assert_eq!(366ConstantData::from(0x0102030405060708u64.to_le_bytes().as_ref()).to_string(),367"0x0102030405060708"368);369}370371#[test]372fn iterate_over_constant_data() {373let c = ConstantData::from([1, 2, 3].as_ref());374let mut iter = c.iter();375assert_eq!(iter.next(), Some(&1));376assert_eq!(iter.next(), Some(&2));377assert_eq!(iter.next(), Some(&3));378assert_eq!(iter.next(), None);379}380381#[test]382fn add_to_constant_data() {383let d = ConstantData::from([1, 2].as_ref());384let e = d.append(i16::from(3u8));385assert_eq!(e.into_vec(), vec![1, 2, 3, 0])386}387388#[test]389fn extend_constant_data() {390let d = ConstantData::from([1, 2].as_ref());391assert_eq!(d.expand_to(4).into_vec(), vec![1, 2, 0, 0])392}393394#[test]395#[should_panic]396fn extend_constant_data_to_invalid_length() {397ConstantData::from([1, 2].as_ref()).expand_to(1);398}399400#[test]401fn parse_constant_data_and_restringify() {402// Verify that parsing of `from` succeeds and stringifies to `to`.403fn parse_ok(from: &str, to: &str) {404let parsed = from.parse::<ConstantData>().unwrap();405assert_eq!(parsed.to_string(), to);406}407408// Verify that parsing of `from` fails with `error_msg`.409fn parse_err(from: &str, error_msg: &str) {410let parsed = from.parse::<ConstantData>();411assert!(412parsed.is_err(),413"Expected a parse error but parsing succeeded: {from}"414);415assert_eq!(parsed.err().unwrap(), error_msg);416}417418parse_ok("0x00", "0x00");419parse_ok("0x00000042", "0x00000042");420parse_ok(421"0x0102030405060708090a0b0c0d0e0f00",422"0x0102030405060708090a0b0c0d0e0f00",423);424parse_ok("0x_0000_0043_21", "0x0000004321");425426parse_err("", "Expected a hexadecimal string, e.g. 0x1234");427parse_err("0x", "Expected a hexadecimal string, e.g. 0x1234");428parse_err(429"0x042",430"Hexadecimal string must have an even number of digits",431);432parse_err(433"0x00000000000000000000000000000000000000000000000000",434"Hexadecimal string has too many digits to fit in a 128-bit vector",435);436parse_err("0xrstu", "Unable to parse as hexadecimal");437parse_err("0x__", "Hexadecimal string must have some digits");438}439440#[test]441fn verify_stored_bytes_in_constant_data() {442assert_eq!("0x01".parse::<ConstantData>().unwrap().into_vec(), [1]);443assert_eq!(ConstantData::from([1, 0].as_ref()).0, [1, 0]);444assert_eq!(ConstantData::from(vec![1, 0, 0, 0]).0, [1, 0, 0, 0]);445}446447#[test]448fn check_constant_data_endianness_as_uimm128() {449fn parse_to_uimm128(from: &str) -> Vec<u8> {450from.parse::<ConstantData>()451.unwrap()452.expand_to(16)453.into_vec()454}455456assert_eq!(457parse_to_uimm128("0x42"),458[0x42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]459);460assert_eq!(461parse_to_uimm128("0x00"),462[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]463);464assert_eq!(465parse_to_uimm128("0x12345678"),466[0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]467);468assert_eq!(469parse_to_uimm128("0x1234_5678"),470[0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]471);472}473474#[test]475fn constant_ieee128() {476let value = Ieee128::with_bits(0x000102030405060708090a0b0c0d0e0f);477let constant = ConstantData::from(value);478assert_eq!(479constant.as_slice(),480&[4810xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0482]483);484assert_eq!(Ieee128::try_from(&constant).unwrap().bits(), value.bits());485}486}487488489