use std::collections::HashMap;
use rucc_base::Interner;
use rucc_ir::{Meta, MetaNode, Module, TbaaNode};
use rucc_types::{FloatKind, IntKind, TypeId, TypeKind, Types};
const ROOT: &str = "char";
const POINTER: &str = "pointer";
#[derive(Debug, Default)]
pub(crate) struct Tree {
root: Option<Meta>,
nodes: HashMap<String, Meta>,
}
impl Tree {
pub(crate) fn node(
&mut self,
module: &mut Module,
names: &mut Interner,
types: &Types,
ty: TypeId,
) -> Option<Meta> {
match spelling(types, ty)? {
Name::Character => Some(self.root(module, names)),
Name::Distinct(name) => {
if let Some(&found) = self.nodes.get(&name) {
return Some(found);
}
let parent = self.root(module, names);
let symbol = names.intern(&name);
let node = module.add_meta(MetaNode::Tbaa(TbaaNode {
name: symbol,
parent: Some(parent),
offset: 0,
}));
self.nodes.insert(name, node);
Some(node)
}
}
}
pub(crate) fn root(&mut self, module: &mut Module, names: &mut Interner) -> Meta {
match self.root {
Some(root) => root,
None => {
let name = names.intern(ROOT);
let root =
module.add_meta(MetaNode::Tbaa(TbaaNode { name, parent: None, offset: 0 }));
self.root = Some(root);
root
}
}
}
}
enum Name {
Character,
Distinct(String),
}
fn spelling(types: &Types, ty: TypeId) -> Option<Name> {
match types.kind(types.canonical(ty)) {
TypeKind::Bool => Some(Name::Distinct("bool".to_string())),
TypeKind::Int(kind) => Some(match integer(kind) {
Some(name) => Name::Distinct(name.to_string()),
None => Name::Character,
}),
TypeKind::BitInt { width, .. } => Some(Name::Distinct(format!("_BitInt({width})"))),
TypeKind::Float(kind) => Some(Name::Distinct(floating(kind).to_string())),
TypeKind::Complex(part) => match spelling(types, part)? {
Name::Distinct(name) => Some(Name::Distinct(format!("_Complex {name}"))),
Name::Character => Some(Name::Distinct("_Complex char".to_string())),
},
TypeKind::Pointer(_) => Some(Name::Distinct(POINTER.to_string())),
TypeKind::Atomic(inner) => spelling(types, inner),
TypeKind::Enum(id) => spelling(types, types.enum_info(id).underlying?),
_ => None,
}
}
const fn integer(kind: IntKind) -> Option<&'static str> {
match kind {
IntKind::Char | IntKind::SChar | IntKind::UChar => None,
IntKind::Short | IntKind::UShort => Some("short"),
IntKind::Int | IntKind::UInt => Some("int"),
IntKind::Long | IntKind::ULong => Some("long"),
IntKind::LongLong | IntKind::ULongLong => Some("long long"),
IntKind::Int128 | IntKind::UInt128 => Some("__int128"),
}
}
const fn floating(kind: FloatKind) -> &'static str {
match kind {
FloatKind::Float16 => "_Float16",
FloatKind::Float => "float",
FloatKind::Float32 => "_Float32",
FloatKind::Double => "double",
FloatKind::Float32x => "_Float32x",
FloatKind::Float64 => "_Float64",
FloatKind::LongDouble => "long double",
FloatKind::Float64x => "_Float64x",
FloatKind::Float128 => "_Float128",
}
}
#[cfg(test)]
mod tests {
use rucc_ir::MetaNode;
use rucc_target::TargetInfo;
use rucc_types::{ArrayLen, Qualifiers};
use super::*;
struct Fixture {
names: Interner,
types: Types,
module: Module,
tree: Tree,
}
impl Fixture {
fn new() -> Self {
let mut names = Interner::new();
let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse().expect("a triple"));
let module = Module::new(names.intern("test"), &target);
Self { names, types: Types::new(), module, tree: Tree::default() }
}
fn node(&mut self, ty: TypeId) -> Option<Meta> {
self.tree.node(&mut self.module, &mut self.names, &self.types, ty)
}
fn name(&self, node: Meta) -> &str {
match self.module[node] {
MetaNode::Tbaa(tbaa) => self.names.resolve(tbaa.name),
MetaNode::Plane(_) => panic!("a plane node in the aliasing tree"),
}
}
fn parent(&self, node: Meta) -> Option<Meta> {
match self.module[node] {
MetaNode::Tbaa(tbaa) => tbaa.parent,
MetaNode::Plane(_) => panic!("a plane node in the aliasing tree"),
}
}
}
#[test]
fn every_scalar_hangs_under_the_character_type() {
let mut fix = Fixture::new();
let int = fix.types.int(IntKind::Int);
let node = fix.node(int).expect("a node for int");
assert_eq!(fix.name(node), "int");
let root = fix.parent(node).expect("a parent for int");
assert_eq!(fix.name(root), "char");
assert_eq!(fix.parent(root), None, "the character type is the root");
}
#[test]
fn a_character_type_is_the_root_itself_rather_than_a_node_under_it() {
let mut fix = Fixture::new();
let plain = fix.types.int(IntKind::Char);
let signed = fix.types.int(IntKind::SChar);
let unsigned = fix.types.int(IntKind::UChar);
let root = fix.node(plain).expect("a node for char");
assert_eq!(fix.parent(root), None);
assert_eq!(fix.node(signed), Some(root));
assert_eq!(fix.node(unsigned), Some(root));
}
#[test]
fn the_unsigned_version_of_an_integer_type_is_the_same_node() {
let mut fix = Fixture::new();
let signed = fix.types.int(IntKind::Long);
let unsigned = fix.types.int(IntKind::ULong);
assert_eq!(fix.node(signed), fix.node(unsigned));
let wider = fix.types.int(IntKind::LongLong);
assert_ne!(fix.node(signed), fix.node(wider));
}
#[test]
fn an_enumeration_shares_with_what_it_is_represented_in() {
let mut fix = Fixture::new();
let int = fix.types.int(IntKind::Int);
let id = fix.types.declare_enum(None);
let enumeration = fix.types.enumeration(id);
assert_eq!(fix.node(enumeration), None);
fix.types.complete_enum(id, int, false);
assert_eq!(fix.node(enumeration), fix.node(int));
}
#[test]
fn a_typedef_and_a_qualifier_are_the_type_underneath_them() {
let mut fix = Fixture::new();
let long = fix.types.int(IntKind::ULong);
let name = fix.names.intern("size_t");
let sugar = fix.types.typedef(name, long);
let konst = fix.types.qualified(sugar, Qualifiers::CONST);
assert_eq!(fix.node(sugar), fix.node(long));
assert_eq!(fix.node(konst), fix.node(long));
}
#[test]
fn every_object_pointer_is_one_node() {
let mut fix = Fixture::new();
let int = fix.types.int(IntKind::Int);
let double = fix.types.float(FloatKind::Double);
let one = fix.types.pointer(int);
let other = fix.types.pointer(double);
let node = fix.node(one).expect("a node for a pointer");
assert_eq!(fix.name(node), "pointer");
assert_eq!(fix.node(other), Some(node));
}
#[test]
fn two_floating_types_with_the_same_format_are_still_two_nodes() {
let mut fix = Fixture::new();
let real = fix.types.float(FloatKind::Float);
let named = fix.types.float(FloatKind::Float32);
assert_ne!(fix.node(real), fix.node(named));
}
#[test]
fn the_types_that_are_reached_by_address_get_no_node() {
let mut fix = Fixture::new();
let int = fix.types.int(IntKind::Int);
let array = fix.types.array(int, ArrayLen::Fixed(4));
assert_eq!(fix.node(array), None);
let void = fix.types.void();
assert_eq!(fix.node(void), None);
}
#[test]
fn asking_twice_for_one_type_adds_one_node() {
let mut fix = Fixture::new();
let int = fix.types.int(IntKind::Int);
let short = fix.types.int(IntKind::Short);
let first = fix.node(int);
assert_eq!(fix.node(int), first);
fix.node(short);
assert_eq!(fix.module.metadata().count(), 3);
}
#[test]
fn a_module_whose_accesses_name_nothing_has_no_tree_at_all() {
let mut fix = Fixture::new();
let void = fix.types.void();
assert_eq!(fix.node(void), None);
assert_eq!(fix.module.metadata().count(), 0, "not even the root");
}
}