use alloc::vec::Vec;
use miden_air::RowIndex;
use miden_core::{Word, stack::MIN_STACK_DEPTH};
use super::{
ExecutionError, Felt, FieldElement, ONE, STACK_TRACE_WIDTH, StackInputs, StackOutputs, ZERO,
};
mod trace;
use trace::StackTrace;
mod overflow;
pub(crate) use overflow::OverflowTable;
mod aux_trace;
pub use aux_trace::AuxTraceBuilder;
#[cfg(test)]
pub(crate) use aux_trace::OverflowTableRow;
#[cfg(test)]
mod tests;
const MAX_TOP_IDX: usize = MIN_STACK_DEPTH - 1;
#[derive(Debug)]
pub struct Stack {
clk: RowIndex,
trace: StackTrace,
overflow: OverflowTable,
active_depth: usize,
full_depth: usize,
}
impl Stack {
pub fn new(
inputs: &StackInputs,
init_trace_capacity: usize,
save_overflow_history: bool,
) -> Self {
let trace = StackTrace::new(&**inputs, init_trace_capacity, MIN_STACK_DEPTH, ZERO);
Self {
clk: RowIndex::from(0),
trace,
overflow: OverflowTable::new(save_overflow_history),
active_depth: MIN_STACK_DEPTH,
full_depth: MIN_STACK_DEPTH,
}
}
pub fn depth(&self) -> usize {
self.active_depth
}
pub fn current_clk(&self) -> RowIndex {
self.clk
}
pub fn trace_len(&self) -> usize {
self.clk.into()
}
pub fn peek(&self) -> Felt {
self.trace.peek_at(self.clk)
}
pub fn get_state_at(&self, clk: RowIndex) -> Vec<Felt> {
let mut result = Vec::with_capacity(self.active_depth);
self.trace.append_state_into(&mut result, clk);
if clk == self.clk {
self.overflow.append_into(&mut result);
} else {
self.overflow.append_from_history_at(clk, &mut result);
}
result
}
pub fn build_stack_outputs(&self) -> Result<StackOutputs, ExecutionError> {
if self.overflow.total_num_elements() != 0 {
return Err(ExecutionError::OutputStackOverflow(self.overflow.total_num_elements()));
}
let mut stack_items = Vec::with_capacity(self.active_depth);
self.trace.append_state_into(&mut stack_items, self.clk);
Ok(StackOutputs::new(stack_items).expect("processor stack handling logic is valid"))
}
pub fn get(&self, pos: usize) -> Felt {
if pos < MIN_STACK_DEPTH {
self.trace.get_stack_value_at(self.clk, pos)
} else {
let overflow_index = pos - MIN_STACK_DEPTH;
self.overflow.get_element_at(overflow_index).unwrap_or(ZERO)
}
}
pub fn get_word(&self, start_idx: usize) -> Word {
[
self.get(start_idx + 3),
self.get(start_idx + 2),
self.get(start_idx + 1),
self.get(start_idx),
]
.into()
}
pub fn set(&mut self, pos: usize, value: Felt) {
debug_assert!(pos < MIN_STACK_DEPTH, "stack underflow");
self.trace.set_stack_value_at(self.clk + 1, pos, value);
}
pub fn copy_state(&mut self, start_pos: usize) {
self.trace.copy_stack_state_at(
self.clk.into(),
start_pos,
Felt::try_from(self.active_depth as u64)
.expect("value is greater than or equal to the field modulus"),
self.overflow.last_update_clk_in_current_ctx(),
);
}
pub fn shift_left(&mut self, start_pos: usize) {
debug_assert!(start_pos > 0, "start position must be greater than 0");
debug_assert!(start_pos <= MIN_STACK_DEPTH, "start position cannot exceed stack top size");
let (next_depth, next_overflow_addr) = self.shift_left_no_helpers(start_pos);
self.trace.set_helpers_at(self.clk.as_usize(), next_depth, next_overflow_addr);
}
pub fn shift_right(&mut self, start_pos: usize) {
debug_assert!(start_pos < MIN_STACK_DEPTH, "start position cannot exceed stack top size");
self.trace.stack_shift_right_at(self.clk, start_pos);
let to_overflow = self.trace.get_stack_value_at(self.clk, MAX_TOP_IDX);
self.overflow.push(to_overflow);
self.active_depth += 1;
self.full_depth += 1;
}
fn shift_left_no_helpers(&mut self, start_pos: usize) -> (Felt, Felt) {
match self.active_depth {
0..=MAX_TOP_IDX => unreachable!("stack underflow"),
MIN_STACK_DEPTH => {
self.trace.stack_shift_left_no_helpers(self.clk, start_pos, ZERO, None)
},
_ => {
let from_overflow =
self.overflow.pop().expect("overflow table was empty on left shift");
let helpers = self.trace.stack_shift_left_no_helpers(
self.clk,
start_pos,
from_overflow,
Some(self.overflow.last_update_clk_in_current_ctx()),
);
self.active_depth -= 1;
self.full_depth -= 1;
helpers
},
}
}
pub fn shift_left_and_start_context(&mut self) -> (usize, Felt) {
const START_POSITION: usize = 1;
self.shift_left_no_helpers(START_POSITION);
let next_depth = self.start_context();
self.trace.set_helpers_at(
self.clk.as_usize(),
Felt::from(self.active_depth as u32),
self.overflow.last_update_clk_in_current_ctx(),
);
next_depth
}
pub fn start_context(&mut self) -> (usize, Felt) {
let current_depth = self.active_depth;
let current_overflow_addr = self.overflow.last_update_clk_in_current_ctx();
self.active_depth = MIN_STACK_DEPTH;
self.overflow.start_context();
(current_depth, current_overflow_addr)
}
pub fn restore_context(&mut self, stack_depth: usize) {
debug_assert!(stack_depth <= self.full_depth, "stack depth too big");
debug_assert_eq!(self.active_depth, MIN_STACK_DEPTH, "overflow table not empty");
self.active_depth = stack_depth;
self.overflow.restore_context();
}
pub fn into_trace(self, trace_len: usize, num_rand_rows: usize) -> super::StackTrace {
let clk = self.current_clk().as_usize();
assert!(clk + num_rand_rows <= trace_len, "target trace length too small");
assert_eq!(self.active_depth, self.full_depth, "inconsistent stack depth");
let mut trace = self.trace.into_array();
for column in trace.iter_mut() {
let last_value = column[clk];
column[clk..].fill(last_value);
column.resize(trace_len, last_value);
}
super::StackTrace { trace }
}
pub fn ensure_trace_capacity(&mut self) {
self.trace.ensure_trace_capacity(self.clk);
}
pub fn advance_clock(&mut self) {
self.clk += 1_u32;
self.overflow.advance_clock();
}
#[cfg(any(test, feature = "testing"))]
pub fn trace_state(&self) -> [Felt; MIN_STACK_DEPTH] {
self.trace.get_stack_state_at(self.clk)
}
#[cfg(test)]
pub fn helpers_state(&self) -> [Felt; miden_air::trace::stack::NUM_STACK_HELPER_COLS] {
self.trace.get_helpers_state_at(self.clk)
}
}