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, F64, Bool, Void, USize, ISize, ComptimeInt, /// Top type: this is the supertype of all types, and any value can coerce into it. /// Although Rust's `()` is not a top type, it can be thought of as a top type in some contexts. Top, /// Bottom type: this is the subtype of all types, and it can coerce into a value of any type. /// Akin to Rust's `!`. Bottom, UInt(u16), SInt(u16), } impl From for SimpleType { fn from(value: u32) -> Self { let [discriminant, bits] = *crate::common::u32_as_u16_slice(&value); match discriminant { 0 => Self::F32, 1 => Self::F64, 2 => Self::Bool, 3 => Self::Void, 4 => Self::USize, 5 => Self::ISize, 6 => Self::ComptimeInt, 7 => Self::Top, 8 => Self::Bottom, 9 => Self::UInt(bits), 10 => Self::SInt(bits), _ => panic!("{value} is not a simple type"), } } } impl From for u32 { fn from(value: SimpleType) -> Self { match value { SimpleType::F32 => crate::common::u32_from_u16_slice(&[0, 0]), SimpleType::F64 => crate::common::u32_from_u16_slice(&[1, 0]), SimpleType::Bool => crate::common::u32_from_u16_slice(&[2, 0]), SimpleType::Void => crate::common::u32_from_u16_slice(&[3, 0]), SimpleType::USize => crate::common::u32_from_u16_slice(&[4, 0]), SimpleType::ISize => crate::common::u32_from_u16_slice(&[5, 0]), SimpleType::ComptimeInt => crate::common::u32_from_u16_slice(&[6, 0]), SimpleType::Top => crate::common::u32_from_u16_slice(&[7, 0]), SimpleType::Bottom => crate::common::u32_from_u16_slice(&[8, 0]), SimpleType::UInt(bits) => crate::common::u32_from_u16_slice(&[9, bits]), SimpleType::SInt(bits) => crate::common::u32_from_u16_slice(&[10, bits]), } } } impl Display for SimpleType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let fmt: std::borrow::Cow = match self { SimpleType::F32 => "f32".into(), SimpleType::F64 => "f64".into(), SimpleType::Bool => "bool".into(), SimpleType::Void => "void".into(), SimpleType::USize => "usize".into(), SimpleType::ISize => "isize".into(), SimpleType::ComptimeInt => "comptime_int".into(), SimpleType::Top => "⊤".into(), SimpleType::Bottom => "⊥".into(), SimpleType::UInt(bits) => format!("u{bits}").into(), SimpleType::SInt(bits) => format!("i{bits}").into(), }; write!(f, "{fmt}",) } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Tag { String, SIntSmall, UIntSmall, TrueValue, FalseValue, UInt64, SInt64, F32, F64, PositiveInt, NegativeInt, SimpleType, PointerType, ArrayType, FunctionType, StructType, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(super) struct Item { pub(super) tag: Tag, pub(super) index: u32, } #[derive(Debug, Clone, PartialEq)] #[non_exhaustive] pub enum Key<'a> { String { str: &'a str, }, Bytes { bytes: &'a [u8], }, SIntSmall { bits: i32, }, UIntSmall { bits: u32, }, SInt64 { bits: i64, }, UInt64 { bits: u64, }, F32 { bits: f32, }, F64 { bits: f64, }, PositiveInt { bigint: BigInt, }, NegativeInt { bigint: BigInt, }, 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, } pub struct KeyDisplay<'a> { ip: &'a InternPool, key: Key<'a>, } impl Display for KeyDisplay<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.key { Key::String { str } => write!(f, "\"{str}\"")?, Key::Bytes { bytes } => write!(f, "{bytes:>02x?}")?, Key::SIntSmall { bits } => write!(f, "{bits}")?, Key::UIntSmall { bits } => write!(f, "{bits}")?, Key::SInt64 { bits } => write!(f, "{bits}")?, Key::UInt64 { bits } => write!(f, "{bits}")?, Key::F32 { bits } => write!(f, "{bits}")?, Key::F64 { bits } => write!(f, "{bits}")?, Key::PositiveInt { ref bigint } => write!(f, "{bigint}")?, Key::NegativeInt { ref bigint } => write!(f, "{bigint}")?, Key::SimpleType { ty } => write!(f, "{ty}")?, Key::PointerType { pointee, flags } => { write!(f, "*{flags}{}", self.ip.display_key(pointee))? } Key::ArrayType { pointee, flags, length, } => write!(f, "[{flags}{}; {length}]", self.ip.display_key(pointee))?, Key::FunctionType { return_type, ref parameters, } => { write!(f, "fn (")?; let mut iter = parameters.iter().map(|&ty| self.ip.display_key(ty)); let Some(next) = iter.next() else { return Ok(()); }; write!(f, "{next}")?; for item in iter { write!(f, ", {item}")?; } write!(f, ") -> {}", self.ip.display_key(return_type))?; } Key::StructType { decl, name, packed, c_like, ref fields, } => { _ = (decl, name, packed, c_like, fields); todo!() } Key::TrueValue => write!(f, "true")?, Key::FalseValue => write!(f, "false")?, } Ok(()) } } 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::Bytes { bytes } => bytes.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::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 Display for PointerFlags { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if self.is_const { write!(f, "const ")? } if self.volatile { write!(f, "volatile ")? } if self.noalias { write!(f, "noalias ")? } Ok(()) } } 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(u32); impl Display for Index { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "${}", self.index()) } } impl Index { pub fn new(inner: u32) -> Self { Self(inner) } pub fn into_u32(self) -> u32 { unsafe { core::mem::transmute(self) } } pub fn from_u32(inner: u32) -> Self { unsafe { core::mem::transmute(inner) } } 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 InternPoolWrapper(core::cell::UnsafeCell); impl InternPoolWrapper { pub fn as_mut(&self) -> &mut InternPool { unsafe { &mut *self.0.get() } } pub fn as_ref(&self) -> &InternPool { unsafe { &*self.0.get() } } pub fn new() -> Self { InternPool::new().into() } } impl From for InternPoolWrapper { fn from(value: InternPool) -> Self { Self(core::cell::UnsafeCell::new(value)) } } impl core::ops::Deref for InternPoolWrapper { type Target = InternPool; fn deref(&self) -> &Self::Target { unsafe { &*self.0.get() } } } impl core::ops::DerefMut for InternPoolWrapper { fn deref_mut(&mut self) -> &mut Self::Target { self.as_mut() } } impl AsRef for InternPoolWrapper { fn as_ref(&self) -> &InternPool { Self::as_ref(self) } } impl AsRef for InternPool { fn as_ref(&self) -> &InternPool { self } } // impl AsMut for InternPoolWrapper { // fn as_mut(&mut self) -> &mut InternPool { // Self::as_mut(self) // } // } // impl AsMut for InternPool { // fn as_mut(&mut self) -> &mut InternPool { // self // } // } impl core::fmt::Debug for InternPoolWrapper { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("InternPoolWrapper") .field(self.as_ref()) .finish() } } 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() } } impl InternPool { pub fn display_key(&self, index: Index) -> KeyDisplay<'_> { KeyDisplay { ip: self, key: self.get_key(index), } } } 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!( TOP => Key::SimpleType {ty: SimpleType::Top,}, BOTTOM => Key::SimpleType {ty: SimpleType::Bottom,}, 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,}, I0 => Key::SimpleType { ty: SimpleType::SInt(0) }, U0 => Key::SimpleType { ty: SimpleType::UInt(0) }, I1 => Key::SimpleType { ty: SimpleType::SInt(1) }, U1 => Key::SimpleType { ty: SimpleType::UInt(1) }, I8 => Key::SimpleType { ty: SimpleType::SInt(8) }, U8 => Key::SimpleType { ty: SimpleType::UInt(8) }, I16 => Key::SimpleType { ty: SimpleType::SInt(16) }, U16 => Key::SimpleType { ty: SimpleType::UInt(16) }, I32 => Key::SimpleType { ty: SimpleType::SInt(32) }, U32 => Key::SimpleType { ty: SimpleType::UInt(32) }, I64 => Key::SimpleType { ty: SimpleType::SInt(64) }, U64 => Key::SimpleType { ty: SimpleType::UInt(64) }, I128 => Key::SimpleType { ty: SimpleType::SInt(128) }, U128 => Key::SimpleType { ty: SimpleType::UInt(128) }, TRUE => Key::TrueValue, FALSE => Key::FalseValue, EMPTY_STRING => Key::String { str: "" }, EMPTY_BYTES => Key::Bytes { bytes: &[] }, ); 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, }) } // Assumes the type is present in the pool. fn get_simple_type_unchecked(&self, ty: SimpleType) -> Index { self.get_assume_present(&Key::SimpleType { ty }) } pub fn get_u0_type(&self) -> Index { self.get_simple_type_unchecked(SimpleType::UInt(0)) } pub fn get_i0_type(&self) -> Index { self.get_simple_type_unchecked(SimpleType::SInt(0)) } pub fn get_u1_type(&self) -> Index { self.get_simple_type_unchecked(SimpleType::UInt(1)) } pub fn get_i1_type(&self) -> Index { self.get_simple_type_unchecked(SimpleType::SInt(1)) } pub fn get_u8_type(&self) -> Index { self.get_simple_type_unchecked(SimpleType::UInt(8)) } pub fn get_i8_type(&self) -> Index { self.get_simple_type_unchecked(SimpleType::SInt(8)) } pub fn get_u16_type(&self) -> Index { self.get_simple_type_unchecked(SimpleType::UInt(16)) } pub fn get_i16_type(&self) -> Index { self.get_simple_type_unchecked(SimpleType::SInt(16)) } pub fn get_u32_type(&self) -> Index { self.get_simple_type_unchecked(SimpleType::UInt(32)) } pub fn get_i32_type(&self) -> Index { self.get_simple_type_unchecked(SimpleType::SInt(32)) } pub fn get_u64_type(&self) -> Index { self.get_simple_type_unchecked(SimpleType::UInt(64)) } pub fn get_i64_type(&self) -> Index { self.get_simple_type_unchecked(SimpleType::SInt(64)) } pub fn get_u128_type(&self) -> Index { self.get_simple_type_unchecked(SimpleType::UInt(128)) } pub fn get_i128_type(&self) -> Index { self.get_simple_type_unchecked(SimpleType::SInt(128)) } pub fn get_top_type(&self) -> Index { self.get_assume_present(&Key::SimpleType { ty: SimpleType::Top, }) } pub fn get_bottom_type(&self) -> Index { self.get_assume_present(&Key::SimpleType { ty: SimpleType::Bottom, }) } } #[derive(Debug, Clone, Copy)] pub struct TypeInfo { pub bitsize: u32, pub bitalign: u32, pub signed: bool, } impl TypeInfo { /// byte size pub fn size(&self) -> u32 { self.bitsize.div_ceil(8) } /// byte align pub fn align(&self) -> u32 { self.bitalign.div_ceil(8) } } 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::SimpleType { ty: SimpleType::SInt(_) | SimpleType::UInt(_), }, ) => Some(rhs), ( Key::SimpleType { ty: SimpleType::F32, } | Key::SimpleType { ty: SimpleType::F64, } | Key::SimpleType { ty: SimpleType::USize, } | Key::SimpleType { ty: SimpleType::ISize, } | Key::SimpleType { ty: SimpleType::SInt(_) | SimpleType::UInt(_), }, 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::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::UInt(bits) | SimpleType::SInt(bits) => { Type::from_bitsize_int(bits as u32) } SimpleType::Top | SimpleType::Bottom | SimpleType::ComptimeInt => { panic!("{ty} can't be turned into a mir type") } }, Key::ArrayType { .. } => { panic!("arrays can't be directly accessed in mir") } Key::FunctionType { .. } => Type::Function, 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::SimpleType { ty } => match ty { SimpleType::USize | SimpleType::UInt(_) => false, SimpleType::ISize | SimpleType::SInt(_) => 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::SimpleType { ty } => match ty { SimpleType::SInt(bits) => TypeInfo { bitsize: bits as u32, bitalign: (bits as u32).next_multiple_of(8).next_power_of_two(), signed: true, }, SimpleType::UInt(bits) => TypeInfo { bitsize: bits as u32, bitalign: (bits as u32).next_multiple_of(8).next_power_of_two(), signed: false, }, 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::Top | SimpleType::Bottom => { panic!("top and bottom types are not sized") } 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 insert_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_or_insert({ if i.signed { Key::SimpleType { ty: SimpleType::SInt(i.bits), } } else { Key::SimpleType { ty: SimpleType::UInt(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.insert_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.insert_ast1_type(pointer_bits, ty)) .collect::>(); let return_type = self.from_ast1_type(pointer_bits, &return_type); self.get_or_insert(Key::FunctionType { return_type, parameters, }) } crate::ast::Type::Void => self.get_void_type(), _ => { todo!() } } } pub fn from_ast1_type(&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::SimpleType { ty: SimpleType::SInt(i.bits), } } else { Key::SimpleType { ty: SimpleType::UInt(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_assume_present(&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_assume_present(&Key::FunctionType { 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::SimpleType { ty } => match ty { SimpleType::F32 => Type::Floating(crate::ast::FloatingType::Binary32), SimpleType::F64 => Type::Floating(crate::ast::FloatingType::Binary64), SimpleType::SInt(bits) => Type::Integer(IntegralType::new(true, bits)), SimpleType::UInt(bits) => Type::Integer(IntegralType::new(false, bits)), 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(), SimpleType::Top | SimpleType::Bottom => { panic!("top and bottom types cannot be converted to ast1 types") } }, 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::Bytes { bytes } => { let len = bytes.len() as u32; let start = self.extend_strings(bytes); 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::SimpleType { ty } => self.create_item(Tag::SimpleType, ty.into()), Key::PointerType { pointee, flags } => { let flags = flags.pack(); let i = self.extend_words([pointee.index() as u32, flags as u32]); self.create_item(Tag::PointerType, i) } Key::ArrayType { pointee, flags, length, } => { let flags = flags.pack(); let i = self.extend_words([pointee.index() as u32, 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.index() as u32)); 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::new(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::SimpleType => { let ty = item.idx() as u32; Key::SimpleType { ty: SimpleType::from(ty), } } Tag::PointerType => { let pointee = Index::new(self.words[item.idx()]); let flags = PointerFlags::unpack(self.words[item.idx() + 1] as u8); Key::PointerType { pointee, flags } } Tag::ArrayType => { let pointee = Index::new(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::new(self.words[item.idx()]); let decl = super::Index::from_u32(self.words[item.idx() + 1]).unwrap(); 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::new(n), Index::new(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::new(i)) .collect::>(); ( self.get_assume_present(&Key::SimpleType { ty: SimpleType::Void, }), params, ) } else { let return_type = Index::new(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::new(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::SimpleType { ty: SimpleType::SInt(bits), }, false => Key::SimpleType { ty: SimpleType::UInt(bits), }, }; self.get_or_insert(key) } pub fn get_unsigned_integer(&mut self, value: u64) -> Index { let key = match value { _ if value <= u32::MAX as u64 => Key::UIntSmall { bits: value as u32 }, _ => Key::UInt64 { bits: value as u64 }, }; self.get_or_insert(key) } pub fn get_bytes_index(&mut self, bytes: &[u8]) -> Index { self.get_or_insert(Key::Bytes { bytes }) } pub fn try_get_bytes_index(&self, bytes: &[u8]) -> Option { self.try_get_index(&Key::Bytes { bytes }) } pub fn insert_string(&mut self, str: &str) -> Index { self.get_string_index(str) } pub fn insert_bytes(&mut self, bytes: &[u8]) -> Index { self.get_bytes_index(bytes) } 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 try_get_pointee_type(&self, pointer: Index) -> Option { match self.get_key(pointer) { Key::PointerType { pointee, .. } | Key::ArrayType { pointee, .. } => Some(pointee), _ => None, } } pub fn try_get_return_type(&self, func: Index) -> Option { match self.get_key(func) { Key::FunctionType { return_type, .. } => Some(return_type), _ => None, } } 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>( &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 } pub fn get_bytes(&self, index: Index) -> &[u8] { let key = self.get_key(index); assert!(matches!(key, Key::Bytes { .. })); variant!(key => Key::Bytes { bytes }); bytes } fn check_bounds(&self, index: Index) -> Option { ((index.index() as u32) < self.len()).then_some(index) } pub(super) fn get_item(&self, index: Index) -> Option { self.check_bounds(index).map(|i| Item { tag: self.tags[i.index()], index: self.indices[i.index()], }) } } impl InternPool { pub fn is_function(&self, index: Index) -> bool { matches!(self.get_key(index), Key::FunctionType { .. }) } pub fn is_type(&self, index: Index) -> bool { matches!( self.get_key(index), Key::FunctionType { .. } | Key::ArrayType { .. } | Key::PointerType { .. } | Key::SimpleType { .. } | Key::StructType { .. } ) } }