1use std::{
2 borrow::{Borrow, BorrowMut},
3 marker::PhantomData,
4};
5
6use openvm_circuit::{
7 arch::{
8 AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge,
9 ExecutionBus, ExecutionState, MinimalInstruction, Result, VmAdapterAir, VmAdapterChip,
10 VmAdapterInterface,
11 },
12 system::{
13 memory::{
14 offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols},
15 MemoryAddress, MemoryController, OfflineMemory, RecordId,
16 },
17 program::ProgramBus,
18 },
19};
20use openvm_circuit_primitives_derive::AlignedBorrow;
21use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP};
22use openvm_native_compiler::conversion::AS;
23use openvm_stark_backend::{
24 interaction::InteractionBuilder,
25 p3_air::BaseAir,
26 p3_field::{Field, FieldAlgebra, PrimeField32},
27};
28use serde::{Deserialize, Serialize};
29use serde_big_array::BigArray;
30
31#[repr(C)]
32#[derive(Debug, Serialize, Deserialize)]
33pub struct VectorReadRecord<const NUM_READS: usize, const READ_SIZE: usize> {
34 #[serde(with = "BigArray")]
35 pub reads: [RecordId; NUM_READS],
36}
37
38#[repr(C)]
39#[derive(Debug, Serialize, Deserialize)]
40pub struct VectorWriteRecord<const WRITE_SIZE: usize> {
41 pub from_state: ExecutionState<u32>,
42 pub writes: [RecordId; 1],
43}
44
45#[allow(dead_code)]
46#[derive(Debug)]
47pub struct ConvertAdapterChip<F: Field, const READ_SIZE: usize, const WRITE_SIZE: usize> {
48 pub air: ConvertAdapterAir<READ_SIZE, WRITE_SIZE>,
49 _marker: PhantomData<F>,
50}
51
52impl<F: PrimeField32, const READ_SIZE: usize, const WRITE_SIZE: usize>
53 ConvertAdapterChip<F, READ_SIZE, WRITE_SIZE>
54{
55 pub fn new(
56 execution_bus: ExecutionBus,
57 program_bus: ProgramBus,
58 memory_bridge: MemoryBridge,
59 ) -> Self {
60 Self {
61 air: ConvertAdapterAir {
62 execution_bridge: ExecutionBridge::new(execution_bus, program_bus),
63 memory_bridge,
64 },
65 _marker: PhantomData,
66 }
67 }
68}
69
70#[repr(C)]
71#[derive(AlignedBorrow)]
72pub struct ConvertAdapterCols<T, const READ_SIZE: usize, const WRITE_SIZE: usize> {
73 pub from_state: ExecutionState<T>,
74 pub a_pointer: T,
75 pub b_pointer: T,
76 pub writes_aux: [MemoryWriteAuxCols<T, WRITE_SIZE>; 1],
77 pub reads_aux: [MemoryReadAuxCols<T>; 1],
78}
79
80#[derive(Clone, Copy, Debug, derive_new::new)]
81pub struct ConvertAdapterAir<const READ_SIZE: usize, const WRITE_SIZE: usize> {
82 pub(super) execution_bridge: ExecutionBridge,
83 pub(super) memory_bridge: MemoryBridge,
84}
85
86impl<F: Field, const READ_SIZE: usize, const WRITE_SIZE: usize> BaseAir<F>
87 for ConvertAdapterAir<READ_SIZE, WRITE_SIZE>
88{
89 fn width(&self) -> usize {
90 ConvertAdapterCols::<F, READ_SIZE, WRITE_SIZE>::width()
91 }
92}
93
94impl<AB: InteractionBuilder, const READ_SIZE: usize, const WRITE_SIZE: usize> VmAdapterAir<AB>
95 for ConvertAdapterAir<READ_SIZE, WRITE_SIZE>
96{
97 type Interface =
98 BasicAdapterInterface<AB::Expr, MinimalInstruction<AB::Expr>, 1, 1, READ_SIZE, WRITE_SIZE>;
99
100 fn eval(
101 &self,
102 builder: &mut AB,
103 local: &[AB::Var],
104 ctx: AdapterAirContext<AB::Expr, Self::Interface>,
105 ) {
106 let cols: &ConvertAdapterCols<_, READ_SIZE, WRITE_SIZE> = local.borrow();
107 let timestamp = cols.from_state.timestamp;
108 let mut timestamp_delta = 0usize;
109 let mut timestamp_pp = || {
110 timestamp_delta += 1;
111 timestamp + AB::F::from_canonical_usize(timestamp_delta - 1)
112 };
113
114 let d = AB::Expr::TWO;
115 let e = AB::Expr::from_canonical_u32(AS::Native as u32);
116
117 self.memory_bridge
118 .read(
119 MemoryAddress::new(e.clone(), cols.b_pointer),
120 ctx.reads[0].clone(),
121 timestamp_pp(),
122 &cols.reads_aux[0],
123 )
124 .eval(builder, ctx.instruction.is_valid.clone());
125
126 self.memory_bridge
127 .write(
128 MemoryAddress::new(d.clone(), cols.a_pointer),
129 ctx.writes[0].clone(),
130 timestamp_pp(),
131 &cols.writes_aux[0],
132 )
133 .eval(builder, ctx.instruction.is_valid.clone());
134
135 self.execution_bridge
136 .execute_and_increment_or_set_pc(
137 ctx.instruction.opcode,
138 [
139 cols.a_pointer.into(),
140 cols.b_pointer.into(),
141 AB::Expr::ZERO,
142 d,
143 e,
144 ],
145 cols.from_state,
146 AB::F::from_canonical_usize(timestamp_delta),
147 (DEFAULT_PC_STEP, ctx.to_pc),
148 )
149 .eval(builder, ctx.instruction.is_valid);
150 }
151
152 fn get_from_pc(&self, local: &[AB::Var]) -> AB::Var {
153 let cols: &ConvertAdapterCols<_, READ_SIZE, WRITE_SIZE> = local.borrow();
154 cols.from_state.pc
155 }
156}
157
158impl<F: PrimeField32, const READ_SIZE: usize, const WRITE_SIZE: usize> VmAdapterChip<F>
159 for ConvertAdapterChip<F, READ_SIZE, WRITE_SIZE>
160{
161 type ReadRecord = VectorReadRecord<1, READ_SIZE>;
162 type WriteRecord = VectorWriteRecord<WRITE_SIZE>;
163 type Air = ConvertAdapterAir<READ_SIZE, WRITE_SIZE>;
164 type Interface = BasicAdapterInterface<F, MinimalInstruction<F>, 1, 1, READ_SIZE, WRITE_SIZE>;
165
166 fn preprocess(
167 &mut self,
168 memory: &mut MemoryController<F>,
169 instruction: &Instruction<F>,
170 ) -> Result<(
171 <Self::Interface as VmAdapterInterface<F>>::Reads,
172 Self::ReadRecord,
173 )> {
174 let Instruction { b, e, .. } = *instruction;
175
176 let y_val = memory.read::<READ_SIZE>(e, b);
177
178 Ok(([y_val.1], Self::ReadRecord { reads: [y_val.0] }))
179 }
180
181 fn postprocess(
182 &mut self,
183 memory: &mut MemoryController<F>,
184 instruction: &Instruction<F>,
185 from_state: ExecutionState<u32>,
186 output: AdapterRuntimeContext<F, Self::Interface>,
187 _read_record: &Self::ReadRecord,
188 ) -> Result<(ExecutionState<u32>, Self::WriteRecord)> {
189 let Instruction { a, d, .. } = *instruction;
190 let (write_id, _) = memory.write::<WRITE_SIZE>(d, a, output.writes[0]);
191
192 Ok((
193 ExecutionState {
194 pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP),
195 timestamp: memory.timestamp(),
196 },
197 Self::WriteRecord {
198 from_state,
199 writes: [write_id],
200 },
201 ))
202 }
203
204 fn generate_trace_row(
205 &self,
206 row_slice: &mut [F],
207 read_record: Self::ReadRecord,
208 write_record: Self::WriteRecord,
209 memory: &OfflineMemory<F>,
210 ) {
211 let aux_cols_factory = memory.aux_cols_factory();
212 let row_slice: &mut ConvertAdapterCols<_, READ_SIZE, WRITE_SIZE> = row_slice.borrow_mut();
213
214 let read = memory.record_by_id(read_record.reads[0]);
215 let write = memory.record_by_id(write_record.writes[0]);
216
217 row_slice.from_state = write_record.from_state.map(F::from_canonical_u32);
218 row_slice.a_pointer = write.pointer;
219 row_slice.b_pointer = read.pointer;
220
221 aux_cols_factory.generate_read_aux(read, &mut row_slice.reads_aux[0]);
222 aux_cols_factory.generate_write_aux(write, &mut row_slice.writes_aux[0]);
223 }
224
225 fn air(&self) -> &Self::Air {
226 &self.air
227 }
228}