use alloc::{format, vec::Vec};
use core::{
cell::{Ref, RefMut},
hash::Hash,
marker::PhantomData,
};
use rustc_hash::FxHashSet;
use crate::{
arg_error,
basic_block::BasicBlock,
common_traits::{Named, Verify},
context::{Context, Ptr},
debug_info,
identifier::Identifier,
linked_list::{ContainsLinkedList, LinkedList},
location::{Located, Location},
operation::{DefUseVerifyErr, Operation},
printable::Printable,
result::Result,
r#type::{TypeHandle, Typed},
verify_err, verify_error,
};
pub(crate) trait DefUseParticipant: Copy + Hash + Eq {}
impl DefUseParticipant for Value {}
impl DefUseParticipant for Ptr<BasicBlock> {}
pub(crate) struct DefNode<T: DefUseParticipant> {
uses: FxHashSet<Use<T>>,
}
impl<T: DefUseParticipant> DefNode<T> {
pub(crate) fn new() -> DefNode<T> {
DefNode {
uses: FxHashSet::default(),
}
}
pub(crate) fn is_used(&self) -> bool {
!self.uses.is_empty()
}
pub(crate) fn num_uses(&self) -> usize {
self.uses.len()
}
pub(crate) fn has_use_of(&self, r#use: &Use<T>) -> bool {
self.uses.contains(r#use)
}
pub(crate) fn uses(&self) -> impl Iterator<Item = Use<T>> + '_ {
self.uses.iter().cloned()
}
pub(crate) fn add_use(&mut self, self_descr: T, r#use: Use<T>) -> UseNode<T> {
if !self.uses.insert(r#use) {
panic!("Def: Attempt to insert an existing use");
}
UseNode { def: self_descr }
}
pub(crate) fn remove_use(&mut self, r#use: Use<T>) {
if !self.uses.remove(&r#use) {
panic!("Def: Attempt to remove a use that doesn't exist");
}
}
pub(crate) fn replace_use_with(ctx: &Context, this: &T, r#use: &Use<T>, other: &T)
where
T: DefTrait + UseTrait,
{
if core::ptr::eq(&*this.get_defnode_ref(ctx), &*other.get_defnode_ref(ctx)) {
return;
}
let new_use_node = other.get_defnode_mut(ctx).add_use(*other, *r#use);
*T::get_usenode_mut(r#use, ctx) = new_use_node;
this.get_defnode_mut(ctx).uses.remove(r#use);
}
}
#[derive(Debug, thiserror::Error)]
enum UseError {
#[error("Use does not correspond to any operand of its user operation")]
OperandNotInUserOp,
#[error("Use does not correspond to any successor of its user operation")]
SuccessorNotInUserOp,
}
pub(crate) trait UseTrait: DefUseParticipant {
fn try_find_index(r#use: &Use<Self>, ctx: &Context) -> Result<usize>;
fn find(r#use: &Use<Self>, ctx: &Context) -> usize {
Self::try_find_index(r#use, ctx)
.expect("Use is not in its user operation's operands/successors")
}
fn get_usenode_ref<'a>(r#use: &Use<Self>, ctx: &'a Context) -> Ref<'a, UseNode<Self>>;
fn get_usenode_mut<'a>(r#use: &Use<Self>, ctx: &'a Context) -> RefMut<'a, UseNode<Self>>;
}
pub(crate) trait DefTrait: DefUseParticipant {
fn get_defnode_ref<'a>(&self, ctx: &'a Context) -> Ref<'a, DefNode<Self>>;
fn get_defnode_mut<'a>(&self, ctx: &'a Context) -> RefMut<'a, DefNode<Self>>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DefiningEntity {
Op(Ptr<Operation>),
Block(Ptr<BasicBlock>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Value {
pub(crate) val_uid: u64,
pub(crate) defining_entity: DefiningEntity,
}
#[derive(Debug, thiserror::Error)]
pub enum ValueError {
#[error("Invalid Value: Defining operation has no result corresponding to this value")]
NotInOpResult,
#[error("Invalid Value: Defining block has no argument corresponding to this value")]
NotInBlockArgument,
}
impl Value {
pub fn defining_entity(&self) -> DefiningEntity {
self.defining_entity
}
pub fn defining_op(&self) -> Option<Ptr<Operation>> {
match self.defining_entity {
DefiningEntity::Op(op) => Some(op),
DefiningEntity::Block(_) => None,
}
}
pub fn defining_block(&self) -> Option<Ptr<BasicBlock>> {
match self.defining_entity {
DefiningEntity::Op(_) => None,
DefiningEntity::Block(block) => Some(block),
}
}
pub fn try_find_index(&self, ctx: &Context) -> Result<usize> {
match self.defining_entity {
DefiningEntity::Op(op) => {
let op = op.deref(ctx);
op.results
.iter()
.position(|res| res.val_uid == self.val_uid)
.ok_or_else(|| arg_error!(op.loc(), ValueError::NotInOpResult))
}
DefiningEntity::Block(block) => {
let block = block.deref(ctx);
block
.args
.iter()
.position(|arg| arg.val_uid == self.val_uid)
.ok_or_else(|| arg_error!(block.loc(), ValueError::NotInBlockArgument))
}
}
}
pub fn find_index(&self, ctx: &Context) -> usize {
self.try_find_index(ctx)
.expect("Value is not currently defined by its defining entity")
}
pub fn num_uses(&self, ctx: &Context) -> usize {
self.get_defnode_ref(ctx).num_uses()
}
pub fn uses(&self, ctx: &Context) -> Vec<Use<Value>> {
self.get_defnode_ref(ctx).uses().collect()
}
pub fn is_used(&self, ctx: &Context) -> bool {
self.get_defnode_ref(ctx).is_used()
}
pub fn replace_some_uses_with<P: Fn(&Context, &Use<Value>) -> bool>(
&self,
ctx: &Context,
predicate: P,
other: &Value,
) {
let touched_uses: FxHashSet<_> = self
.get_defnode_ref(ctx)
.uses
.iter()
.filter(|r#use| predicate(ctx, r#use))
.cloned()
.collect();
for r#use in &touched_uses {
DefNode::replace_use_with(ctx, self, r#use, other);
}
}
pub fn replace_all_uses_with(&self, ctx: &Context, other: &Value) {
self.replace_some_uses_with(ctx, |_, _| true, other);
}
pub fn replace_use_with(&self, ctx: &Context, r#use: Use<Value>, other: &Value) {
DefNode::replace_use_with(ctx, self, &r#use, other);
}
pub fn loc(&self, ctx: &Context) -> Location {
match self.defining_entity {
DefiningEntity::Op(op) => op.deref(ctx).loc(),
DefiningEntity::Block(block) => block.deref(ctx).loc(),
}
}
pub fn set_type(&self, ctx: &Context, ty: TypeHandle) {
let index = self.find_index(ctx);
match self.defining_entity {
DefiningEntity::Op(op) => op.deref_mut(ctx).results[index].set_type(ty),
DefiningEntity::Block(block) => block.deref_mut(ctx).args[index].set_type(ctx, ty),
}
}
pub fn get_defining_block(&self, ctx: &Context) -> Option<Ptr<BasicBlock>> {
match self.defining_entity {
DefiningEntity::Op(op) => op.deref(ctx).get_parent_block(),
DefiningEntity::Block(block) => Some(block),
}
}
}
impl Verify for Value {
fn verify(&self, ctx: &Context) -> Result<()> {
for r#use in self.uses(ctx) {
let opd_idx = <Self as UseTrait>::try_find_index(&r#use, ctx)
.map_err(|_| verify_error!(self.loc(ctx), DefUseVerifyErr::OperandNotUseOfDef))?;
let use_operand = r#use.user_op.deref(ctx).get_operand(opd_idx);
if use_operand != *self {
verify_err!(self.loc(ctx), DefUseVerifyErr::OperandNotUseOfDef)?;
}
}
self.get_type(ctx).verify(ctx)
}
}
impl Typed for Value {
fn get_type(&self, ctx: &Context) -> TypeHandle {
let index = self.find_index(ctx);
match self.defining_entity {
DefiningEntity::Op(op) => op.deref(ctx).results[index].get_type(),
DefiningEntity::Block(block) => block.deref(ctx).args[index].get_type(ctx),
}
}
}
impl Named for Value {
fn given_name(&self, ctx: &Context) -> Option<Identifier> {
let index = self.find_index(ctx);
match self.defining_entity {
DefiningEntity::Op(op) => debug_info::get_operation_result_name(ctx, op, index),
DefiningEntity::Block(block) => debug_info::get_block_arg_name(ctx, block, index),
}
}
fn id(&self, _ctx: &Context) -> Identifier {
Identifier::try_from(format!("v{}", self.val_uid)).unwrap()
}
}
impl Printable for Value {
fn fmt(
&self,
ctx: &Context,
_state: &crate::printable::State,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
write!(f, "{}", self.unique_name(ctx))
}
}
impl DefTrait for Value {
fn get_defnode_ref<'a>(&self, ctx: &'a Context) -> Ref<'a, DefNode<Self>> {
let index = self.find_index(ctx);
match self.defining_entity {
DefiningEntity::Op(op) => {
let op = op.deref(ctx);
Ref::map(op, |opref| &opref.results[index].def)
}
DefiningEntity::Block(block) => {
let block = block.deref(ctx);
Ref::map(block, |blockref| &blockref.args[index].def)
}
}
}
fn get_defnode_mut<'a>(&self, ctx: &'a Context) -> RefMut<'a, DefNode<Self>> {
let index = self.find_index(ctx);
match self.defining_entity {
DefiningEntity::Op(op) => {
let op = op.deref_mut(ctx);
RefMut::map(op, |opref| &mut opref.results[index].def)
}
DefiningEntity::Block(block) => {
let block = block.deref_mut(ctx);
RefMut::map(block, |blockref| &mut blockref.args[index].def)
}
}
}
}
impl UseTrait for Value {
fn try_find_index(r#use: &Use<Self>, ctx: &Context) -> Result<usize> {
let op = r#use.user_op.deref(ctx);
op.operands
.iter()
.position(|operand| operand.use_uid == r#use.use_uid)
.ok_or_else(|| arg_error!(op.loc(), UseError::OperandNotInUserOp))
}
fn get_usenode_ref<'a>(r#use: &Use<Self>, ctx: &'a Context) -> Ref<'a, UseNode<Self>> {
let index = <Self as UseTrait>::find(r#use, ctx);
let op = r#use.user_op.deref(ctx);
Ref::map(op, |opref| &opref.operands[index].r#use)
}
fn get_usenode_mut<'a>(r#use: &Use<Self>, ctx: &'a Context) -> RefMut<'a, UseNode<Value>> {
let index = <Self as UseTrait>::find(r#use, ctx);
let op = r#use.user_op.deref_mut(ctx);
RefMut::map(op, |opref| &mut opref.operands[index].r#use)
}
}
impl Ptr<BasicBlock> {
pub fn has_pred(&self, ctx: &Context) -> bool {
self.deref(ctx).preds.is_used()
}
pub fn is_pred(&self, ctx: &Context, pred: Ptr<BasicBlock>) -> bool {
self.deref(ctx)
.preds
.uses()
.any(|r#use| r#use.user_op.deref(ctx).get_parent_block() == Some(pred))
}
pub fn num_preds(&self, ctx: &Context) -> usize {
self.deref(ctx).preds.num_uses()
}
pub fn get_pred(&self, ctx: &Context, i: usize) -> Ptr<BasicBlock> {
self.deref(ctx)
.preds
.uses()
.nth(i)
.expect("Predecessor index out of bounds")
.user_op
.deref(ctx)
.get_container()
.expect("Terminator branching to this block is not in any basic block")
}
pub fn preds(&self, ctx: &Context) -> Vec<Ptr<BasicBlock>> {
self.deref(ctx)
.preds
.uses()
.map(|r#use| {
r#use
.user_op
.deref(ctx)
.get_container()
.expect("Terminator branching to this block is not in any basic block")
})
.collect()
}
pub fn uses(&self, ctx: &Context) -> Vec<Use<Ptr<BasicBlock>>> {
self.deref(ctx).preds.uses().collect()
}
pub fn is_succ_of(&self, ctx: &Context, pred: Ptr<BasicBlock>) -> bool {
pred.deref(ctx)
.get_tail()
.is_some_and(|pred_term| pred_term.deref(ctx).successors().any(|succ| self == &succ))
}
pub fn retarget_some_preds_to<P: Fn(&Context, Ptr<BasicBlock>) -> bool>(
&self,
ctx: &Context,
predicate: P,
other: Ptr<BasicBlock>,
) {
let predicate = |ctx: &Context, r#use: &Use<Ptr<BasicBlock>>| {
let pred_block = r#use
.user_op
.deref(ctx)
.get_container()
.expect("Predecessor block must be in a Region");
predicate(ctx, pred_block)
};
let touched_uses: FxHashSet<_> = self
.get_defnode_ref(ctx)
.uses
.iter()
.filter(|r#use| predicate(ctx, r#use))
.cloned()
.collect();
for r#use in &touched_uses {
DefNode::replace_use_with(ctx, self, r#use, &other);
}
}
pub fn retarget_all_preds_to(&self, ctx: &Context, other: Ptr<BasicBlock>) {
self.retarget_some_preds_to(ctx, |_, _| true, other);
}
pub fn retarget_pred_to(
&self,
ctx: &Context,
block_use: Use<Ptr<BasicBlock>>,
other: Ptr<BasicBlock>,
) {
DefNode::replace_use_with(ctx, self, &block_use, &other);
}
}
impl Named for Ptr<BasicBlock> {
fn given_name(&self, ctx: &Context) -> Option<Identifier> {
self.deref(ctx).given_name(ctx)
}
fn id(&self, ctx: &Context) -> Identifier {
self.deref(ctx).id(ctx)
}
}
impl DefTrait for Ptr<BasicBlock> {
fn get_defnode_ref<'a>(&self, ctx: &'a Context) -> Ref<'a, DefNode<Self>> {
let block = self.deref(ctx);
Ref::map(block, |blockref| &blockref.preds)
}
fn get_defnode_mut<'a>(&self, ctx: &'a Context) -> RefMut<'a, DefNode<Self>> {
let block = self.deref_mut(ctx);
RefMut::map(block, |blockref| &mut blockref.preds)
}
}
impl UseTrait for Ptr<BasicBlock> {
fn try_find_index(r#use: &Use<Self>, ctx: &Context) -> Result<usize> {
let op = r#use.user_op.deref(ctx);
op.successors
.iter()
.position(|succ| succ.use_uid == r#use.use_uid)
.ok_or_else(|| arg_error!(op.loc(), UseError::SuccessorNotInUserOp))
}
fn get_usenode_ref<'a>(r#use: &Use<Self>, ctx: &'a Context) -> Ref<'a, UseNode<Self>> {
let succ_idx = <Self as UseTrait>::find(r#use, ctx);
let op = r#use.user_op.deref(ctx);
Ref::map(op, |opref| &opref.successors[succ_idx].r#use)
}
fn get_usenode_mut<'a>(
r#use: &Use<Ptr<BasicBlock>>,
ctx: &'a Context,
) -> RefMut<'a, UseNode<Ptr<BasicBlock>>> {
let succ_idx = <Self as UseTrait>::find(r#use, ctx);
let op = r#use.user_op.deref_mut(ctx);
RefMut::map(op, |opref| &mut opref.successors[succ_idx].r#use)
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct UseNode<T: DefUseParticipant> {
def: T,
}
impl<T: DefUseParticipant> UseNode<T> {
pub(crate) fn get_def(&self) -> T {
self.def
}
}
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
#[allow(private_bounds)]
pub struct Use<T: DefUseParticipant> {
pub(crate) user_op: Ptr<Operation>,
pub(crate) use_uid: u64,
pub(crate) _dummy: PhantomData<T>,
}
#[allow(private_bounds)]
impl<T: UseTrait> Use<T> {
pub fn try_find_index(&self, ctx: &Context) -> Result<usize> {
T::try_find_index(self, ctx)
}
pub fn find_index(&self, ctx: &Context) -> usize {
T::find(self, ctx)
}
pub fn get_def(&self, ctx: &Context) -> T {
UseTrait::get_usenode_ref(self, ctx).get_def()
}
pub fn user_op(&self) -> Ptr<Operation> {
self.user_op
}
}
impl Typed for Use<Value> {
fn get_type(&self, ctx: &Context) -> TypeHandle {
self.get_def(ctx).get_type(ctx)
}
}