SeaLang/src/ast2/intern.rs

1301 lines
39 KiB
Rust

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<u8> 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<Index>,
},
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<H: Hasher>(&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<Tag>,
indices: Vec<u32>,
//
strings: Vec<u8>,
words: Vec<u32>,
hashed: BTreeMap<u64, Index>,
}
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<Index> {
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::<Vec<_>>();
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<Item = Key<'a>>>(&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<B: AsRef<[u8]>>(&mut self, b: B) -> u32 {
let idx = self.strings.len() as u32;
self.strings.extend(b.as_ref());
idx
}
fn extend_words<I: IntoIterator<Item = u32>>(&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::<u8, SimpleType>(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::<Vec<_>>()
} 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::<Vec<_>>();
(
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::<Vec<_>>();
(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<Index> {
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<Index> {
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<Index> {
self.try_get_index(&Key::SimpleType { ty })
}
pub fn get_function_type<P: IntoIterator<Item = Index>>(
&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<P: IntoIterator<Item = Index>>(
&self,
return_type: Index,
parameters: P,
) -> Option<Index> {
self.try_get_index(&Key::FunctionType {
return_type,
parameters: parameters.into_iter().collect(),
})
}
pub fn get_pointer_type(
&mut self,
pointee: Index,
flags: Option<PointerFlags>,
) -> 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<PointerFlags>,
) -> Option<Index> {
self.try_get_index(
&(Key::PointerType {
pointee,
flags: flags.unwrap_or_default(),
}),
)
}
pub fn insert_or_replace_struct_type<
I: IntoIterator<Item = (Index, Index)>,
>(
&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<Index> {
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<PointerFlags>,
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<PointerFlags>,
length: u32,
) -> Option<Index> {
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> {
(index.0 < self.len()).then_some(index)
}
fn get_item(&self, index: Index) -> Option<Item> {
self.check_bounds(index).map(|i| Item {
tag: self.tags[i.index()],
index: self.indices[i.index()],
})
}
}