openvm_circuit/system/phantom/
execution.rs

1use 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    // SysPhantom::{CtStart, CtEnd} are only handled in Preflight Execution, so the only SysPhantom
172    // to handle here is DebugPanic.
173    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}