use alloc::{boxed::Box, format, rc::Rc, vec};
use super::OperationState;
use crate::{
BlockArgument, BlockRef, BuildableOp, Context, OperationRef, ProgramPoint, RegionRef,
SourceSpan, Type, Value,
diagnostics::{LabeledSpan, Report, Severity, miette::diagnostic},
};
pub trait Builder: Listener {
fn context(&self) -> &Context;
fn context_rc(&self) -> Rc<Context>;
fn insertion_point(&self) -> &ProgramPoint;
fn clear_insertion_point(&mut self) -> ProgramPoint;
fn restore_insertion_point(&mut self, ip: ProgramPoint);
fn set_insertion_point(&mut self, ip: ProgramPoint);
#[inline]
fn set_insertion_point_before(&mut self, op: OperationRef) {
self.set_insertion_point(ProgramPoint::before(op));
}
#[inline]
fn set_insertion_point_after(&mut self, op: OperationRef) {
self.set_insertion_point(ProgramPoint::after(op));
}
fn set_insertion_point_after_value(&mut self, value: &dyn Value) {
let pp = if let Some(op) = value.get_defining_op() {
ProgramPoint::after(op)
} else {
let block_argument = value.downcast_ref::<BlockArgument>().unwrap();
ProgramPoint::at_start_of(block_argument.owner())
};
self.set_insertion_point(pp);
}
#[inline]
fn set_insertion_point_to_start(&mut self, block: BlockRef) {
self.set_insertion_point(ProgramPoint::at_start_of(block));
}
#[inline]
fn set_insertion_point_to_end(&mut self, block: BlockRef) {
self.set_insertion_point(ProgramPoint::at_end_of(block));
}
fn insertion_block(&self) -> Option<BlockRef> {
self.insertion_point().block()
}
fn create_block(&mut self, parent: RegionRef, ip: Option<BlockRef>, args: &[Type]) -> BlockRef {
let mut block = self.context_rc().create_block_with_params(args.iter().cloned());
if let Some(at) = ip {
let region = at.parent().unwrap();
assert!(
RegionRef::ptr_eq(&parent, ®ion),
"insertion point region differs from 'parent'"
);
block.borrow_mut().insert_after(at);
} else {
block.borrow_mut().insert_at_end(parent);
}
self.notify_block_inserted(block, None, None);
self.set_insertion_point_to_end(block);
block
}
fn create_block_before(&mut self, before: BlockRef, args: &[Type]) -> BlockRef {
let mut block = self.context_rc().create_block_with_params(args.iter().cloned());
block.borrow_mut().insert_before(before);
self.notify_block_inserted(block, None, None);
self.set_insertion_point_to_end(block);
block
}
fn insert(&mut self, op: OperationRef) {
let ip = self.insertion_point();
match *ip {
ProgramPoint::Block {
block,
position: point,
} => match point {
crate::Position::Before => op.insert_at_start(block),
crate::Position::After => op.insert_at_end(block),
},
ProgramPoint::Op {
op: other_op,
position: point,
..
} => match point {
crate::Position::Before => op.insert_before(other_op),
crate::Position::After => {
op.insert_after(other_op);
self.set_insertion_point(ProgramPoint::after(op));
}
},
ProgramPoint::Invalid => panic!("insertion point is invalid/unset"),
}
self.notify_operation_inserted(op, *self.insertion_point());
}
fn create_operation(&mut self, state: &mut OperationState) -> Result<OperationRef, Report> {
let mut op = state.name.alloc_default(self.context_rc());
op.borrow_mut().set_span(state.span);
let mut builder = crate::GenericOperationBuilder::new(self, op);
for prop in op.name().properties() {
if !state.attrs.iter().any(|p| p.name == prop.name) {
return Err(Report::from(diagnostic!(
severity = Severity::Error,
labels = vec![LabeledSpan::at(
state.span,
format!("missing required property '{}'", prop.name)
)],
"invalid operation"
)));
}
}
for attr in state.attrs.drain(..) {
if state.name.has_property(attr.name) {
builder.with_property_boxed(attr.name, attr.value)?;
} else {
builder.with_attr_boxed(attr.name, attr.value);
}
}
for (i, group) in state.operands.drain(..).enumerate() {
builder.with_operands_in_group(i, group);
}
for successor in state.successors.drain(..) {
builder.with_pending_successor(successor);
}
if !state.regions.is_empty() {
let mut op = op.borrow_mut();
let regions = op.regions_mut();
for region in state.regions.drain(..) {
regions.push_back(region);
}
}
if !state.results.is_empty() {
builder.with_results(state.results.drain(..));
}
builder.build()
}
}
pub trait BuilderExt: Builder {
#[inline(always)]
fn create<T, Args>(&mut self, span: SourceSpan) -> <T as BuildableOp<Args>>::Builder<'_, Self>
where
Args: core::marker::Tuple,
T: BuildableOp<Args>,
{
<T as BuildableOp<Args>>::builder(self, span)
}
}
impl<B: ?Sized + Builder> BuilderExt for B {}
pub struct OpBuilder<L = NoopBuilderListener> {
context: Rc<Context>,
listener: Option<L>,
ip: ProgramPoint,
}
impl OpBuilder {
pub fn new(context: Rc<Context>) -> Self {
Self {
context,
listener: None,
ip: ProgramPoint::default(),
}
}
}
impl<L: Listener> OpBuilder<L> {
pub fn with_listener<L2>(self, listener: L2) -> OpBuilder<L2>
where
L2: Listener,
{
OpBuilder {
context: self.context,
listener: Some(listener),
ip: self.ip,
}
}
pub fn listener(&self) -> Option<&L> {
self.listener.as_ref()
}
#[inline]
pub fn into_parts(self) -> (Rc<Context>, Option<L>, ProgramPoint) {
(self.context, self.listener, self.ip)
}
}
impl<L: Listener> Listener for OpBuilder<L> {
fn kind(&self) -> ListenerType {
self.listener.as_ref().map(|l| l.kind()).unwrap_or(ListenerType::Builder)
}
fn notify_operation_inserted(&self, op: OperationRef, prev: ProgramPoint) {
if let Some(listener) = self.listener.as_ref() {
listener.notify_operation_inserted(op, prev);
}
}
fn notify_block_inserted(
&self,
block: BlockRef,
prev: Option<RegionRef>,
ip: Option<BlockRef>,
) {
if let Some(listener) = self.listener.as_ref() {
listener.notify_block_inserted(block, prev, ip);
}
}
}
impl<L: Listener> Builder for OpBuilder<L> {
#[inline(always)]
fn context(&self) -> &Context {
self.context.as_ref()
}
#[inline(always)]
fn context_rc(&self) -> Rc<Context> {
self.context.clone()
}
#[inline(always)]
fn insertion_point(&self) -> &ProgramPoint {
&self.ip
}
#[inline]
fn clear_insertion_point(&mut self) -> ProgramPoint {
let ip = self.ip;
self.ip = ProgramPoint::Invalid;
ip
}
#[inline]
fn restore_insertion_point(&mut self, ip: ProgramPoint) {
self.ip = ip;
}
#[inline(always)]
fn set_insertion_point(&mut self, ip: ProgramPoint) {
self.ip = ip;
}
}
#[derive(Debug, Copy, Clone)]
pub enum ListenerType {
Builder,
Rewriter,
}
#[allow(unused_variables)]
pub trait Listener {
fn kind(&self) -> ListenerType;
fn notify_operation_inserted(&self, op: OperationRef, prev: ProgramPoint) {}
fn notify_block_inserted(
&self,
block: BlockRef,
prev: Option<RegionRef>,
ip: Option<BlockRef>,
) {
}
}
impl<L: Listener> Listener for Option<L> {
fn kind(&self) -> ListenerType {
ListenerType::Builder
}
fn notify_block_inserted(
&self,
block: BlockRef,
prev: Option<RegionRef>,
ip: Option<BlockRef>,
) {
if let Some(listener) = self.as_ref() {
listener.notify_block_inserted(block, prev, ip);
}
}
fn notify_operation_inserted(&self, op: OperationRef, prev: ProgramPoint) {
if let Some(listener) = self.as_ref() {
listener.notify_operation_inserted(op, prev);
}
}
}
impl<L: ?Sized + Listener> Listener for Box<L> {
#[inline]
fn kind(&self) -> ListenerType {
(**self).kind()
}
fn notify_operation_inserted(&self, op: OperationRef, prev: ProgramPoint) {
(**self).notify_operation_inserted(op, prev)
}
fn notify_block_inserted(
&self,
block: BlockRef,
prev: Option<RegionRef>,
ip: Option<BlockRef>,
) {
(**self).notify_block_inserted(block, prev, ip)
}
}
impl<L: ?Sized + Listener> Listener for Rc<L> {
#[inline]
fn kind(&self) -> ListenerType {
(**self).kind()
}
fn notify_operation_inserted(&self, op: OperationRef, prev: ProgramPoint) {
(**self).notify_operation_inserted(op, prev)
}
fn notify_block_inserted(
&self,
block: BlockRef,
prev: Option<RegionRef>,
ip: Option<BlockRef>,
) {
(**self).notify_block_inserted(block, prev, ip)
}
}
pub struct NoopBuilderListener;
impl Listener for NoopBuilderListener {
#[inline]
fn kind(&self) -> ListenerType {
ListenerType::Builder
}
}
#[doc(hidden)]
#[allow(unused_variables)]
trait RestoreInsertionPointOnDrop {
fn restore_insertion_point_on_drop(&mut self, ip: ProgramPoint);
}
impl<B: ?Sized> RestoreInsertionPointOnDrop for InsertionGuard<'_, B> {
#[inline(always)]
default fn restore_insertion_point_on_drop(&mut self, _ip: ProgramPoint) {}
}
impl<B: ?Sized + Builder> RestoreInsertionPointOnDrop for InsertionGuard<'_, B> {
fn restore_insertion_point_on_drop(&mut self, ip: ProgramPoint) {
self.builder.restore_insertion_point(ip);
}
}
pub struct InsertionGuard<'a, B: ?Sized> {
builder: &'a mut B,
ip: ProgramPoint,
}
impl<'a, B> InsertionGuard<'a, B>
where
B: ?Sized + Builder,
{
#[allow(unused)]
pub fn new(builder: &'a mut B) -> Self {
let ip = *builder.insertion_point();
Self { builder, ip }
}
}
impl<B: ?Sized> core::ops::Deref for InsertionGuard<'_, B> {
type Target = B;
#[inline(always)]
fn deref(&self) -> &Self::Target {
self.builder
}
}
impl<B: ?Sized> core::ops::DerefMut for InsertionGuard<'_, B> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
self.builder
}
}
impl<B: ?Sized> Drop for InsertionGuard<'_, B> {
fn drop(&mut self) {
let ip = self.ip;
self.restore_insertion_point_on_drop(ip);
}
}
impl<B: ?Sized + Listener> Listener for InsertionGuard<'_, B> {
fn kind(&self) -> ListenerType {
self.builder.kind()
}
fn notify_block_inserted(
&self,
block: BlockRef,
prev: Option<RegionRef>,
ip: Option<BlockRef>,
) {
self.builder.notify_block_inserted(block, prev, ip);
}
fn notify_operation_inserted(&self, op: OperationRef, prev: ProgramPoint) {
self.builder.notify_operation_inserted(op, prev);
}
}
impl<B: ?Sized + Builder> Builder for InsertionGuard<'_, B> {
fn context(&self) -> &Context {
self.builder.context()
}
fn context_rc(&self) -> Rc<Context> {
self.builder.context_rc()
}
fn insertion_point(&self) -> &ProgramPoint {
self.builder.insertion_point()
}
fn clear_insertion_point(&mut self) -> ProgramPoint {
self.builder.clear_insertion_point()
}
fn restore_insertion_point(&mut self, ip: ProgramPoint) {
self.builder.restore_insertion_point(ip);
}
fn set_insertion_point(&mut self, ip: ProgramPoint) {
self.builder.set_insertion_point(ip);
}
fn set_insertion_point_before(&mut self, op: OperationRef) {
self.builder.set_insertion_point_before(op);
}
fn set_insertion_point_after(&mut self, op: OperationRef) {
self.builder.set_insertion_point_after(op);
}
fn set_insertion_point_after_value(&mut self, value: &dyn Value) {
self.builder.set_insertion_point_after_value(value);
}
fn set_insertion_point_to_start(&mut self, block: BlockRef) {
self.builder.set_insertion_point_to_start(block);
}
fn set_insertion_point_to_end(&mut self, block: BlockRef) {
self.builder.set_insertion_point_to_end(block);
}
fn insertion_block(&self) -> Option<BlockRef> {
self.builder.insertion_block()
}
fn create_block(&mut self, parent: RegionRef, ip: Option<BlockRef>, args: &[Type]) -> BlockRef {
self.builder.create_block(parent, ip, args)
}
fn create_block_before(&mut self, before: BlockRef, args: &[Type]) -> BlockRef {
self.builder.create_block_before(before, args)
}
fn insert(&mut self, op: OperationRef) {
self.builder.insert(op);
}
}