use miden_assembly_syntax::{
debuginfo::{SourceSpan, Spanned},
diagnostics::{RelatedLabel, Report},
};
use miden_core::{Felt, WORD_SIZE, operations::Operation::*};
use super::{BasicBlockBuilder, push_u32_value};
use crate::{ProcedureContext, fmp::push_offset_fmp_sequence};
pub fn mem_read(
block_builder: &mut BasicBlockBuilder,
proc_ctx: &ProcedureContext,
addr: Option<u32>,
is_local: bool,
is_single: bool,
instr_span: SourceSpan,
) -> Result<(), Report> {
if let Some(addr) = addr {
if is_local {
let num_locals = proc_ctx.num_locals();
local_to_absolute_addr(
block_builder,
proc_ctx,
addr as u16,
num_locals,
is_single,
instr_span,
)?;
} else {
push_u32_value(block_builder, addr);
}
} else {
assert!(!is_local, "local always contains addr value");
}
if is_single {
block_builder.push_op(MLoad);
} else {
block_builder.push_op(MLoadW);
}
Ok(())
}
pub fn mem_write_imm(
block_builder: &mut BasicBlockBuilder,
proc_ctx: &ProcedureContext,
addr: u32,
is_local: bool,
is_single: bool,
instr_span: SourceSpan,
) -> Result<(), Report> {
if is_local {
local_to_absolute_addr(
block_builder,
proc_ctx,
addr as u16,
proc_ctx.num_locals(),
is_single,
instr_span,
)?;
} else {
push_u32_value(block_builder, addr);
}
if is_single {
block_builder.push_op(MStore);
block_builder.push_op(Drop);
} else {
block_builder.push_op(MStoreW);
}
Ok(())
}
pub fn local_to_absolute_addr(
block_builder: &mut BasicBlockBuilder,
proc_ctx: &ProcedureContext,
index_of_local: u16,
num_proc_locals: u16,
is_single: bool,
instr_span: SourceSpan,
) -> Result<(), Report> {
if num_proc_locals == 0 {
return Err(RelatedLabel::error("invalid procedure local reference")
.with_labeled_span(
proc_ctx.span(),
"this procedure definition does not allocate any locals",
)
.with_labeled_span(instr_span, "the procedure local index referenced here is invalid")
.with_source_file(proc_ctx.source_manager().get(instr_span.source_id()).ok())
.into());
}
let max = if is_single {
num_proc_locals - 1
} else {
num_proc_locals.checked_sub(4).ok_or_else(|| {
RelatedLabel::error("invalid procedure local reference")
.with_labeled_span(
proc_ctx.span(),
format!("this procedure only allocates {num_proc_locals} locals"),
)
.with_labeled_span(instr_span, "but this instruction expects at least 4")
.with_source_file(proc_ctx.source_manager().get(instr_span.source_id()).ok())
})?
};
if index_of_local > max {
return Err(RelatedLabel::error("invalid procedure local index")
.with_help(
if is_single {
"the index is greater than the number of allocated locals"
} else {
"this instruction expects a word-sized value, so at least 4 locals must be addressable at the given index"
}
)
.with_labeled_span(proc_ctx.span(), format!("this procedure only allocates {num_proc_locals} locals"))
.with_labeled_span(instr_span, "but this local index would reach out of bounds")
.with_source_file(proc_ctx.source_manager().get(instr_span.source_id()).ok())
.into()
);
}
let aligned_num_locals = num_proc_locals.next_multiple_of(WORD_SIZE as u16);
let fmp_offset_of_local = -Felt::from_u16(aligned_num_locals - index_of_local);
block_builder.push_ops(push_offset_fmp_sequence(fmp_offset_of_local));
Ok(())
}