openvm_deferral_circuit/call/
execution.rs1use std::{
2 array::from_fn,
3 borrow::{Borrow, BorrowMut},
4 slice::from_raw_parts,
5};
6
7use itertools::Itertools;
8use openvm_circuit::{arch::*, system::memory::online::GuestMemory};
9use openvm_circuit_primitives::AlignedBytesBorrow;
10use openvm_deferral_transpiler::DeferralOpcode;
11use openvm_instructions::{
12 instruction::Instruction,
13 program::DEFAULT_PC_STEP,
14 riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS},
15 LocalOpcode, DEFERRAL_AS,
16};
17use openvm_stark_sdk::config::baby_bear_poseidon2::DIGEST_SIZE;
18
19use super::DeferralCallExecutor;
20use crate::{
21 poseidon2::deferral_poseidon2_chip,
22 utils::{
23 byte_commit_to_f, combine_output, join_memory_ops, memory_op_chunk, COMMIT_MEMORY_OPS,
24 COMMIT_NUM_BYTES, DIGEST_MEMORY_OPS, OUTPUT_TOTAL_MEMORY_OPS,
25 },
26 DeferralFn, CALL_AIR_REL_IDX, POSEIDON2_AIR_REL_IDX,
27};
28
29#[derive(AlignedBytesBorrow, Clone)]
30#[repr(C)]
31struct DeferralCallPrecompute<'a> {
32 rd_ptr: u32,
33 rs_ptr: u32,
34 deferral_idx: u32,
35 input_acc_ptr: u32,
36 output_acc_ptr: u32,
37 deferral_fn: &'a DeferralFn,
38}
39
40impl DeferralCallExecutor {
41 #[inline(always)]
42 fn pre_compute_impl<'a, F: VmField>(
43 &'a self,
44 pc: u32,
45 inst: &Instruction<F>,
46 data: &mut DeferralCallPrecompute<'a>,
47 ) -> Result<(), StaticProgramError> {
48 let Instruction {
49 opcode,
50 a,
51 b,
52 c,
53 d,
54 e,
55 ..
56 } = inst;
57
58 if opcode.local_opcode_idx(DeferralOpcode::CLASS_OFFSET) != DeferralOpcode::CALL as usize
59 || d.as_canonical_u32() != RV32_REGISTER_AS
60 || e.as_canonical_u32() != RV32_MEMORY_AS
61 {
62 return Err(StaticProgramError::InvalidInstruction(pc));
63 }
64
65 let deferral_idx = c.as_canonical_u32();
66 let deferral_fn = self
67 .deferral_fns
68 .get(deferral_idx as usize)
69 .ok_or(StaticProgramError::InvalidInstruction(pc))?;
70
71 const DIGEST_SIZE_U32: u32 = DIGEST_SIZE as u32;
72 let input_acc_ptr = 2 * deferral_idx * DIGEST_SIZE_U32;
73 *data = DeferralCallPrecompute {
74 rd_ptr: a.as_canonical_u32(),
75 rs_ptr: b.as_canonical_u32(),
76 deferral_idx,
77 input_acc_ptr,
78 output_acc_ptr: input_acc_ptr + DIGEST_SIZE_U32,
79 deferral_fn: deferral_fn.as_ref(),
80 };
81
82 Ok(())
83 }
84}
85
86impl<F: VmField> InterpreterExecutor<F> for DeferralCallExecutor {
87 fn pre_compute_size(&self) -> usize {
88 size_of::<DeferralCallPrecompute>()
89 }
90
91 #[cfg(not(feature = "tco"))]
92 fn pre_compute<Ctx>(
93 &self,
94 pc: u32,
95 inst: &Instruction<F>,
96 data: &mut [u8],
97 ) -> Result<ExecuteFunc<F, Ctx>, StaticProgramError>
98 where
99 Ctx: ExecutionCtxTrait,
100 {
101 let pre_compute: &mut DeferralCallPrecompute = data.borrow_mut();
102 self.pre_compute_impl(pc, inst, pre_compute)?;
103 Ok(execute_e1_impl::<_, _>)
104 }
105
106 #[cfg(feature = "tco")]
107 fn handler<Ctx>(
108 &self,
109 pc: u32,
110 inst: &Instruction<F>,
111 data: &mut [u8],
112 ) -> Result<Handler<F, Ctx>, StaticProgramError>
113 where
114 Ctx: ExecutionCtxTrait,
115 {
116 let pre_compute: &mut DeferralCallPrecompute = data.borrow_mut();
117 self.pre_compute_impl(pc, inst, pre_compute)?;
118 Ok(execute_e1_handler::<_, _>)
119 }
120}
121
122#[cfg(feature = "aot")]
123impl<F: VmField> AotExecutor<F> for DeferralCallExecutor {}
124
125impl<F: VmField> InterpreterMeteredExecutor<F> for DeferralCallExecutor {
126 fn metered_pre_compute_size(&self) -> usize {
127 size_of::<E2PreCompute<DeferralCallPrecompute>>()
128 }
129
130 #[cfg(not(feature = "tco"))]
131 fn metered_pre_compute<Ctx>(
132 &self,
133 air_idx: usize,
134 pc: u32,
135 inst: &Instruction<F>,
136 data: &mut [u8],
137 ) -> Result<ExecuteFunc<F, Ctx>, StaticProgramError>
138 where
139 Ctx: MeteredExecutionCtxTrait,
140 {
141 let pre_compute: &mut E2PreCompute<DeferralCallPrecompute> = data.borrow_mut();
142 pre_compute.chip_idx = air_idx as u32;
143 self.pre_compute_impl(pc, inst, &mut pre_compute.data)?;
144 Ok(execute_e2_impl::<_, _>)
145 }
146
147 #[cfg(feature = "tco")]
148 fn metered_handler<Ctx>(
149 &self,
150 air_idx: usize,
151 pc: u32,
152 inst: &Instruction<F>,
153 data: &mut [u8],
154 ) -> Result<Handler<F, Ctx>, StaticProgramError>
155 where
156 Ctx: MeteredExecutionCtxTrait,
157 {
158 let pre_compute: &mut E2PreCompute<DeferralCallPrecompute> = data.borrow_mut();
159 pre_compute.chip_idx = air_idx as u32;
160 self.pre_compute_impl(pc, inst, &mut pre_compute.data)?;
161 Ok(execute_e2_handler::<_, _>)
162 }
163}
164
165#[cfg(feature = "aot")]
166impl<F: VmField> AotMeteredExecutor<F> for DeferralCallExecutor {}
167
168#[inline(always)]
169unsafe fn execute_e12_impl<F: VmField, CTX: ExecutionCtxTrait>(
170 pre_compute: &DeferralCallPrecompute,
171 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
172) {
173 let output_ptr = u32::from_le_bytes(exec_state.vm_read(RV32_REGISTER_AS, pre_compute.rd_ptr));
174 let input_ptr = u32::from_le_bytes(exec_state.vm_read(RV32_REGISTER_AS, pre_compute.rs_ptr));
175
176 let input_commit_chunks: [[u8; DEFAULT_BLOCK_SIZE]; COMMIT_MEMORY_OPS] = from_fn(|i| {
177 exec_state.vm_read(RV32_MEMORY_AS, input_ptr + (i * DEFAULT_BLOCK_SIZE) as u32)
178 });
179 let input_commit_bytes: [_; COMMIT_NUM_BYTES] = join_memory_ops(input_commit_chunks);
180 let input_commit: [F; _] = byte_commit_to_f(&input_commit_bytes.map(F::from_u8));
181 let old_input_acc_chunks: [[F; DEFAULT_BLOCK_SIZE]; DIGEST_MEMORY_OPS] = from_fn(|i| {
182 exec_state.vm_read(
183 DEFERRAL_AS,
184 pre_compute.input_acc_ptr + (i * DEFAULT_BLOCK_SIZE) as u32,
185 )
186 });
187 let old_output_acc_chunks: [[F; DEFAULT_BLOCK_SIZE]; DIGEST_MEMORY_OPS] = from_fn(|i| {
188 exec_state.vm_read(
189 DEFERRAL_AS,
190 pre_compute.output_acc_ptr + (i * DEFAULT_BLOCK_SIZE) as u32,
191 )
192 });
193 let old_input_acc = join_memory_ops(old_input_acc_chunks);
194 let old_output_acc = join_memory_ops(old_output_acc_chunks);
195
196 let poseidon2_chip = deferral_poseidon2_chip();
197 let (output_commit, output_len) = pre_compute.deferral_fn.execute(
198 &input_commit_bytes.to_vec(),
199 &mut exec_state.streams.deferrals[pre_compute.deferral_idx as usize],
200 pre_compute.deferral_idx,
201 &poseidon2_chip,
202 );
203 let output_f_commit =
204 byte_commit_to_f(&output_commit.iter().map(|v| F::from_u8(*v)).collect_vec());
205
206 let output_key = combine_output(output_commit, output_len.to_le_bytes());
208
209 let new_input_acc = poseidon2_chip.perm(&old_input_acc, &input_commit, true);
210 let new_output_acc = poseidon2_chip.perm(&old_output_acc, &output_f_commit, true);
211
212 for chunk_idx in 0..OUTPUT_TOTAL_MEMORY_OPS {
213 exec_state.vm_write::<u8, DEFAULT_BLOCK_SIZE>(
214 RV32_MEMORY_AS,
215 output_ptr + (chunk_idx * DEFAULT_BLOCK_SIZE) as u32,
216 &memory_op_chunk(&output_key, chunk_idx),
217 );
218 }
219 for chunk_idx in 0..DIGEST_MEMORY_OPS {
220 exec_state.vm_write::<F, DEFAULT_BLOCK_SIZE>(
221 DEFERRAL_AS,
222 pre_compute.input_acc_ptr + (chunk_idx * DEFAULT_BLOCK_SIZE) as u32,
223 &memory_op_chunk(&new_input_acc, chunk_idx),
224 );
225 }
226 for chunk_idx in 0..DIGEST_MEMORY_OPS {
227 exec_state.vm_write::<F, DEFAULT_BLOCK_SIZE>(
228 DEFERRAL_AS,
229 pre_compute.output_acc_ptr + (chunk_idx * DEFAULT_BLOCK_SIZE) as u32,
230 &memory_op_chunk(&new_output_acc, chunk_idx),
231 );
232 }
233
234 let pc = exec_state.pc();
235 exec_state.set_pc(pc.wrapping_add(DEFAULT_PC_STEP));
236}
237
238#[create_handler]
239#[inline(always)]
240unsafe fn execute_e1_impl<F: VmField, CTX: ExecutionCtxTrait>(
241 pre_compute: *const u8,
242 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
243) {
244 let pre_compute: &DeferralCallPrecompute =
245 from_raw_parts(pre_compute, size_of::<DeferralCallPrecompute>()).borrow();
246 execute_e12_impl(pre_compute, exec_state);
247}
248
249#[create_handler]
250#[inline(always)]
251unsafe fn execute_e2_impl<F: VmField, CTX: MeteredExecutionCtxTrait>(
252 pre_compute: *const u8,
253 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
254) {
255 let pre_compute: &E2PreCompute<DeferralCallPrecompute> = from_raw_parts(
256 pre_compute,
257 size_of::<E2PreCompute<DeferralCallPrecompute>>(),
258 )
259 .borrow();
260
261 execute_e12_impl(&pre_compute.data, exec_state);
262 exec_state
263 .ctx
264 .on_height_change(pre_compute.chip_idx as usize, 1);
265
266 exec_state.ctx.on_height_change(
270 pre_compute.chip_idx as usize + (CALL_AIR_REL_IDX - POSEIDON2_AIR_REL_IDX),
271 2,
272 );
273}