use rucc_base::float::Format;
use rucc_target::TargetInfo;
use crate::classify::bare;
use crate::kind::{ArrayLen, FloatKind, IntKind, TypeKind};
use crate::types::{TypeId, Types};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Layout {
pub size: u64,
pub align: u64,
}
impl Layout {
#[must_use]
pub const fn new(size: u64, align: u64) -> Layout {
Layout { size, align }
}
#[must_use]
const fn scalar(size: u64) -> Layout {
Layout { size, align: size }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LayoutError {
Incomplete,
Function,
TooLarge,
}
impl std::fmt::Display for LayoutError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let text = match self {
LayoutError::Incomplete => "the type is incomplete",
LayoutError::Function => "a function type has no size",
LayoutError::TooLarge => "the type is larger than the address space",
};
f.write_str(text)
}
}
impl std::error::Error for LayoutError {}
pub fn layout(types: &Types, id: TypeId, target: &TargetInfo) -> Result<Layout, LayoutError> {
let id = types.canonical(id);
match types.kind(id) {
TypeKind::Void => Err(LayoutError::Incomplete),
TypeKind::Bool => Ok(Layout::scalar(1)),
TypeKind::Int(kind) => Ok(Layout::scalar(u64::from(int_width(kind, target) / 8))),
TypeKind::Float(kind) => Ok(Layout::scalar(u64::from(float_width(kind, target) / 8))),
TypeKind::Complex(kind) => {
let part = Layout::scalar(u64::from(float_width(kind, target) / 8));
Ok(Layout::new(part.size * 2, part.align))
}
TypeKind::BitInt { width, .. } => Ok(bit_int_layout(width, target)),
TypeKind::Pointer(_) => Ok(Layout::scalar(u64::from(target.pointer_width / 8))),
TypeKind::Function(_) => Err(LayoutError::Function),
TypeKind::Atomic(inner) => {
let inner = layout(types, inner, target)?;
Ok(atomic_layout(inner))
}
TypeKind::Array { elem, len } => {
let ArrayLen::Fixed(count) = len else {
return Err(LayoutError::Incomplete);
};
let elem = layout(types, elem, target)?;
let size = elem.size.checked_mul(count).ok_or(LayoutError::TooLarge)?;
Ok(Layout::new(size, elem.align))
}
TypeKind::Vector { elem, len } => {
let elem = layout(types, elem, target)?;
let raw = elem.size.checked_mul(u64::from(len)).ok_or(LayoutError::TooLarge)?;
Ok(vector_layout(raw))
}
TypeKind::Record(record) => types.record_info(record).layout.ok_or(LayoutError::Incomplete),
TypeKind::Enum(id) => {
let underlying = types.enum_info(id).underlying.ok_or(LayoutError::Incomplete)?;
layout(types, underlying, target)
}
TypeKind::Typedef { underlying, .. } => layout(types, underlying, target),
}
}
#[must_use]
pub fn int_width(kind: IntKind, target: &TargetInfo) -> u32 {
match kind {
IntKind::Char | IntKind::SChar | IntKind::UChar => 8,
IntKind::Short | IntKind::UShort => 16,
IntKind::Int | IntKind::UInt => 32,
IntKind::Long | IntKind::ULong => target.long_width,
IntKind::LongLong | IntKind::ULongLong => 64,
IntKind::Int128 | IntKind::UInt128 => 128,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct IntegerInfo {
pub signed: bool,
pub width: u32,
}
impl IntegerInfo {
#[must_use]
pub const fn new(signed: bool, width: u32) -> IntegerInfo {
IntegerInfo { signed, width }
}
#[must_use]
pub const fn wrap(self, raw: i128) -> i128 {
if self.width == 0 {
return 0;
}
if self.width >= 128 {
return raw;
}
let unused = 128 - self.width;
if self.signed {
(raw << unused) >> unused
} else {
(((raw as u128) << unused) >> unused) as i128
}
}
#[must_use]
pub const fn holds(self, raw: i128) -> bool {
self.wrap(raw) == raw
}
}
#[must_use]
pub fn integer_info(types: &Types, id: TypeId, target: &TargetInfo) -> Option<IntegerInfo> {
match bare(types, id) {
TypeKind::Bool => Some(IntegerInfo::new(false, 1)),
TypeKind::Int(kind) => {
Some(IntegerInfo::new(kind.is_signed(target.char_is_signed), int_width(kind, target)))
}
TypeKind::BitInt { signed, width } => Some(IntegerInfo::new(signed, width)),
TypeKind::Enum(id) => {
let underlying = types.enum_info(id).underlying?;
integer_info(types, underlying, target)
}
_ => None,
}
}
#[must_use]
pub fn float_width(kind: FloatKind, target: &TargetInfo) -> u32 {
match kind {
FloatKind::Float16 => 16,
FloatKind::Float | FloatKind::Float32 => 32,
FloatKind::Double | FloatKind::Float32x | FloatKind::Float64 => 64,
FloatKind::LongDouble => target.long_double_width,
FloatKind::Float64x | FloatKind::Float128 => 128,
}
}
#[must_use]
pub fn float_format(kind: FloatKind, target: &TargetInfo) -> Format {
match kind {
FloatKind::Float16 => Format::Half,
FloatKind::Float | FloatKind::Float32 => Format::Single,
FloatKind::Double | FloatKind::Float32x | FloatKind::Float64 => Format::Double,
FloatKind::LongDouble => target.long_double_format,
FloatKind::Float64x => target.float64x_format,
FloatKind::Float128 => Format::Quad,
}
}
fn bit_int_layout(width: u32, target: &TargetInfo) -> Layout {
let bytes = u64::from(width).div_ceil(8);
if bytes <= 8 {
return Layout::scalar(bytes.max(1).next_power_of_two());
}
let granule = u64::from(target.bit_int_granule / 8);
Layout::new(bytes.next_multiple_of(granule), granule)
}
fn atomic_layout(inner: Layout) -> Layout {
if inner.size.is_power_of_two() && inner.size <= 16 {
return Layout::new(inner.size, inner.align.max(inner.size));
}
inner
}
fn vector_layout(raw: u64) -> Layout {
let size = raw.max(1).next_power_of_two();
Layout::scalar(size)
}