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