openvm_circuit/system/cuda/
boundary.rs1use openvm_circuit::{
2 arch::DEFAULT_BLOCK_SIZE, system::memory::persistent::PersistentBoundaryCols,
3 utils::next_power_of_two_or_zero,
4};
5use openvm_circuit_primitives::Chip;
6use openvm_cuda_backend::{base::DeviceMatrix, prelude::F, GpuBackend};
7use openvm_cuda_common::{copy::MemCopyH2D, d_buffer::DeviceBuffer, stream::GpuDeviceCtx};
8use openvm_stark_backend::prover::{AirProvingContext, MatrixDimensions};
9
10use super::{poseidon2::SharedBuffer, DIGEST_WIDTH};
11use crate::cuda_abi::boundary::persistent_boundary_tracegen;
12
13pub struct BoundaryChipGPU {
14 pub device_ctx: GpuDeviceCtx,
15 pub poseidon2_buffer: SharedBuffer<F>,
16 pub initial_leaves: Vec<*const std::ffi::c_void>,
20 pub records: Option<DeviceBuffer<u32>>,
21 pub num_records: Option<usize>,
22 pub trace_width: Option<usize>,
23}
24
25const BLOCKS_PER_CHUNK: usize = DIGEST_WIDTH / DEFAULT_BLOCK_SIZE;
26
27#[repr(C)]
28#[derive(Clone, Copy)]
29pub struct PersistentBoundaryRecord {
30 pub address_space: u32,
31 pub ptr: u32,
32 pub timestamps: [u32; BLOCKS_PER_CHUNK],
33 pub values: [F; DIGEST_WIDTH],
34}
35
36impl BoundaryChipGPU {
37 pub fn new(poseidon2_buffer: SharedBuffer<F>, device_ctx: GpuDeviceCtx) -> Self {
38 Self {
39 device_ctx,
40 poseidon2_buffer,
41 initial_leaves: Vec::new(),
42 records: None,
43 num_records: None,
44 trace_width: None,
45 }
46 }
47
48 pub fn finalize_records<const CHUNK: usize>(&mut self, records: Vec<PersistentBoundaryRecord>) {
49 self.num_records = Some(records.len());
50 self.trace_width = Some(PersistentBoundaryCols::<F, CHUNK>::width());
51 self.records = Some(if records.is_empty() {
52 DeviceBuffer::new()
53 } else {
54 records
55 .to_device_on(&self.device_ctx)
56 .unwrap()
57 .as_buffer::<u32>()
58 });
59 }
60
61 pub fn finalize_records_device<const CHUNK: usize>(
62 &mut self,
63 records: DeviceBuffer<u32>,
64 num_records: usize,
65 ) {
66 self.num_records = Some(num_records);
67 self.trace_width = Some(PersistentBoundaryCols::<F, CHUNK>::width());
68 self.records = Some(records);
69 }
70
71 pub fn trace_width(&self) -> usize {
72 self.trace_width.expect("Finalize records to get width")
73 }
74
75 pub fn records(&self) -> &DeviceBuffer<u32> {
76 self.records
77 .as_ref()
78 .expect("Finalize records to get buffer")
79 }
80}
81
82impl<RA> Chip<RA, GpuBackend> for BoundaryChipGPU {
83 fn generate_proving_ctx(&self, _: RA) -> AirProvingContext<GpuBackend> {
84 let num_records = self.num_records.unwrap();
85 if num_records == 0 {
86 let trace =
89 DeviceMatrix::<F>::with_capacity_on(1, self.trace_width(), &self.device_ctx);
90 trace.buffer().fill_zero_on(&self.device_ctx).unwrap();
91 return AirProvingContext::simple_no_pis(trace);
92 }
93 let unpadded_height = 2 * num_records;
94 let trace_height = next_power_of_two_or_zero(unpadded_height);
95 let trace =
96 DeviceMatrix::<F>::with_capacity_on(trace_height, self.trace_width(), &self.device_ctx);
97 trace.buffer().fill_zero_on(&self.device_ctx).unwrap();
98 let mem_ptrs = self.initial_leaves.to_device_on(&self.device_ctx).unwrap();
99 let poseidon2_records = self.poseidon2_buffer.records();
100 unsafe {
101 persistent_boundary_tracegen(
102 trace.buffer(),
103 trace.height(),
104 trace.width(),
105 &mem_ptrs,
106 self.records.as_ref().unwrap(),
107 num_records,
108 &poseidon2_records,
109 &self.poseidon2_buffer.idx,
110 self.device_ctx.stream.as_raw(),
111 )
112 .expect("Failed to generate boundary trace");
113 }
114 AirProvingContext::simple_no_pis(trace)
115 }
116}