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