1use std::{
2 array::from_fn,
3 borrow::{Borrow, BorrowMut},
4 iter::{once, zip},
5};
6
7use itertools::izip;
8use openvm_circuit::{
9 arch::{
10 get_record_from_slice, AdapterAirContext, AdapterTraceExecutor, AdapterTraceFiller,
11 ExecutionBridge, ExecutionState, VecHeapAdapterInterface, VmAdapterAir,
12 },
13 system::memory::{
14 offline_checker::{
15 MemoryBridge, MemoryReadAuxCols, MemoryReadAuxRecord, MemoryWriteAuxCols,
16 MemoryWriteBytesAuxRecord,
17 },
18 online::TracingMemory,
19 MemoryAddress, MemoryAuxColsFactory,
20 },
21};
22use openvm_circuit_primitives::{
23 bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip},
24 AlignedBytesBorrow, ColumnsAir, StructReflection, StructReflectionHelper,
25};
26use openvm_circuit_primitives_derive::AlignedBorrow;
27use openvm_instructions::{
28 instruction::Instruction,
29 program::DEFAULT_PC_STEP,
30 riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS},
31};
32use openvm_rv32im_circuit::adapters::{
33 abstract_compose, tracing_read, tracing_write, RV32_CELL_BITS, RV32_REGISTER_NUM_LIMBS,
34};
35use openvm_stark_backend::{
36 interaction::InteractionBuilder,
37 p3_air::BaseAir,
38 p3_field::{Field, PrimeCharacteristicRing, PrimeField32},
39};
40
41#[repr(C)]
49#[derive(AlignedBorrow, StructReflection, Debug)]
50pub struct Rv32VecHeapAdapterCols<
51 T,
52 const NUM_READS: usize,
53 const BLOCKS_PER_READ: usize,
54 const BLOCKS_PER_WRITE: usize,
55 const READ_SIZE: usize,
56 const WRITE_SIZE: usize,
57> {
58 pub from_state: ExecutionState<T>,
59
60 pub rs_ptr: [T; NUM_READS],
61 pub rd_ptr: T,
62
63 pub rs_val: [[T; RV32_REGISTER_NUM_LIMBS]; NUM_READS],
64 pub rd_val: [T; RV32_REGISTER_NUM_LIMBS],
65
66 pub rs_read_aux: [MemoryReadAuxCols<T>; NUM_READS],
67 pub rd_read_aux: MemoryReadAuxCols<T>,
68
69 pub reads_aux: [[MemoryReadAuxCols<T>; BLOCKS_PER_READ]; NUM_READS],
70 pub writes_aux: [MemoryWriteAuxCols<T, WRITE_SIZE>; BLOCKS_PER_WRITE],
71}
72
73#[allow(dead_code)]
74#[derive(Clone, Copy, Debug, derive_new::new, ColumnsAir)]
75#[columns_via(Rv32VecHeapAdapterCols<u8, NUM_READS, BLOCKS_PER_READ, BLOCKS_PER_WRITE, READ_SIZE, WRITE_SIZE>)]
76pub struct Rv32VecHeapAdapterAir<
77 const NUM_READS: usize,
78 const BLOCKS_PER_READ: usize,
79 const BLOCKS_PER_WRITE: usize,
80 const READ_SIZE: usize,
81 const WRITE_SIZE: usize,
82> {
83 pub(super) execution_bridge: ExecutionBridge,
84 pub(super) memory_bridge: MemoryBridge,
85 pub bus: BitwiseOperationLookupBus,
86 address_bits: usize,
88}
89
90impl<
91 F: Field,
92 const NUM_READS: usize,
93 const BLOCKS_PER_READ: usize,
94 const BLOCKS_PER_WRITE: usize,
95 const READ_SIZE: usize,
96 const WRITE_SIZE: usize,
97 > BaseAir<F>
98 for Rv32VecHeapAdapterAir<NUM_READS, BLOCKS_PER_READ, BLOCKS_PER_WRITE, READ_SIZE, WRITE_SIZE>
99{
100 fn width(&self) -> usize {
101 Rv32VecHeapAdapterCols::<
102 F,
103 NUM_READS,
104 BLOCKS_PER_READ,
105 BLOCKS_PER_WRITE,
106 READ_SIZE,
107 WRITE_SIZE,
108 >::width()
109 }
110}
111
112impl<
113 AB: InteractionBuilder,
114 const NUM_READS: usize,
115 const BLOCKS_PER_READ: usize,
116 const BLOCKS_PER_WRITE: usize,
117 const READ_SIZE: usize,
118 const WRITE_SIZE: usize,
119 > VmAdapterAir<AB>
120 for Rv32VecHeapAdapterAir<NUM_READS, BLOCKS_PER_READ, BLOCKS_PER_WRITE, READ_SIZE, WRITE_SIZE>
121{
122 type Interface = VecHeapAdapterInterface<
123 AB::Expr,
124 NUM_READS,
125 BLOCKS_PER_READ,
126 BLOCKS_PER_WRITE,
127 READ_SIZE,
128 WRITE_SIZE,
129 >;
130
131 fn eval(
132 &self,
133 builder: &mut AB,
134 local: &[AB::Var],
135 ctx: AdapterAirContext<AB::Expr, Self::Interface>,
136 ) {
137 let cols: &Rv32VecHeapAdapterCols<
138 _,
139 NUM_READS,
140 BLOCKS_PER_READ,
141 BLOCKS_PER_WRITE,
142 READ_SIZE,
143 WRITE_SIZE,
144 > = local.borrow();
145 let timestamp = cols.from_state.timestamp;
146 let mut timestamp_delta: usize = 0;
147 let mut timestamp_pp = || {
148 timestamp_delta += 1;
149 timestamp + AB::F::from_usize(timestamp_delta - 1)
150 };
151
152 for (ptr, val, aux) in izip!(cols.rs_ptr, cols.rs_val, &cols.rs_read_aux).chain(once((
154 cols.rd_ptr,
155 cols.rd_val,
156 &cols.rd_read_aux,
157 ))) {
158 self.memory_bridge
159 .read(
160 MemoryAddress::new(AB::F::from_u32(RV32_REGISTER_AS), ptr),
161 val,
162 timestamp_pp(),
163 aux,
164 )
165 .eval(builder, ctx.instruction.is_valid.clone());
166 }
167
168 let need_range_check: Vec<AB::Var> = cols
174 .rs_val
175 .iter()
176 .chain(std::iter::repeat_n(&cols.rd_val, 2))
177 .map(|val| val[RV32_REGISTER_NUM_LIMBS - 1])
178 .collect();
179
180 let limb_shift =
183 AB::F::from_usize(1 << (RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.address_bits));
184
185 for pair in need_range_check.chunks_exact(2) {
189 self.bus
190 .send_range(pair[0] * limb_shift, pair[1] * limb_shift)
191 .eval(builder, ctx.instruction.is_valid.clone());
192 }
193
194 let rd_val_f: AB::Expr = abstract_compose(cols.rd_val);
196 let rs_val_f: [AB::Expr; NUM_READS] = cols.rs_val.map(abstract_compose);
197
198 let e = AB::F::from_u32(RV32_MEMORY_AS);
199 for (address, reads, reads_aux) in izip!(rs_val_f, ctx.reads, &cols.reads_aux,) {
201 for (i, (read, aux)) in zip(reads, reads_aux).enumerate() {
202 self.memory_bridge
203 .read(
204 MemoryAddress::new(
205 e,
206 address.clone() + AB::Expr::from_usize(i * READ_SIZE),
207 ),
208 read,
209 timestamp_pp(),
210 aux,
211 )
212 .eval(builder, ctx.instruction.is_valid.clone());
213 }
214 }
215
216 for (i, (write, aux)) in zip(ctx.writes, &cols.writes_aux).enumerate() {
218 self.memory_bridge
219 .write(
220 MemoryAddress::new(e, rd_val_f.clone() + AB::Expr::from_usize(i * WRITE_SIZE)),
221 write,
222 timestamp_pp(),
223 aux,
224 )
225 .eval(builder, ctx.instruction.is_valid.clone());
226 }
227
228 self.execution_bridge
229 .execute_and_increment_or_set_pc(
230 ctx.instruction.opcode,
231 [
232 cols.rd_ptr.into(),
233 cols.rs_ptr
234 .first()
235 .map(|&x| x.into())
236 .unwrap_or(AB::Expr::ZERO),
237 cols.rs_ptr
238 .get(1)
239 .map(|&x| x.into())
240 .unwrap_or(AB::Expr::ZERO),
241 AB::Expr::from_u32(RV32_REGISTER_AS),
242 e.into(),
243 ],
244 cols.from_state,
245 AB::F::from_usize(timestamp_delta),
246 (DEFAULT_PC_STEP, ctx.to_pc),
247 )
248 .eval(builder, ctx.instruction.is_valid.clone());
249 }
250
251 fn get_from_pc(&self, local: &[AB::Var]) -> AB::Var {
252 let cols: &Rv32VecHeapAdapterCols<
253 _,
254 NUM_READS,
255 BLOCKS_PER_READ,
256 BLOCKS_PER_WRITE,
257 READ_SIZE,
258 WRITE_SIZE,
259 > = local.borrow();
260 cols.from_state.pc
261 }
262}
263
264#[repr(C)]
266#[derive(AlignedBytesBorrow, Debug)]
267pub struct Rv32VecHeapAdapterRecord<
268 const NUM_READS: usize,
269 const BLOCKS_PER_READ: usize,
270 const BLOCKS_PER_WRITE: usize,
271 const READ_SIZE: usize,
272 const WRITE_SIZE: usize,
273> {
274 pub from_pc: u32,
275 pub from_timestamp: u32,
276
277 pub rs_ptrs: [u32; NUM_READS],
278 pub rd_ptr: u32,
279
280 pub rs_vals: [u32; NUM_READS],
281 pub rd_val: u32,
282
283 pub rs_read_aux: [MemoryReadAuxRecord; NUM_READS],
284 pub rd_read_aux: MemoryReadAuxRecord,
285
286 pub reads_aux: [[MemoryReadAuxRecord; BLOCKS_PER_READ]; NUM_READS],
287 pub writes_aux: [MemoryWriteBytesAuxRecord<WRITE_SIZE>; BLOCKS_PER_WRITE],
288}
289
290#[derive(derive_new::new, Clone, Copy)]
291pub struct Rv32VecHeapAdapterExecutor<
292 const NUM_READS: usize,
293 const BLOCKS_PER_READ: usize,
294 const BLOCKS_PER_WRITE: usize,
295 const READ_SIZE: usize,
296 const WRITE_SIZE: usize,
297> {
298 pointer_max_bits: usize,
299}
300
301#[derive(derive_new::new)]
302pub struct Rv32VecHeapAdapterFiller<
303 const NUM_READS: usize,
304 const BLOCKS_PER_READ: usize,
305 const BLOCKS_PER_WRITE: usize,
306 const READ_SIZE: usize,
307 const WRITE_SIZE: usize,
308> {
309 pointer_max_bits: usize,
310 pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip<RV32_CELL_BITS>,
311}
312
313impl<
314 F: PrimeField32,
315 const NUM_READS: usize,
316 const BLOCKS_PER_READ: usize,
317 const BLOCKS_PER_WRITE: usize,
318 const READ_SIZE: usize,
319 const WRITE_SIZE: usize,
320 > AdapterTraceExecutor<F>
321 for Rv32VecHeapAdapterExecutor<
322 NUM_READS,
323 BLOCKS_PER_READ,
324 BLOCKS_PER_WRITE,
325 READ_SIZE,
326 WRITE_SIZE,
327 >
328{
329 const WIDTH: usize = Rv32VecHeapAdapterCols::<
330 F,
331 NUM_READS,
332 BLOCKS_PER_READ,
333 BLOCKS_PER_WRITE,
334 READ_SIZE,
335 WRITE_SIZE,
336 >::width();
337 type ReadData = [[[u8; READ_SIZE]; BLOCKS_PER_READ]; NUM_READS];
338 type WriteData = [[u8; WRITE_SIZE]; BLOCKS_PER_WRITE];
339 type RecordMut<'a> = &'a mut Rv32VecHeapAdapterRecord<
340 NUM_READS,
341 BLOCKS_PER_READ,
342 BLOCKS_PER_WRITE,
343 READ_SIZE,
344 WRITE_SIZE,
345 >;
346
347 #[inline(always)]
348 fn start(pc: u32, memory: &TracingMemory, record: &mut Self::RecordMut<'_>) {
349 record.from_pc = pc;
350 record.from_timestamp = memory.timestamp;
351 }
352
353 fn read(
354 &self,
355 memory: &mut TracingMemory,
356 instruction: &Instruction<F>,
357 record: &mut &mut Rv32VecHeapAdapterRecord<
358 NUM_READS,
359 BLOCKS_PER_READ,
360 BLOCKS_PER_WRITE,
361 READ_SIZE,
362 WRITE_SIZE,
363 >,
364 ) -> Self::ReadData {
365 let &Instruction { a, b, c, d, e, .. } = instruction;
366
367 debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS);
368 debug_assert_eq!(e.as_canonical_u32(), RV32_MEMORY_AS);
369
370 record.rs_vals = from_fn(|i| {
372 record.rs_ptrs[i] = if i == 0 { b } else { c }.as_canonical_u32();
373 u32::from_le_bytes(tracing_read(
374 memory,
375 RV32_REGISTER_AS,
376 record.rs_ptrs[i],
377 &mut record.rs_read_aux[i].prev_timestamp,
378 ))
379 });
380
381 record.rd_ptr = a.as_canonical_u32();
382 record.rd_val = u32::from_le_bytes(tracing_read(
383 memory,
384 RV32_REGISTER_AS,
385 a.as_canonical_u32(),
386 &mut record.rd_read_aux.prev_timestamp,
387 ));
388
389 from_fn(|i| {
391 debug_assert!(
392 (record.rs_vals[i] + (READ_SIZE * BLOCKS_PER_READ - 1) as u32)
393 < (1 << self.pointer_max_bits) as u32
394 );
395 from_fn(|j| {
396 tracing_read(
397 memory,
398 RV32_MEMORY_AS,
399 record.rs_vals[i] + (j * READ_SIZE) as u32,
400 &mut record.reads_aux[i][j].prev_timestamp,
401 )
402 })
403 })
404 }
405
406 fn write(
407 &self,
408 memory: &mut TracingMemory,
409 instruction: &Instruction<F>,
410 data: Self::WriteData,
411 record: &mut &mut Rv32VecHeapAdapterRecord<
412 NUM_READS,
413 BLOCKS_PER_READ,
414 BLOCKS_PER_WRITE,
415 READ_SIZE,
416 WRITE_SIZE,
417 >,
418 ) {
419 debug_assert_eq!(instruction.e.as_canonical_u32(), RV32_MEMORY_AS);
420
421 debug_assert!(
422 record.rd_val as usize + WRITE_SIZE * BLOCKS_PER_WRITE - 1
423 < (1 << self.pointer_max_bits)
424 );
425
426 #[allow(clippy::needless_range_loop)]
427 for i in 0..BLOCKS_PER_WRITE {
428 tracing_write(
429 memory,
430 RV32_MEMORY_AS,
431 record.rd_val + (i * WRITE_SIZE) as u32,
432 data[i],
433 &mut record.writes_aux[i].prev_timestamp,
434 &mut record.writes_aux[i].prev_data,
435 );
436 }
437 }
438}
439
440impl<
441 F: PrimeField32,
442 const NUM_READS: usize,
443 const BLOCKS_PER_READ: usize,
444 const BLOCKS_PER_WRITE: usize,
445 const READ_SIZE: usize,
446 const WRITE_SIZE: usize,
447 > AdapterTraceFiller<F>
448 for Rv32VecHeapAdapterFiller<
449 NUM_READS,
450 BLOCKS_PER_READ,
451 BLOCKS_PER_WRITE,
452 READ_SIZE,
453 WRITE_SIZE,
454 >
455{
456 const WIDTH: usize = Rv32VecHeapAdapterCols::<
457 F,
458 NUM_READS,
459 BLOCKS_PER_READ,
460 BLOCKS_PER_WRITE,
461 READ_SIZE,
462 WRITE_SIZE,
463 >::width();
464
465 fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory<F>, mut adapter_row: &mut [F]) {
466 let record: &Rv32VecHeapAdapterRecord<
470 NUM_READS,
471 BLOCKS_PER_READ,
472 BLOCKS_PER_WRITE,
473 READ_SIZE,
474 WRITE_SIZE,
475 > = unsafe { get_record_from_slice(&mut adapter_row, ()) };
476
477 let cols: &mut Rv32VecHeapAdapterCols<
478 F,
479 NUM_READS,
480 BLOCKS_PER_READ,
481 BLOCKS_PER_WRITE,
482 READ_SIZE,
483 WRITE_SIZE,
484 > = adapter_row.borrow_mut();
485
486 debug_assert!(self.pointer_max_bits <= RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS);
489 let limb_shift_bits = RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.pointer_max_bits;
490 const MSL_SHIFT: usize = RV32_CELL_BITS * (RV32_REGISTER_NUM_LIMBS - 1);
491 if NUM_READS > 1 {
492 self.bitwise_lookup_chip.request_range(
493 (record.rs_vals[0] >> MSL_SHIFT) << limb_shift_bits,
494 (record.rs_vals[1] >> MSL_SHIFT) << limb_shift_bits,
495 );
496 self.bitwise_lookup_chip.request_range(
497 (record.rd_val >> MSL_SHIFT) << limb_shift_bits,
498 (record.rd_val >> MSL_SHIFT) << limb_shift_bits,
499 );
500 } else {
501 self.bitwise_lookup_chip.request_range(
502 (record.rs_vals[0] >> MSL_SHIFT) << limb_shift_bits,
503 (record.rd_val >> MSL_SHIFT) << limb_shift_bits,
504 );
505 }
506
507 let timestamp_delta = NUM_READS + 1 + NUM_READS * BLOCKS_PER_READ + BLOCKS_PER_WRITE;
508 let mut timestamp = record.from_timestamp + timestamp_delta as u32;
509 let mut timestamp_mm = || {
510 timestamp -= 1;
511 timestamp
512 };
513
514 record
516 .writes_aux
517 .iter()
518 .rev()
519 .zip(cols.writes_aux.iter_mut().rev())
520 .for_each(|(write, cols_write)| {
521 cols_write.set_prev_data(write.prev_data.map(F::from_u8));
522 mem_helper.fill(write.prev_timestamp, timestamp_mm(), cols_write.as_mut());
523 });
524
525 record
526 .reads_aux
527 .iter()
528 .zip(cols.reads_aux.iter_mut())
529 .rev()
530 .for_each(|(reads, cols_reads)| {
531 reads
532 .iter()
533 .zip(cols_reads.iter_mut())
534 .rev()
535 .for_each(|(read, cols_read)| {
536 mem_helper.fill(read.prev_timestamp, timestamp_mm(), cols_read.as_mut());
537 });
538 });
539
540 mem_helper.fill(
541 record.rd_read_aux.prev_timestamp,
542 timestamp_mm(),
543 cols.rd_read_aux.as_mut(),
544 );
545
546 record
547 .rs_read_aux
548 .iter()
549 .zip(cols.rs_read_aux.iter_mut())
550 .rev()
551 .for_each(|(aux, cols_aux)| {
552 mem_helper.fill(aux.prev_timestamp, timestamp_mm(), cols_aux.as_mut());
553 });
554
555 cols.rd_val = record.rd_val.to_le_bytes().map(F::from_u8);
556 cols.rs_val
557 .iter_mut()
558 .rev()
559 .zip(record.rs_vals.iter().rev())
560 .for_each(|(cols_val, val)| {
561 *cols_val = val.to_le_bytes().map(F::from_u8);
562 });
563 cols.rd_ptr = F::from_u32(record.rd_ptr);
564 cols.rs_ptr
565 .iter_mut()
566 .rev()
567 .zip(record.rs_ptrs.iter().rev())
568 .for_each(|(cols_ptr, ptr)| {
569 *cols_ptr = F::from_u32(*ptr);
570 });
571 cols.from_state.timestamp = F::from_u32(record.from_timestamp);
572 cols.from_state.pc = F::from_u32(record.from_pc);
573 }
574}