use alloc::vec::Vec;
use miden_air::Felt;
use miden_core::{ONE, Word, ZERO};
use crate::ContextId;
#[derive(Debug, Default, Clone)]
pub struct BlockStack {
blocks: Vec<BlockInfo>,
}
impl BlockStack {
pub fn push(
&mut self,
addr: Felt,
block_type: BlockType,
ctx_info: Option<ExecutionContextInfo>,
) -> Felt {
if block_type == BlockType::Call
|| block_type == BlockType::SysCall
|| block_type == BlockType::Dyncall
{
debug_assert!(ctx_info.is_some(), "no execution context provided for a CALL block");
} else {
debug_assert!(ctx_info.is_none(), "execution context provided for a non-CALL block");
}
let (parent_addr, is_loop_body, _is_first_child) = match self.blocks.last() {
Some(parent) => match parent.block_type {
BlockType::Loop(loop_entered) => {
debug_assert!(loop_entered, "parent is un-entered loop");
(parent.addr, true, false)
},
BlockType::Join(first_child_executed) => {
(parent.addr, false, !first_child_executed)
},
_ => (parent.addr, false, false),
},
None => (ZERO, false, false),
};
self.blocks.push(BlockInfo {
addr,
block_type,
parent_addr,
ctx_info,
is_loop_body,
});
parent_addr
}
pub fn pop(&mut self) -> BlockInfo {
let block = self.blocks.pop().expect("block stack is empty");
if let Some(parent) = self.blocks.last_mut()
&& let BlockType::Join(first_child_executed) = parent.block_type
&& !first_child_executed
{
parent.block_type = BlockType::Join(true);
}
block
}
pub fn is_empty(&self) -> bool {
self.blocks.is_empty()
}
pub fn peek(&self) -> &BlockInfo {
self.blocks.last().expect("block stack is empty")
}
pub fn peek_mut(&mut self) -> &mut BlockInfo {
self.blocks.last_mut().expect("block stack is empty")
}
}
#[derive(Debug, Clone)]
pub struct BlockInfo {
pub addr: Felt,
block_type: BlockType,
pub parent_addr: Felt,
pub ctx_info: Option<ExecutionContextInfo>,
pub is_loop_body: bool,
}
impl BlockInfo {
pub fn is_entered_loop(&self) -> Felt {
if self.block_type == BlockType::Loop(true) {
ONE
} else {
ZERO
}
}
pub const fn is_loop_body(&self) -> Felt {
if self.is_loop_body { ONE } else { ZERO }
}
pub const fn is_call(&self) -> Felt {
match self.block_type {
BlockType::Call | BlockType::Dyncall => ONE,
_ => ZERO,
}
}
pub const fn is_syscall(&self) -> Felt {
match self.block_type {
BlockType::SysCall => ONE,
_ => ZERO,
}
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct ExecutionContextInfo {
pub parent_ctx: ContextId,
pub parent_fn_hash: Word,
pub parent_stack_depth: u32,
pub parent_next_overflow_addr: Felt,
}
impl ExecutionContextInfo {
pub fn new(
parent_ctx: ContextId,
parent_fn_hash: Word,
parent_stack_depth: u32,
parent_next_overflow_addr: Felt,
) -> Self {
Self {
parent_fn_hash,
parent_ctx,
parent_stack_depth,
parent_next_overflow_addr,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BlockType {
Join(bool), Split,
Loop(bool), Call,
Dyn,
Dyncall,
SysCall,
BasicBlock,
}