use std::collections::HashMap;
use rucc_base::Symbol;
use rucc_ir::{Attrs, DataLayout, Meta, Module};
#[derive(Clone, Copy, Debug)]
enum Named {
Func(Attrs),
Global,
Second,
}
#[derive(Clone, Debug, Default)]
pub struct Outside {
names: HashMap<Symbol, Named>,
parents: Vec<Option<Meta>>,
layout: Option<DataLayout>,
}
impl Outside {
#[must_use]
pub fn of(module: &Module) -> Self {
let mut names = HashMap::new();
for id in module.funcs() {
names.insert(module[id].name, Named::Func(module[id].attrs));
}
for id in module.globals() {
names.insert(module[id].name, Named::Global);
}
for id in module.aliases() {
names.insert(module[id].name, Named::Second);
}
let parents = module.metadata().map(|node| module[node].parent()).collect();
Self { names, parents, layout: Some(module.datalayout) }
}
#[must_use]
pub fn one_object(&self, name: Symbol) -> bool {
matches!(self.names.get(&name), Some(Named::Func(_) | Named::Global))
}
#[must_use]
pub fn attrs(&self, name: Symbol) -> Option<Attrs> {
match self.names.get(&name)? {
Named::Func(attrs) => Some(*attrs),
Named::Global | Named::Second => None,
}
}
#[must_use]
pub fn parent(&self, node: Meta) -> Option<Meta> {
self.parents.get(node.index()).copied().flatten()
}
#[must_use]
pub fn pointer_bytes(&self) -> Option<u64> {
Some(u64::from(self.layout?.pointer_bits).div_ceil(8))
}
}
#[cfg(test)]
mod tests {
use rucc_base::Interner;
use rucc_ir::{Alias, AttrSet, Attrs, Func, Global, MetaNode, Module, Signature, TbaaNode};
use rucc_target::{TargetInfo, Triple};
use super::Outside;
fn module(names: &mut Interner) -> Module {
let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
Module::new(names.intern("t.c"), &target)
}
#[test]
fn a_function_and_a_global_each_name_one_object() {
let mut names = Interner::new();
let mut module = module(&mut names);
let f = names.intern("f");
let table = names.intern("table");
module.add_func(Func::new(f, Signature::new()));
module.add_global(Global::new(table, 16, 8));
let outside = Outside::of(&module);
assert!(outside.one_object(f));
assert!(outside.one_object(table));
}
#[test]
fn an_alias_and_a_name_nobody_has_do_not() {
let mut names = Interner::new();
let mut module = module(&mut names);
let here = names.intern("here");
let second = names.intern("second");
module.add_func(Func::new(here, Signature::new()));
module.add_alias(Alias::new(second, here));
let outside = Outside::of(&module);
assert!(!outside.one_object(second));
assert!(!outside.one_object(names.intern("nowhere")));
}
#[test]
fn a_function_carries_what_it_is_declared_to_do_to_memory() {
let mut names = Interner::new();
let mut module = module(&mut names);
let f = names.intern("f");
let mut func = Func::new(f, Signature::new());
func.attrs = Attrs { set: AttrSet::READONLY, ..Attrs::default() };
module.add_func(func);
let table = names.intern("table");
module.add_global(Global::new(table, 16, 8));
let outside = Outside::of(&module);
assert!(outside.attrs(f).expect("it is a function").set.contains(AttrSet::READONLY));
assert!(outside.attrs(table).is_none(), "a global is not something a call names");
}
#[test]
fn a_type_node_knows_the_one_above_it() {
let mut names = Interner::new();
let mut module = module(&mut names);
let root = module.add_meta(MetaNode::Tbaa(TbaaNode {
name: names.intern("omnipotent char"),
parent: None,
offset: 0,
}));
let under = module.add_meta(MetaNode::Tbaa(TbaaNode {
name: names.intern("int"),
parent: Some(root),
offset: 0,
}));
let outside = Outside::of(&module);
assert_eq!(outside.parent(under), Some(root));
assert_eq!(outside.parent(root), None);
}
#[test]
fn the_empty_one_knows_nothing_and_says_so() {
let mut names = Interner::new();
let outside = Outside::default();
assert!(!outside.one_object(names.intern("f")));
assert!(outside.attrs(names.intern("f")).is_none());
assert!(outside.pointer_bytes().is_none());
}
#[test]
fn a_module_says_how_wide_an_address_is() {
let mut names = Interner::new();
let module = module(&mut names);
assert_eq!(Outside::of(&module).pointer_bytes(), Some(8));
}
}