use crate::{
arg_error_noloc,
basic_block::BasicBlock,
common_traits::Verify,
dialect::{Dialect, DialectName},
identifier::Identifier,
operation::Operation,
printable::{self, Printable},
region::Region,
result::Result,
storage_uniquer::UniqueStore,
r#type::TypeObj,
uniqued_any::UniquedAny,
verify_err_noloc,
};
use rustc_hash::{FxHashMap, FxHashSet};
use slotmap::{SlotMap, new_key_type};
use std::{
any::{Any, TypeId},
cell::{Ref, RefCell, RefMut},
fmt::{Debug, Display},
hash::Hash,
marker::PhantomData,
sync::LazyLock,
};
new_key_type! {
pub struct ArenaIndex;
}
new_key_type! {
pub struct AuxDataIndex;
}
impl Display for ArenaIndex {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.0)
}
}
pub type Arena<T> = SlotMap<ArenaIndex, RefCell<T>>;
pub struct Context {
pub(crate) operations: Arena<Operation>,
pub(crate) basic_blocks: Arena<BasicBlock>,
pub(crate) regions: Arena<Region>,
pub(crate) dialects: FxHashMap<DialectName, Dialect>,
pub(crate) type_store: UniqueStore<TypeObj>,
pub(crate) uniqued_any_store: UniqueStore<UniquedAny>,
pub aux_data: SlotMap<AuxDataIndex, Box<dyn Any>>,
pub aux_data_map: FxHashMap<Identifier, AuxDataIndex>,
#[cfg(test)]
pub(crate) linked_list_store: crate::linked_list::tests::LinkedListTestArena,
}
impl Context {
pub fn new() -> Context {
Self::default()
}
pub fn is_ir_empty(&self) -> bool {
self.operations.is_empty() && self.basic_blocks.is_empty() && self.regions.is_empty()
}
}
impl Default for Context {
fn default() -> Self {
let mut ctx = Context {
operations: Arena::default(),
basic_blocks: Arena::default(),
regions: Arena::default(),
dialects: FxHashMap::default(),
type_store: UniqueStore::default(),
uniqued_any_store: UniqueStore::default(),
aux_data: SlotMap::with_key(),
aux_data_map: FxHashMap::default(),
#[cfg(test)]
linked_list_store: crate::linked_list::tests::LinkedListTestArena::default(),
};
if let Err(err) = &*DICT_KEYS_VERIFIER {
panic!("{}", err.err);
}
for registration in get_context_registrations() {
registration(&mut ctx);
}
ctx
}
}
pub(crate) mod private {
use std::{cell::RefCell, marker::PhantomData};
use super::{Arena, ArenaIndex, Context, Ptr};
pub trait ArenaObj
where
Self: Sized,
{
fn get_arena(ctx: &Context) -> &Arena<Self>;
fn get_arena_mut(ctx: &mut Context) -> &mut Arena<Self>;
fn get_self_ptr(&self, ctx: &Context) -> Ptr<Self>;
fn dealloc_sub_objects(ptr: Ptr<Self>, ctx: &mut Context);
fn alloc<T: FnOnce(Ptr<Self>) -> Self>(ctx: &mut Context, f: T) -> Ptr<Self> {
let creator = |idx: ArenaIndex| {
let t = f(Ptr::<Self> {
idx,
_dummy: PhantomData::<Self>,
});
RefCell::new(t)
};
Ptr::<Self> {
idx: Self::get_arena_mut(ctx).insert_with_key(creator),
_dummy: PhantomData,
}
}
fn dealloc(ptr: Ptr<Self>, ctx: &mut Context) {
Self::dealloc_sub_objects(ptr, ctx);
Self::get_arena_mut(ctx).remove(ptr.idx);
}
}
}
use private::ArenaObj;
pub struct Ptr<T: ArenaObj> {
pub(crate) idx: ArenaIndex,
pub(crate) _dummy: PhantomData<T>,
}
impl<T: ArenaObj> std::fmt::Debug for Ptr<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Ptr<{}>[{}]", std::any::type_name::<T>(), self.idx)
}
}
#[derive(Debug, thiserror::Error)]
#[error("Attempt to dereference a dangling Ptr")]
pub struct DanglingPtrDerefError;
impl<'a, T: ArenaObj> Ptr<T> {
#[track_caller]
pub fn deref(&self, ctx: &'a Context) -> Ref<'a, T> {
T::get_arena(ctx)
.get(self.idx)
.expect("Dangling Ptr deref")
.borrow()
}
#[track_caller]
pub fn deref_mut(&self, ctx: &'a Context) -> RefMut<'a, T> {
T::get_arena(ctx)
.get(self.idx)
.expect("Dangling Ptr deref_mut")
.borrow_mut()
}
pub fn try_deref(&self, ctx: &'a Context) -> Result<Ref<'a, T>> {
T::get_arena(ctx)
.get(self.idx)
.ok_or_else(|| arg_error_noloc!(DanglingPtrDerefError))?
.try_borrow()
.map_err(|err| arg_error_noloc!(err))
}
pub fn try_deref_mut(&self, ctx: &'a Context) -> Result<RefMut<'a, T>> {
T::get_arena(ctx)
.get(self.idx)
.ok_or_else(|| arg_error_noloc!(DanglingPtrDerefError))?
.try_borrow_mut()
.map_err(|err| arg_error_noloc!(err))
}
pub(crate) fn make_name(&self, name_base: &str) -> Identifier {
let idx = format!("{}", self.idx);
(name_base.to_string() + &idx).try_into().unwrap()
}
}
impl<T: ArenaObj> Clone for Ptr<T> {
fn clone(&self) -> Ptr<T> {
*self
}
}
impl<T: ArenaObj> Copy for Ptr<T> {}
impl<T: ArenaObj> PartialEq for Ptr<T> {
fn eq(&self, other: &Self) -> bool {
self.idx == other.idx
}
}
impl<T: ArenaObj> Eq for Ptr<T> {}
impl<T: ArenaObj + 'static> Hash for Ptr<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
TypeId::of::<T>().hash(state);
self.idx.hash(state);
}
}
impl<T: ArenaObj + Printable> Printable for Ptr<T> {
fn fmt(
&self,
ctx: &Context,
state: &printable::State,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
self.deref(ctx).fmt(ctx, state, f)
}
}
impl<T: ArenaObj + Verify> Verify for Ptr<T> {
fn verify(&self, ctx: &Context) -> Result<()> {
self.deref(ctx).verify(ctx)
}
}
#[doc(hidden)]
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct DictKeyId {
pub id: Identifier,
pub file: &'static str,
pub line: u32,
pub column: u32,
}
pub type ContextRegistration = fn(&mut Context);
#[doc(hidden)]
#[cfg(not(target_family = "wasm"))]
pub mod statics {
use super::*;
#[::pliron::linkme::distributed_slice]
#[linkme(crate = ::pliron::linkme)]
pub static DICT_KEY_IDS: [LazyLock<DictKeyId>];
pub fn get_dict_key_ids() -> impl Iterator<Item = &'static LazyLock<DictKeyId>> {
DICT_KEY_IDS.iter()
}
#[::pliron::linkme::distributed_slice]
#[linkme(crate = ::pliron::linkme)]
pub static CONTEXT_REGISTRATIONS: [LazyLock<ContextRegistration>];
pub fn get_context_registrations()
-> impl Iterator<Item = &'static LazyLock<ContextRegistration>> {
CONTEXT_REGISTRATIONS.iter()
}
}
#[cfg(target_family = "wasm")]
pub mod statics {
use super::*;
use crate::utils::inventory::LazyLockWrapper;
::pliron::inventory::collect!(LazyLockWrapper<DictKeyId>);
pub fn get_dict_key_ids() -> impl Iterator<Item = &'static LazyLock<DictKeyId>> {
::pliron::inventory::iter::<LazyLockWrapper<DictKeyId>>().map(|llw| llw.0)
}
::pliron::inventory::collect!(LazyLockWrapper<ContextRegistration>);
pub fn get_context_registrations()
-> impl Iterator<Item = &'static LazyLock<ContextRegistration>> {
::pliron::inventory::iter::<LazyLockWrapper<ContextRegistration>>().map(|llw| llw.0)
}
}
pub use statics::*;
#[doc(hidden)]
pub static DICT_KEYS_VERIFIER: LazyLock<Result<()>> = LazyLock::new(verify_dict_keys);
#[doc(hidden)]
pub(crate) fn collect_deduped_interface_verifiers<Id, AllVerifiers, Verifier>(
interface_verifiers: impl Iterator<Item = &'static LazyLock<(Id, AllVerifiers)>>,
) -> FxHashMap<Id, Vec<Verifier>>
where
Id: Eq + Hash + Clone + 'static,
AllVerifiers: Fn() -> Vec<Verifier> + Clone + 'static,
Verifier: Eq + Hash + Clone,
{
let mut grouped = FxHashMap::default();
for lazy in interface_verifiers {
let (id, all_verifiers_for_interface) = &**lazy;
grouped
.entry(id.clone())
.and_modify(|verifiers: &mut Vec<AllVerifiers>| {
verifiers.push(all_verifiers_for_interface.clone())
})
.or_insert(vec![all_verifiers_for_interface.clone()]);
}
grouped
.into_iter()
.map(|(id, verifiers)| {
let mut dedupd_verifiers = Vec::new();
let mut seen = FxHashSet::default();
for verifier_fn_list in verifiers {
for verifier in verifier_fn_list() {
if seen.insert(verifier.clone()) {
dedupd_verifiers.push(verifier);
}
}
}
(id, dedupd_verifiers)
})
.collect()
}
#[doc(hidden)]
pub fn verify_dict_keys() -> Result<()> {
let mut seen: FxHashMap<Identifier, (&'static str, u32, u32)> = FxHashMap::default();
for key in get_dict_key_ids() {
if let Some((file, line, column)) = seen.get(&key.id) {
return verify_err_noloc!(
"Duplicate dictionary key \"{}\" declared in {}:{}:{} and {}:{}:{}",
key.id,
file,
line,
column,
key.file,
key.line,
key.column
);
}
seen.insert(key.id.clone(), (key.file, key.line, key.column));
}
Ok(())
}
#[macro_export]
macro_rules! dict_key {
( $(#[$outer:meta])*
$decl:ident, $name:expr
) => {
const _: () = {
#[cfg_attr(not(target_family = "wasm"),
::pliron::linkme::distributed_slice(::pliron::context::DICT_KEY_IDS), linkme(crate = ::pliron::linkme))]
pub static $decl: std::sync::LazyLock<::pliron::context::DictKeyId> =
std::sync::LazyLock::new(|| ::pliron::context::DictKeyId {
id: $name.try_into().unwrap(),
file: file!(),
line: line!(),
column: column!(),
});
#[cfg(target_family = "wasm")]
::pliron::inventory::submit! {
::pliron::utils::inventory::LazyLockWrapper(&$decl)
}
};
$(#[$outer])*
pub static $decl: std::sync::LazyLock<::pliron::identifier::Identifier> =
std::sync::LazyLock::new(|| $name.try_into().unwrap());
};
}
#[macro_export]
macro_rules! context_registration {
( $(#[$outer:meta])*
$registration:expr
) => {
const _: () = {
$(#[$outer])*
#[cfg_attr(not(target_family = "wasm"),
::pliron::linkme::distributed_slice(::pliron::context::CONTEXT_REGISTRATIONS), linkme(crate = ::pliron::linkme))]
static CONTEXT_REGISTRATION: std::sync::LazyLock<::pliron::context::ContextRegistration> =
std::sync::LazyLock::new(|| $registration);
#[cfg(target_family = "wasm")]
::pliron::inventory::submit! {
::pliron::utils::inventory::LazyLockWrapper(&CONTEXT_REGISTRATION)
}
};
};
}