mod canonicalization;
mod foldable;
mod info;
mod types;
use alloc::format;
pub use self::{
canonicalization::Canonicalizable,
foldable::{FoldResult, Foldable, OpFoldResult},
info::TraitInfo,
types::*,
};
use super::BlockRef;
use crate::{
AttributeRef, Context, Operation,
derive::operation_trait,
diagnostics::{Report, Severity, Spanned},
};
#[operation_trait]
pub trait Commutative {}
#[operation_trait]
pub trait ConstantLike {}
#[operation_trait]
pub trait ReturnLike {}
#[operation_trait]
pub trait Terminator {}
#[operation_trait]
pub trait NoTerminator {}
#[operation_trait]
pub trait Idempotent {}
#[operation_trait]
pub trait Involution {}
#[operation_trait]
pub trait IsolatedFromAbove {}
#[operation_trait]
pub trait HasOnlyGraphRegion {}
#[operation_trait]
pub trait GraphRegionNoTerminator:
NoTerminator + SingleBlock + crate::RegionKindInterface + HasOnlyGraphRegion
{
}
pub trait BranchOpInterface: crate::Op {
fn get_successor_operands(&self, index: usize) -> crate::SuccessorOperandRange<'_> {
let op = <Self as crate::Op>::as_operation(self);
let operand_group = op.successors()[index].operand_group as usize;
crate::SuccessorOperandRange::forward(op.operands().group(operand_group))
}
fn get_successor_operands_mut(&mut self, index: usize) -> crate::SuccessorOperandRangeMut<'_> {
let op = <Self as crate::Op>::as_operation_mut(self);
let operand_group = op.successors()[index].operand_group as usize;
crate::SuccessorOperandRangeMut::forward(op.operands_mut().group_mut(operand_group))
}
fn get_successor_block_argument(
&self,
operand_index: usize,
) -> Option<crate::BlockArgumentRef> {
let op = <Self as crate::Op>::as_operation(self);
let operand_groups = op.operands().num_groups();
let mut next_index = 0usize;
for operand_group in 0..operand_groups {
let group_size = op.operands().group(operand_group).len();
if (next_index..(next_index + group_size)).contains(&operand_index) {
let arg_index = operand_index - next_index;
let succ_info =
op.successors().iter().find(|s| operand_group == s.operand_group as usize)?;
return succ_info
.block
.borrow()
.successor()
.borrow()
.arguments()
.get(arg_index)
.cloned();
}
next_index += group_size;
}
None
}
#[inline]
#[allow(unused_variables)]
fn get_successor_for_operands(
&self,
operands: &[Option<AttributeRef>],
) -> Option<crate::SuccessorInfo> {
None
}
fn are_types_compatible(&self, lhs: &crate::Type, rhs: &crate::Type) -> bool {
lhs == rhs
}
fn change_branch_destination(&mut self, old_dest: BlockRef, new_dest: BlockRef) {
let op = <Self as crate::Op>::as_operation_mut(self);
assert_eq!(old_dest.borrow().num_arguments(), new_dest.borrow().num_arguments());
for successor_info in op.successors_mut().iter_mut() {
if successor_info.successor() == old_dest {
successor_info.block.borrow_mut().set(new_dest);
}
}
}
}
pub trait SelectLikeOpInterface {
fn get_condition(&self) -> crate::ValueRef;
fn get_true_value(&self) -> crate::ValueRef;
fn get_false_value(&self) -> crate::ValueRef;
}
#[operation_trait]
pub trait UnaryOp {
#[verifier]
fn is_unary_op(op: &Operation, context: &Context) -> Result<(), Report> {
if op.num_operands() == 1 {
Ok(())
} else {
Err(context
.diagnostics()
.diagnostic(Severity::Error)
.with_message(::alloc::format!("invalid operation {}", op.name()))
.with_primary_label(
op.span(),
format!("incorrect number of operands, expected 1, got {}", op.num_operands()),
)
.with_help(
"this operator implements 'UnaryOp', which requires it to have exactly one \
operand",
)
.into_report())
}
}
}
#[operation_trait]
pub trait BinaryOp {
#[verifier]
fn is_binary_op(op: &Operation, context: &Context) -> Result<(), Report> {
if op.num_operands() == 2 {
Ok(())
} else {
Err(context
.diagnostics()
.diagnostic(Severity::Error)
.with_message(::alloc::format!("invalid operation {}", op.name()))
.with_primary_label(
op.span(),
format!("incorrect number of operands, expected 2, got {}", op.num_operands()),
)
.with_help(
"this operator implements 'BinaryOp', which requires it to have exactly two \
operands",
)
.into_report())
}
}
}
#[operation_trait]
pub trait NoRegionArguments {
#[verifier]
fn no_region_arguments(op: &Operation, context: &Context) -> Result<(), Report> {
for region in op.regions().iter() {
if region.is_empty() {
continue;
}
if region.entry().has_arguments() {
return Err(context
.diagnostics()
.diagnostic(Severity::Error)
.with_message(::alloc::format!("invalid operation {}", op.name()))
.with_primary_label(
op.span(),
"this operation does not permit regions with arguments, but one was found",
)
.into_report());
}
}
Ok(())
}
}
#[operation_trait]
pub trait SingleBlock {
#[verifier]
fn has_only_single_block_regions(op: &Operation, context: &Context) -> Result<(), Report> {
for region in op.regions().iter() {
if region.body().iter().count() > 1 {
return Err(context
.diagnostics()
.diagnostic(Severity::Error)
.with_message(::alloc::format!("invalid operation {}", op.name()))
.with_primary_label(
op.span(),
"this operation requires single-block regions, but regions with multiple \
blocks were found",
)
.into_report());
}
}
Ok(())
}
}
#[operation_trait]
pub trait SingleRegion {
#[verifier]
fn has_exactly_one_region(op: &Operation, context: &Context) -> Result<(), Report> {
let num_regions = op.num_regions();
if num_regions != 1 {
return Err(context
.diagnostics()
.diagnostic(Severity::Error)
.with_message(::alloc::format!("invalid operation {}", op.name()))
.with_primary_label(
op.span(),
format!("this operation requires exactly one region, but got {num_regions}"),
)
.into_report());
}
Ok(())
}
}