openvm_circuit/arch/testing/program/
mod.rs

1use std::{borrow::BorrowMut, mem::size_of, sync::Arc};
2
3use air::ProgramDummyAir;
4use openvm_instructions::instruction::Instruction;
5use openvm_stark_backend::{
6    config::{StarkGenericConfig, Val},
7    p3_field::{Field, FieldAlgebra, PrimeField32},
8    p3_matrix::dense::RowMajorMatrix,
9    prover::types::AirProofInput,
10    AirRef, Chip, ChipUsageGetter,
11};
12
13use crate::{
14    arch::ExecutionState,
15    system::program::{ProgramBus, ProgramExecutionCols},
16};
17
18mod air;
19
20#[derive(Debug)]
21pub struct ProgramTester<F: Field> {
22    pub bus: ProgramBus,
23    pub records: Vec<ProgramExecutionCols<F>>,
24}
25
26impl<F: PrimeField32> ProgramTester<F> {
27    pub fn new(bus: ProgramBus) -> Self {
28        Self {
29            bus,
30            records: vec![],
31        }
32    }
33
34    pub fn execute(&mut self, instruction: &Instruction<F>, initial_state: &ExecutionState<u32>) {
35        self.records.push(ProgramExecutionCols {
36            pc: F::from_canonical_u32(initial_state.pc),
37            opcode: instruction.opcode.to_field(),
38            a: instruction.a,
39            b: instruction.b,
40            c: instruction.c,
41            d: instruction.d,
42            e: instruction.e,
43            f: instruction.f,
44            g: instruction.g,
45        });
46    }
47}
48
49impl<F: Field> ProgramTester<F> {
50    fn width() -> usize {
51        size_of::<ProgramExecutionCols<u8>>() + 1
52    }
53}
54
55impl<SC: StarkGenericConfig> Chip<SC> for ProgramTester<Val<SC>> {
56    fn air(&self) -> AirRef<SC> {
57        Arc::new(ProgramDummyAir::new(self.bus))
58    }
59
60    fn generate_air_proof_input(self) -> AirProofInput<SC> {
61        let height = self.records.len().next_power_of_two();
62        let width = self.trace_width();
63        let mut values = Val::<SC>::zero_vec(height * width);
64        // This zip only goes through records. The padding rows between records.len()..height
65        // are filled with zeros - in particular count = 0 so nothing is added to bus.
66        for (row, record) in values.chunks_mut(width).zip(self.records) {
67            *(row[..width - 1]).borrow_mut() = record;
68            row[width - 1] = Val::<SC>::ONE;
69        }
70        AirProofInput::simple_no_pis(RowMajorMatrix::new(values, width))
71    }
72}
73
74impl<F: Field> ChipUsageGetter for ProgramTester<F> {
75    fn air_name(&self) -> String {
76        "ProgramDummyAir".to_string()
77    }
78    fn current_trace_height(&self) -> usize {
79        self.records.len()
80    }
81    fn trace_width(&self) -> usize {
82        Self::width()
83    }
84}