openvm_circuit/arch/testing/
test_adapter.rs

1use std::{
2    borrow::{Borrow, BorrowMut},
3    collections::VecDeque,
4    fmt::Debug,
5};
6
7use openvm_circuit_primitives_derive::AlignedBorrow;
8use openvm_instructions::instruction::Instruction;
9use openvm_stark_backend::{
10    interaction::InteractionBuilder,
11    p3_air::BaseAir,
12    p3_field::{Field, FieldAlgebra, PrimeField32},
13};
14use serde::{Deserialize, Serialize};
15
16use crate::{
17    arch::{
18        AdapterAirContext, AdapterRuntimeContext, DynAdapterInterface, DynArray, ExecutionBridge,
19        ExecutionState, MinimalInstruction, Result, VmAdapterAir, VmAdapterChip,
20    },
21    system::memory::{MemoryController, OfflineMemory},
22};
23
24// Replaces A: VmAdapterChip while testing VmCoreChip functionality, as it has no
25// constraints and thus cannot cause a failure.
26pub struct TestAdapterChip<F> {
27    /// List of the return values of `preprocess` this chip should provide on each sequential call.
28    pub prank_reads: VecDeque<Vec<F>>,
29    /// List of `pc_inc` to use in `postprocess` on each sequential call.
30    /// Defaults to `4` if not provided.
31    pub prank_pc_inc: VecDeque<Option<u32>>,
32
33    pub air: TestAdapterAir,
34}
35
36impl<F> TestAdapterChip<F> {
37    pub fn new(
38        prank_reads: Vec<Vec<F>>,
39        prank_pc_inc: Vec<Option<u32>>,
40        execution_bridge: ExecutionBridge,
41    ) -> Self {
42        Self {
43            prank_reads: prank_reads.into(),
44            prank_pc_inc: prank_pc_inc.into(),
45            air: TestAdapterAir { execution_bridge },
46        }
47    }
48}
49
50#[derive(Clone, Serialize, Deserialize)]
51pub struct TestAdapterRecord<T> {
52    pub from_pc: u32,
53    pub operands: [T; 7],
54}
55
56impl<F: PrimeField32> VmAdapterChip<F> for TestAdapterChip<F> {
57    type ReadRecord = ();
58    type WriteRecord = TestAdapterRecord<F>;
59    type Air = TestAdapterAir;
60    type Interface = DynAdapterInterface<F>;
61
62    fn preprocess(
63        &mut self,
64        _memory: &mut MemoryController<F>,
65        _instruction: &Instruction<F>,
66    ) -> Result<(DynArray<F>, Self::ReadRecord)> {
67        Ok((
68            self.prank_reads
69                .pop_front()
70                .expect("Not enough prank reads provided")
71                .into(),
72            (),
73        ))
74    }
75
76    fn postprocess(
77        &mut self,
78        memory: &mut MemoryController<F>,
79        instruction: &Instruction<F>,
80        from_state: ExecutionState<u32>,
81        _output: AdapterRuntimeContext<F, Self::Interface>,
82        _read_record: &Self::ReadRecord,
83    ) -> Result<(ExecutionState<u32>, Self::WriteRecord)> {
84        let pc_inc = self
85            .prank_pc_inc
86            .pop_front()
87            .map(|x| x.unwrap_or(4))
88            .unwrap_or(4);
89        Ok((
90            ExecutionState {
91                pc: from_state.pc + pc_inc,
92                timestamp: memory.timestamp(),
93            },
94            TestAdapterRecord {
95                operands: [
96                    instruction.a,
97                    instruction.b,
98                    instruction.c,
99                    instruction.d,
100                    instruction.e,
101                    instruction.f,
102                    instruction.g,
103                ],
104                from_pc: from_state.pc,
105            },
106        ))
107    }
108
109    fn generate_trace_row(
110        &self,
111        row_slice: &mut [F],
112        _read_record: Self::ReadRecord,
113        write_record: Self::WriteRecord,
114        _memory: &OfflineMemory<F>,
115    ) {
116        let cols: &mut TestAdapterCols<F> = row_slice.borrow_mut();
117        cols.from_pc = F::from_canonical_u32(write_record.from_pc);
118        cols.operands = write_record.operands;
119        // row_slice[0] = F::from_canonical_u32(write_record.from_pc);
120        // row_slice[1..].copy_from_slice(&write_record.operands);
121    }
122
123    fn air(&self) -> &Self::Air {
124        &self.air
125    }
126}
127
128#[derive(Clone, Copy, Debug)]
129pub struct TestAdapterAir {
130    pub execution_bridge: ExecutionBridge,
131}
132
133#[repr(C)]
134#[derive(AlignedBorrow)]
135pub struct TestAdapterCols<T> {
136    pub from_pc: T,
137    pub operands: [T; 7],
138}
139
140impl<F: Field> BaseAir<F> for TestAdapterAir {
141    fn width(&self) -> usize {
142        TestAdapterCols::<F>::width()
143    }
144}
145
146impl<AB: InteractionBuilder> VmAdapterAir<AB> for TestAdapterAir {
147    type Interface = DynAdapterInterface<AB::Expr>;
148
149    fn eval(
150        &self,
151        builder: &mut AB,
152        local: &[AB::Var],
153        ctx: AdapterAirContext<AB::Expr, Self::Interface>,
154    ) {
155        let processed_instruction: MinimalInstruction<AB::Expr> = ctx.instruction.into();
156        let cols: &TestAdapterCols<AB::Var> = local.borrow();
157        self.execution_bridge
158            .execute_and_increment_or_set_pc(
159                processed_instruction.opcode,
160                cols.operands.to_vec(),
161                ExecutionState {
162                    pc: cols.from_pc.into(),
163                    timestamp: AB::Expr::ONE,
164                },
165                AB::Expr::ZERO,
166                (4, ctx.to_pc),
167            )
168            .eval(builder, processed_instruction.is_valid);
169    }
170
171    fn get_from_pc(&self, local: &[AB::Var]) -> AB::Var {
172        let cols: &TestAdapterCols<AB::Var> = local.borrow();
173        cols.from_pc
174    }
175}