1use std::{array::from_fn, borrow::BorrowMut, sync::Arc};
2
3use itertools::Itertools;
4use openvm_circuit::{
5 arch::{
6 get_record_from_slice, AdapterTraceExecutor, AdapterTraceFiller, EmptyAdapterCoreLayout,
7 ExecutionError, PreflightExecutor, RecordArena, TraceFiller, VmField, VmStateMut,
8 DEFAULT_BLOCK_SIZE,
9 },
10 system::memory::{
11 offline_checker::{MemoryReadAuxRecord, MemoryWriteAuxRecord, MemoryWriteBytesAuxRecord},
12 online::TracingMemory,
13 MemoryAuxColsFactory,
14 },
15};
16use openvm_circuit_primitives::{
17 bitwise_op_lookup::SharedBitwiseOperationLookupChip, AlignedBytesBorrow,
18};
19use openvm_deferral_transpiler::DeferralOpcode;
20use openvm_instructions::{
21 instruction::Instruction,
22 program::DEFAULT_PC_STEP,
23 riscv::{RV32_CELL_BITS, RV32_MEMORY_AS, RV32_REGISTER_AS, RV32_REGISTER_NUM_LIMBS},
24};
25use openvm_rv32im_circuit::adapters::{tracing_read, tracing_write};
26use openvm_stark_backend::p3_field::PrimeField32;
27use openvm_stark_sdk::config::baby_bear_poseidon2::DIGEST_SIZE;
28
29use crate::{
30 adapters::{tracing_read_deferral, tracing_write_deferral},
31 call::{DeferralCallAdapterCols, DeferralCallCoreCols, DeferralCallReads, DeferralCallWrites},
32 canonicity::CanonicityTraceGen,
33 count::DeferralCircuitCountChip,
34 poseidon2::{deferral_poseidon2_chip, DeferralPoseidon2Chip},
35 utils::{
36 byte_commit_to_f, combine_output, join_memory_ops, memory_op_chunk, COMMIT_MEMORY_OPS,
37 DIGEST_MEMORY_OPS, F_NUM_BYTES, OUTPUT_TOTAL_MEMORY_OPS,
38 },
39 DeferralFn,
40};
41
42#[repr(C)]
45#[derive(AlignedBytesBorrow, Debug)]
46pub struct DeferralCallCoreRecord<F> {
47 pub deferral_idx: F,
48 pub read_data: DeferralCallReads<u8, F>,
49 pub write_data: DeferralCallWrites<u8, F>,
50}
51
52#[derive(Clone, derive_new::new)]
53pub struct DeferralCallCoreExecutor<A> {
54 pub(in crate::call) adapter: A,
55 pub(in crate::call) deferral_fns: Vec<Arc<DeferralFn>>,
56}
57
58#[derive(Clone, derive_new::new)]
59pub struct DeferralCallCoreFiller<A, F: VmField> {
60 adapter: A,
61 count_chip: Arc<DeferralCircuitCountChip>,
62 poseidon2_chip: Arc<DeferralPoseidon2Chip<F>>,
63 bitwise_lookup_chip: SharedBitwiseOperationLookupChip<RV32_CELL_BITS>,
64 address_bits: usize,
65}
66
67impl<F, A, RA> PreflightExecutor<F, RA> for DeferralCallCoreExecutor<A>
68where
69 F: VmField,
70 A: 'static
71 + AdapterTraceExecutor<
72 F,
73 ReadData = DeferralCallReads<u8, F>,
74 WriteData = DeferralCallWrites<u8, F>,
75 >,
76 for<'buf> RA: RecordArena<
77 'buf,
78 EmptyAdapterCoreLayout<F, A>,
79 (A::RecordMut<'buf>, &'buf mut DeferralCallCoreRecord<F>),
80 >,
81{
82 fn get_opcode_name(&self, _opcode: usize) -> String {
83 format!("{:?}", DeferralOpcode::CALL)
84 }
85
86 fn execute(
87 &self,
88 state: VmStateMut<F, TracingMemory, RA>,
89 instruction: &Instruction<F>,
90 ) -> Result<(), ExecutionError> {
91 let (mut adapter_record, core_record) = state.ctx.alloc(EmptyAdapterCoreLayout::new());
92 A::start(*state.pc, state.memory, &mut adapter_record);
93 core_record.deferral_idx = instruction.c;
94
95 let read_data = self
96 .adapter
97 .read(state.memory, instruction, &mut adapter_record);
98 core_record.read_data = read_data;
99
100 let input_commit = byte_commit_to_f(&read_data.input_commit.map(F::from_u8));
101 let def_idx = instruction.c.as_canonical_u32();
102 let poseidon2_chip = deferral_poseidon2_chip();
103
104 let (output_commit, output_len) = self.deferral_fns[def_idx as usize].execute(
105 &read_data.input_commit.to_vec(),
106 &mut state.streams.deferrals[def_idx as usize],
107 def_idx,
108 &poseidon2_chip,
109 );
110
111 let output_f_commit =
112 byte_commit_to_f(&output_commit.iter().map(|v| F::from_u8(*v)).collect_vec());
113 let new_input_acc = poseidon2_chip.perm(&read_data.old_input_acc, &input_commit, true);
114 let new_output_acc = poseidon2_chip.perm(&read_data.old_output_acc, &output_f_commit, true);
115
116 let output_len_u32 =
117 u32::try_from(output_len).expect("deferral output length should fit in a u32");
118 let write_data = DeferralCallWrites {
119 output_commit: output_commit.try_into().unwrap(),
120 output_len: output_len_u32.to_le_bytes(),
121 new_input_acc,
122 new_output_acc,
123 };
124 core_record.write_data = write_data;
125 self.adapter
126 .write(state.memory, instruction, write_data, &mut adapter_record);
127
128 *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP);
129 Ok(())
130 }
131}
132
133impl<F, A> TraceFiller<F> for DeferralCallCoreFiller<A, F>
134where
135 F: VmField,
136 A: 'static + AdapterTraceFiller<F>,
137{
138 fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory<F>, row_slice: &mut [F]) {
139 let (adapter_row, mut core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) };
142 self.adapter.fill_trace_row(mem_helper, adapter_row);
143
144 let record: &DeferralCallCoreRecord<F> =
147 unsafe { get_record_from_slice(&mut core_row, ()) };
148 let cols: &mut DeferralCallCoreCols<F> = core_row.borrow_mut();
149
150 let input_commit_f = record.read_data.input_commit.map(F::from_u8);
151 let output_commit_f = record.write_data.output_commit.map(F::from_u8);
152 let output_len_f = record.write_data.output_len.map(F::from_u8);
153
154 self.count_chip
155 .add_count(record.deferral_idx.as_canonical_u32());
156
157 let input_f_commit: [F; _] = byte_commit_to_f(&input_commit_f);
158 let output_f_commit: [F; _] = byte_commit_to_f(&output_commit_f);
159 self.poseidon2_chip
160 .perm_and_record(&record.read_data.old_input_acc, &input_f_commit, true);
161 self.poseidon2_chip.perm_and_record(
162 &record.read_data.old_output_acc,
163 &output_f_commit,
164 true,
165 );
166
167 for bytes in record.write_data.output_commit.chunks_exact(2) {
168 self.bitwise_lookup_chip
169 .request_range(bytes[0] as u32, bytes[1] as u32);
170 }
171 for bytes in record.write_data.output_len.chunks_exact(2) {
172 self.bitwise_lookup_chip
173 .request_range(bytes[0] as u32, bytes[1] as u32);
174 }
175
176 debug_assert!(RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS >= self.address_bits);
178 let limb_shift_bits = RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.address_bits;
179 self.bitwise_lookup_chip.request_range(
180 (record.write_data.output_len[RV32_REGISTER_NUM_LIMBS - 1] as u32) << limb_shift_bits,
181 0,
182 );
183
184 let input_commit_rcs = input_commit_f
186 .chunks_exact(F_NUM_BYTES)
187 .zip(cols.input_commit_lt_aux.iter_mut())
188 .map(|(bytes, aux)| {
189 let x_le = from_fn(|i| bytes[i]);
190 CanonicityTraceGen::generate_subrow(&x_le, aux)
191 })
192 .collect_vec();
193 for rc_pair in input_commit_rcs.chunks_exact(2) {
194 self.bitwise_lookup_chip
195 .request_range(rc_pair[0], rc_pair[1]);
196 }
197
198 let output_commit_rcs = output_commit_f
199 .chunks_exact(F_NUM_BYTES)
200 .zip(cols.output_commit_lt_aux.iter_mut())
201 .map(|(bytes, aux)| {
202 let x_le = from_fn(|i| bytes[i]);
203 CanonicityTraceGen::generate_subrow(&x_le, aux)
204 })
205 .collect_vec();
206 for rc_pair in output_commit_rcs.chunks_exact(2) {
207 self.bitwise_lookup_chip
208 .request_range(rc_pair[0], rc_pair[1]);
209 }
210
211 cols.writes.new_output_acc = record.write_data.new_output_acc;
212 cols.writes.new_input_acc = record.write_data.new_input_acc;
213 cols.writes.output_len = output_len_f;
214 cols.writes.output_commit = output_commit_f;
215 cols.reads.old_output_acc = record.read_data.old_output_acc;
216 cols.reads.old_input_acc = record.read_data.old_input_acc;
217 cols.reads.input_commit = input_commit_f;
218 cols.deferral_idx = record.deferral_idx;
219 cols.is_valid = F::ONE;
220 }
221}
222
223#[repr(C)]
226#[derive(AlignedBytesBorrow, Debug)]
227pub struct DeferralCallAdapterRecord<F> {
228 pub from_pc: u32,
229 pub from_timestamp: u32,
230 pub rd_ptr: F,
231 pub rs_ptr: F,
232
233 pub rd_val: [u8; RV32_REGISTER_NUM_LIMBS],
235 pub rs_val: [u8; RV32_REGISTER_NUM_LIMBS],
236 pub rd_aux: MemoryReadAuxRecord,
237 pub rs_aux: MemoryReadAuxRecord,
238
239 pub input_commit_aux: [MemoryReadAuxRecord; COMMIT_MEMORY_OPS],
241 pub old_input_acc_aux: [MemoryReadAuxRecord; DIGEST_MEMORY_OPS],
242 pub old_output_acc_aux: [MemoryReadAuxRecord; DIGEST_MEMORY_OPS],
243
244 pub output_commit_and_len_aux:
246 [MemoryWriteBytesAuxRecord<DEFAULT_BLOCK_SIZE>; OUTPUT_TOTAL_MEMORY_OPS],
247 pub new_input_acc_aux: [MemoryWriteAuxRecord<F, DEFAULT_BLOCK_SIZE>; DIGEST_MEMORY_OPS],
248 pub new_output_acc_aux: [MemoryWriteAuxRecord<F, DEFAULT_BLOCK_SIZE>; DIGEST_MEMORY_OPS],
249}
250
251#[derive(Clone, Copy)]
252pub struct DeferralCallAdapterExecutor;
253
254#[derive(Clone, derive_new::new)]
255pub struct DeferralCallAdapterFiller {
256 bitwise_lookup_chip: SharedBitwiseOperationLookupChip<RV32_CELL_BITS>,
257 address_bits: usize,
258}
259
260impl<F: PrimeField32> AdapterTraceExecutor<F> for DeferralCallAdapterExecutor {
261 const WIDTH: usize = DeferralCallAdapterCols::<u8>::width();
262 type ReadData = DeferralCallReads<u8, F>;
263 type WriteData = DeferralCallWrites<u8, F>;
264 type RecordMut<'a> = &'a mut DeferralCallAdapterRecord<F>;
265
266 fn start(pc: u32, memory: &TracingMemory, record: &mut Self::RecordMut<'_>) {
267 record.from_pc = pc;
268 record.from_timestamp = memory.timestamp;
269 }
270
271 fn read(
272 &self,
273 memory: &mut TracingMemory,
274 instruction: &Instruction<F>,
275 record: &mut Self::RecordMut<'_>,
276 ) -> Self::ReadData {
277 let &Instruction { a, b, c, d, e, .. } = instruction;
278 debug_assert_eq!(d.as_canonical_u32(), RV32_REGISTER_AS);
279 debug_assert_eq!(e.as_canonical_u32(), RV32_MEMORY_AS);
280 record.rd_ptr = a;
281 record.rs_ptr = b;
282
283 record.rd_val = tracing_read(
284 memory,
285 d.as_canonical_u32(),
286 a.as_canonical_u32(),
287 &mut record.rd_aux.prev_timestamp,
288 );
289 record.rs_val = tracing_read(
290 memory,
291 d.as_canonical_u32(),
292 b.as_canonical_u32(),
293 &mut record.rs_aux.prev_timestamp,
294 );
295
296 let input_commit_chunks: [[u8; DEFAULT_BLOCK_SIZE]; COMMIT_MEMORY_OPS] = from_fn(|i| {
297 tracing_read(
298 memory,
299 e.as_canonical_u32(),
300 u32::from_le_bytes(record.rs_val) + (i * DEFAULT_BLOCK_SIZE) as u32,
301 &mut record.input_commit_aux[i].prev_timestamp,
302 )
303 });
304 let input_commit = join_memory_ops(input_commit_chunks);
305
306 let deferral_idx = c.as_canonical_u32();
307
308 const DIGEST_SIZE_U32: u32 = DIGEST_SIZE as u32;
309 let input_acc_ptr = 2 * deferral_idx * DIGEST_SIZE_U32;
310 let output_acc_ptr = input_acc_ptr + DIGEST_SIZE_U32;
311
312 let old_input_acc_chunks: [[F; DEFAULT_BLOCK_SIZE]; DIGEST_MEMORY_OPS] = from_fn(|i| {
313 tracing_read_deferral(
314 memory,
315 input_acc_ptr + (i * DEFAULT_BLOCK_SIZE) as u32,
316 &mut record.old_input_acc_aux[i].prev_timestamp,
317 )
318 });
319 let old_output_acc_chunks: [[F; DEFAULT_BLOCK_SIZE]; DIGEST_MEMORY_OPS] = from_fn(|i| {
320 tracing_read_deferral(
321 memory,
322 output_acc_ptr + (i * DEFAULT_BLOCK_SIZE) as u32,
323 &mut record.old_output_acc_aux[i].prev_timestamp,
324 )
325 });
326 let old_input_acc = join_memory_ops(old_input_acc_chunks);
327 let old_output_acc = join_memory_ops(old_output_acc_chunks);
328
329 DeferralCallReads {
330 input_commit,
331 old_input_acc,
332 old_output_acc,
333 }
334 }
335
336 fn write(
337 &self,
338 memory: &mut TracingMemory,
339 instruction: &Instruction<F>,
340 data: Self::WriteData,
341 record: &mut Self::RecordMut<'_>,
342 ) {
343 let &Instruction { c, e, .. } = instruction;
344 debug_assert_eq!(e.as_canonical_u32(), RV32_MEMORY_AS);
345
346 let output_len_full = from_fn(|i| {
347 if i < F_NUM_BYTES {
348 data.output_len[i]
349 } else {
350 0u8
351 }
352 });
353
354 let output_commit_and_len = combine_output(data.output_commit, output_len_full);
355 for chunk_idx in 0..OUTPUT_TOTAL_MEMORY_OPS {
356 tracing_write(
357 memory,
358 e.as_canonical_u32(),
359 u32::from_le_bytes(record.rd_val) + (chunk_idx * DEFAULT_BLOCK_SIZE) as u32,
360 memory_op_chunk(&output_commit_and_len, chunk_idx),
361 &mut record.output_commit_and_len_aux[chunk_idx].prev_timestamp,
362 &mut record.output_commit_and_len_aux[chunk_idx].prev_data,
363 );
364 }
365
366 let deferral_idx = c.as_canonical_u32();
367
368 const DIGEST_SIZE_U32: u32 = DIGEST_SIZE as u32;
369 let input_acc_ptr = 2 * deferral_idx * DIGEST_SIZE_U32;
370 let output_acc_ptr = input_acc_ptr + DIGEST_SIZE_U32;
371
372 for chunk_idx in 0..DIGEST_MEMORY_OPS {
373 tracing_write_deferral(
374 memory,
375 input_acc_ptr + (chunk_idx * DEFAULT_BLOCK_SIZE) as u32,
376 memory_op_chunk(&data.new_input_acc, chunk_idx),
377 &mut record.new_input_acc_aux[chunk_idx].prev_timestamp,
378 &mut record.new_input_acc_aux[chunk_idx].prev_data,
379 );
380 }
381
382 for chunk_idx in 0..DIGEST_MEMORY_OPS {
383 tracing_write_deferral(
384 memory,
385 output_acc_ptr + (chunk_idx * DEFAULT_BLOCK_SIZE) as u32,
386 memory_op_chunk(&data.new_output_acc, chunk_idx),
387 &mut record.new_output_acc_aux[chunk_idx].prev_timestamp,
388 &mut record.new_output_acc_aux[chunk_idx].prev_data,
389 );
390 }
391 }
392}
393
394impl<F: PrimeField32> AdapterTraceFiller<F> for DeferralCallAdapterFiller {
395 const WIDTH: usize = DeferralCallAdapterCols::<u8>::width();
396
397 #[inline(always)]
398 fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory<F>, mut adapter_row: &mut [F]) {
399 let record: &DeferralCallAdapterRecord<F> =
402 unsafe { get_record_from_slice(&mut adapter_row, ()) };
403 let adapter_row: &mut DeferralCallAdapterCols<F> = adapter_row.borrow_mut();
404
405 debug_assert!(RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS >= self.address_bits);
408 let limb_shift_bits = RV32_CELL_BITS * RV32_REGISTER_NUM_LIMBS - self.address_bits;
409
410 self.bitwise_lookup_chip.request_range(
411 (record.rd_val[RV32_REGISTER_NUM_LIMBS - 1] as u32) << limb_shift_bits,
412 (record.rs_val[RV32_REGISTER_NUM_LIMBS - 1] as u32) << limb_shift_bits,
413 );
414
415 let timestamp_delta =
418 2 + COMMIT_MEMORY_OPS + OUTPUT_TOTAL_MEMORY_OPS + 4 * DIGEST_MEMORY_OPS;
419 let mut timestamp = record.from_timestamp + timestamp_delta as u32;
420 let mut timestamp_mm = || {
421 timestamp -= 1;
422 timestamp
423 };
424
425 for chunk_idx in (0..DIGEST_MEMORY_OPS).rev() {
427 adapter_row.new_output_acc_aux[chunk_idx]
428 .set_prev_data(record.new_output_acc_aux[chunk_idx].prev_data);
429 mem_helper.fill(
430 record.new_output_acc_aux[chunk_idx].prev_timestamp,
431 timestamp_mm(),
432 adapter_row.new_output_acc_aux[chunk_idx].as_mut(),
433 );
434 }
435 for chunk_idx in (0..DIGEST_MEMORY_OPS).rev() {
436 adapter_row.new_input_acc_aux[chunk_idx]
437 .set_prev_data(record.new_input_acc_aux[chunk_idx].prev_data);
438 mem_helper.fill(
439 record.new_input_acc_aux[chunk_idx].prev_timestamp,
440 timestamp_mm(),
441 adapter_row.new_input_acc_aux[chunk_idx].as_mut(),
442 );
443 }
444 for chunk_idx in (0..OUTPUT_TOTAL_MEMORY_OPS).rev() {
445 adapter_row.output_commit_and_len_aux[chunk_idx].set_prev_data(
446 record.output_commit_and_len_aux[chunk_idx]
447 .prev_data
448 .map(F::from_u8),
449 );
450 mem_helper.fill(
451 record.output_commit_and_len_aux[chunk_idx].prev_timestamp,
452 timestamp_mm(),
453 adapter_row.output_commit_and_len_aux[chunk_idx].as_mut(),
454 );
455 }
456
457 for chunk_idx in (0..DIGEST_MEMORY_OPS).rev() {
458 mem_helper.fill(
459 record.old_output_acc_aux[chunk_idx].prev_timestamp,
460 timestamp_mm(),
461 adapter_row.old_output_acc_aux[chunk_idx].as_mut(),
462 );
463 }
464 for chunk_idx in (0..DIGEST_MEMORY_OPS).rev() {
465 mem_helper.fill(
466 record.old_input_acc_aux[chunk_idx].prev_timestamp,
467 timestamp_mm(),
468 adapter_row.old_input_acc_aux[chunk_idx].as_mut(),
469 );
470 }
471 for chunk_idx in (0..COMMIT_MEMORY_OPS).rev() {
472 mem_helper.fill(
473 record.input_commit_aux[chunk_idx].prev_timestamp,
474 timestamp_mm(),
475 adapter_row.input_commit_aux[chunk_idx].as_mut(),
476 );
477 }
478
479 mem_helper.fill(
480 record.rs_aux.prev_timestamp,
481 timestamp_mm(),
482 adapter_row.rs_aux.as_mut(),
483 );
484 mem_helper.fill(
485 record.rd_aux.prev_timestamp,
486 timestamp_mm(),
487 adapter_row.rd_aux.as_mut(),
488 );
489 adapter_row.rs_val = record.rs_val.map(F::from_u8);
490 adapter_row.rd_val = record.rd_val.map(F::from_u8);
491
492 adapter_row.rs_ptr = record.rs_ptr;
493 adapter_row.rd_ptr = record.rd_ptr;
494 adapter_row.from_state.timestamp = F::from_u32(record.from_timestamp);
495 adapter_row.from_state.pc = F::from_u32(record.from_pc);
496 }
497}