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