use core::{
fmt::{self, Display},
hash::Hash,
};
use derive_more::{Eq, PartialEq};
use derive_new::new;
use pliron::{
attribute::AttrObj,
basic_block::BasicBlock,
graph::HasLabel,
opts::mem2reg::AllocInfo,
printable::{self, Printable},
region::Region,
utils::table::{HMap, SmallMap, SmallSet},
value::DefiningEntity,
};
use crate::prelude::*;
pub type LogicalResult = core::result::Result<(), ()>;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum DeletionKind {
Keep,
Delete,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MemoryDefiningEntity {
Op(Ptr<Operation>),
Block(Ptr<BasicBlock>),
LiveOnEntry,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MemoryValue {
val_uid: u64,
#[eq(skip)]
defining_entity: MemoryDefiningEntity,
}
impl Hash for MemoryValue {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.val_uid.hash(state);
}
}
impl Display for MemoryValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self == &MemoryValue::LIVE_ON_ENTRY {
write!(f, "LiveOnEntry")
} else {
write!(f, "M{}", self.val_uid)
}
}
}
impl MemoryValue {
pub const LIVE_ON_ENTRY: MemoryValue = MemoryValue {
val_uid: 0,
defining_entity: MemoryDefiningEntity::LiveOnEntry,
};
pub fn defining_entity(&self) -> Option<DefiningEntity> {
match self.defining_entity {
MemoryDefiningEntity::Op(op) => Some(DefiningEntity::Op(op)),
MemoryDefiningEntity::Block(block) => Some(DefiningEntity::Block(block)),
MemoryDefiningEntity::LiveOnEntry => None,
}
}
}
#[derive(Default)]
pub struct MemorySSAContext {
last_idx: u64,
}
impl MemorySSAContext {
pub fn new_value_in_block(&mut self, block: Ptr<BasicBlock>) -> MemoryValue {
self.last_idx += 1;
MemoryValue {
val_uid: self.last_idx,
defining_entity: MemoryDefiningEntity::Block(block),
}
}
pub fn new_value_at_op(&mut self, op: Ptr<Operation>) -> MemoryValue {
self.last_idx += 1;
MemoryValue {
val_uid: self.last_idx,
defining_entity: MemoryDefiningEntity::Op(op),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum MemoryRegionPredecessor {
Parent,
Block(Ptr<BasicBlock>),
}
impl Printable for MemoryRegionPredecessor {
fn fmt(&self, ctx: &Context, _: &printable::State, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MemoryRegionPredecessor::Parent => f.write_str("Parent"),
MemoryRegionPredecessor::Block(block) => {
write!(f, "{}", block.label(ctx))
}
}
}
}
pub type RegionMemoryPhiInputs = SmallMap<MemoryRegionPredecessor, MemoryValue, 2>;
pub enum RegionMemoryValue {
Forward(MemoryValue),
RegionPhi(RegionMemoryPhiInputs),
}
#[op_interface]
pub trait PromotableRegionOpInterface {
verify_op_succ!();
fn is_region_promotable(
&self,
ctx: &Context,
alloc: &AllocInfo,
region: Ptr<Region>,
has_value_stores: bool,
) -> bool;
fn setup_promotion(
&self,
ctx: &mut Context,
alloc: &AllocInfo,
reaching_def: Value,
has_value_stores: bool,
regions_to_process: &mut SmallMap<Ptr<Region>, Value, 2>,
);
fn finalize_promotion(
&self,
ctx: &mut Context,
alloc: &AllocInfo,
entry_reaching_def: Value,
has_value_stores: bool,
reaching_at_block_end: &HMap<Ptr<BasicBlock>, Value>,
) -> Value;
}
#[op_interface]
pub trait MemorySSARegionOpInterface {
verify_op_succ!();
fn setup_memory_ssa(
&self,
ctx: &Context,
state: &mut MemorySSAContext,
reaching_def: MemoryValue,
has_memory_defs: bool,
regions_to_process: &mut SmallMap<Ptr<Region>, MemoryValue, 2>,
);
#[allow(
clippy::too_many_arguments,
reason = "packing them into structs would be more complex"
)]
fn finalize_memory_ssa(
&self,
ctx: &Context,
state: &mut MemorySSAContext,
entry_reaching_def: MemoryValue,
has_memory_defs: bool,
reaching_at_region_entry: &HMap<Ptr<Region>, MemoryValue>,
reaching_at_block_end: &HMap<Ptr<BasicBlock>, MemoryValue>,
region_phis: &mut SmallMap<Ptr<Region>, RegionMemoryPhiInputs, 2>,
) -> RegionMemoryValue;
}
#[type_interface]
pub trait DestructurableTypeInterface {
verify_ty_succ!();
fn subelement_index_map(&self, ctx: &Context) -> Option<HMap<AttrObj, TypeHandle>>;
fn type_at_index(&self, ctx: &Context, index: &AttrObj) -> TypeHandle;
}
#[op_interface]
pub trait DestructurableConstructorOpInterface {
verify_op_succ!();
fn destructurable_values(&self, ctx: &Context) -> Vec<DestructurableValueSlot>;
fn destructure(
&self,
ctx: &mut Context,
value: &DestructurableValueSlot,
used_indices: &SmallSet<AttrObj, 8>,
rewriter: &mut PassRewriter,
new_constructors: &mut Vec<TraitOp<dyn DestructurableConstructorOpInterface>>,
) -> HMap<AttrObj, ValueSlot>;
fn handle_destructuring_complete(
&self,
ctx: &mut Context,
value: &DestructurableValueSlot,
rewriter: &mut PassRewriter,
) -> Option<TraitOp<dyn DestructurableConstructorOpInterface>>;
}
#[op_interface]
pub trait DestructurableAccessorOpInterface {
verify_op_succ!();
fn can_rewire(
&self,
ctx: &Context,
value: &DestructurableValueSlot,
used_indices: &mut SmallSet<AttrObj, 8>,
must_be_safely_used: &mut Vec<ValueSlot>,
) -> bool;
fn rewire(
&self,
ctx: &mut Context,
value: &DestructurableValueSlot,
subvalues: &HMap<AttrObj, ValueSlot>,
rewriter: &mut PassRewriter,
) -> DeletionKind;
}
#[op_interface]
pub trait SafeMemorySlotAccessOpInterface {
verify_op_succ!();
#[allow(clippy::result_unit_err)]
fn ensure_only_safe_accesses(
&self,
ctx: &Context,
value: &ValueSlot,
must_be_safely_used: &mut Vec<ValueSlot>,
) -> LogicalResult;
}
#[derive(new, Debug)]
pub struct ValueSlot {
pub value: Value,
pub elem_ty: TypeHandle,
}
#[derive(Debug)]
pub struct DestructurableValueSlot {
pub slot: ValueSlot,
pub subelement_types: HMap<AttrObj, TypeHandle>,
}