openvm_circuit/system/program/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use openvm_instructions::{
    instruction::{DebugInfo, Instruction},
    program::Program,
};
use openvm_stark_backend::{p3_field::PrimeField64, ChipUsageGetter};

use crate::{arch::ExecutionError, system::program::trace::padding_instruction};

#[cfg(test)]
pub mod tests;

mod air;
mod bus;
pub mod trace;

pub use air::*;
pub use bus::*;

const EXIT_CODE_FAIL: usize = 1;

#[derive(Debug)]
pub struct ProgramChip<F> {
    pub air: ProgramAir,
    pub program: Program<F>,
    pub true_program_length: usize,
    pub execution_frequencies: Vec<usize>,
}

impl<F: PrimeField64> ProgramChip<F> {
    pub fn new(bus: ProgramBus) -> Self {
        Self {
            execution_frequencies: vec![],
            program: Program::default(),
            true_program_length: 0,
            air: ProgramAir { bus },
        }
    }

    pub fn new_with_program(program: Program<F>, bus: ProgramBus) -> Self {
        let mut ret = Self::new(bus);
        ret.set_program(program);
        ret
    }

    pub fn set_program(&mut self, mut program: Program<F>) {
        let true_program_length = program.len();
        while !program.len().is_power_of_two() {
            program.push_instruction(padding_instruction());
        }
        self.true_program_length = true_program_length;
        self.execution_frequencies = vec![0; program.len()];
        self.program = program;
    }

    fn get_pc_index(&self, pc: u32) -> Result<usize, ExecutionError> {
        let step = self.program.step;
        let pc_base = self.program.pc_base;
        let pc_index = ((pc - pc_base) / step) as usize;
        if !(0..self.true_program_length).contains(&pc_index) {
            return Err(ExecutionError::PcOutOfBounds {
                pc,
                step,
                pc_base,
                program_len: self.true_program_length,
            });
        }
        Ok(pc_index)
    }

    pub fn get_instruction(
        &mut self,
        pc: u32,
    ) -> Result<(Instruction<F>, Option<DebugInfo>), ExecutionError> {
        let pc_index = self.get_pc_index(pc)?;
        self.execution_frequencies[pc_index] += 1;
        self.program
            .get_instruction_and_debug_info(pc_index)
            .ok_or(ExecutionError::PcNotFound {
                pc,
                step: self.program.step,
                pc_base: self.program.pc_base,
                program_len: self.program.len(),
            })
    }
}

impl<F: PrimeField64> ChipUsageGetter for ProgramChip<F> {
    fn air_name(&self) -> String {
        "ProgramChip".to_string()
    }

    fn constant_trace_height(&self) -> Option<usize> {
        Some(self.true_program_length.next_power_of_two())
    }

    fn current_trace_height(&self) -> usize {
        self.true_program_length
    }

    fn trace_width(&self) -> usize {
        1
    }
}