openvm_circuit/system/phantom/
execution.rs1use std::{
2 borrow::{Borrow, BorrowMut},
3 mem::size_of,
4};
5
6use openvm_circuit_primitives_derive::AlignedBytesBorrow;
7use openvm_instructions::{
8 instruction::Instruction, program::DEFAULT_PC_STEP, PhantomDiscriminant, SysPhantom,
9};
10use openvm_stark_backend::p3_field::PrimeField32;
11use rand::rngs::StdRng;
12
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, ExecutionError, Executor, MeteredExecutor, PhantomSubExecutor,
22 StaticProgramError, Streams, VmExecState,
23 },
24 system::{memory::online::GuestMemory, phantom::PhantomExecutor},
25};
26
27#[derive(Clone, AlignedBytesBorrow)]
28#[repr(C)]
29pub(super) struct PhantomOperands {
30 pub(super) a: u32,
31 pub(super) b: u32,
32 pub(super) c: u32,
33}
34
35#[derive(Clone, AlignedBytesBorrow)]
36#[repr(C)]
37struct PhantomPreCompute<F> {
38 operands: PhantomOperands,
39 sub_executor: *const dyn PhantomSubExecutor<F>,
40}
41
42impl<F> Executor<F> for PhantomExecutor<F>
43where
44 F: PrimeField32,
45{
46 #[inline(always)]
47 fn pre_compute_size(&self) -> usize {
48 size_of::<PhantomPreCompute<F>>()
49 }
50 #[cfg(not(feature = "tco"))]
51 #[inline(always)]
52 fn pre_compute<Ctx>(
53 &self,
54 _pc: u32,
55 inst: &Instruction<F>,
56 data: &mut [u8],
57 ) -> Result<ExecuteFunc<F, Ctx>, StaticProgramError>
58 where
59 Ctx: ExecutionCtxTrait,
60 {
61 let data: &mut PhantomPreCompute<F> = data.borrow_mut();
62 self.pre_compute_impl(inst, data);
63 Ok(execute_e1_handler)
64 }
65
66 #[cfg(feature = "tco")]
67 fn handler<Ctx>(
68 &self,
69 _pc: u32,
70 inst: &Instruction<F>,
71 data: &mut [u8],
72 ) -> Result<Handler<F, Ctx>, StaticProgramError>
73 where
74 Ctx: ExecutionCtxTrait,
75 {
76 let data: &mut PhantomPreCompute<F> = data.borrow_mut();
77 self.pre_compute_impl(inst, data);
78 Ok(execute_e1_handler)
79 }
80}
81
82pub(super) struct PhantomStateMut<'a, F> {
83 pub(super) pc: &'a mut u32,
84 pub(super) memory: &'a mut GuestMemory,
85 pub(super) streams: &'a mut Streams<F>,
86 pub(super) rng: &'a mut StdRng,
87}
88
89impl<F> PhantomExecutor<F>
90where
91 F: PrimeField32,
92{
93 #[inline(always)]
94 fn pre_compute_impl(&self, inst: &Instruction<F>, data: &mut PhantomPreCompute<F>) {
95 let c = inst.c.as_canonical_u32();
96 *data = PhantomPreCompute {
97 operands: PhantomOperands {
98 a: inst.a.as_canonical_u32(),
99 b: inst.b.as_canonical_u32(),
100 c,
101 },
102 sub_executor: self
103 .phantom_executors
104 .get(&PhantomDiscriminant(c as u16))
105 .unwrap_or_else(|| panic!("Phantom executor not found for insn {inst:?}"))
106 .as_ref(),
107 };
108 }
109}
110
111impl<F> MeteredExecutor<F> for PhantomExecutor<F>
112where
113 F: PrimeField32,
114{
115 fn metered_pre_compute_size(&self) -> usize {
116 size_of::<E2PreCompute<PhantomPreCompute<F>>>()
117 }
118
119 #[cfg(not(feature = "tco"))]
120 fn metered_pre_compute<Ctx>(
121 &self,
122 chip_idx: usize,
123 _pc: u32,
124 inst: &Instruction<F>,
125 data: &mut [u8],
126 ) -> Result<ExecuteFunc<F, Ctx>, StaticProgramError>
127 where
128 Ctx: MeteredExecutionCtxTrait,
129 {
130 let e2_data: &mut E2PreCompute<PhantomPreCompute<F>> = data.borrow_mut();
131 e2_data.chip_idx = chip_idx as u32;
132 self.pre_compute_impl(inst, &mut e2_data.data);
133 Ok(execute_e2_handler)
134 }
135
136 #[cfg(feature = "tco")]
137 fn metered_handler<Ctx>(
138 &self,
139 chip_idx: usize,
140 _pc: u32,
141 inst: &Instruction<F>,
142 data: &mut [u8],
143 ) -> Result<Handler<F, Ctx>, StaticProgramError>
144 where
145 Ctx: MeteredExecutionCtxTrait,
146 {
147 let e2_data: &mut E2PreCompute<PhantomPreCompute<F>> = data.borrow_mut();
148 e2_data.chip_idx = chip_idx as u32;
149 self.pre_compute_impl(inst, &mut e2_data.data);
150 Ok(execute_e2_handler)
151 }
152}
153
154#[inline(always)]
155fn execute_impl<F>(
156 state: PhantomStateMut<F>,
157 operands: &PhantomOperands,
158 sub_executor: &dyn PhantomSubExecutor<F>,
159) -> Result<(), ExecutionError> {
160 let &PhantomOperands { a, b, c } = operands;
161
162 let discriminant = PhantomDiscriminant(c as u16);
163 if let Some(discr) = SysPhantom::from_repr(discriminant.0) {
166 if discr == SysPhantom::DebugPanic {
167 return Err(ExecutionError::Fail {
168 pc: *state.pc,
169 msg: "DebugPanic",
170 });
171 }
172 }
173 sub_executor
174 .phantom_execute(
175 state.memory,
176 state.streams,
177 state.rng,
178 discriminant,
179 a,
180 b,
181 (c >> 16) as u16,
182 )
183 .map_err(|e| ExecutionError::Phantom {
184 pc: *state.pc,
185 discriminant,
186 inner: e,
187 })?;
188
189 Ok(())
190}
191
192#[inline(always)]
193unsafe fn execute_e12_impl<F: PrimeField32, CTX: ExecutionCtxTrait>(
194 pre_compute: &PhantomPreCompute<F>,
195 instret: &mut u64,
196 pc: &mut u32,
197 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
198) -> Result<(), ExecutionError> {
199 let sub_executor = &*pre_compute.sub_executor;
200 execute_impl(
201 PhantomStateMut {
202 pc,
203 memory: &mut exec_state.vm_state.memory,
204 streams: &mut exec_state.vm_state.streams,
205 rng: &mut exec_state.vm_state.rng,
206 },
207 &pre_compute.operands,
208 sub_executor,
209 )?;
210 *pc += DEFAULT_PC_STEP;
211 *instret += 1;
212
213 Ok(())
214}
215
216#[create_handler]
217#[inline(always)]
218unsafe fn execute_e1_impl<F: PrimeField32, CTX: ExecutionCtxTrait>(
219 pre_compute: &[u8],
220 instret: &mut u64,
221 pc: &mut u32,
222 _instret_end: u64,
223 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
224) -> Result<(), ExecutionError> {
225 let pre_compute: &PhantomPreCompute<F> = pre_compute.borrow();
226 execute_e12_impl(pre_compute, instret, pc, exec_state)
227}
228
229#[create_handler]
230#[inline(always)]
231unsafe fn execute_e2_impl<F: PrimeField32, CTX: MeteredExecutionCtxTrait>(
232 pre_compute: &[u8],
233 instret: &mut u64,
234 pc: &mut u32,
235 _arg: u64,
236 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
237) -> Result<(), ExecutionError> {
238 let pre_compute: &E2PreCompute<PhantomPreCompute<F>> = pre_compute.borrow();
239 exec_state
240 .ctx
241 .on_height_change(pre_compute.chip_idx as usize, 1);
242 execute_e12_impl(&pre_compute.data, instret, pc, exec_state)
243}