openvm_circuit/system/phantom/
execution.rs

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