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