use alloc::{
borrow::Borrow,
string::{String, ToString},
sync::Arc,
vec::Vec,
};
use miden_assembly_syntax::{
ast::Instruction,
debuginfo::{Location, Span},
diagnostics::Report,
};
use miden_core::{
Felt,
events::SystemEvent,
mast::{DebugVarId, DecoratorId, MastNodeId},
operations::{AssemblyOp, DebugVarInfo, Decorator, DecoratorList, Operation},
};
use crate::{ProcedureContext, assembler::BodyWrapper, mast_forest_builder::MastForestBuilder};
#[derive(Debug)]
struct PendingAsmOp {
op_start: usize,
location: Option<Location>,
context_name: String,
op: String,
}
#[derive(Debug)]
pub struct BasicBlockBuilder<'a> {
ops: Vec<Operation>,
decorators: DecoratorList,
epilogue: Vec<Operation>,
pending_asm_op: Option<PendingAsmOp>,
asm_ops: Vec<(usize, AssemblyOp)>,
debug_vars: Vec<(usize, DebugVarId)>,
mast_forest_builder: &'a mut MastForestBuilder,
}
impl<'a> BasicBlockBuilder<'a> {
pub(super) fn new(
wrapper: Option<BodyWrapper>,
mast_forest_builder: &'a mut MastForestBuilder,
) -> Self {
match wrapper {
Some(wrapper) => Self {
ops: wrapper.prologue,
decorators: Vec::new(),
epilogue: wrapper.epilogue,
pending_asm_op: None,
asm_ops: Vec::new(),
debug_vars: Vec::new(),
mast_forest_builder,
},
None => Self {
ops: Default::default(),
decorators: Default::default(),
epilogue: Default::default(),
pending_asm_op: None,
asm_ops: Vec::new(),
debug_vars: Default::default(),
mast_forest_builder,
},
}
}
}
impl BasicBlockBuilder<'_> {
pub fn mast_forest_builder(&self) -> &MastForestBuilder {
self.mast_forest_builder
}
pub fn mast_forest_builder_mut(&mut self) -> &mut MastForestBuilder {
self.mast_forest_builder
}
}
impl BasicBlockBuilder<'_> {
pub fn push_op(&mut self, op: Operation) {
self.ops.push(op);
}
pub fn push_ops<I, O>(&mut self, ops: I)
where
I: IntoIterator<Item = O>,
O: Borrow<Operation>,
{
self.ops.extend(ops.into_iter().map(|o| *o.borrow()));
}
pub fn push_op_many(&mut self, op: Operation, n: usize) {
let new_len = self.ops.len() + n;
self.ops.resize(new_len, op);
}
pub fn push_system_event(&mut self, sys_event: SystemEvent) {
let event_id = sys_event.event_id();
self.push_ops([Operation::Push(event_id.as_felt()), Operation::Emit, Operation::Drop]);
}
}
impl BasicBlockBuilder<'_> {
pub fn push_decorator(&mut self, decorator: Decorator) -> Result<(), Report> {
let decorator_id = self.mast_forest_builder.ensure_decorator(decorator)?;
self.decorators.push((self.ops.len(), decorator_id));
Ok(())
}
pub fn track_instruction(
&mut self,
instruction: &Span<Instruction>,
proc_ctx: &ProcedureContext,
) -> Result<(), Report> {
let span = instruction.span();
self.pending_asm_op = Some(PendingAsmOp {
op_start: self.ops.len(),
location: proc_ctx.source_manager().location(span).ok(),
context_name: proc_ctx.path().to_string(),
op: instruction.to_string(),
});
Ok(())
}
pub fn set_instruction_cycle_count(&mut self) -> Option<AssemblyOp> {
let pending = self.pending_asm_op.take().expect("no pending asm op to finalize");
let cycle_count = self.ops.len() - pending.op_start;
let asm_op =
AssemblyOp::new(pending.location, pending.context_name, cycle_count as u8, pending.op);
match cycle_count {
0 => Some(asm_op),
_ => {
self.asm_ops.push((pending.op_start, asm_op));
None
},
}
}
pub fn push_debug_var(&mut self, debug_var: DebugVarInfo) -> Result<(), Report> {
let debug_var_id = self.mast_forest_builder.add_debug_var(debug_var)?;
self.debug_vars.push((self.ops.len(), debug_var_id));
Ok(())
}
}
impl BasicBlockBuilder<'_> {
pub fn make_basic_block(&mut self) -> Result<Option<MastNodeId>, Report> {
if !self.ops.is_empty() {
let ops = self.ops.drain(..).collect();
let decorators = self.decorators.drain(..).collect();
let asm_ops = core::mem::take(&mut self.asm_ops);
let debug_vars: Vec<(usize, DebugVarId)> = self.debug_vars.drain(..).collect();
let basic_block_node_id = self.mast_forest_builder.ensure_block(
ops,
decorators,
asm_ops,
debug_vars,
vec![],
vec![],
)?;
Ok(Some(basic_block_node_id))
} else {
Ok(None)
}
}
pub fn try_into_basic_block(mut self) -> Result<BasicBlockOrDecorators, Report> {
self.ops.append(&mut self.epilogue);
if let Some(basic_block_node_id) = self.make_basic_block()? {
Ok(BasicBlockOrDecorators::BasicBlock(basic_block_node_id))
} else if let Some(decorator_ids) = self.drain_decorators() {
Ok(BasicBlockOrDecorators::Decorators(decorator_ids))
} else {
Ok(BasicBlockOrDecorators::Nothing)
}
}
pub fn drain_decorators(&mut self) -> Option<Vec<DecoratorId>> {
assert!(self.ops.is_empty());
if !self.decorators.is_empty() {
Some(self.decorators.drain(..).map(|(_, decorator_id)| decorator_id).collect())
} else {
None
}
}
}
pub enum BasicBlockOrDecorators {
BasicBlock(MastNodeId),
Decorators(Vec<DecoratorId>),
Nothing,
}
impl BasicBlockBuilder<'_> {
pub fn register_error(&mut self, msg: Arc<str>) -> Felt {
self.mast_forest_builder.register_error(msg)
}
}