1use std::{
2 array::from_fn,
3 borrow::{Borrow, BorrowMut},
4 iter::zip,
5};
6
7use itertools::izip;
8use openvm_circuit::{
9 arch::{
10 get_record_from_slice, AdapterAirContext, AdapterTraceExecutor, AdapterTraceFiller,
11 ExecutionBridge, ExecutionState, VecHeapBranchAdapterInterface, VmAdapterAir,
12 },
13 system::memory::{
14 offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryReadAuxRecord},
15 online::TracingMemory,
16 MemoryAddress, MemoryAuxColsFactory,
17 },
18};
19use openvm_circuit_primitives::{
20 bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip},
21 AlignedBytesBorrow, ColumnsAir, StructReflection, StructReflectionHelper,
22};
23use openvm_circuit_primitives_derive::AlignedBorrow;
24use openvm_instructions::{
25 instruction::Instruction,
26 program::DEFAULT_PC_STEP,
27 riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS},
28};
29use openvm_rv32im_circuit::adapters::{
30 abstract_compose, tracing_read, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS,
31};
32use openvm_stark_backend::{
33 interaction::InteractionBuilder,
34 p3_air::BaseAir,
35 p3_field::{Field, PrimeCharacteristicRing, PrimeField32},
36};
37
38#[repr(C)]
45#[derive(AlignedBorrow, StructReflection, Debug)]
46pub struct Rv32VecHeapBranchAdapterCols<
47 T,
48 const NUM_READS: usize,
49 const BLOCKS_PER_READ: usize,
50 const READ_SIZE: usize,
51> {
52 pub from_state: ExecutionState<T>,
53
54 pub rs_ptr: [T; NUM_READS],
55 pub rs_val: [[T; RV32_REGISTER_NUM_LIMBS]; NUM_READS],
56 pub rs_read_aux: [MemoryReadAuxCols<T>; NUM_READS],
57
58 pub reads_aux: [[MemoryReadAuxCols<T>; BLOCKS_PER_READ]; NUM_READS],
59}
60
61#[allow(dead_code)]
62#[derive(Clone, Copy, Debug, derive_new::new, ColumnsAir)]
63#[columns_via(Rv32VecHeapBranchAdapterCols<u8, NUM_READS, BLOCKS_PER_READ, READ_SIZE>)]
64pub struct Rv32VecHeapBranchAdapterAir<
65 const NUM_READS: usize,
66 const BLOCKS_PER_READ: usize,
67 const READ_SIZE: usize,
68> {
69 pub(super) execution_bridge: ExecutionBridge,
70 pub(super) memory_bridge: MemoryBridge,
71 pub bus: BitwiseOperationLookupBus,
72 address_bits: usize,
74}
75
76impl<F: Field, const NUM_READS: usize, const BLOCKS_PER_READ: usize, const READ_SIZE: usize>
77 BaseAir<F> for Rv32VecHeapBranchAdapterAir<NUM_READS, BLOCKS_PER_READ, READ_SIZE>
78{
79 fn width(&self) -> usize {
80 Rv32VecHeapBranchAdapterCols::<F, NUM_READS, BLOCKS_PER_READ, READ_SIZE>::width()
81 }
82}
83
84impl<
85 AB: InteractionBuilder,
86 const NUM_READS: usize,
87 const BLOCKS_PER_READ: usize,
88 const READ_SIZE: usize,
89 > VmAdapterAir<AB> for Rv32VecHeapBranchAdapterAir<NUM_READS, BLOCKS_PER_READ, READ_SIZE>
90{
91 type Interface = VecHeapBranchAdapterInterface<AB::Expr, NUM_READS, BLOCKS_PER_READ, READ_SIZE>;
92
93 fn eval(
94 &self,
95 builder: &mut AB,
96 local: &[AB::Var],
97 ctx: AdapterAirContext<AB::Expr, Self::Interface>,
98 ) {
99 let cols: &Rv32VecHeapBranchAdapterCols<_, NUM_READS, BLOCKS_PER_READ, READ_SIZE> =
100 local.borrow();
101 let timestamp = cols.from_state.timestamp;
102 let mut timestamp_delta: usize = 0;
103 let mut timestamp_pp = || {
104 timestamp_delta += 1;
105 timestamp + AB::F::from_usize(timestamp_delta - 1)
106 };
107
108 for (ptr, val, aux) in izip!(cols.rs_ptr, cols.rs_val, &cols.rs_read_aux) {
110 self.memory_bridge
111 .read(
112 MemoryAddress::new(AB::F::from_u32(RV32_REGISTER_AS), ptr),
113 val,
114 timestamp_pp(),
115 aux,
116 )
117 .eval(builder, ctx.instruction.is_valid.clone());
118 }
119
120 let need_range_check: Vec<AB::Var> = cols
124 .rs_val
125 .iter()
126 .map(|val| val[RV32_REGISTER_NUM_LIMBS - 1])
127 .collect();
128
129 let limb_shift =
132 AB::F::from_usize(1 << (RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.address_bits));
133
134 for pair in need_range_check.chunks(2) {
135 self.bus
136 .send_range(
137 pair[0] * limb_shift,
138 pair.get(1).map(|x| (*x).into()).unwrap_or(AB::Expr::ZERO) * limb_shift,
139 )
140 .eval(builder, ctx.instruction.is_valid.clone());
141 }
142
143 let rs_val_f: [AB::Expr; NUM_READS] = cols.rs_val.map(abstract_compose);
145
146 let e = AB::F::from_u32(RV32_MEMORY_AS);
147 for (address, reads, reads_aux) in izip!(rs_val_f, ctx.reads, &cols.reads_aux) {
149 for (i, (read, aux)) in zip(reads, reads_aux).enumerate() {
150 self.memory_bridge
151 .read(
152 MemoryAddress::new(
153 e,
154 address.clone() + AB::Expr::from_usize(i * READ_SIZE),
155 ),
156 read,
157 timestamp_pp(),
158 aux,
159 )
160 .eval(builder, ctx.instruction.is_valid.clone());
161 }
162 }
163
164 self.execution_bridge
165 .execute_and_increment_or_set_pc(
166 ctx.instruction.opcode,
167 [
168 cols.rs_ptr
169 .first()
170 .map(|&x| x.into())
171 .unwrap_or(AB::Expr::ZERO),
172 cols.rs_ptr
173 .get(1)
174 .map(|&x| x.into())
175 .unwrap_or(AB::Expr::ZERO),
176 ctx.instruction.immediate,
177 AB::Expr::from_u32(RV32_REGISTER_AS),
178 e.into(),
179 ],
180 cols.from_state,
181 AB::F::from_usize(timestamp_delta),
182 (DEFAULT_PC_STEP, ctx.to_pc),
183 )
184 .eval(builder, ctx.instruction.is_valid.clone());
185 }
186
187 fn get_from_pc(&self, local: &[AB::Var]) -> AB::Var {
188 let cols: &Rv32VecHeapBranchAdapterCols<_, NUM_READS, BLOCKS_PER_READ, READ_SIZE> =
189 local.borrow();
190 cols.from_state.pc
191 }
192}
193
194#[repr(C)]
196#[derive(AlignedBytesBorrow, Debug)]
197pub struct Rv32VecHeapBranchAdapterRecord<
198 const NUM_READS: usize,
199 const BLOCKS_PER_READ: usize,
200 const READ_SIZE: usize,
201> {
202 pub from_pc: u32,
203 pub from_timestamp: u32,
204
205 pub rs_ptrs: [u32; NUM_READS],
206 pub rs_vals: [u32; NUM_READS],
207
208 pub rs_read_aux: [MemoryReadAuxRecord; NUM_READS],
209 pub reads_aux: [[MemoryReadAuxRecord; BLOCKS_PER_READ]; NUM_READS],
210}
211
212#[derive(derive_new::new, Clone, Copy)]
213pub struct Rv32VecHeapBranchAdapterExecutor<
214 const NUM_READS: usize,
215 const BLOCKS_PER_READ: usize,
216 const READ_SIZE: usize,
217> {
218 pointer_max_bits: usize,
219}
220
221#[derive(derive_new::new)]
222pub struct Rv32VecHeapBranchAdapterFiller<
223 const NUM_READS: usize,
224 const BLOCKS_PER_READ: usize,
225 const READ_SIZE: usize,
226> {
227 pointer_max_bits: usize,
228 pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip<RV32_CELL_BITS>,
229}
230
231impl<
232 F: PrimeField32,
233 const NUM_READS: usize,
234 const BLOCKS_PER_READ: usize,
235 const READ_SIZE: usize,
236 > AdapterTraceExecutor<F>
237 for Rv32VecHeapBranchAdapterExecutor<NUM_READS, BLOCKS_PER_READ, READ_SIZE>
238{
239 const WIDTH: usize =
240 Rv32VecHeapBranchAdapterCols::<F, NUM_READS, BLOCKS_PER_READ, READ_SIZE>::width();
241 type ReadData = [[[u8; READ_SIZE]; BLOCKS_PER_READ]; NUM_READS];
242 type WriteData = ();
243 type RecordMut<'a> =
244 &'a mut Rv32VecHeapBranchAdapterRecord<NUM_READS, BLOCKS_PER_READ, READ_SIZE>;
245
246 #[inline(always)]
247 fn start(pc: u32, memory: &TracingMemory, record: &mut Self::RecordMut<'_>) {
248 record.from_pc = pc;
249 record.from_timestamp = memory.timestamp;
250 }
251
252 fn read(
253 &self,
254 memory: &mut TracingMemory,
255 instruction: &Instruction<F>,
256 record: &mut &mut Rv32VecHeapBranchAdapterRecord<NUM_READS, BLOCKS_PER_READ, READ_SIZE>,
257 ) -> Self::ReadData {
258 let &Instruction { a, b, d, e, .. } = instruction;
259
260 debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS);
261 debug_assert_eq!(e.as_canonical_u32(), RV32_MEMORY_AS);
262
263 record.rs_vals = from_fn(|i| {
265 record.rs_ptrs[i] = if i == 0 { a } else { b }.as_canonical_u32();
266 u32::from_le_bytes(tracing_read(
267 memory,
268 RV32_REGISTER_AS,
269 record.rs_ptrs[i],
270 &mut record.rs_read_aux[i].prev_timestamp,
271 ))
272 });
273
274 from_fn(|i| {
276 debug_assert!(
277 (record.rs_vals[i] + (READ_SIZE * BLOCKS_PER_READ - 1) as u32)
278 < (1 << self.pointer_max_bits) as u32
279 );
280 from_fn(|j| {
281 tracing_read(
282 memory,
283 RV32_MEMORY_AS,
284 record.rs_vals[i] + (j * READ_SIZE) as u32,
285 &mut record.reads_aux[i][j].prev_timestamp,
286 )
287 })
288 })
289 }
290
291 fn write(
292 &self,
293 _memory: &mut TracingMemory,
294 _instruction: &Instruction<F>,
295 _data: Self::WriteData,
296 _record: &mut Self::RecordMut<'_>,
297 ) {
298 }
300}
301
302impl<
303 F: PrimeField32,
304 const NUM_READS: usize,
305 const BLOCKS_PER_READ: usize,
306 const READ_SIZE: usize,
307 > AdapterTraceFiller<F>
308 for Rv32VecHeapBranchAdapterFiller<NUM_READS, BLOCKS_PER_READ, READ_SIZE>
309{
310 const WIDTH: usize =
311 Rv32VecHeapBranchAdapterCols::<F, NUM_READS, BLOCKS_PER_READ, READ_SIZE>::width();
312
313 fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory<F>, mut adapter_row: &mut [F]) {
314 let record: &Rv32VecHeapBranchAdapterRecord<NUM_READS, BLOCKS_PER_READ, READ_SIZE> =
318 unsafe { get_record_from_slice(&mut adapter_row, ()) };
319
320 let cols: &mut Rv32VecHeapBranchAdapterCols<F, NUM_READS, BLOCKS_PER_READ, READ_SIZE> =
321 adapter_row.borrow_mut();
322
323 debug_assert!(self.pointer_max_bits <= RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS);
326 let limb_shift_bits = RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.pointer_max_bits;
327 const MSL_SHIFT: usize = RV32_CELL_BITS * (RV32_REGISTER_NUM_LIMBS - 1);
328 self.bitwise_lookup_chip.request_range(
329 (record.rs_vals[0] >> MSL_SHIFT) << limb_shift_bits,
330 if NUM_READS > 1 {
331 (record.rs_vals[1] >> MSL_SHIFT) << limb_shift_bits
332 } else {
333 0
334 },
335 );
336
337 let timestamp_delta = NUM_READS + NUM_READS * BLOCKS_PER_READ;
338 let mut timestamp = record.from_timestamp + timestamp_delta as u32;
339 let mut timestamp_mm = || {
340 timestamp -= 1;
341 timestamp
342 };
343
344 record
346 .reads_aux
347 .iter()
348 .zip(cols.reads_aux.iter_mut())
349 .rev()
350 .for_each(|(reads, cols_reads)| {
351 reads
352 .iter()
353 .zip(cols_reads.iter_mut())
354 .rev()
355 .for_each(|(read, cols_read)| {
356 mem_helper.fill(read.prev_timestamp, timestamp_mm(), cols_read.as_mut());
357 });
358 });
359
360 record
361 .rs_read_aux
362 .iter()
363 .zip(cols.rs_read_aux.iter_mut())
364 .rev()
365 .for_each(|(aux, cols_aux)| {
366 mem_helper.fill(aux.prev_timestamp, timestamp_mm(), cols_aux.as_mut());
367 });
368
369 cols.rs_val
370 .iter_mut()
371 .rev()
372 .zip(record.rs_vals.iter().rev())
373 .for_each(|(cols_val, val)| {
374 *cols_val = val.to_le_bytes().map(F::from_u8);
375 });
376 cols.rs_ptr
377 .iter_mut()
378 .rev()
379 .zip(record.rs_ptrs.iter().rev())
380 .for_each(|(cols_ptr, ptr)| {
381 *cols_ptr = F::from_u32(*ptr);
382 });
383 cols.from_state.timestamp = F::from_u32(record.from_timestamp);
384 cols.from_state.pc = F::from_u32(record.from_pc);
385 }
386}