1use openvm_circuit_primitives::AlignedBytesBorrow;
2use openvm_circuit_primitives_derive::AlignedBorrow;
3use openvm_instructions::{
4 instruction::Instruction, program::DEFAULT_PC_STEP, PhantomDiscriminant, VmOpcode,
5};
6use openvm_stark_backend::{
7 interaction::{BusIndex, InteractionBuilder, PermutationCheckBus},
8 p3_field::FieldAlgebra,
9};
10use rand::rngs::StdRng;
11use serde::{Deserialize, Serialize};
12use thiserror::Error;
13
14use super::{execution_mode::ExecutionCtxTrait, Streams, VmExecState};
15#[cfg(feature = "tco")]
16use crate::arch::interpreter::InterpretedInstance;
17#[cfg(feature = "metrics")]
18use crate::metrics::VmMetrics;
19use crate::{
20 arch::{execution_mode::MeteredExecutionCtxTrait, ExecutorInventoryError, MatrixRecordArena},
21 system::{
22 memory::online::{GuestMemory, TracingMemory},
23 program::ProgramBus,
24 },
25};
26
27#[derive(Error, Debug)]
28pub enum ExecutionError {
29 #[error("execution failed at pc {pc}, err: {msg}")]
30 Fail { pc: u32, msg: &'static str },
31 #[error("pc {0} out of bounds")]
32 PcOutOfBounds(u32),
33 #[error("unreachable instruction at pc {0}")]
34 Unreachable(u32),
35 #[error("at pc {pc}, opcode {opcode} was not enabled")]
36 DisabledOperation { pc: u32, opcode: VmOpcode },
37 #[error("at pc = {pc}")]
38 HintOutOfBounds { pc: u32 },
39 #[error("at pc {pc}, tried to publish into index {public_value_index} when num_public_values = {num_public_values}")]
40 PublicValueIndexOutOfBounds {
41 pc: u32,
42 num_public_values: usize,
43 public_value_index: usize,
44 },
45 #[error("at pc {pc}, tried to publish {new_value} into index {public_value_index} but already had {existing_value}")]
46 PublicValueNotEqual {
47 pc: u32,
48 public_value_index: usize,
49 existing_value: usize,
50 new_value: usize,
51 },
52 #[error("at pc {pc}, phantom sub-instruction not found for discriminant {}", .discriminant.0)]
53 PhantomNotFound {
54 pc: u32,
55 discriminant: PhantomDiscriminant,
56 },
57 #[error("at pc {pc}, discriminant {}, phantom error: {inner}", .discriminant.0)]
58 Phantom {
59 pc: u32,
60 discriminant: PhantomDiscriminant,
61 inner: eyre::Error,
62 },
63 #[error("program must terminate")]
64 DidNotTerminate,
65 #[error("program exit code {0}")]
66 FailedWithExitCode(u32),
67 #[error("trace buffer out of bounds: requested {requested} but capacity is {capacity}")]
68 TraceBufferOutOfBounds { requested: usize, capacity: usize },
69 #[error("inventory error: {0}")]
70 Inventory(#[from] ExecutorInventoryError),
71 #[error("static program error: {0}")]
72 Static(#[from] StaticProgramError),
73}
74
75#[derive(Error, Debug)]
77pub enum StaticProgramError {
78 #[error("invalid instruction at pc {0}")]
79 InvalidInstruction(u32),
80 #[error("Too many executors")]
81 TooManyExecutors,
82 #[error("at pc {pc}, opcode {opcode} was not enabled")]
83 DisabledOperation { pc: u32, opcode: VmOpcode },
84 #[error("Executor not found for opcode {opcode}")]
85 ExecutorNotFound { opcode: VmOpcode },
86}
87
88pub type ExecuteFunc<F, CTX> =
93 unsafe fn(pre_compute: &[u8], exec_state: &mut VmExecState<F, GuestMemory, CTX>);
94
95#[cfg(feature = "tco")]
102pub type Handler<F, CTX> = unsafe fn(
103 interpreter: &InterpretedInstance<F, CTX>,
104 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
105);
106
107pub trait Executor<F> {
112 fn pre_compute_size(&self) -> usize;
113
114 fn pre_compute<Ctx>(
115 &self,
116 pc: u32,
117 inst: &Instruction<F>,
118 data: &mut [u8],
119 ) -> Result<ExecuteFunc<F, Ctx>, StaticProgramError>
120 where
121 Ctx: ExecutionCtxTrait;
122
123 #[cfg(feature = "tco")]
128 fn handler<Ctx>(
129 &self,
130 pc: u32,
131 inst: &Instruction<F>,
132 data: &mut [u8],
133 ) -> Result<Handler<F, Ctx>, StaticProgramError>
134 where
135 Ctx: ExecutionCtxTrait;
136}
137
138pub trait MeteredExecutor<F> {
143 fn metered_pre_compute_size(&self) -> usize;
144
145 fn metered_pre_compute<Ctx>(
146 &self,
147 air_idx: usize,
148 pc: u32,
149 inst: &Instruction<F>,
150 data: &mut [u8],
151 ) -> Result<ExecuteFunc<F, Ctx>, StaticProgramError>
152 where
153 Ctx: MeteredExecutionCtxTrait;
154
155 #[cfg(feature = "tco")]
161 fn metered_handler<Ctx>(
162 &self,
163 air_idx: usize,
164 pc: u32,
165 inst: &Instruction<F>,
166 data: &mut [u8],
167 ) -> Result<Handler<F, Ctx>, StaticProgramError>
168 where
169 Ctx: MeteredExecutionCtxTrait;
170}
171
172pub trait PreflightExecutor<F, RA = MatrixRecordArena<F>> {
178 fn execute(
181 &self,
182 state: VmStateMut<F, TracingMemory, RA>,
183 instruction: &Instruction<F>,
184 ) -> Result<(), ExecutionError>;
185
186 fn get_opcode_name(&self, opcode: usize) -> String;
189}
190
191#[derive(derive_new::new)]
195pub struct VmStateMut<'a, F, MEM, RA> {
196 pub pc: &'a mut u32,
197 pub memory: &'a mut MEM,
198 pub streams: &'a mut Streams<F>,
199 pub rng: &'a mut StdRng,
200 pub(crate) custom_pvs: &'a mut Vec<Option<F>>,
202 pub ctx: &'a mut RA,
203 #[cfg(feature = "metrics")]
204 pub metrics: &'a mut VmMetrics,
205}
206
207#[derive(Clone, AlignedBytesBorrow)]
210#[repr(C)]
211pub struct E2PreCompute<DATA> {
212 pub chip_idx: u32,
213 pub data: DATA,
214}
215
216#[repr(C)]
217#[derive(Clone, Copy, Debug, PartialEq, Default, AlignedBorrow, Serialize, Deserialize)]
218pub struct ExecutionState<T> {
219 pub pc: T,
220 pub timestamp: T,
221}
222
223#[derive(Clone, Copy, Debug)]
224pub struct ExecutionBus {
225 pub inner: PermutationCheckBus,
226}
227
228impl ExecutionBus {
229 pub const fn new(index: BusIndex) -> Self {
230 Self {
231 inner: PermutationCheckBus::new(index),
232 }
233 }
234
235 #[inline(always)]
236 pub fn index(&self) -> BusIndex {
237 self.inner.index
238 }
239}
240
241#[derive(Copy, Clone, Debug)]
242pub struct ExecutionBridge {
243 execution_bus: ExecutionBus,
244 program_bus: ProgramBus,
245}
246
247pub struct ExecutionBridgeInteractor<AB: InteractionBuilder> {
248 execution_bus: ExecutionBus,
249 program_bus: ProgramBus,
250 opcode: AB::Expr,
251 operands: Vec<AB::Expr>,
252 from_state: ExecutionState<AB::Expr>,
253 to_state: ExecutionState<AB::Expr>,
254}
255
256pub enum PcIncOrSet<T> {
257 Inc(T),
258 Set(T),
259}
260
261impl<T> ExecutionState<T> {
262 pub fn new(pc: impl Into<T>, timestamp: impl Into<T>) -> Self {
263 Self {
264 pc: pc.into(),
265 timestamp: timestamp.into(),
266 }
267 }
268
269 #[allow(clippy::should_implement_trait)]
270 pub fn from_iter<I: Iterator<Item = T>>(iter: &mut I) -> Self {
271 let mut next = || iter.next().unwrap();
272 Self {
273 pc: next(),
274 timestamp: next(),
275 }
276 }
277
278 pub fn flatten(self) -> [T; 2] {
279 [self.pc, self.timestamp]
280 }
281
282 pub fn get_width() -> usize {
283 2
284 }
285
286 pub fn map<U: Clone, F: Fn(T) -> U>(self, function: F) -> ExecutionState<U> {
287 ExecutionState::from_iter(&mut self.flatten().map(function).into_iter())
288 }
289}
290
291impl ExecutionBus {
292 pub fn execute_and_increment_pc<AB: InteractionBuilder>(
294 &self,
295 builder: &mut AB,
296 enabled: impl Into<AB::Expr>,
297 prev_state: ExecutionState<AB::Expr>,
298 timestamp_change: impl Into<AB::Expr>,
299 ) {
300 let next_state = ExecutionState {
301 pc: prev_state.pc.clone() + AB::F::ONE,
302 timestamp: prev_state.timestamp.clone() + timestamp_change.into(),
303 };
304 self.execute(builder, enabled, prev_state, next_state);
305 }
306
307 pub fn execute<AB: InteractionBuilder>(
309 &self,
310 builder: &mut AB,
311 enabled: impl Into<AB::Expr>,
312 prev_state: ExecutionState<impl Into<AB::Expr>>,
313 next_state: ExecutionState<impl Into<AB::Expr>>,
314 ) {
315 let enabled = enabled.into();
316 self.inner.receive(
317 builder,
318 [prev_state.pc.into(), prev_state.timestamp.into()],
319 enabled.clone(),
320 );
321 self.inner.send(
322 builder,
323 [next_state.pc.into(), next_state.timestamp.into()],
324 enabled,
325 );
326 }
327}
328
329impl ExecutionBridge {
330 pub fn new(execution_bus: ExecutionBus, program_bus: ProgramBus) -> Self {
331 Self {
332 execution_bus,
333 program_bus,
334 }
335 }
336
337 pub fn execute_and_increment_or_set_pc<AB: InteractionBuilder>(
340 &self,
341 opcode: impl Into<AB::Expr>,
342 operands: impl IntoIterator<Item = impl Into<AB::Expr>>,
343 from_state: ExecutionState<impl Into<AB::Expr> + Clone>,
344 timestamp_change: impl Into<AB::Expr>,
345 pc_kind: impl Into<PcIncOrSet<AB::Expr>>,
346 ) -> ExecutionBridgeInteractor<AB> {
347 let to_state = ExecutionState {
348 pc: match pc_kind.into() {
349 PcIncOrSet::Set(to_pc) => to_pc,
350 PcIncOrSet::Inc(pc_inc) => from_state.pc.clone().into() + pc_inc,
351 },
352 timestamp: from_state.timestamp.clone().into() + timestamp_change.into(),
353 };
354 self.execute(opcode, operands, from_state, to_state)
355 }
356
357 pub fn execute_and_increment_pc<AB: InteractionBuilder>(
358 &self,
359 opcode: impl Into<AB::Expr>,
360 operands: impl IntoIterator<Item = impl Into<AB::Expr>>,
361 from_state: ExecutionState<impl Into<AB::Expr> + Clone>,
362 timestamp_change: impl Into<AB::Expr>,
363 ) -> ExecutionBridgeInteractor<AB> {
364 let to_state = ExecutionState {
365 pc: from_state.pc.clone().into() + AB::Expr::from_canonical_u32(DEFAULT_PC_STEP),
366 timestamp: from_state.timestamp.clone().into() + timestamp_change.into(),
367 };
368 self.execute(opcode, operands, from_state, to_state)
369 }
370
371 pub fn execute<AB: InteractionBuilder>(
372 &self,
373 opcode: impl Into<AB::Expr>,
374 operands: impl IntoIterator<Item = impl Into<AB::Expr>>,
375 from_state: ExecutionState<impl Into<AB::Expr> + Clone>,
376 to_state: ExecutionState<impl Into<AB::Expr>>,
377 ) -> ExecutionBridgeInteractor<AB> {
378 ExecutionBridgeInteractor {
379 execution_bus: self.execution_bus,
380 program_bus: self.program_bus,
381 opcode: opcode.into(),
382 operands: operands.into_iter().map(Into::into).collect(),
383 from_state: from_state.map(Into::into),
384 to_state: to_state.map(Into::into),
385 }
386 }
387}
388
389impl<AB: InteractionBuilder> ExecutionBridgeInteractor<AB> {
390 pub fn eval(self, builder: &mut AB, enabled: impl Into<AB::Expr>) {
392 let enabled = enabled.into();
393
394 self.program_bus.lookup_instruction(
396 builder,
397 self.from_state.pc.clone(),
398 self.opcode,
399 self.operands,
400 enabled.clone(),
401 );
402
403 self.execution_bus
404 .execute(builder, enabled, self.from_state, self.to_state);
405 }
406}
407
408impl<T: FieldAlgebra> From<(u32, Option<T>)> for PcIncOrSet<T> {
409 fn from((pc_inc, to_pc): (u32, Option<T>)) -> Self {
410 match to_pc {
411 None => PcIncOrSet::Inc(T::from_canonical_u32(pc_inc)),
412 Some(to_pc) => PcIncOrSet::Set(to_pc),
413 }
414 }
415}
416
417#[allow(clippy::too_many_arguments)]
426pub trait PhantomSubExecutor<F>: Send + Sync {
427 fn phantom_execute(
428 &self,
429 memory: &GuestMemory,
430 streams: &mut Streams<F>,
431 rng: &mut StdRng,
432 discriminant: PhantomDiscriminant,
433 a: u32,
434 b: u32,
435 c_upper: u16,
436 ) -> eyre::Result<()>;
437}