midenc-hir 0.8.1

High-level Intermediate Representation for Miden Assembly
use crate::{
    dialects::builtin::{FunctionRef, attributes::LocalVariable},
    *,
};

pub struct FunctionBuilder<'f, B: ?Sized> {
    pub func: FunctionRef,
    builder: &'f mut B,
}

impl<'f, B: ?Sized + Builder> FunctionBuilder<'f, B> {
    pub fn new(mut function: FunctionRef, builder: &'f mut B) -> Self {
        let mut func = function.borrow_mut();
        let current_block = if func.body().is_empty() {
            func.create_entry_block()
        } else {
            func.last_block()
        };

        builder.set_insertion_point_to_end(current_block);

        Self {
            func: function,
            builder,
        }
    }

    pub fn at(builder: &'f mut B, func: FunctionRef, ip: ProgramPoint) -> Self {
        builder.set_insertion_point(ip);

        Self { func, builder }
    }

    pub fn as_parts_mut(&mut self) -> (FunctionRef, &mut B) {
        (self.func, self.builder)
    }

    pub fn body_region(&self) -> RegionRef {
        self.func.borrow().body().as_region_ref()
    }

    pub fn entry_block(&self) -> BlockRef {
        self.func.borrow().entry_block()
    }

    #[inline]
    pub fn current_block(&self) -> BlockRef {
        self.builder.insertion_block().expect("builder has no insertion point set")
    }

    #[inline]
    pub fn switch_to_block(&mut self, block: BlockRef) {
        self.builder.set_insertion_point_to_end(block);
    }

    pub fn create_block(&mut self) -> BlockRef {
        let ip = *self.builder.insertion_point();
        let block = self.builder.create_block(self.body_region(), None, &[]);
        self.builder.restore_insertion_point(ip);
        block
    }

    pub fn create_block_in_region(&mut self, region: RegionRef) -> BlockRef {
        let ip = *self.builder.insertion_point();
        let block = self.builder.create_block(region, None, &[]);
        self.builder.restore_insertion_point(ip);
        block
    }

    pub fn detach_block(&mut self, mut block: BlockRef) {
        assert_ne!(
            block,
            self.current_block(),
            "cannot remove block the builder is currently inserting in"
        );
        assert_eq!(
            block.parent().map(|p| RegionRef::as_ptr(&p)),
            Some(RegionRef::as_ptr(&self.func.borrow().body().as_region_ref())),
            "cannot detach a block that does not belong to this function"
        );
        unsafe {
            let mut func = self.func.borrow_mut();
            let mut body = func.body_mut();
            body.body_mut().cursor_mut_from_ptr(block).remove();
        }
        block.borrow_mut().uses_mut().clear();
    }

    pub fn append_block_param(&mut self, block: BlockRef, ty: Type, span: SourceSpan) -> ValueRef {
        self.builder.context().append_block_argument(block, ty, span)
    }

    pub fn alloc_local(&mut self, ty: Type) -> LocalVariable {
        self.func.borrow_mut().alloc_local(ty)
    }

    pub fn builder(&self) -> &B {
        self.builder
    }

    pub fn builder_mut(&mut self) -> &mut B {
        self.builder
    }
}