use core::{fmt, ops::Range};
use crate::{dialect::RegionPtrExt, prelude::*};
use derive_more::From;
use pliron::{
attribute::AttrObj,
builtin::ops::FuncOp,
graph::HasLabel,
linked_list::ContainsLinkedList,
printable::{self, Printable},
region::Region,
utils::table::IMap,
value::Use,
};
use smallvec::SmallVec;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum RegionPredecessor {
Parent,
Terminator(TraitOp<dyn RegionBranchTerminatorOpInterface>),
}
impl Printable for RegionPredecessor {
fn fmt(&self, ctx: &Context, _: &printable::State, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RegionPredecessor::Parent => f.write_str("Parent"),
RegionPredecessor::Terminator(op) => {
let block = op.get_operation().deref(ctx).get_parent_block().unwrap();
write!(f, "{}", block.label(ctx))
}
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, From)]
pub enum RegionSuccessor {
Region(Ptr<Region>),
AfterOp,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct InvocationBounds {
lower_bound: usize,
upper_bound: Option<usize>,
}
impl InvocationBounds {
pub fn unknown() -> Self {
Self {
lower_bound: 0,
upper_bound: None,
}
}
pub fn never() -> Self {
Self {
lower_bound: 0,
upper_bound: Some(0),
}
}
pub fn once() -> Self {
Self {
lower_bound: 1,
upper_bound: Some(1),
}
}
pub fn zero_or_one() -> Self {
Self {
lower_bound: 0,
upper_bound: Some(1),
}
}
}
#[op_interface]
pub trait CallableOpInterface {
verify_op_succ!();
fn callable_region(&self, ctx: &Context) -> Option<Ptr<Region>>;
fn argument_types(&self, ctx: &Context) -> Vec<TypeHandle>;
fn result_types(&self, ctx: &Context) -> Vec<TypeHandle>;
}
#[op_interface_impl]
impl CallableOpInterface for FuncOp {
fn callable_region(&self, ctx: &Context) -> Option<Ptr<Region>> {
Some(self.get_region(ctx))
}
fn argument_types(&self, ctx: &Context) -> Vec<TypeHandle> {
let ty = self.get_attr_builtin_func_type(ctx).unwrap().get_type(ctx);
type_cast::<dyn FunctionTypeInterface>(&*ty.deref(ctx))
.unwrap()
.arg_types()
}
fn result_types(&self, ctx: &Context) -> Vec<TypeHandle> {
let ty = self.get_attr_builtin_func_type(ctx).unwrap().get_type(ctx);
type_cast::<dyn FunctionTypeInterface>(&*ty.deref(ctx))
.unwrap()
.res_types()
}
}
#[format]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum SymbolVisibility {
Public,
Private,
Nested,
}
#[op_interface]
pub trait SymbolOpInterface: pliron::builtin::op_interfaces::SymbolOpInterface {
verify_op_succ!();
fn get_visibility(&self, ctx: &Context) -> SymbolVisibility;
fn set_visibility(&self, ctx: &mut Context, visibility: SymbolVisibility);
fn is_public(&self, ctx: &Context) -> bool {
matches!(self.get_visibility(ctx), SymbolVisibility::Public)
}
fn is_private(&self, ctx: &Context) -> bool {
matches!(self.get_visibility(ctx), SymbolVisibility::Private)
}
fn is_nested(&self, ctx: &Context) -> bool {
matches!(self.get_visibility(ctx), SymbolVisibility::Nested)
}
}
#[op_interface]
pub trait CallOpInterface: pliron::builtin::op_interfaces::CallOpInterface {
verify_op_succ!();
fn args_range(&self, ctx: &Context) -> Range<usize>;
}
#[op_interface]
pub trait RegionBranchOpInterface {
verify_op_succ!();
fn entry_successor_operands(
&self,
ctx: &Context,
successor: RegionSuccessor,
) -> Vec<Use<Value>> {
let _ = (ctx, successor);
vec![]
}
fn entry_successor_regions(
&self,
ctx: &Context,
operands: &[Option<AttrObj>],
) -> Vec<RegionSuccessor> {
let _ = (ctx, operands);
self.successor_regions(ctx, RegionPredecessor::Parent)
}
fn successor_regions(&self, ctx: &Context, pred: RegionPredecessor) -> Vec<RegionSuccessor>;
fn all_successor_regions_of_region(
&self,
ctx: &Context,
region: Ptr<Region>,
) -> Vec<RegionSuccessor> {
let mut res = vec![];
for block in region.deref(ctx).iter(ctx) {
let Some(term) = block.deref(ctx).get_terminator(ctx) else {
continue;
};
if let Some(terminator) = TraitOp::try_from_op(term, ctx) {
res.extend(self.successor_regions(ctx, RegionPredecessor::Terminator(terminator)));
}
}
res
}
fn successor_inputs(&self, ctx: &Context, successor: RegionSuccessor) -> Vec<Value>;
fn predecessors(&self, ctx: &Context, successor: RegionSuccessor) -> Vec<RegionPredecessor> {
let mut predecessors = vec![];
for pred in all_region_predecessors(self.get_operation(), ctx) {
let successors = self.successor_regions(ctx, pred);
let is_pred = successors.iter().any(|succ| succ == &successor);
if is_pred {
predecessors.push(pred);
}
}
predecessors
}
fn predecessor_values(
&self,
ctx: &Context,
successor: RegionSuccessor,
index: usize,
) -> Vec<Use<Value>> {
let mut predecessor_values = vec![];
let predecessors = self.predecessors(ctx, successor);
for predecessor in predecessors {
match predecessor {
RegionPredecessor::Parent => {
predecessor_values.push(self.entry_successor_operands(ctx, successor)[index]);
}
RegionPredecessor::Terminator(term) => {
predecessor_values.push(term.successor_operands(ctx, successor)[index]);
}
}
}
predecessor_values
}
fn region_invocation_bounds(
&self,
ctx: &Context,
operands: &[Option<AttrObj>],
) -> Vec<InvocationBounds> {
let _ = (ctx, operands);
vec![InvocationBounds::unknown()]
}
fn are_types_compatible(&self, ctx: &Context, lhs: TypeHandle, rhs: TypeHandle) -> bool {
let _ = ctx;
lhs == rhs
}
}
type RegionBranchSuccessorMapping = IMap<Use<Value>, SmallVec<[Value; 4]>>;
impl dyn RegionBranchOpInterface {
pub fn successor_operands(
&self,
ctx: &Context,
src: RegionPredecessor,
dest: RegionSuccessor,
) -> Vec<Use<Value>> {
match src {
RegionPredecessor::Parent => self.entry_successor_operands(ctx, dest),
RegionPredecessor::Terminator(term) => term.successor_operands(ctx, dest),
}
}
pub fn non_successor_inputs(&self, ctx: &Context, successor: RegionSuccessor) -> Vec<Value> {
let results = match successor {
RegionSuccessor::Region(region) => region.arguments(ctx),
RegionSuccessor::AfterOp => {
let parent = self.get_operation().deref(ctx).get_parent_op(ctx).unwrap();
parent.results(ctx)
}
};
let successor_inputs = self.successor_inputs(ctx, successor);
results
.into_iter()
.enumerate()
.filter(|(i, _)| !successor_inputs.iter().any(|it| it.find_index(ctx) == *i))
.map(|it| it.1)
.collect()
}
pub fn all_region_predecessors(&self, ctx: &Context) -> Vec<RegionPredecessor> {
all_region_predecessors(self.get_operation(), ctx)
}
pub fn successor_operand_input_mapping(
&self,
ctx: &Context,
src: RegionPredecessor,
) -> RegionBranchSuccessorMapping {
let successors = self.successor_regions(ctx, src);
let mut mapping = RegionBranchSuccessorMapping::default();
for dst in successors {
let operands = self.successor_operands(ctx, src, dst);
let inputs = self.successor_inputs(ctx, dst);
assert_eq!(
operands.len(),
inputs.len(),
"expected the same number of operands and inputs"
);
for (operand, input) in operands.into_iter().zip(inputs) {
mapping.entry(operand).or_default().push(input);
}
}
mapping
}
}
pub fn all_region_predecessors(op: Ptr<Operation>, ctx: &Context) -> Vec<RegionPredecessor> {
let mut predecessors = vec![RegionPredecessor::Parent];
for region in op.deref(ctx).regions() {
for block in region.deref(ctx).iter(ctx) {
if let Some(term) = block.deref(ctx).get_terminator(ctx)
&& let Some(term) =
TraitOp::<dyn RegionBranchTerminatorOpInterface>::try_from_op(term, ctx)
{
predecessors.push(RegionPredecessor::Terminator(term));
}
}
}
predecessors
}
#[op_interface]
pub trait RegionBranchTerminatorOpInterface {
verify_op_succ!();
fn successor_operands(&self, ctx: &Context, successor: RegionSuccessor) -> Vec<Use<Value>>;
fn successor_regions(
&self,
ctx: &Context,
operands: &[Option<AttrObj>],
) -> Vec<RegionSuccessor> {
let _ = operands;
let op = TraitOp::try_from_op(self.get_operation(), ctx).unwrap();
let parent = self.get_operation().deref(ctx).get_parent_op(ctx).unwrap();
let parent = parent.dyn_op(ctx);
op_cast::<dyn RegionBranchOpInterface>(&*parent)
.unwrap()
.successor_regions(ctx, RegionPredecessor::Terminator(op))
}
}