use std::{ collections::BTreeMap, fmt::Display, hash::{Hash, Hasher}, }; use num_bigint::{BigInt, BigUint, Sign}; use crate::{ ast::IntegralType, common::{from_lo_hi_dwords, into_lo_hi_dwords}, variant, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[repr(u8)] pub enum SimpleType { F32 = 0, F64, Bool, Void, USize, ISize, ComptimeInt, } impl From for SimpleType { fn from(value: u8) -> Self { match value { 0 => Self::F32, 1 => Self::F64, 2 => Self::Bool, 3 => Self::Void, 4 => Self::USize, 5 => Self::ISize, 6 => Self::ComptimeInt, _ => panic!("{value} is not a simple type"), } } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Tag { String, SIntSmall, UIntSmall, TrueValue, FalseValue, UInt64, SInt64, F32, F64, PositiveInt, NegativeInt, UIntType, SIntType, SimpleType, PointerType, ArrayType, FunctionType, StructType, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] struct Item { tag: Tag, index: u32, } #[derive(Debug, Clone, PartialEq)] #[non_exhaustive] pub enum Key<'a> { String { str: &'a str, }, SIntSmall { bits: i32, }, UIntSmall { bits: u32, }, SInt64 { bits: i64, }, UInt64 { bits: u64, }, F32 { bits: f32, }, F64 { bits: f64, }, PositiveInt { bigint: BigInt, }, NegativeInt { bigint: BigInt, }, UIntType { bit_width: u16, }, SIntType { bit_width: u16, }, SimpleType { ty: SimpleType, }, PointerType { pointee: Index, flags: PointerFlags, }, ArrayType { pointee: Index, flags: PointerFlags, length: u32, }, FunctionType { return_type: Index, parameters: Vec, }, StructType { decl: super::Index, name: Index, packed: bool, c_like: bool, /// vec of (Name, Type) fields: Vec<(Index, Index)>, }, TrueValue, FalseValue, } impl Hash for Key<'_> { fn hash(&self, state: &mut H) { core::mem::discriminant(self).hash(state); match self { Key::String { str } => str.hash(state), Key::SIntSmall { bits } => bits.hash(state), Key::UIntSmall { bits } => bits.hash(state), Key::SInt64 { bits } => bits.hash(state), Key::UInt64 { bits } => bits.hash(state), Key::F32 { bits } => ordered_float::OrderedFloat(*bits).hash(state), Key::F64 { bits } => ordered_float::OrderedFloat(*bits).hash(state), Key::PositiveInt { bigint } => bigint.hash(state), Key::NegativeInt { bigint } => bigint.hash(state), Key::UIntType { bit_width: bits } => bits.hash(state), Key::SIntType { bit_width: bits } => bits.hash(state), Key::SimpleType { ty } => ty.hash(state), Key::PointerType { pointee, flags } => (pointee, flags).hash(state), Key::ArrayType { pointee, flags, length, } => (*pointee, *flags, *length).hash(state), Key::StructType { name, decl, .. } => (*name, *decl).hash(state), Key::FunctionType { return_type, parameters, } => (return_type, parameters).hash(state), Key::TrueValue | Key::FalseValue => {} } } } // #[repr(packed)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] pub struct PointerFlags { pub volatile: bool, pub is_const: bool, pub noalias: bool, } impl PointerFlags { pub fn new(is_const: bool, volatile: bool, noalias: bool) -> Self { Self { is_const, volatile, noalias, } } pub fn union(self, other: Self) -> Self { Self::new( self.is_const || other.is_const, self.volatile || other.volatile, self.noalias || other.noalias, ) } pub fn pack(self) -> u8 { (self.volatile as u8) << 0 | (self.is_const as u8) << 1 | (self.noalias as u8) << 2 } pub fn unpack(packed: u8) -> Self { Self { volatile: packed & (1 << 0) != 0, is_const: packed & (1 << 1) != 0, noalias: packed & (1 << 2) != 0, } } } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] pub struct StructFlags { pub packed: bool, pub c_like: bool, pub num_fields: u32, } impl StructFlags { const MASK: u32 = (1u32 << 30) - 1; pub fn new(packed: bool, c_like: bool, num_fields: u32) -> Self { assert!(num_fields < (1 << 30)); Self { packed, c_like, num_fields, } } pub fn pack(self) -> u32 { assert!(self.num_fields < (1 << 30)); (self.packed as u32) << 31 | (self.c_like as u32) << 30 | self.num_fields & Self::MASK } pub fn unpack(packed: u32) -> Self { Self { packed: packed & (1 << 31) != 0, c_like: packed & (1 << 30) != 0, num_fields: packed & Self::MASK, } } } #[derive(Debug, Clone, Copy)] struct FunctionInfo { void_return: bool, num_params: u32, } impl FunctionInfo { fn new(void_return: bool, num_params: u32) -> Self { Self { void_return, num_params, } } const MASK: u32 = 1u32 << (u32::BITS - 1); fn pack(self) -> u32 { (self.void_return as u32 * Self::MASK) | self.num_params & !Self::MASK } fn unpack(packed: u32) -> Self { Self { void_return: packed & Self::MASK != 0, num_params: packed & !Self::MASK, } } fn len(self) -> u32 { self.void_return as u32 + self.num_params } } impl Item { fn idx(self) -> usize { self.index as usize } } #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Index(pub u32); impl Display for Index { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "#{}", self.0) } } impl Index { pub fn into_u32(self) -> u32 { unsafe { core::mem::transmute(self) } } pub fn as_u32(&self) -> &u32 { unsafe { core::mem::transmute(self) } } fn index(&self) -> usize { self.0 as usize } pub fn is_valid(&self) -> bool { self.0 != u32::MAX } pub fn invalid() -> Self { Self(u32::MAX) } } pub struct InternPool { tags: Vec, indices: Vec, // strings: Vec, words: Vec, hashed: BTreeMap, } impl std::fmt::Debug for InternPool { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("InternPool") .field_with("keys", |f| { let mut list = f.debug_list(); let keys = (0..self.indices.len()) .map(|i| Index(i as u32)) .map(|idx| (idx, self.get_key(idx))); for (idx, key) in keys { list.entry_with(|f| write!(f, "{}: {key:?}", idx.0)); } list.finish() }) .field_with("hashed", |f| { let mut list = f.debug_list(); for (hash, idx) in self.hashed.iter() { list.entry_with(|f| write!(f, "{hash}: {}", idx.0)); } list.finish() }) .finish_non_exhaustive() } } macro_rules! static_keys { ($($name:ident => $def:expr),* $(,)?) => { impl Index { $( pub const $name: Self = Self(${index()}); )* } const STATIC_KEYS: [Key; ${count($def)}] = [ $($def),* ]; }; } static_keys!( BOOL => Key::SimpleType {ty: SimpleType::Bool,}, F32 => Key::SimpleType {ty: SimpleType::F32,}, F64 => Key::SimpleType {ty: SimpleType::F64,}, USIZE => Key::SimpleType {ty: SimpleType::USize,}, ISIZE => Key::SimpleType {ty: SimpleType::ISize,}, VOID => Key::SimpleType {ty: SimpleType::Void,}, COMPTIME_INT => Key::SimpleType {ty: SimpleType::ComptimeInt,}, I1 => Key::SIntType { bit_width: 1 }, U1 => Key::UIntType { bit_width: 1 }, I0 => Key::SIntType { bit_width: 0 }, U0 => Key::UIntType { bit_width: 0 }, I8 => Key::SIntType { bit_width: 8 }, U8 => Key::UIntType { bit_width: 8 }, I16 => Key::SIntType { bit_width: 16 }, U16 => Key::UIntType { bit_width: 16 }, I32 => Key::SIntType { bit_width: 32 }, U32 => Key::UIntType { bit_width: 32 }, I64 => Key::SIntType { bit_width: 64 }, U64 => Key::UIntType { bit_width: 64 }, I128 => Key::SIntType { bit_width: 128 }, U128 => Key::UIntType { bit_width: 128 }, TRUE => Key::TrueValue, FALSE => Key::FalseValue, ); impl InternPool { pub fn get_void_type(&self) -> Index { self.get_assume_present(&Key::SimpleType { ty: SimpleType::Void, }) } pub fn get_bool_type(&self) -> Index { self.get_assume_present(&Key::SimpleType { ty: SimpleType::Bool, }) } pub fn get_true_value(&self) -> Index { self.get_assume_present(&Key::TrueValue) } pub fn get_false_value(&self) -> Index { self.get_assume_present(&Key::FalseValue) } pub fn get_f32_type(&self) -> Index { self.get_assume_present(&Key::SimpleType { ty: SimpleType::F32, }) } pub fn get_f64_type(&self) -> Index { self.get_assume_present(&Key::SimpleType { ty: SimpleType::F64, }) } pub fn get_comptime_int_type(&self) -> Index { self.get_assume_present(&Key::SimpleType { ty: SimpleType::ComptimeInt, }) } pub fn get_usize_type(&self) -> Index { self.get_assume_present(&Key::SimpleType { ty: SimpleType::USize, }) } pub fn get_isize_type(&self) -> Index { self.get_assume_present(&Key::SimpleType { ty: SimpleType::ISize, }) } pub fn get_u0_type(&self) -> Index { self.get_assume_present(&Key::UIntType { bit_width: 0 }) } pub fn get_i0_type(&self) -> Index { self.get_assume_present(&Key::SIntType { bit_width: 0 }) } pub fn get_u1_type(&self) -> Index { self.get_assume_present(&Key::UIntType { bit_width: 1 }) } pub fn get_i1_type(&self) -> Index { self.get_assume_present(&Key::SIntType { bit_width: 1 }) } pub fn get_u8_type(&self) -> Index { self.get_assume_present(&Key::UIntType { bit_width: 8 }) } pub fn get_i8_type(&self) -> Index { self.get_assume_present(&Key::SIntType { bit_width: 8 }) } pub fn get_u16_type(&self) -> Index { self.get_assume_present(&Key::UIntType { bit_width: 16 }) } pub fn get_i16_type(&self) -> Index { self.get_assume_present(&Key::SIntType { bit_width: 16 }) } pub fn get_u32_type(&self) -> Index { self.get_assume_present(&Key::UIntType { bit_width: 32 }) } pub fn get_i32_type(&self) -> Index { self.get_assume_present(&Key::SIntType { bit_width: 32 }) } pub fn get_u64_type(&self) -> Index { self.get_assume_present(&Key::UIntType { bit_width: 64 }) } pub fn get_i64_type(&self) -> Index { self.get_assume_present(&Key::SIntType { bit_width: 64 }) } } #[derive(Debug, Clone, Copy)] pub struct TypeInfo { pub bitsize: u32, pub bitalign: u32, pub signed: bool, } impl InternPool { pub fn peer_type(&mut self, lhs: Index, rhs: Index) -> Option { if lhs == rhs { return Some(rhs); } let lt = self.get_key(lhs); let rt = self.get_key(rhs); match (lt, rt) { ( Key::SimpleType { ty: SimpleType::ComptimeInt, }, Key::SimpleType { ty: SimpleType::F32, } | Key::SimpleType { ty: SimpleType::F64, } | Key::SimpleType { ty: SimpleType::USize, } | Key::SimpleType { ty: SimpleType::ISize, } | Key::SIntType { .. } | Key::UIntType { .. }, ) => Some(rhs), ( Key::SimpleType { ty: SimpleType::F32, } | Key::SimpleType { ty: SimpleType::F64, } | Key::SimpleType { ty: SimpleType::USize, } | Key::SimpleType { ty: SimpleType::ISize, } | Key::SIntType { .. } | Key::UIntType { .. }, Key::SimpleType { ty: SimpleType::ComptimeInt, }, ) => Some(lhs), ( Key::PointerType { pointee: lp, flags: lf, }, Key::PointerType { pointee: rp, flags: rf, }, ) => { if lp == rp { Some(self.get_pointer_type(lp, Some(lf.union(rf)))) } else { None } } _ => None, } } pub fn to_mir_type( &self, index: Index, _ptr_size: TypeInfo, ) -> crate::mir::Type { use crate::mir::Type; match self.get_key(index) { Key::UIntType { bit_width: bits } => { let bits = bits as u32; Type::from_bitsize_int(bits) } Key::SIntType { bit_width: bits } => { let bits = bits as u32; Type::from_bitsize_int(bits) } Key::SimpleType { ty } => match ty { SimpleType::F32 => Type::SinglePrecision, SimpleType::F64 => Type::DoublePrecision, SimpleType::Bool => Type::Byte, SimpleType::Void => { todo!("void can't be turned into a mir type") } SimpleType::ISize | SimpleType::USize => Type::QWord, SimpleType::ComptimeInt => { panic!("comptime int can't be turned into a mir type") } }, Key::ArrayType { .. } => { panic!("arrays can't be directly accessed in mir") } Key::FunctionType { .. } | Key::PointerType { .. } => Type::QWord, Key::StructType { .. } => { panic!("arrays can't be directly accessed in mir") } _ => { panic!("index was not a type") } } } pub fn is_type_signed(&self, index: Index, _ptr_size: TypeInfo) -> bool { match self.get_key(index) { Key::UIntType { .. } => false, Key::SIntType { .. } => true, Key::SimpleType { ty } => match ty { SimpleType::USize => false, SimpleType::ISize => true, _ => false, }, Key::PointerType { .. } => false, Key::ArrayType { .. } => false, Key::FunctionType { .. } => false, Key::StructType { .. } => false, _ => false, } } pub fn size_of_type(&self, index: Index, ptr_size: TypeInfo) -> TypeInfo { match self.get_key(index) { Key::UIntType { bit_width: bits } => { let bits = bits as u32; TypeInfo { bitsize: bits, bitalign: bits.next_multiple_of(8).next_power_of_two(), signed: false, } } Key::SIntType { bit_width: bits } => { let bits = bits as u32; TypeInfo { bitsize: bits, bitalign: bits.next_multiple_of(8).next_power_of_two(), signed: true, } } Key::SimpleType { ty } => match ty { SimpleType::F32 => TypeInfo { bitsize: 32, bitalign: 32, signed: true, }, SimpleType::F64 => TypeInfo { bitsize: 64, bitalign: 64, signed: true, }, SimpleType::Bool => TypeInfo { bitsize: 1, bitalign: 1, signed: false, }, SimpleType::Void => TypeInfo { bitsize: 0, bitalign: 0, signed: false, }, SimpleType::USize => ptr_size, SimpleType::ISize => ptr_size, SimpleType::ComptimeInt => panic!("comptime int is unsized"), }, Key::PointerType { .. } => ptr_size, Key::ArrayType { pointee, length, .. } => { let element_size = self.size_of_type(pointee, ptr_size); let bitsize = element_size.bitalign * length; TypeInfo { bitsize, signed: false, ..element_size } } Key::FunctionType { .. } => ptr_size, Key::StructType { packed, fields, .. } => { // TODO: c-like layout let (size, align) = fields.iter().fold((0, 0), |(size, align), (_name, ty)| { let field_size = self.size_of_type(*ty, ptr_size); let size = size + field_size.bitsize; let size = if packed { size.next_multiple_of(field_size.bitalign) } else { size }; let align = align.max(field_size.bitalign); (size, align) }); TypeInfo { bitsize: size, bitalign: align, signed: false, } } _ => { panic!("index was not a type") } } } pub fn from_ast1_type( &mut self, pointer_bits: u16, ty: &crate::ast::Type, ) -> Index { match ty { crate::ast::Type::Bool => self.get_bool_type(), crate::ast::Type::ComptimeNumber => self.get_comptime_int_type(), crate::ast::Type::Integer(i) => self.get_assume_present(&{ if i.signed { Key::SIntType { bit_width: i.bits } } else { Key::UIntType { bit_width: i.bits } } }), crate::ast::Type::Floating(crate::ast::FloatingType::Binary32) => { self.get_f32_type() } crate::ast::Type::Floating(crate::ast::FloatingType::Binary64) => { self.get_f64_type() } crate::ast::Type::Pointer { constness, pointee } => { let pointee = self.from_ast1_type(pointer_bits, &pointee); self.get_or_insert(Key::PointerType { pointee, flags: PointerFlags::new(*constness, false, false), }) } crate::ast::Type::Fn { parameter_types, return_type, } => { let parameters = parameter_types .iter() .map(|ty| self.from_ast1_type(pointer_bits, ty)) .collect::>(); let return_type = self.from_ast1_type(pointer_bits, &return_type); self.get_function_type(return_type, parameters) } crate::ast::Type::Void => self.get_void_type(), _ => { todo!() } } } pub fn as_ast1_type( &self, pointer_bits: u16, index: Index, ) -> crate::ast::Type { use crate::ast::Type; match self.get_key(index) { Key::UIntType { bit_width: bits } => { Type::Integer(IntegralType::new(false, bits)) } Key::SIntType { bit_width: bits } => { Type::Integer(IntegralType::new(true, bits)) } Key::SimpleType { ty } => match ty { SimpleType::F32 => { Type::Floating(crate::ast::FloatingType::Binary32) } SimpleType::F64 => { Type::Floating(crate::ast::FloatingType::Binary64) } SimpleType::Bool => Type::Bool, SimpleType::Void => Type::Void, SimpleType::USize => { Type::Integer(IntegralType::new(false, pointer_bits)) } SimpleType::ISize => { Type::Integer(IntegralType::new(true, pointer_bits)) } SimpleType::ComptimeInt => Type::comptime_number(), }, Key::PointerType { pointee, flags } => Type::Pointer { constness: flags.is_const, pointee: Box::new(self.as_ast1_type(pointer_bits, pointee)), }, Key::FunctionType { return_type, parameters, } => Type::Fn { parameter_types: parameters .into_iter() .map(|i| self.as_ast1_type(pointer_bits, i)) .collect(), return_type: Box::new( self.as_ast1_type(pointer_bits, return_type), ), }, _ => unimplemented!(), } } } pub const AMD64_POINTER_BITS: u16 = 64; pub const AMD64_POINTER_TYPE_INFO: TypeInfo = TypeInfo { bitsize: 64, bitalign: 64, signed: false, }; impl InternPool { pub fn new() -> Self { let mut this = Self { tags: Vec::new(), indices: Vec::new(), strings: Vec::new(), words: Vec::new(), hashed: BTreeMap::new(), }; this.extend_keys(STATIC_KEYS); this } fn extend_keys<'a, K: IntoIterator>>(&mut self, keys: K) { for k in keys.into_iter() { let mut hasher = std::hash::DefaultHasher::new(); k.hash(&mut hasher); let digest = hasher.finish(); let i = self.insert(k); self.hashed.insert(digest, i); } } fn len(&self) -> u32 { u32::try_from(self.tags.len()) .expect(&format!("more than {} items in internpool!", u32::MAX)) } pub fn get_or_insert(&mut self, key: Key) -> Index { let mut hasher = std::hash::DefaultHasher::new(); key.hash(&mut hasher); let digest = hasher.finish(); if let Some(&idx) = self.hashed.get(&digest) { idx } else { let i = self.insert(key); self.hashed.insert(digest, i); i } } fn insert(&mut self, key: Key) -> Index { match key { Key::String { str } => { let len = str.len() as u32; let start = self.extend_strings(str); let words_idx = self.extend_words([start, len]); self.create_item(Tag::String, words_idx) } Key::SIntSmall { bits } => { self.create_item(Tag::SIntSmall, bits as u32) } Key::UIntSmall { bits } => { self.create_item(Tag::UIntSmall, bits as u32) } Key::F32 { bits } => self.create_item(Tag::F32, bits as u32), Key::F64 { bits } => { let (lo, hi) = into_lo_hi_dwords(bits as u64); let words_idx = self.extend_words([lo, hi]); self.create_item(Tag::F64, words_idx) } Key::SInt64 { bits } => { let (lo, hi) = into_lo_hi_dwords(bits as u64); let i = self.extend_words([lo, hi]); self.create_item(Tag::SInt64, i) } Key::UInt64 { bits } => { let (lo, hi) = into_lo_hi_dwords(bits as u64); let i = self.extend_words([lo, hi]); self.create_item(Tag::UInt64, i) } Key::PositiveInt { bigint } => { let (_, words) = bigint.to_u32_digits(); let i = self.push_word(words.len() as u32); _ = self.extend_words(words); self.create_item(Tag::PositiveInt, i) } Key::NegativeInt { bigint } => { let (_, words) = bigint.to_u32_digits(); let i = self.push_word(words.len() as u32); _ = self.extend_words(words); self.create_item(Tag::NegativeInt, i) } Key::UIntType { bit_width: bits } => { self.create_item(Tag::UIntType, bits as u32) } Key::SIntType { bit_width: bits } => { self.create_item(Tag::SIntType, bits as u32) } Key::SimpleType { ty } => { self.create_item(Tag::SimpleType, ty as u8 as u32) } Key::PointerType { pointee, flags } => { let flags = flags.pack(); let i = self.extend_words([pointee.0, flags as u32]); self.create_item(Tag::PointerType, i) } Key::ArrayType { pointee, flags, length, } => { let flags = flags.pack(); let i = self.extend_words([pointee.0, flags as u32, length]); self.create_item(Tag::ArrayType, i) } Key::StructType { name, decl, packed, c_like, fields, } => { let flags = StructFlags::new(packed, c_like, fields.len() as u32) .pack(); let i = self.extend_words([ name.into_u32(), decl.into_u32(), flags, u32::MAX, ]); if !fields.is_empty() { let fields_offset = self.extend_words( fields .into_iter() .map(|(n, t)| [n.into_u32(), t.into_u32()]) .flatten(), ); self.words[i as usize + 3] = fields_offset; } self.create_item(Tag::StructType, i) } Key::FunctionType { return_type, parameters, } => { let info = FunctionInfo::new( return_type == self.get_simple_type(SimpleType::Void), parameters.len() as u32, ); let start = self.push_word(info.pack()); self.extend_words([return_type.into_u32()]); _ = self.extend_words(parameters.into_iter().map(|i| i.0)); self.create_item(Tag::FunctionType, start) } Key::TrueValue => self.create_item(Tag::TrueValue, 0), Key::FalseValue => self.create_item(Tag::FalseValue, 0), } } fn extend_strings>(&mut self, b: B) -> u32 { let idx = self.strings.len() as u32; self.strings.extend(b.as_ref()); idx } fn extend_words>(&mut self, i: I) -> u32 { let idx = self.words.len() as u32; self.words.extend(i); idx } fn push_word(&mut self, word: u32) -> u32 { let idx = self.words.len() as u32; self.words.push(word); idx } fn create_item(&mut self, tag: Tag, index: u32) -> Index { let len = self.len(); self.tags.push(tag); self.indices.push(index); Index(len) } pub fn get_key(&self, index: Index) -> Key { let item = self.get_item(index).unwrap(); match item.tag { Tag::String => { let start = self.words[item.idx()]; let len = self.words[item.idx() + 1]; let str = unsafe { core::str::from_utf8_unchecked( &self.strings[start as usize..][..len as usize], ) }; Key::String { str } } Tag::UIntSmall => Key::UIntSmall { bits: item.index as u32, }, Tag::SIntSmall => Key::SIntSmall { bits: item.index as i32, }, Tag::F32 => Key::F32 { bits: f32::from_le_bytes(item.index.to_le_bytes()), }, Tag::F64 => { let idx = item.idx(); let bits = from_lo_hi_dwords(self.words[idx], self.words[idx + 1]); Key::F64 { bits: f64::from_le_bytes(bits.to_le_bytes()), } } Tag::SInt64 => { let bits = from_lo_hi_dwords( self.words[item.idx()], self.words[item.idx() + 1], ) as i64; Key::SInt64 { bits } } Tag::UInt64 => { let bits = from_lo_hi_dwords( self.words[item.idx()], self.words[item.idx() + 1], ); Key::UInt64 { bits } } Tag::NegativeInt => { let len = self.words[item.idx()]; let start = item.idx() + 1; let end = start + len as usize; let data = BigUint::from_slice(&self.words[start..end]); let bigint = BigInt::from_biguint(Sign::Minus, data); Key::NegativeInt { bigint } } Tag::PositiveInt => { let len = self.words[item.idx()]; let start = item.idx() + 1; let end = start + len as usize; let data = BigUint::from_slice(&self.words[start..end]); let bigint = BigInt::from_biguint(Sign::Plus, data); Key::PositiveInt { bigint } } Tag::SIntType => Key::SIntType { bit_width: item.index as u16, }, Tag::UIntType => Key::UIntType { bit_width: item.index as u16, }, Tag::SimpleType => { let ty = item.idx() as u8; Key::SimpleType { ty: unsafe { core::mem::transmute::(ty) }, } } Tag::PointerType => { let pointee = Index(self.words[item.idx()]); let flags = PointerFlags::unpack(self.words[item.idx() + 1] as u8); Key::PointerType { pointee, flags } } Tag::ArrayType => { let pointee = Index(self.words[item.idx()]); let flags = PointerFlags::unpack(self.words[item.idx() + 1] as u8); let length = self.words[item.idx() + 2]; Key::ArrayType { pointee, flags, length, } } Tag::StructType => { let name = Index(self.words[item.idx()]); let decl = super::Index::new(self.words[item.idx() + 1]); let flags = StructFlags::unpack(self.words[item.idx() + 2]); let fields = if flags.num_fields != 0 { let fields_offset = self.words[item.idx() + 3] as usize; let fields_end = fields_offset + flags.num_fields as usize * 2; self.words[fields_offset..fields_end] .iter() .cloned() .array_chunks::<2>() .map(|[n, t]| (Index(n), Index(t))) .collect::>() } else { vec![] }; Key::StructType { name, decl, packed: flags.packed, c_like: flags.c_like, fields, } } Tag::FunctionType => { let info = FunctionInfo::unpack(self.words[item.idx()]); let len = info.len(); let (return_type, parameters) = if info.void_return { let start = item.idx() + 1; let end = start + len as usize; let params = self.words[start..end] .iter() .map(|&i| Index(i)) .collect::>(); ( self.get_assume_present(&Key::SimpleType { ty: SimpleType::Void, }), params, ) } else { let return_type = Index(self.words[item.idx() + 1]); let start = item.idx() + 2; let end = start + len as usize; let params = self.words[start..end] .iter() .map(|&i| Index(i)) .collect::>(); (return_type, params) }; Key::FunctionType { return_type, parameters, } } Tag::TrueValue => Key::TrueValue, Tag::FalseValue => Key::FalseValue, } } pub fn try_get_index(&self, key: &Key) -> Option { let mut hasher = std::hash::DefaultHasher::new(); key.hash(&mut hasher); let digest = hasher.finish(); self.hashed.get(&digest).cloned() } pub fn get_assume_present(&self, key: &Key) -> Index { self.try_get_index(&key) .expect(&format!("key {key:?} not present in pool.")) } pub fn get_int_type(&mut self, signed: bool, bits: u16) -> Index { let key = match signed { true => Key::SIntType { bit_width: bits }, false => Key::UIntType { bit_width: bits }, }; self.get_or_insert(key) } pub fn get_string_index(&mut self, str: &str) -> Index { self.get_or_insert(Key::String { str }) } pub fn try_get_string_index(&self, str: &str) -> Option { self.try_get_index(&Key::String { str }) } pub fn get_simple_type(&mut self, ty: SimpleType) -> Index { self.get_or_insert(Key::SimpleType { ty }) } pub fn try_get_simple_type(&self, ty: SimpleType) -> Option { self.try_get_index(&Key::SimpleType { ty }) } pub fn get_function_type>( &mut self, return_type: Index, parameters: P, ) -> Index { self.get_or_insert(Key::FunctionType { return_type, parameters: parameters.into_iter().collect(), }) } pub fn try_get_function_type>( &self, return_type: Index, parameters: P, ) -> Option { self.try_get_index(&Key::FunctionType { return_type, parameters: parameters.into_iter().collect(), }) } pub fn get_pointer_type( &mut self, pointee: Index, flags: Option, ) -> Index { let key = Key::PointerType { pointee, flags: flags.unwrap_or_default(), }; self.get_or_insert(key) } pub fn try_get_pointer_type( &self, pointee: Index, flags: Option, ) -> Option { self.try_get_index( &(Key::PointerType { pointee, flags: flags.unwrap_or_default(), }), ) } pub fn insert_or_replace_struct_type< I: IntoIterator, >( &mut self, name: Index, decl: super::Index, packed: bool, c_like: bool, fields: I, ) -> Index { let key = Key::StructType { name, decl, packed, c_like, fields: vec![], }; if let Some(i) = self.try_get_index(&key).and_then(|i| self.get_item(i)) { let fields_offset = self.extend_words( fields .into_iter() .map(|(n, t)| [n.into_u32(), t.into_u32()]) .flatten(), ); self.words[i.idx() + 3] = fields_offset; let fields_end = self.words.len() as u32; let num_fields = (fields_end - fields_offset) / 2; let flags = StructFlags::new(packed, c_like, num_fields).pack(); self.words[i.idx() + 2] = flags; } self.get_or_insert(key) } pub fn get_struct_type( &mut self, name: Index, decl: super::Index, ) -> Index { let key = Key::StructType { name, decl, packed: false, c_like: false, fields: vec![], }; self.get_or_insert(key) } pub fn try_get_struct_type( &self, name: Index, decl: super::Index, ) -> Option { self.try_get_index(&Key::StructType { name, decl, packed: false, c_like: false, fields: vec![], }) } pub fn get_array_type( &mut self, pointee: Index, flags: Option, length: u32, ) -> Index { let key = Key::ArrayType { pointee, flags: flags.unwrap_or_default(), length, }; self.get_or_insert(key) } pub fn try_get_array_type( &self, pointee: Index, flags: Option, length: u32, ) -> Option { self.try_get_index(&Key::ArrayType { pointee, flags: flags.unwrap_or_default(), length, }) } pub fn get_str(&self, index: Index) -> &str { let key = self.get_key(index); assert!(matches!(key, Key::String { .. })); variant!(key => Key::String { str }); str } fn check_bounds(&self, index: Index) -> Option { (index.0 < self.len()).then_some(index) } fn get_item(&self, index: Index) -> Option { self.check_bounds(index).map(|i| Item { tag: self.tags[i.index()], index: self.indices[i.index()], }) } }