use smallvec::SmallVec;
use super::{Symbol, SymbolName, SymbolNameComponent, SymbolPath, SymbolRef, generate_symbol_name};
use crate::{
FxHashMap, Op, Operation, OperationRef, ProgramPoint, Report, UnsafeIntrusiveEntityRef,
traits::{GraphRegionNoTerminator, NoTerminator, Terminator},
};
pub type SymbolTableRef = UnsafeIntrusiveEntityRef<dyn SymbolTable>;
pub trait SymbolTable {
fn as_symbol_table_operation(&self) -> &Operation;
fn as_symbol_table_operation_mut(&mut self) -> &mut Operation;
fn symbol_manager(&self) -> SymbolManager<'_>;
fn symbol_manager_mut(&mut self) -> SymbolManagerMut<'_>;
fn get(&self, name: SymbolName) -> Option<SymbolRef> {
self.symbol_manager().lookup(name)
}
fn resolve(&self, path: &SymbolPath) -> Option<SymbolRef> {
let found = self.symbol_manager().lookup_symbol_ref(path)?;
found.as_trait_ref::<dyn Symbol>()
}
fn insert_new(&mut self, entry: SymbolRef, ip: ProgramPoint) -> bool {
self.symbol_manager_mut().insert_new(entry, ip)
}
fn insert(&mut self, entry: SymbolRef, ip: ProgramPoint) -> SymbolName {
self.symbol_manager_mut().insert(entry, ip)
}
fn remove(&mut self, name: SymbolName) -> Option<SymbolRef> {
let mut manager = self.symbol_manager_mut();
let mut symbol = manager.lookup(name)?;
manager.remove(symbol);
symbol.borrow_mut().uses_mut().clear();
Some(symbol)
}
fn rename(&mut self, from: SymbolName, to: SymbolName) -> Result<(), Report> {
let mut manager = self.symbol_manager_mut();
let symbol = manager.lookup(from).unwrap_or_else(|| panic!("undefined symbol '{from}'"));
manager.rename_symbol(symbol, to)
}
}
impl dyn SymbolTable {
pub fn as_operation_ref(&self) -> OperationRef {
self.as_symbol_table_operation().as_operation_ref()
}
pub fn find<T: Op + Symbol>(&self, name: SymbolName) -> Option<UnsafeIntrusiveEntityRef<T>> {
let symbol = self.get(name)?;
symbol
.borrow()
.as_symbol_operation()
.as_operation_ref()
.try_downcast_op::<T>()
.ok()
}
}
#[derive(Default, Debug)]
pub struct SymbolMap {
symbols: FxHashMap<SymbolName, SymbolRef>,
uniquing_count: usize,
}
impl SymbolMap {
pub fn build(op: &Operation) -> Self {
let mut symbols = FxHashMap::default();
let region = op.regions().front().get().unwrap();
for op in region.entry().body() {
if let Some(symbol_ref) = op.as_symbol_ref() {
let name = symbol_ref.borrow().name();
symbols
.try_insert(name, symbol_ref)
.expect("expected region to contain uniquely named symbol operations");
}
}
Self {
symbols,
uniquing_count: 0,
}
}
pub fn get(&self, name: impl Into<SymbolName>) -> Option<SymbolRef> {
let name = name.into();
self.symbols.get(&name).cloned()
}
pub fn get_op(&self, name: impl Into<SymbolName>) -> Option<OperationRef> {
let name = name.into();
self.symbols.get(&name).map(|symbol| symbol.borrow().as_operation_ref())
}
pub fn resolve(&self, symbol_table: &Operation, attr: &SymbolPath) -> Option<OperationRef> {
let mut components = attr.components();
if attr.is_absolute() {
let _ = components.next();
let root = if let Some(mut root) = symbol_table.parent_op() {
while let Some(ancestor) = root.borrow().parent_op() {
root = ancestor;
}
root
} else {
symbol_table.as_operation_ref()
};
let root_op = root.borrow();
if let Some(root_symbol) = root_op.as_trait::<dyn Symbol>() {
match components.next()? {
SymbolNameComponent::Leaf(name) => {
return if name == root_symbol.name() {
Some(root)
} else {
None
};
}
SymbolNameComponent::Component(name) => {
if name != root_symbol.name() {
return None;
}
}
SymbolNameComponent::Root => unreachable!(),
}
}
let root_symbol_table = root_op.as_trait::<dyn SymbolTable>()?;
let symbol_manager = root_symbol_table.symbol_manager();
symbol_manager.symbols().resolve_components(components)
} else {
self.resolve_components(components)
}
}
pub fn resolve_all_in(
&self,
_symbol_table: &Operation,
_attr: &SymbolPath,
_nested_references: &mut SmallVec<[OperationRef; 4]>,
) -> Option<OperationRef> {
todo!()
}
fn resolve_components(
&self,
mut components: impl ExactSizeIterator<Item = SymbolNameComponent>,
) -> Option<OperationRef> {
match components.next()? {
super::SymbolNameComponent::Component(name) => {
let mut found = self.get_op(name);
loop {
let op_ref = found.take()?;
let op = op_ref.borrow();
let symbol_table = op.as_trait::<dyn SymbolTable>()?;
let manager = symbol_table.symbol_manager();
match components.next() {
None => return Some(op_ref),
Some(super::SymbolNameComponent::Component(name)) => {
found = manager.lookup_op(name);
}
Some(super::SymbolNameComponent::Leaf(name)) => {
assert_eq!(components.next(), None);
break manager.lookup_op(name);
}
Some(super::SymbolNameComponent::Root) => unreachable!(),
}
}
}
super::SymbolNameComponent::Leaf(name) => self.get_op(name),
super::SymbolNameComponent::Root => {
unreachable!("root component should have already been consumed")
}
}
}
#[inline]
pub fn contains_key<K>(&self, name: &K) -> bool
where
K: ?Sized + core::hash::Hash + hashbrown::Equivalent<SymbolName>,
{
self.symbols.contains_key(name)
}
#[inline]
pub fn remove(&mut self, name: SymbolName) -> Option<SymbolRef> {
self.symbols.remove(&name)
}
#[inline]
pub fn insert_new(&mut self, name: SymbolName, symbol: SymbolRef) -> bool {
self.symbols.try_insert(name, symbol).is_ok()
}
pub fn insert(&mut self, name: SymbolName, mut symbol: SymbolRef) -> SymbolName {
match self.symbols.try_insert(name, symbol) {
Ok(_) => {
symbol.borrow_mut().set_name(name);
name
}
Err(err) => {
if err.entry.get() == &symbol {
assert_eq!(
symbol.borrow().name(),
name,
"name does not match what was registered with the symbol table"
);
return name;
}
let uniqued = generate_symbol_name(name, &mut self.uniquing_count, |name| {
!self.symbols.contains_key(name)
});
symbol.borrow_mut().set_name(uniqued);
self.symbols.insert(uniqued, symbol);
uniqued
}
}
}
pub fn make_unique(&mut self, op: &SymbolRef, tables: &[SymbolManager<'_>]) -> SymbolName {
let name = { op.borrow().name() };
generate_symbol_name(name, &mut self.uniquing_count, |name| {
if self.symbols.contains_key(name) {
return false;
}
!tables.iter().any(|t| t.symbols.contains_key(name))
})
}
pub fn symbols(&self) -> impl Iterator<Item = SymbolRef> + '_ {
self.symbols.values().cloned()
}
}
pub enum Symbols<'a> {
Owned(SymbolMap),
Borrowed(&'a SymbolMap),
}
impl From<SymbolMap> for Symbols<'_> {
fn from(value: SymbolMap) -> Self {
Self::Owned(value)
}
}
impl core::ops::Deref for Symbols<'_> {
type Target = SymbolMap;
fn deref(&self) -> &Self::Target {
match self {
Self::Owned(symbols) => symbols,
Self::Borrowed(symbols) => symbols,
}
}
}
pub enum SymbolsMut<'a> {
Owned(SymbolMap),
Borrowed(&'a mut SymbolMap),
}
impl From<SymbolMap> for SymbolsMut<'_> {
fn from(value: SymbolMap) -> Self {
Self::Owned(value)
}
}
impl core::ops::Deref for SymbolsMut<'_> {
type Target = SymbolMap;
fn deref(&self) -> &Self::Target {
match self {
Self::Owned(symbols) => symbols,
Self::Borrowed(symbols) => symbols,
}
}
}
impl core::ops::DerefMut for SymbolsMut<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
Self::Owned(symbols) => symbols,
Self::Borrowed(symbols) => symbols,
}
}
}
pub struct SymbolManager<'a> {
symbol_table: &'a Operation,
symbols: Symbols<'a>,
}
impl<'a> SymbolManager<'a> {
pub fn new(symbol_table: &'a Operation, symbols: Symbols<'a>) -> Self {
Self {
symbol_table,
symbols,
}
}
pub fn is_root(&self) -> bool {
self.symbol_table.parent().is_none()
}
pub fn symbol_table(&self) -> &Operation {
self.symbol_table
}
pub fn symbols(&self) -> &SymbolMap {
&self.symbols
}
pub fn lookup(&self, name: impl Into<SymbolName>) -> Option<SymbolRef> {
self.symbols.get(name)
}
pub fn lookup_op(&self, name: impl Into<SymbolName>) -> Option<OperationRef> {
self.symbols.get_op(name)
}
pub fn lookup_symbol_ref(&self, attr: &SymbolPath) -> Option<OperationRef> {
self.symbols.resolve(self.symbol_table, attr)
}
}
impl<'a> From<&'a Operation> for SymbolManager<'a> {
fn from(symbol_table: &'a Operation) -> Self {
Self {
symbol_table,
symbols: SymbolMap::build(symbol_table).into(),
}
}
}
pub struct SymbolManagerMut<'a> {
symbol_table: &'a mut Operation,
symbols: SymbolsMut<'a>,
}
impl<'a> SymbolManagerMut<'a> {
pub fn new(symbol_table: &'a mut Operation, symbols: SymbolsMut<'a>) -> Self {
Self {
symbol_table,
symbols,
}
}
pub(crate) fn symbols_mut<'b: 'a, 'this: 'b>(&'this mut self) -> &'this mut SymbolsMut<'b> {
&mut self.symbols
}
pub fn symbol_table(&mut self) -> &Operation {
self.symbol_table
}
pub fn symbol_table_mut(&mut self) -> &mut Operation {
self.symbol_table
}
pub fn lookup(&self, name: impl Into<SymbolName>) -> Option<SymbolRef> {
self.symbols.get(name)
}
pub fn lookup_op(&self, name: impl Into<SymbolName>) -> Option<OperationRef> {
self.symbols.get_op(name)
}
pub fn lookup_symbol_ref(&self, attr: &SymbolPath) -> Option<OperationRef> {
self.symbols.resolve(self.symbol_table, attr)
}
pub fn remove(&mut self, op: SymbolRef) {
let name = {
let symbol = op.borrow();
let symbol_op = symbol.as_operation_ref();
assert_eq!(
symbol_op.borrow().parent_op(),
Some(self.symbol_table.as_operation_ref()),
"expected `op` to be a child of this symbol table"
);
symbol.name()
};
self.symbols.remove(name);
}
pub fn insert_new(&mut self, symbol: SymbolRef, ip: ProgramPoint) -> bool {
let name = symbol.borrow().name();
if self.symbols.contains_key(&name) {
return false;
}
assert_eq!(self.insert(symbol, ip), name, "expected insertion to preserve original name");
true
}
pub fn insert(&mut self, symbol: SymbolRef, mut ip: ProgramPoint) -> SymbolName {
let (name, symbol_op) = {
let sym = symbol.borrow();
let symbol_op = sym.as_operation_ref();
assert!(
symbol_op
.borrow()
.parent_op()
.is_none_or(|p| p == self.symbol_table.as_operation_ref()),
"symbol is already inserted in another op"
);
(sym.name(), symbol_op)
};
if symbol_op.borrow().parent().is_none() {
let requires_terminator = !self.symbol_table.implements::<dyn NoTerminator>()
&& !self.symbol_table.implements::<dyn GraphRegionNoTerminator>();
let mut body = self.symbol_table.region_mut(0);
let mut block = body.entry_mut();
if requires_terminator {
let block_terminator = block
.terminator()
.expect("symbol table op requires a terminator, but one was not found");
if ip.is_unset() {
let ops = block.body_mut();
unsafe {
let mut cursor = ops.cursor_mut_from_ptr(block_terminator);
cursor.insert_before(symbol_op);
}
} else {
let ip_block = ip.block();
assert_eq!(
ip_block,
Some(block.as_block_ref()),
"invalid insertion point: not located in this symbol table"
);
if ip.is_at_block_end() {
assert!(
symbol_op.borrow().implements::<dyn Terminator>(),
"cannot insert symbol after the region terminator"
);
}
ip.cursor_mut().unwrap().insert_after(symbol_op);
}
} else if ip.is_unset() {
block.body_mut().push_back(symbol_op);
} else {
let ip_block = ip.block();
assert_eq!(
ip_block,
Some(block.as_block_ref()),
"invalid insertion point: not located in this symbol table"
);
ip.cursor_mut().unwrap().insert_after(symbol_op);
}
}
self.symbols.insert(name, symbol)
}
pub fn rename_symbol(&mut self, mut op: SymbolRef, to: SymbolName) -> Result<(), Report> {
let name = {
let symbol = op.borrow();
let name = symbol.name();
let symbol_op = symbol.as_symbol_operation();
assert!(
symbol_op
.parent_op()
.is_some_and(|parent| parent == self.symbol_table.as_operation_ref()),
"expected operation to be a child of this symbol table"
);
assert!(
self.lookup(name).as_ref().is_some_and(|o| o == &op),
"current name does not resolve to `op`"
);
assert!(
!self.symbols.contains_key(&to),
"new symbol name given by `to` is already in use"
);
name
};
self.replace_all_symbol_uses(op, to)?;
self.remove(op);
{
op.borrow_mut().set_name(to);
}
self.insert(op, ProgramPoint::default());
assert!(
self.lookup(to).is_some_and(|o| o == op),
"new name does not resolve to renamed op"
);
assert!(!self.symbols.contains_key(&name), "old name still exists");
Ok(())
}
pub fn replace_all_symbol_uses(&mut self, op: SymbolRef, to: SymbolName) -> Result<(), Report> {
let symbol = op.borrow();
let mut users = symbol.uses().front();
while let Some(user) = users.as_pointer() {
users.move_next();
let mut attr = user.borrow().attr;
attr.borrow_mut().path_mut().set_name(to);
}
Ok(())
}
pub fn make_unique(
&mut self,
op: SymbolRef,
tables: &[SymbolManager<'_>],
) -> Result<SymbolName, Report> {
let uniqued = self.symbols.make_unique(&op, tables);
self.rename_symbol(op, uniqued)?;
Ok(uniqued)
}
}
impl<'a> From<&'a mut Operation> for SymbolManagerMut<'a> {
fn from(symbol_table: &'a mut Operation) -> Self {
let symbols = SymbolMap::build(&*symbol_table).into();
Self {
symbol_table,
symbols,
}
}
}