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