use alloc::vec::Vec;
use core::fmt;
use crate::entity::AggId;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[non_exhaustive]
pub enum Type {
Bool,
I8,
I16,
I32,
I64,
U8,
U16,
U32,
U64,
F32,
F64,
Ptr,
Ref,
}
impl Type {
pub const ALL: [Type; 13] = [
Type::Bool,
Type::I8,
Type::I16,
Type::I32,
Type::I64,
Type::U8,
Type::U16,
Type::U32,
Type::U64,
Type::F32,
Type::F64,
Type::Ptr,
Type::Ref,
];
#[inline]
#[must_use]
pub const fn is_int(self) -> bool {
self.int_bits().is_some()
}
#[inline]
#[must_use]
pub const fn is_signed_int(self) -> bool {
matches!(self, Type::I8 | Type::I16 | Type::I32 | Type::I64)
}
#[inline]
#[must_use]
pub const fn is_unsigned_int(self) -> bool {
matches!(self, Type::U8 | Type::U16 | Type::U32 | Type::U64)
}
#[inline]
#[must_use]
pub const fn is_float(self) -> bool {
matches!(self, Type::F32 | Type::F64)
}
#[inline]
#[must_use]
pub const fn is_numeric(self) -> bool {
self.is_int() || self.is_float()
}
#[inline]
#[must_use]
pub const fn int_bits(self) -> Option<u32> {
match self {
Type::I8 | Type::U8 => Some(8),
Type::I16 | Type::U16 => Some(16),
Type::I32 | Type::U32 => Some(32),
Type::I64 | Type::U64 => Some(64),
_ => None,
}
}
#[inline]
#[must_use]
pub const fn bits(self, ptr: PointerWidth) -> u32 {
match self {
Type::Bool => 1,
Type::F32 => 32,
Type::F64 => 64,
Type::Ptr | Type::Ref => ptr.bits(),
other => match other.int_bits() {
Some(b) => b,
None => 0,
},
}
}
#[inline]
#[must_use]
pub const fn size(self, ptr: PointerWidth) -> u32 {
match self {
Type::Bool => 1,
other => other.bits(ptr) / 8,
}
}
#[must_use]
pub const fn int(bits: u32, signed: bool) -> Option<Type> {
Some(match (bits, signed) {
(8, true) => Type::I8,
(16, true) => Type::I16,
(32, true) => Type::I32,
(64, true) => Type::I64,
(8, false) => Type::U8,
(16, false) => Type::U16,
(32, false) => Type::U32,
(64, false) => Type::U64,
_ => return None,
})
}
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Type::Bool => "bool",
Type::I8 => "i8",
Type::I16 => "i16",
Type::I32 => "i32",
Type::I64 => "i64",
Type::U8 => "u8",
Type::U16 => "u16",
Type::U32 => "u32",
Type::U64 => "u64",
Type::F32 => "f32",
Type::F64 => "f64",
Type::Ptr => "ptr",
Type::Ref => "ref",
}
}
#[must_use]
pub fn from_name(name: &str) -> Option<Type> {
Some(match name {
"bool" => Type::Bool,
"i8" => Type::I8,
"i16" => Type::I16,
"i32" => Type::I32,
"i64" => Type::I64,
"u8" => Type::U8,
"u16" => Type::U16,
"u32" => Type::U32,
"u64" => Type::U64,
"f32" => Type::F32,
"f64" => Type::F64,
"ptr" => Type::Ptr,
"ref" => Type::Ref,
_ => return None,
})
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
#[non_exhaustive]
pub enum PointerWidth {
P32,
#[default]
P64,
}
impl PointerWidth {
#[inline]
#[must_use]
pub const fn bytes(self) -> u32 {
match self {
PointerWidth::P32 => 4,
PointerWidth::P64 => 8,
}
}
#[inline]
#[must_use]
pub const fn bits(self) -> u32 {
self.bytes() * 8
}
#[inline]
#[must_use]
pub const fn is_address_int(self, ty: Type) -> bool {
match ty.int_bits() {
Some(bits) => bits == self.bits(),
None => false,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[non_exhaustive]
pub enum MemType {
Scalar(Type),
Agg(AggId),
}
impl From<Type> for MemType {
fn from(ty: Type) -> Self {
MemType::Scalar(ty)
}
}
impl From<AggId> for MemType {
fn from(agg: AggId) -> Self {
MemType::Agg(agg)
}
}
impl fmt::Display for MemType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MemType::Scalar(ty) => write!(f, "{ty}"),
MemType::Agg(agg) => write!(f, "{agg}"),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum Aggregate {
Struct {
fields: Vec<MemType>,
packed: bool,
},
Array {
elem: MemType,
len: u64,
},
}
impl Aggregate {
#[must_use]
pub fn structure(fields: &[MemType]) -> Aggregate {
Aggregate::Struct {
fields: fields.to_vec(),
packed: false,
}
}
#[must_use]
pub fn packed(fields: &[MemType]) -> Aggregate {
Aggregate::Struct {
fields: fields.to_vec(),
packed: true,
}
}
#[must_use]
pub fn array(elem: MemType, len: u64) -> Aggregate {
Aggregate::Array { elem, len }
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Layout {
pub(crate) size: u64,
pub(crate) align: u32,
}
impl Layout {
#[inline]
#[must_use]
pub const fn size(self) -> u64 {
self.size
}
#[inline]
#[must_use]
pub const fn align(self) -> u32 {
self.align
}
}
pub const MAX_AGGREGATE_SIZE: u64 = 1 << 48;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
#[non_exhaustive]
pub enum CallConv {
#[default]
Fast,
C,
SysV64,
Win64,
Aapcs64,
}
impl CallConv {
pub const ALL: [CallConv; 5] = [
CallConv::Fast,
CallConv::C,
CallConv::SysV64,
CallConv::Win64,
CallConv::Aapcs64,
];
#[must_use]
pub const fn name(self) -> &'static str {
match self {
CallConv::Fast => "fast",
CallConv::C => "c",
CallConv::SysV64 => "sysv64",
CallConv::Win64 => "win64",
CallConv::Aapcs64 => "aapcs64",
}
}
#[must_use]
pub fn from_name(name: &str) -> Option<CallConv> {
CallConv::ALL.iter().copied().find(|c| c.name() == name)
}
}
impl fmt::Display for CallConv {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct Signature {
pub params: Vec<Type>,
pub returns: Vec<Type>,
pub conv: CallConv,
}
impl Signature {
#[must_use]
pub fn new(params: &[Type], returns: &[Type]) -> Signature {
Signature {
params: params.to_vec(),
returns: returns.to_vec(),
conv: CallConv::Fast,
}
}
#[must_use]
pub fn with_conv(mut self, conv: CallConv) -> Signature {
self.conv = conv;
self
}
}
impl fmt::Display for Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("(")?;
for (i, ty) in self.params.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
write!(f, "{ty}")?;
}
f.write_str(") -> (")?;
for (i, ty) in self.returns.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
write!(f, "{ty}")?;
}
write!(f, ") {}", self.conv)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
#[test]
fn test_type_names_round_trip() {
for ty in Type::ALL {
assert_eq!(Type::from_name(ty.name()), Some(ty));
}
for conv in CallConv::ALL {
assert_eq!(CallConv::from_name(conv.name()), Some(conv));
}
}
#[test]
fn test_type_sizes_follow_pointer_width() {
assert_eq!(Type::Ptr.size(PointerWidth::P32), 4);
assert_eq!(Type::Ref.size(PointerWidth::P64), 8);
assert_eq!(Type::U64.size(PointerWidth::P32), 8);
assert_eq!(Type::Bool.size(PointerWidth::P32), 1);
}
#[test]
fn test_signature_display() {
let s = Signature::new(&[], &[]);
assert_eq!(s.to_string(), "() -> () fast");
}
}