use crate::ir::entities::{BasicBlockData, FunctionData, Program, ValueData};
use std::borrow::Borrow;
use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::{Result, Write};
use std::num::NonZeroUsize;
use std::path::Path;
use std::rc::Rc;
#[derive(Default)]
pub struct NameManager {
next_id: usize,
cur_scope: ScopeKind,
prefix: Prefix,
global_names: HashSet<StringRc>,
bb_names: HashSet<StringRc>,
global_vars: HashMap<*const ValueData, Rc<String>>,
funcs: HashMap<*const FunctionData, Rc<String>>,
bbs: HashMap<*const BasicBlockData, Rc<String>>,
values: HashMap<*const ValueData, Rc<String>>,
}
impl NameManager {
pub fn new() -> Self {
Self::default()
}
pub fn enter_func_scope(&mut self) {
assert!(
matches!(self.cur_scope, ScopeKind::Global),
"already in function scope"
);
self.cur_scope = ScopeKind::Function;
self.values.clear();
}
pub fn exit_func_scope(&mut self) {
assert!(
matches!(self.cur_scope, ScopeKind::Function),
"not in function scope"
);
self.cur_scope = ScopeKind::Global;
self.bb_names.clear();
self.bbs.clear();
for name in self.values.values() {
self.global_names.remove(name);
}
}
pub fn set_prefix(&mut self, prefix: Prefix) {
self.prefix = prefix;
}
pub fn func_name(&mut self, func: &FunctionData) -> Rc<String> {
let ptr: *const FunctionData = func;
if let Some(name) = self.funcs.get(&ptr) {
name.clone()
} else {
let name = self.next_name_str(func.name(), |s| &mut s.global_names);
self.funcs.insert(ptr, name);
self.funcs[&ptr].clone()
}
}
pub fn bb_name(&mut self, bb: &BasicBlockData) -> Rc<String> {
let ptr: *const BasicBlockData = bb;
if let Some(name) = self.bbs.get(&ptr) {
name.clone()
} else {
let name = self.next_name(bb.name(), |s| &mut s.bb_names);
self.bbs.insert(ptr, name);
self.bbs[&ptr].clone()
}
}
pub fn value_name(&mut self, value: &ValueData) -> Rc<String> {
assert!(!value.kind().is_const(), "can not name constants");
if value.kind().is_global_alloc() {
self.value_name_impl(value, |s| &mut s.global_vars)
} else {
self.value_name_impl(value, |s| &mut s.values)
}
}
fn value_name_impl<F>(&mut self, value: &ValueData, value_set: F) -> Rc<String>
where
F: for<'a> Fn(&'a mut Self) -> &'a mut HashMap<*const ValueData, Rc<String>>,
{
let ptr: *const ValueData = value;
if let Some(name) = value_set(self).get(&ptr) {
name.clone()
} else {
let name = self.next_name(value.name(), |s| &mut s.global_names);
let values = value_set(self);
values.insert(ptr, name);
values[&ptr].clone()
}
}
pub fn temp_value_name(&mut self) -> Rc<String> {
self.next_name(&None, |s| &mut s.global_names)
}
fn next_name<F>(&mut self, name: &Option<String>, name_set: F) -> Rc<String>
where
F: for<'a> FnOnce(&'a mut Self) -> &'a mut HashSet<StringRc>,
{
if let Some(name) = name {
self.next_name_str(name, name_set)
} else {
let name = self.prefix.temp_name(self.next_id);
self.next_id += 1;
let names = name_set(self);
names.insert(name.clone().into());
names.get(&name).unwrap().to_rc()
}
}
fn next_name_str<F>(&mut self, name: &str, name_set: F) -> Rc<String>
where
F: for<'a> FnOnce(&'a mut Self) -> &'a mut HashSet<StringRc>,
{
let name = self.prefix.name(name);
let names = name_set(self);
if !names.contains(&name) {
names.insert(name.clone().into());
names.get(&name).unwrap().to_rc()
} else {
for id in 0.. {
let new_name = format!("{}_{}", name, id);
if !names.contains(&new_name) {
names.insert(new_name.clone().into());
return names.get(&new_name).unwrap().to_rc();
}
}
unreachable!()
}
}
}
#[derive(Clone, Copy, Default)]
enum ScopeKind {
#[default]
Global,
Function,
}
#[derive(Default)]
pub enum Prefix {
#[default]
Default,
Custom {
named: String,
temp: String,
max_len: Option<NonZeroUsize>,
},
}
impl Prefix {
fn name(&self, name: &str) -> String {
match self {
Prefix::Default => name.into(),
Prefix::Custom {
named,
temp,
max_len,
} => {
let mut name = if let Some(name) = name.strip_prefix('@') {
format!("{}{}", named, name)
} else {
format!("{}{}", temp, &name[1..])
};
if let Some(max_len) = max_len {
name.truncate(max_len.get());
}
name
}
}
}
fn temp_name(&self, id: usize) -> String {
match self {
Prefix::Default => format!("%{}", id),
Prefix::Custom { temp, .. } => format!("{}{}", temp, id),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
struct StringRc(Rc<String>);
impl StringRc {
fn to_rc(&self) -> Rc<String> {
self.0.clone()
}
}
impl From<String> for StringRc {
fn from(s: String) -> Self {
Self(Rc::new(s))
}
}
impl From<&str> for StringRc {
fn from(s: &str) -> Self {
Self(Rc::new(s.into()))
}
}
impl Borrow<Rc<String>> for StringRc {
fn borrow(&self) -> &Rc<String> {
&self.0
}
}
impl Borrow<String> for StringRc {
fn borrow(&self) -> &String {
self.0.as_ref()
}
}
impl Borrow<str> for StringRc {
fn borrow(&self) -> &str {
self.0.as_ref()
}
}
pub struct Generator<W: Write, V: Visitor<W>> {
writer: W,
visitor: V,
name_man: NameManager,
}
impl<W: Write, V: Visitor<W>> Generator<W, V> {
pub fn new(writer: W) -> Self
where
V: Default,
{
Self {
writer,
visitor: V::default(),
name_man: NameManager::new(),
}
}
pub fn with_visitor(writer: W, visitor: V) -> Self {
Self {
writer,
visitor,
name_man: NameManager::new(),
}
}
pub fn writer(self) -> W {
self.writer
}
pub fn generate_on(&mut self, program: &Program) -> Result<V::Output> {
self
.visitor
.visit(&mut self.writer, &mut self.name_man, program)
}
}
impl<V: Visitor<File>> Generator<File, V> {
pub fn from_path<P>(path: P) -> Result<Self>
where
V: Default,
P: AsRef<Path>,
{
File::create(path).map(Generator::new)
}
}
pub trait Visitor<W: Write> {
type Output;
fn visit(&mut self, w: &mut W, nm: &mut NameManager, program: &Program) -> Result<Self::Output>;
}