openvm_native_circuit/adapters/
convert_adapter.rs

1use std::{
2    borrow::{Borrow, BorrowMut},
3    mem::size_of,
4};
5
6use openvm_circuit::{
7    arch::{
8        get_record_from_slice, AdapterAirContext, AdapterTraceExecutor, AdapterTraceFiller,
9        BasicAdapterInterface, ExecutionBridge, ExecutionState, MinimalInstruction, VmAdapterAir,
10    },
11    system::{
12        memory::{
13            offline_checker::{
14                MemoryBridge, MemoryReadAuxCols, MemoryReadAuxRecord, MemoryWriteAuxCols,
15                MemoryWriteBytesAuxRecord,
16            },
17            online::TracingMemory,
18            MemoryAddress, MemoryAuxColsFactory,
19        },
20        native_adapter::util::tracing_read_native,
21    },
22};
23use openvm_circuit_primitives::AlignedBytesBorrow;
24use openvm_circuit_primitives_derive::AlignedBorrow;
25use openvm_instructions::{
26    instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_MEMORY_AS,
27};
28use openvm_native_compiler::conversion::AS;
29use openvm_rv32im_circuit::adapters::tracing_write;
30use openvm_stark_backend::{
31    interaction::InteractionBuilder,
32    p3_air::BaseAir,
33    p3_field::{Field, FieldAlgebra, PrimeField32},
34};
35
36#[repr(C)]
37#[derive(AlignedBorrow)]
38pub struct ConvertAdapterCols<T, const READ_SIZE: usize, const WRITE_SIZE: usize> {
39    pub from_state: ExecutionState<T>,
40    pub a_pointer: T,
41    pub b_pointer: T,
42    pub writes_aux: [MemoryWriteAuxCols<T, WRITE_SIZE>; 1],
43    pub reads_aux: [MemoryReadAuxCols<T>; 1],
44}
45
46#[derive(Clone, Copy, Debug, derive_new::new)]
47pub struct ConvertAdapterAir<const READ_SIZE: usize, const WRITE_SIZE: usize> {
48    pub(super) execution_bridge: ExecutionBridge,
49    pub(super) memory_bridge: MemoryBridge,
50}
51
52impl<F: Field, const READ_SIZE: usize, const WRITE_SIZE: usize> BaseAir<F>
53    for ConvertAdapterAir<READ_SIZE, WRITE_SIZE>
54{
55    fn width(&self) -> usize {
56        ConvertAdapterCols::<F, READ_SIZE, WRITE_SIZE>::width()
57    }
58}
59
60impl<AB: InteractionBuilder, const READ_SIZE: usize, const WRITE_SIZE: usize> VmAdapterAir<AB>
61    for ConvertAdapterAir<READ_SIZE, WRITE_SIZE>
62{
63    type Interface =
64        BasicAdapterInterface<AB::Expr, MinimalInstruction<AB::Expr>, 1, 1, READ_SIZE, WRITE_SIZE>;
65
66    fn eval(
67        &self,
68        builder: &mut AB,
69        local: &[AB::Var],
70        ctx: AdapterAirContext<AB::Expr, Self::Interface>,
71    ) {
72        let cols: &ConvertAdapterCols<_, READ_SIZE, WRITE_SIZE> = local.borrow();
73        let timestamp = cols.from_state.timestamp;
74        let mut timestamp_delta = 0usize;
75        let mut timestamp_pp = || {
76            timestamp_delta += 1;
77            timestamp + AB::F::from_canonical_usize(timestamp_delta - 1)
78        };
79
80        let d = AB::Expr::TWO;
81        let e = AB::Expr::from_canonical_u32(AS::Native as u32);
82
83        self.memory_bridge
84            .read(
85                MemoryAddress::new(e.clone(), cols.b_pointer),
86                ctx.reads[0].clone(),
87                timestamp_pp(),
88                &cols.reads_aux[0],
89            )
90            .eval(builder, ctx.instruction.is_valid.clone());
91
92        self.memory_bridge
93            .write(
94                MemoryAddress::new(d.clone(), cols.a_pointer),
95                ctx.writes[0].clone(),
96                timestamp_pp(),
97                &cols.writes_aux[0],
98            )
99            .eval(builder, ctx.instruction.is_valid.clone());
100
101        self.execution_bridge
102            .execute_and_increment_or_set_pc(
103                ctx.instruction.opcode,
104                [
105                    cols.a_pointer.into(),
106                    cols.b_pointer.into(),
107                    AB::Expr::ZERO,
108                    d,
109                    e,
110                ],
111                cols.from_state,
112                AB::F::from_canonical_usize(timestamp_delta),
113                (DEFAULT_PC_STEP, ctx.to_pc),
114            )
115            .eval(builder, ctx.instruction.is_valid);
116    }
117
118    fn get_from_pc(&self, local: &[AB::Var]) -> AB::Var {
119        let cols: &ConvertAdapterCols<_, READ_SIZE, WRITE_SIZE> = local.borrow();
120        cols.from_state.pc
121    }
122}
123
124#[repr(C)]
125#[derive(AlignedBytesBorrow, Debug)]
126pub struct ConvertAdapterRecord<F, const READ_SIZE: usize, const WRITE_SIZE: usize> {
127    pub from_pc: u32,
128    pub from_timestamp: u32,
129
130    pub a_ptr: F,
131    pub b_ptr: F,
132
133    pub read_aux: MemoryReadAuxRecord,
134    pub write_aux: MemoryWriteBytesAuxRecord<WRITE_SIZE>,
135}
136
137#[derive(derive_new::new, Clone, Copy)]
138pub struct ConvertAdapterExecutor<const READ_SIZE: usize, const WRITE_SIZE: usize>;
139
140#[derive(derive_new::new)]
141pub struct ConvertAdapterFiller<const READ_SIZE: usize, const WRITE_SIZE: usize>;
142
143impl<F: PrimeField32, const READ_SIZE: usize, const WRITE_SIZE: usize> AdapterTraceExecutor<F>
144    for ConvertAdapterExecutor<READ_SIZE, WRITE_SIZE>
145{
146    const WIDTH: usize = size_of::<ConvertAdapterCols<u8, READ_SIZE, WRITE_SIZE>>();
147    type ReadData = [F; READ_SIZE];
148    type WriteData = [u8; WRITE_SIZE];
149    type RecordMut<'a> = &'a mut ConvertAdapterRecord<F, READ_SIZE, WRITE_SIZE>;
150
151    #[inline(always)]
152    fn start(pc: u32, memory: &TracingMemory, record: &mut Self::RecordMut<'_>) {
153        record.from_pc = pc;
154        record.from_timestamp = memory.timestamp;
155    }
156
157    #[inline(always)]
158    fn read(
159        &self,
160        memory: &mut TracingMemory,
161        instruction: &Instruction<F>,
162        record: &mut Self::RecordMut<'_>,
163    ) -> Self::ReadData {
164        let &Instruction { b, e, .. } = instruction;
165        debug_assert_eq!(e.as_canonical_u32(), AS::Native as u32);
166
167        record.b_ptr = b;
168
169        tracing_read_native(
170            memory,
171            b.as_canonical_u32(),
172            &mut record.read_aux.prev_timestamp,
173        )
174    }
175
176    #[inline(always)]
177    fn write(
178        &self,
179        memory: &mut TracingMemory,
180        instruction: &Instruction<F>,
181        data: Self::WriteData,
182        record: &mut Self::RecordMut<'_>,
183    ) {
184        let &Instruction { a, d, .. } = instruction;
185
186        debug_assert_eq!(d.as_canonical_u32(), RV32_MEMORY_AS);
187
188        record.a_ptr = a;
189        tracing_write(
190            memory,
191            RV32_MEMORY_AS,
192            a.as_canonical_u32(),
193            data,
194            &mut record.write_aux.prev_timestamp,
195            &mut record.write_aux.prev_data,
196        );
197    }
198}
199
200impl<F: PrimeField32, const READ_SIZE: usize, const WRITE_SIZE: usize> AdapterTraceFiller<F>
201    for ConvertAdapterFiller<READ_SIZE, WRITE_SIZE>
202{
203    const WIDTH: usize = size_of::<ConvertAdapterCols<u8, READ_SIZE, WRITE_SIZE>>();
204
205    #[inline(always)]
206    fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory<F>, mut row_slice: &mut [F]) {
207        // SAFETY:
208        // - caller ensures `adapter_row` contains a valid record representation that was previously
209        //   written by the executor
210        let record: &ConvertAdapterRecord<F, READ_SIZE, WRITE_SIZE> =
211            unsafe { get_record_from_slice(&mut row_slice, ()) };
212        let adapter_row: &mut ConvertAdapterCols<F, READ_SIZE, WRITE_SIZE> = row_slice.borrow_mut();
213
214        // Writing in reverse order to avoid overwriting the `record`
215        mem_helper.fill(
216            record.read_aux.prev_timestamp,
217            record.from_timestamp,
218            adapter_row.reads_aux[0].as_mut(),
219        );
220
221        adapter_row.writes_aux[0]
222            .set_prev_data(record.write_aux.prev_data.map(F::from_canonical_u8));
223        mem_helper.fill(
224            record.write_aux.prev_timestamp,
225            record.from_timestamp + 1,
226            adapter_row.writes_aux[0].as_mut(),
227        );
228
229        adapter_row.b_pointer = record.b_ptr;
230        adapter_row.a_pointer = record.a_ptr;
231
232        adapter_row.from_state.timestamp = F::from_canonical_u32(record.from_timestamp);
233        adapter_row.from_state.pc = F::from_canonical_u32(record.from_pc);
234    }
235}