openvm_deferral_circuit/output/
cuda.rs1use std::{array::from_fn, mem::size_of, sync::Arc};
2
3use derive_new::new;
4use openvm_circuit::{
5 arch::{DenseRecordArena, SizedRecord},
6 utils::next_power_of_two_or_zero,
7};
8use openvm_circuit_primitives::{
9 bitwise_op_lookup::BitwiseOperationLookupChipGPU, var_range::VariableRangeCheckerChipGPU, Chip,
10};
11use openvm_cuda_backend::{base::DeviceMatrix, prelude::F, GpuBackend};
12use openvm_cuda_common::{copy::MemCopyH2D, d_buffer::DeviceBuffer};
13use openvm_instructions::riscv::RV32_CELL_BITS;
14use openvm_stark_backend::{p3_field::PrimeCharacteristicRing, prover::AirProvingContext};
15use openvm_stark_sdk::config::baby_bear_poseidon2::DIGEST_SIZE;
16
17use super::{
18 DeferralOutputCols, DeferralOutputLayout, DeferralOutputMetadata, DeferralOutputRecordHeader,
19 DeferralOutputRecordMut,
20};
21use crate::{
22 cuda_abi::output::{self, DeferralOutputPerCall, DeferralOutputPerRow},
23 poseidon2::{deferral_poseidon2_chip, DeferralPoseidon2SharedBuffer},
24 utils::f_commit_to_bytes,
25};
26
27#[derive(new)]
28pub struct DeferralOutputChipGpu {
29 pub range_checker: Arc<VariableRangeCheckerChipGPU>,
30 pub bitwise_lookup: Arc<BitwiseOperationLookupChipGPU<RV32_CELL_BITS>>,
31 pub address_bits: usize,
32 pub timestamp_max_bits: usize,
33 pub count: Arc<DeviceBuffer<u32>>,
34 pub num_deferral_circuits: usize,
35 pub poseidon2: DeferralPoseidon2SharedBuffer,
36}
37
38impl Chip<DenseRecordArena, GpuBackend> for DeferralOutputChipGpu {
39 fn generate_proving_ctx(&self, mut arena: DenseRecordArena) -> AirProvingContext<GpuBackend> {
40 let records = arena.allocated_mut();
41 if records.is_empty() {
42 return AirProvingContext::simple_no_pis(DeviceMatrix::dummy());
43 }
44
45 let poseidon2_chip = deferral_poseidon2_chip::<F>();
46 let mut per_call = Vec::<DeferralOutputPerCall>::new();
47 let mut per_row = Vec::<DeferralOutputPerRow>::new();
48
49 let mut offset = 0usize;
50 while offset < records.len() {
51 let header_offset = offset;
52 let header: &DeferralOutputRecordHeader = unsafe {
53 &*(records.as_ptr().add(header_offset) as *const DeferralOutputRecordHeader)
54 };
55
56 let num_rows = header.num_rows as usize;
57 let output_len = (num_rows - 1) * DIGEST_SIZE;
58 let write_bytes = unsafe {
59 std::slice::from_raw_parts(
60 records
61 .as_ptr()
62 .add(header_offset + size_of::<DeferralOutputRecordHeader>()),
63 output_len,
64 )
65 };
66
67 let mut current_poseidon2_res = [F::ZERO; DIGEST_SIZE];
68 let call_idx =
69 u32::try_from(per_call.len()).expect("deferral output call index should fit u32");
70 let header_offset_u32 =
71 u32::try_from(header_offset).expect("record byte offset should fit u32");
72
73 for section_idx in 0..num_rows {
74 let sponge_inputs = if section_idx == 0 {
75 let mut input = [F::ZERO; DIGEST_SIZE];
76 input[0] = F::from_u32(header.deferral_idx);
77 input[1] = F::from_usize(output_len);
78 input
79 } else {
80 let base = (section_idx - 1) * DIGEST_SIZE;
81 from_fn(|i| F::from_u8(write_bytes[base + i]))
82 };
83
84 current_poseidon2_res = poseidon2_chip.perm(
85 &sponge_inputs,
86 ¤t_poseidon2_res,
87 section_idx + 1 == num_rows,
88 );
89
90 per_row.push(DeferralOutputPerRow {
91 header_offset: header_offset_u32,
92 section_idx: section_idx as u32,
93 call_idx,
94 poseidon2_res: current_poseidon2_res,
95 });
96 }
97
98 per_call.push(DeferralOutputPerCall {
99 output_commit: f_commit_to_bytes(¤t_poseidon2_res),
100 });
101
102 let layout = DeferralOutputLayout::new(DeferralOutputMetadata { num_rows });
103 let record_size =
104 <DeferralOutputRecordMut<'_> as SizedRecord<DeferralOutputLayout>>::size(&layout);
105 let record_alignment = <DeferralOutputRecordMut<'_> as SizedRecord<
106 DeferralOutputLayout,
107 >>::alignment(&layout);
108 offset += record_size.next_multiple_of(record_alignment);
109 }
110 debug_assert_eq!(offset, records.len());
111
112 let rows_used = per_row.len();
113 let trace_height = next_power_of_two_or_zero(rows_used);
114 let trace_width = DeferralOutputCols::<F>::width();
115 let device_ctx = &self.range_checker.device_ctx;
116 let trace = DeviceMatrix::<F>::with_capacity_on(trace_height, trace_width, device_ctx);
117
118 let d_raw_records = records.to_device_on(device_ctx).unwrap();
119 let d_per_call = per_call.to_device_on(device_ctx).unwrap();
120 let d_per_row = per_row.to_device_on(device_ctx).unwrap();
121
122 unsafe {
123 output::tracegen(
124 trace.buffer(),
125 trace_height,
126 trace_width,
127 &d_raw_records,
128 &d_per_call,
129 &d_per_row,
130 rows_used,
131 &self.count,
132 self.num_deferral_circuits,
133 &self.range_checker.count,
134 self.timestamp_max_bits as u32,
135 &self.bitwise_lookup.count,
136 RV32_CELL_BITS as u32,
137 self.address_bits,
138 &self.poseidon2.records,
139 &self.poseidon2.counts,
140 &self.poseidon2.idx,
141 self.poseidon2.records.len(),
143 device_ctx.stream.as_raw(),
144 )
145 .expect("Failed to generate deferral output trace");
146 }
147
148 AirProvingContext::simple_no_pis(trace)
149 }
150}