openvm_circuit/system/public_values/
execution.rs1use std::{
2 borrow::{Borrow, BorrowMut},
3 mem::size_of,
4};
5
6use openvm_circuit_primitives::AlignedBytesBorrow;
7use openvm_instructions::{
8 instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_IMM_AS, NATIVE_AS,
9};
10use openvm_stark_backend::p3_field::PrimeField32;
11
12use super::PublicValuesExecutor;
13#[cfg(not(feature = "tco"))]
14use crate::arch::ExecuteFunc;
15#[cfg(feature = "tco")]
16use crate::arch::Handler;
17#[cfg(feature = "aot")]
18use crate::arch::{AotExecutor, AotMeteredExecutor};
19use crate::{
20 arch::{
21 create_handler,
22 execution_mode::{ExecutionCtxTrait, MeteredExecutionCtxTrait},
23 E2PreCompute, InterpreterExecutor, InterpreterMeteredExecutor, StaticProgramError,
24 VmExecState,
25 },
26 system::memory::online::GuestMemory,
27 utils::{transmute_field_to_u32, transmute_u32_to_field},
28};
29
30#[derive(AlignedBytesBorrow)]
31#[repr(C)]
32struct PublicValuesPreCompute {
33 b_or_imm: u32,
34 c_or_imm: u32,
35}
36
37impl<F, A> PublicValuesExecutor<F, A>
38where
39 F: PrimeField32,
40{
41 fn pre_compute_impl(
42 &self,
43 inst: &Instruction<F>,
44 data: &mut PublicValuesPreCompute,
45 ) -> (bool, bool) {
46 let &Instruction { b, c, e, f, .. } = inst;
47
48 let e = e.as_canonical_u32();
49 let f = f.as_canonical_u32();
50
51 let b_is_imm = e == RV32_IMM_AS;
52 let c_is_imm = f == RV32_IMM_AS;
53
54 let b_or_imm = if b_is_imm {
55 transmute_field_to_u32(&b)
56 } else {
57 b.as_canonical_u32()
58 };
59 let c_or_imm = if c_is_imm {
60 transmute_field_to_u32(&c)
61 } else {
62 c.as_canonical_u32()
63 };
64
65 *data = PublicValuesPreCompute { b_or_imm, c_or_imm };
66
67 (b_is_imm, c_is_imm)
68 }
69}
70
71macro_rules! dispatch {
72 ($execute_impl:ident, $b_is_imm:ident, $c_is_imm:ident) => {
73 match ($b_is_imm, $c_is_imm) {
74 (true, true) => Ok($execute_impl::<_, _, true, true>),
75 (true, false) => Ok($execute_impl::<_, _, true, false>),
76 (false, true) => Ok($execute_impl::<_, _, false, true>),
77 (false, false) => Ok($execute_impl::<_, _, false, false>),
78 }
79 };
80}
81
82impl<F, A> InterpreterExecutor<F> for PublicValuesExecutor<F, A>
83where
84 F: PrimeField32,
85{
86 #[inline(always)]
87 fn pre_compute_size(&self) -> usize {
88 size_of::<PublicValuesPreCompute>()
89 }
90
91 #[cfg(not(feature = "tco"))]
92 #[inline(always)]
93 fn pre_compute<Ctx>(
94 &self,
95 _pc: u32,
96 inst: &Instruction<F>,
97 data: &mut [u8],
98 ) -> Result<ExecuteFunc<F, Ctx>, StaticProgramError>
99 where
100 Ctx: ExecutionCtxTrait,
101 {
102 let data: &mut PublicValuesPreCompute = data.borrow_mut();
103 let (b_is_imm, c_is_imm) = self.pre_compute_impl(inst, data);
104
105 dispatch!(execute_e1_handler, b_is_imm, c_is_imm)
106 }
107
108 #[cfg(feature = "tco")]
109 fn handler<Ctx>(
110 &self,
111 _pc: u32,
112 inst: &Instruction<F>,
113 data: &mut [u8],
114 ) -> Result<Handler<F, Ctx>, StaticProgramError>
115 where
116 Ctx: ExecutionCtxTrait,
117 {
118 let data: &mut PublicValuesPreCompute = data.borrow_mut();
119 let (b_is_imm, c_is_imm) = self.pre_compute_impl(inst, data);
120
121 dispatch!(execute_e1_handler, b_is_imm, c_is_imm)
122 }
123}
124
125#[cfg(feature = "aot")]
126impl<F, A> AotExecutor<F> for PublicValuesExecutor<F, A> where F: PrimeField32 {}
127
128impl<F, A> InterpreterMeteredExecutor<F> for PublicValuesExecutor<F, A>
129where
130 F: PrimeField32,
131{
132 fn metered_pre_compute_size(&self) -> usize {
133 size_of::<E2PreCompute<PublicValuesPreCompute>>()
134 }
135
136 #[cfg(not(feature = "tco"))]
137 fn metered_pre_compute<Ctx>(
138 &self,
139 chip_idx: usize,
140 _pc: u32,
141 inst: &Instruction<F>,
142 data: &mut [u8],
143 ) -> Result<ExecuteFunc<F, Ctx>, StaticProgramError>
144 where
145 Ctx: MeteredExecutionCtxTrait,
146 {
147 let data: &mut E2PreCompute<PublicValuesPreCompute> = data.borrow_mut();
148 data.chip_idx = chip_idx as u32;
149 let (b_is_imm, c_is_imm) = self.pre_compute_impl(inst, &mut data.data);
150
151 dispatch!(execute_e2_handler, b_is_imm, c_is_imm)
152 }
153
154 #[cfg(feature = "tco")]
155 fn metered_handler<Ctx>(
156 &self,
157 chip_idx: usize,
158 _pc: u32,
159 inst: &Instruction<F>,
160 data: &mut [u8],
161 ) -> Result<Handler<F, Ctx>, StaticProgramError>
162 where
163 Ctx: MeteredExecutionCtxTrait,
164 {
165 let data: &mut E2PreCompute<PublicValuesPreCompute> = data.borrow_mut();
166 data.chip_idx = chip_idx as u32;
167 let (b_is_imm, c_is_imm) = self.pre_compute_impl(inst, &mut data.data);
168
169 dispatch!(execute_e2_handler, b_is_imm, c_is_imm)
170 }
171}
172
173#[cfg(feature = "aot")]
174impl<F, A> AotMeteredExecutor<F> for PublicValuesExecutor<F, A> where F: PrimeField32 {}
175
176#[inline(always)]
177unsafe fn execute_e12_impl<F: PrimeField32, CTX, const B_IS_IMM: bool, const C_IS_IMM: bool>(
178 pre_compute: &PublicValuesPreCompute,
179 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
180) where
181 CTX: ExecutionCtxTrait,
182{
183 let value = if B_IS_IMM {
184 transmute_u32_to_field(&pre_compute.b_or_imm)
185 } else {
186 exec_state.vm_read::<F, 1>(NATIVE_AS, pre_compute.b_or_imm)[0]
187 };
188 let index = if C_IS_IMM {
189 transmute_u32_to_field(&pre_compute.c_or_imm)
190 } else {
191 exec_state.vm_read::<F, 1>(NATIVE_AS, pre_compute.c_or_imm)[0]
192 };
193
194 let idx: usize = index.as_canonical_u32() as usize;
195 {
196 let custom_pvs = &mut exec_state.vm_state.custom_pvs;
197
198 if custom_pvs[idx].is_none() {
199 custom_pvs[idx] = Some(value);
200 } else {
201 panic!("Custom public value {idx} already set");
204 }
205 }
206 let pc = exec_state.pc();
207 exec_state.set_pc(pc.wrapping_add(DEFAULT_PC_STEP));
208}
209
210#[create_handler]
211#[inline(always)]
212unsafe fn execute_e1_impl<F: PrimeField32, CTX, const B_IS_IMM: bool, const C_IS_IMM: bool>(
213 pre_compute: *const u8,
214 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
215) where
216 CTX: ExecutionCtxTrait,
217{
218 let pre_compute: &PublicValuesPreCompute =
219 std::slice::from_raw_parts(pre_compute, size_of::<PublicValuesPreCompute>()).borrow();
220 execute_e12_impl::<_, _, B_IS_IMM, C_IS_IMM>(pre_compute, exec_state);
221}
222
223#[create_handler]
224#[inline(always)]
225unsafe fn execute_e2_impl<F: PrimeField32, CTX, const B_IS_IMM: bool, const C_IS_IMM: bool>(
226 pre_compute: *const u8,
227 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
228) where
229 CTX: MeteredExecutionCtxTrait,
230{
231 let pre_compute: &E2PreCompute<PublicValuesPreCompute> = std::slice::from_raw_parts(
232 pre_compute,
233 size_of::<E2PreCompute<PublicValuesPreCompute>>(),
234 )
235 .borrow();
236 exec_state
237 .ctx
238 .on_height_change(pre_compute.chip_idx as usize, 1);
239 execute_e12_impl::<_, _, B_IS_IMM, C_IS_IMM>(&pre_compute.data, exec_state);
240}