use rucc_ir::{Float, Type};
use rucc_target::TargetInfo;
use rucc_types::{
ArrayLen, FloatKind, LayoutError, Qualifiers, TypeId, TypeKind, Types, float_format,
integer_info, layout,
};
#[must_use]
pub(crate) fn value_type(types: &Types, target: &TargetInfo, id: TypeId) -> Option<Type> {
match types.kind(types.canonical(id)) {
TypeKind::Bool => Some(Type::I1),
TypeKind::Int(_) | TypeKind::BitInt { .. } | TypeKind::Enum(_) => {
let info = integer_info(types, id, target)?;
Some(Type::int(info.width))
}
TypeKind::Float(kind) => Some(Type::float(format_of(kind, target)?)),
TypeKind::Pointer(_) => Some(Type::PTR),
TypeKind::Atomic(inner) => value_type(types, target, inner),
TypeKind::Vector { elem, len } => {
let lane = value_type(types, target, elem)?;
Some(Type::vector(lane, len))
}
_ => None,
}
}
#[must_use]
pub(crate) fn format_of(kind: FloatKind, target: &TargetInfo) -> Option<Float> {
ir_format(float_format(kind, target))
}
#[must_use]
pub(crate) fn ir_format(format: rucc_base::float::Format) -> Option<Float> {
use rucc_base::float::Format;
match format {
Format::Half => Some(Float::F16),
Format::Single => Some(Float::F32),
Format::Double => Some(Float::F64),
Format::X87Extended => Some(Float::F80),
Format::Quad => Some(Float::F128),
Format::BFloat16 => None,
}
}
#[must_use]
pub(crate) fn float_format_of(
types: &Types,
target: &TargetInfo,
id: TypeId,
) -> Option<rucc_base::float::Format> {
match types.kind(types.canonical(id)) {
TypeKind::Float(kind) => Some(float_format(kind, target)),
TypeKind::Atomic(inner) => float_format_of(types, target, inner),
_ => None,
}
}
#[must_use]
pub(crate) fn is_read_only(types: &Types, id: TypeId) -> bool {
let mut id = types.canonical(id);
loop {
let quals = types.quals(id);
if quals.has(Qualifiers::VOLATILE) {
return false;
}
if quals.has(Qualifiers::CONST) {
return true;
}
match types.kind(id) {
TypeKind::Array { elem, .. } => id = types.canonical(elem),
_ => return false,
}
}
}
#[must_use]
pub(crate) fn is_variable_length(types: &Types, id: TypeId) -> bool {
match types.kind(types.canonical(id)) {
TypeKind::Array { elem, len } => {
matches!(len, ArrayLen::Variable(_) | ArrayLen::Star) || is_variable_length(types, elem)
}
_ => false,
}
}
#[must_use]
pub(crate) fn is_signed(types: &Types, target: &TargetInfo, id: TypeId) -> bool {
match integer_info(types, id, target) {
Some(info) => info.signed,
None => !matches!(types.kind(types.canonical(id)), TypeKind::Pointer(_)),
}
}
#[must_use]
pub(crate) fn size_of(types: &Types, target: &TargetInfo, id: TypeId) -> u64 {
match layout(types, id, target) {
Ok(layout) => layout.size,
Err(LayoutError::Incomplete | LayoutError::Function) => 1,
Err(_) => 0,
}
}
#[must_use]
pub(crate) fn align_of(types: &Types, target: &TargetInfo, id: TypeId) -> u32 {
match layout(types, id, target) {
Ok(layout) => u32::try_from(layout.align).unwrap_or(1).max(1),
Err(_) => 1,
}
}
#[must_use]
pub(crate) fn address_type(target: &TargetInfo) -> Type {
Type::int(target.pointer_width)
}
#[cfg(test)]
mod tests {
use rucc_types::{ArrayLen, IntKind};
use super::*;
fn target() -> TargetInfo {
TargetInfo::new("x86_64-unknown-linux-gnu".parse().expect("a triple"))
}
#[test]
fn the_scalar_types_have_ir_types_and_the_rest_have_none() {
let mut types = Types::new();
let target = target();
let int = types.int(IntKind::Int);
let long = types.int(IntKind::Long);
let pointer = types.pointer(int);
let array = types.array(int, ArrayLen::Fixed(4));
assert_eq!(value_type(&types, &target, int), Some(Type::int(32)));
assert_eq!(value_type(&types, &target, long), Some(Type::int(64)));
assert_eq!(value_type(&types, &target, types.boolean()), Some(Type::I1));
assert_eq!(value_type(&types, &target, pointer), Some(Type::PTR));
assert_eq!(value_type(&types, &target, types.void()), None);
assert_eq!(value_type(&types, &target, array), None);
}
#[test]
fn the_qualifier_on_an_array_is_the_one_on_its_elements() {
let mut types = Types::new();
let int = types.int(IntKind::Int);
let konst = types.qualified(int, Qualifiers::CONST);
let array = types.array(konst, ArrayLen::Fixed(4));
assert!(is_read_only(&types, konst));
assert!(is_read_only(&types, array));
assert!(!is_read_only(&types, int));
let plain = types.array(int, ArrayLen::Fixed(4));
assert!(!is_read_only(&types, plain));
let both = types.qualified(int, Qualifiers::CONST.with(Qualifiers::VOLATILE));
assert!(!is_read_only(&types, both));
}
#[test]
fn a_variable_length_array_is_one_however_deeply_it_is_buried() {
let mut types = Types::new();
let int = types.int(IntKind::Int);
let fixed = types.array(int, ArrayLen::Fixed(4));
let variable = types.array(int, ArrayLen::Star);
let outer = types.array(variable, ArrayLen::Fixed(3));
let pointer = types.pointer(variable);
assert!(!is_variable_length(&types, fixed));
assert!(is_variable_length(&types, variable));
assert!(is_variable_length(&types, outer));
assert!(!is_variable_length(&types, pointer));
}
#[test]
fn a_size_the_type_does_not_have_is_the_one_gnu_c_gives_it() {
let mut types = Types::new();
let target = target();
let incomplete = types.array(types.int(IntKind::Int), ArrayLen::Unknown);
assert_eq!(size_of(&types, &target, types.void()), 1);
assert_eq!(size_of(&types, &target, incomplete), 1);
assert_eq!(align_of(&types, &target, incomplete), 1);
}
}