use super::{Felt, Word, ONE, ZERO};
use crate::system::ContextId;
use alloc::vec::Vec;
#[derive(Default)]
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 {
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,
is_first_child,
});
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() {
if let BlockType::Join(first_child_executed) = parent.block_type {
if !first_child_executed {
parent.block_type = BlockType::Join(true);
}
}
}
block
}
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)]
pub struct BlockInfo {
pub addr: Felt,
block_type: BlockType,
pub parent_addr: Felt,
pub ctx_info: Option<ExecutionContextInfo>,
pub is_loop_body: bool,
#[allow(dead_code)] pub is_first_child: 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 => 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_fmp: Felt,
pub parent_stack_depth: u32,
pub parent_next_overflow_addr: Felt,
}
impl ExecutionContextInfo {
pub fn new(
parent_ctx: ContextId,
parent_fn_hash: Word,
parent_fmp: Felt,
parent_stack_depth: u32,
parent_next_overflow_addr: Felt,
) -> Self {
Self {
parent_fn_hash,
parent_ctx,
parent_fmp,
parent_stack_depth,
parent_next_overflow_addr,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BlockType {
Join(bool), Split,
Loop(bool), Call,
Dyn,
SysCall,
Span,
}