use miden_assembly_syntax::{
Felt, ParsingError,
debuginfo::{SourceSpan, Span},
diagnostics::{RelatedError, RelatedLabel, Report},
};
use miden_core::{events::SystemEvent, field::Field, operations::Operation::*};
use super::BasicBlockBuilder;
use crate::{MAX_EXP_BITS, ONE, ProcedureContext, ZERO};
const TWO: Felt = Felt::new(2);
pub fn assertw(span_builder: &mut BasicBlockBuilder, err_code: Felt) {
span_builder.push_ops([
MovUp4,
Eq,
Assert(err_code),
MovUp3,
Eq,
Assert(err_code),
MovUp2,
Eq,
Assert(err_code),
Eq,
Assert(err_code),
]);
}
pub fn add_imm(span_builder: &mut BasicBlockBuilder, imm: Felt) {
if imm == ZERO {
span_builder.push_op(Noop);
} else if imm == ONE {
span_builder.push_op(Incr);
} else if imm == TWO {
span_builder.push_ops([Incr, Incr]);
} else {
span_builder.push_ops([Push(imm), Add]);
}
}
pub fn sub_imm(span_builder: &mut BasicBlockBuilder, imm: Felt) {
if imm == ZERO {
span_builder.push_op(Noop);
} else {
span_builder.push_ops([Push(-imm), Add]);
}
}
pub fn mul_imm(span_builder: &mut BasicBlockBuilder, imm: Felt) {
if imm == ZERO {
span_builder.push_ops([Drop, Pad]);
} else if imm == ONE {
span_builder.push_op(Noop);
} else {
span_builder.push_ops([Push(imm), Mul]);
}
}
pub fn div_imm(
span_builder: &mut BasicBlockBuilder,
proc_ctx: &mut ProcedureContext,
imm: Span<Felt>,
) -> Result<(), Report> {
if imm == ZERO {
let source_span = imm.span();
let source_file = proc_ctx.source_manager().get(source_span.source_id()).ok();
let error = Report::new(ParsingError::DivisionByZero { span: source_span });
return Err(if let Some(source_file) = source_file {
RelatedError::new(error.with_source_code(source_file)).into()
} else {
RelatedError::new(error).into()
});
} else if imm == ONE {
span_builder.push_op(Noop);
} else {
span_builder.push_ops([Push(imm.into_inner().inverse()), Mul]);
}
Ok(())
}
pub fn pow2(span_builder: &mut BasicBlockBuilder) {
append_pow2_op(span_builder);
}
pub fn append_pow2_op(span_builder: &mut BasicBlockBuilder) {
span_builder.push_op(Push(Felt::from_u8(2)));
span_builder.push_ops([Pad, Incr]);
span_builder.push_ops([Swap, Pad]);
span_builder.push_ops([Expacc, Expacc, Expacc, Expacc, Expacc, Expacc]);
span_builder.push_ops([Drop, Drop]);
span_builder.push_ops([Swap, Eqz, Assert(ZERO)]);
}
pub fn exp(
span_builder: &mut BasicBlockBuilder,
proc_ctx: &ProcedureContext,
num_pow_bits: u8,
span: SourceSpan,
) -> Result<(), Report> {
if num_pow_bits > MAX_EXP_BITS {
return Err(RelatedLabel::error("invalid argument")
.with_labeled_span(span, "this instruction argument is out of range")
.with_help(format!("value must be in the range 0..={MAX_EXP_BITS}"))
.with_source_file(proc_ctx.source_manager().get(span.source_id()).ok())
.into());
}
span_builder.push_ops([Pad, Incr, MovUp2, Pad]);
span_builder.push_op_many(Expacc, num_pow_bits as usize);
span_builder.push_ops([Drop, Drop]);
span_builder.push_ops([Swap, Eqz, Assert(ZERO)]);
Ok(())
}
pub fn exp_imm(
span_builder: &mut BasicBlockBuilder,
proc_ctx: &ProcedureContext,
pow: Felt,
span: SourceSpan,
) -> Result<(), Report> {
if pow.as_canonical_u64() <= 7 {
perform_exp_for_small_power(span_builder, pow.as_canonical_u64());
Ok(())
} else {
let num_pow_bits = (64 - pow.as_canonical_u64().leading_zeros()) as u8;
span_builder.push_op(Push(pow));
exp(span_builder, proc_ctx, num_pow_bits, span)
}
}
fn perform_exp_for_small_power(span_builder: &mut BasicBlockBuilder, pow: u64) {
match pow {
0 => {
span_builder.push_op(Drop);
span_builder.push_op(Pad);
span_builder.push_op(Incr);
},
1 => span_builder.push_op(Noop), 2 => {
span_builder.push_op(Dup0);
span_builder.push_op(Mul);
},
3 => {
span_builder.push_op_many(Dup0, 2);
span_builder.push_op_many(Mul, 2);
},
4 => {
span_builder.push_op_many(Dup0, 3);
span_builder.push_op_many(Mul, 3);
},
5 => {
span_builder.push_op_many(Dup0, 4);
span_builder.push_op_many(Mul, 4);
},
6 => {
span_builder.push_op_many(Dup0, 5);
span_builder.push_op_many(Mul, 5);
},
7 => {
span_builder.push_op_many(Dup0, 6);
span_builder.push_op_many(Mul, 6);
},
_ => unreachable!("pow must be less than 8"),
}
}
pub fn ilog2(block_builder: &mut BasicBlockBuilder) {
block_builder.push_system_event(SystemEvent::ILog2);
block_builder.push_op(AdvPop);
block_builder.push_op(Dup0);
append_pow2_op(block_builder);
block_builder.push_ops([MovUp2, Dup1, Dup1, Swap]);
gte(block_builder);
block_builder.push_ops([Assert(ZERO), MovDn2]);
block_builder.push_ops([MovUp2, Swap, Push(Felt::from_u8(2)), Mul]);
lt(block_builder);
block_builder.push_ops([Dup1, Push(Felt::from_u8(63)), Eq, Or, Assert(ZERO)]);
}
pub fn eq_imm(span_builder: &mut BasicBlockBuilder, imm: Felt) {
if imm == ZERO {
span_builder.push_op(Eqz);
} else {
span_builder.push_ops([Push(imm), Eq]);
}
}
pub fn neq_imm(span_builder: &mut BasicBlockBuilder, imm: Felt) {
if imm == ZERO {
span_builder.push_ops([Eqz, Not]);
} else {
span_builder.push_ops([Push(imm), Eq, Not]);
}
}
pub fn eqw(span_builder: &mut BasicBlockBuilder) {
span_builder.push_ops([
Dup7, Dup4, Eq,
Dup7, Dup4, Eq, And, Dup6, Dup3, Eq, And, Dup5, Dup2, Eq, And,
]);
}
pub fn lt(span_builder: &mut BasicBlockBuilder) {
split_elements(span_builder);
check_lt_high_bits(span_builder);
check_lt(span_builder);
set_result(span_builder);
}
pub fn lte(span_builder: &mut BasicBlockBuilder) {
split_elements(span_builder);
check_lt_high_bits(span_builder);
check_lte(span_builder);
set_result(span_builder);
}
pub fn gt(span_builder: &mut BasicBlockBuilder) {
split_elements(span_builder);
check_gt_high_bits(span_builder);
check_lt(span_builder);
set_result(span_builder);
}
pub fn gte(span_builder: &mut BasicBlockBuilder) {
split_elements(span_builder);
check_gt_high_bits(span_builder);
check_lte(span_builder);
set_result(span_builder);
}
pub fn is_odd(span_builder: &mut BasicBlockBuilder) {
span_builder.push_ops([U32split, Swap, Drop, Pad, Incr, U32and]);
}
fn split_elements(span_builder: &mut BasicBlockBuilder) {
span_builder.push_op(Swap);
span_builder.push_op(U32split);
span_builder.push_op(MovUp2);
span_builder.push_op(U32split);
}
fn check_lt_and_eq(span_builder: &mut BasicBlockBuilder) {
span_builder.push_op(U32sub);
span_builder.push_ops([Swap, Eqz]);
}
fn check_lt_high_bits(span_builder: &mut BasicBlockBuilder) {
span_builder.push_op(MovUp3);
span_builder.push_op(MovUp2);
check_lt_and_eq(span_builder);
span_builder.push_op(MovUp2);
span_builder.push_op(MovUp3);
span_builder.push_op(Swap);
}
fn check_lt(span_builder: &mut BasicBlockBuilder) {
span_builder.push_op(U32sub);
span_builder.push_ops([Swap, Drop]);
}
fn set_result(span_builder: &mut BasicBlockBuilder) {
span_builder.push_op(And);
span_builder.push_op(Or);
}
fn check_lte(span_builder: &mut BasicBlockBuilder) {
span_builder.push_op(U32sub);
span_builder.push_ops([Swap, Eqz]);
span_builder.push_op(Or);
}
fn check_gt_high_bits(span_builder: &mut BasicBlockBuilder) {
span_builder.push_op(Swap);
span_builder.push_op(MovUp3);
check_lt_and_eq(span_builder);
span_builder.push_op(MovUp2);
span_builder.push_op(MovUp3);
}