1use std::{cell::RefCell, rc::Rc};
2
3use openvm_circuit_primitives_derive::AlignedBorrow;
4use openvm_instructions::{
5 instruction::Instruction, program::DEFAULT_PC_STEP, PhantomDiscriminant, VmOpcode,
6};
7use openvm_stark_backend::{
8 interaction::{BusIndex, InteractionBuilder, PermutationCheckBus},
9 p3_field::FieldAlgebra,
10};
11use serde::{Deserialize, Serialize};
12use thiserror::Error;
13
14use super::Streams;
15use crate::system::{memory::MemoryController, program::ProgramBus};
16
17pub type Result<T> = std::result::Result<T, ExecutionError>;
18
19#[derive(Error, Debug)]
20pub enum ExecutionError {
21 #[error("execution failed at pc {pc}")]
22 Fail { pc: u32 },
23 #[error("pc {pc} not found for program of length {program_len}, with pc_base {pc_base} and step = {step}")]
24 PcNotFound {
25 pc: u32,
26 step: u32,
27 pc_base: u32,
28 program_len: usize,
29 },
30 #[error("pc {pc} out of bounds for program of length {program_len}, with pc_base {pc_base} and step = {step}")]
31 PcOutOfBounds {
32 pc: u32,
33 step: u32,
34 pc_base: u32,
35 program_len: usize,
36 },
37 #[error("at pc {pc}, opcode {opcode} was not enabled")]
38 DisabledOperation { pc: u32, opcode: VmOpcode },
39 #[error("at pc = {pc}")]
40 HintOutOfBounds { pc: u32 },
41 #[error("at pc {pc}, tried to publish into index {public_value_index} when num_public_values = {num_public_values}")]
42 PublicValueIndexOutOfBounds {
43 pc: u32,
44 num_public_values: usize,
45 public_value_index: usize,
46 },
47 #[error("at pc {pc}, tried to publish {new_value} into index {public_value_index} but already had {existing_value}")]
48 PublicValueNotEqual {
49 pc: u32,
50 public_value_index: usize,
51 existing_value: usize,
52 new_value: usize,
53 },
54 #[error("at pc {pc}, phantom sub-instruction not found for discriminant {}", .discriminant.0)]
55 PhantomNotFound {
56 pc: u32,
57 discriminant: PhantomDiscriminant,
58 },
59 #[error("at pc {pc}, discriminant {}, phantom error: {inner}", .discriminant.0)]
60 Phantom {
61 pc: u32,
62 discriminant: PhantomDiscriminant,
63 inner: eyre::Error,
64 },
65 #[error("program must terminate")]
66 DidNotTerminate,
67 #[error("program exit code {0}")]
68 FailedWithExitCode(u32),
69}
70
71pub trait InstructionExecutor<F> {
72 fn execute(
75 &mut self,
76 memory: &mut MemoryController<F>,
77 instruction: &Instruction<F>,
78 from_state: ExecutionState<u32>,
79 ) -> Result<ExecutionState<u32>>;
80
81 fn get_opcode_name(&self, opcode: usize) -> String;
84}
85
86impl<F, C: InstructionExecutor<F>> InstructionExecutor<F> for RefCell<C> {
87 fn execute(
88 &mut self,
89 memory: &mut MemoryController<F>,
90 instruction: &Instruction<F>,
91 prev_state: ExecutionState<u32>,
92 ) -> Result<ExecutionState<u32>> {
93 self.borrow_mut().execute(memory, instruction, prev_state)
94 }
95
96 fn get_opcode_name(&self, opcode: usize) -> String {
97 self.borrow().get_opcode_name(opcode)
98 }
99}
100
101impl<F, C: InstructionExecutor<F>> InstructionExecutor<F> for Rc<RefCell<C>> {
102 fn execute(
103 &mut self,
104 memory: &mut MemoryController<F>,
105 instruction: &Instruction<F>,
106 prev_state: ExecutionState<u32>,
107 ) -> Result<ExecutionState<u32>> {
108 self.borrow_mut().execute(memory, instruction, prev_state)
109 }
110
111 fn get_opcode_name(&self, opcode: usize) -> String {
112 self.borrow().get_opcode_name(opcode)
113 }
114}
115
116#[repr(C)]
117#[derive(Clone, Copy, Debug, PartialEq, Default, AlignedBorrow, Serialize, Deserialize)]
118pub struct ExecutionState<T> {
119 pub pc: T,
120 pub timestamp: T,
121}
122
123#[derive(Clone, Copy, Debug)]
124pub struct ExecutionBus {
125 pub inner: PermutationCheckBus,
126}
127
128impl ExecutionBus {
129 pub const fn new(index: BusIndex) -> Self {
130 Self {
131 inner: PermutationCheckBus::new(index),
132 }
133 }
134
135 #[inline(always)]
136 pub fn index(&self) -> BusIndex {
137 self.inner.index
138 }
139}
140
141#[derive(Copy, Clone, Debug)]
142pub struct ExecutionBridge {
143 execution_bus: ExecutionBus,
144 program_bus: ProgramBus,
145}
146
147pub struct ExecutionBridgeInteractor<AB: InteractionBuilder> {
148 execution_bus: ExecutionBus,
149 program_bus: ProgramBus,
150 opcode: AB::Expr,
151 operands: Vec<AB::Expr>,
152 from_state: ExecutionState<AB::Expr>,
153 to_state: ExecutionState<AB::Expr>,
154}
155
156pub enum PcIncOrSet<T> {
157 Inc(T),
158 Set(T),
159}
160
161impl<T> ExecutionState<T> {
162 pub fn new(pc: impl Into<T>, timestamp: impl Into<T>) -> Self {
163 Self {
164 pc: pc.into(),
165 timestamp: timestamp.into(),
166 }
167 }
168
169 #[allow(clippy::should_implement_trait)]
170 pub fn from_iter<I: Iterator<Item = T>>(iter: &mut I) -> Self {
171 let mut next = || iter.next().unwrap();
172 Self {
173 pc: next(),
174 timestamp: next(),
175 }
176 }
177
178 pub fn flatten(self) -> [T; 2] {
179 [self.pc, self.timestamp]
180 }
181
182 pub fn get_width() -> usize {
183 2
184 }
185
186 pub fn map<U: Clone, F: Fn(T) -> U>(self, function: F) -> ExecutionState<U> {
187 ExecutionState::from_iter(&mut self.flatten().map(function).into_iter())
188 }
189}
190
191impl ExecutionBus {
192 pub fn execute_and_increment_pc<AB: InteractionBuilder>(
194 &self,
195 builder: &mut AB,
196 enabled: impl Into<AB::Expr>,
197 prev_state: ExecutionState<AB::Expr>,
198 timestamp_change: impl Into<AB::Expr>,
199 ) {
200 let next_state = ExecutionState {
201 pc: prev_state.pc.clone() + AB::F::ONE,
202 timestamp: prev_state.timestamp.clone() + timestamp_change.into(),
203 };
204 self.execute(builder, enabled, prev_state, next_state);
205 }
206
207 pub fn execute<AB: InteractionBuilder>(
209 &self,
210 builder: &mut AB,
211 enabled: impl Into<AB::Expr>,
212 prev_state: ExecutionState<impl Into<AB::Expr>>,
213 next_state: ExecutionState<impl Into<AB::Expr>>,
214 ) {
215 let enabled = enabled.into();
216 self.inner.receive(
217 builder,
218 [prev_state.pc.into(), prev_state.timestamp.into()],
219 enabled.clone(),
220 );
221 self.inner.send(
222 builder,
223 [next_state.pc.into(), next_state.timestamp.into()],
224 enabled,
225 );
226 }
227}
228
229impl ExecutionBridge {
230 pub fn new(execution_bus: ExecutionBus, program_bus: ProgramBus) -> Self {
231 Self {
232 execution_bus,
233 program_bus,
234 }
235 }
236
237 pub fn execute_and_increment_or_set_pc<AB: InteractionBuilder>(
239 &self,
240 opcode: impl Into<AB::Expr>,
241 operands: impl IntoIterator<Item = impl Into<AB::Expr>>,
242 from_state: ExecutionState<impl Into<AB::Expr> + Clone>,
243 timestamp_change: impl Into<AB::Expr>,
244 pc_kind: impl Into<PcIncOrSet<AB::Expr>>,
245 ) -> ExecutionBridgeInteractor<AB> {
246 let to_state = ExecutionState {
247 pc: match pc_kind.into() {
248 PcIncOrSet::Set(to_pc) => to_pc,
249 PcIncOrSet::Inc(pc_inc) => from_state.pc.clone().into() + pc_inc,
250 },
251 timestamp: from_state.timestamp.clone().into() + timestamp_change.into(),
252 };
253 self.execute(opcode, operands, from_state, to_state)
254 }
255
256 pub fn execute_and_increment_pc<AB: InteractionBuilder>(
257 &self,
258 opcode: impl Into<AB::Expr>,
259 operands: impl IntoIterator<Item = impl Into<AB::Expr>>,
260 from_state: ExecutionState<impl Into<AB::Expr> + Clone>,
261 timestamp_change: impl Into<AB::Expr>,
262 ) -> ExecutionBridgeInteractor<AB> {
263 let to_state = ExecutionState {
264 pc: from_state.pc.clone().into() + AB::Expr::from_canonical_u32(DEFAULT_PC_STEP),
265 timestamp: from_state.timestamp.clone().into() + timestamp_change.into(),
266 };
267 self.execute(opcode, operands, from_state, to_state)
268 }
269
270 pub fn execute<AB: InteractionBuilder>(
271 &self,
272 opcode: impl Into<AB::Expr>,
273 operands: impl IntoIterator<Item = impl Into<AB::Expr>>,
274 from_state: ExecutionState<impl Into<AB::Expr> + Clone>,
275 to_state: ExecutionState<impl Into<AB::Expr>>,
276 ) -> ExecutionBridgeInteractor<AB> {
277 ExecutionBridgeInteractor {
278 execution_bus: self.execution_bus,
279 program_bus: self.program_bus,
280 opcode: opcode.into(),
281 operands: operands.into_iter().map(Into::into).collect(),
282 from_state: from_state.map(Into::into),
283 to_state: to_state.map(Into::into),
284 }
285 }
286}
287
288impl<AB: InteractionBuilder> ExecutionBridgeInteractor<AB> {
289 pub fn eval(self, builder: &mut AB, enabled: impl Into<AB::Expr>) {
291 let enabled = enabled.into();
292
293 self.program_bus.lookup_instruction(
295 builder,
296 self.from_state.pc.clone(),
297 self.opcode,
298 self.operands,
299 enabled.clone(),
300 );
301
302 self.execution_bus
303 .execute(builder, enabled, self.from_state, self.to_state);
304 }
305}
306
307impl<T: FieldAlgebra> From<(u32, Option<T>)> for PcIncOrSet<T> {
308 fn from((pc_inc, to_pc): (u32, Option<T>)) -> Self {
309 match to_pc {
310 None => PcIncOrSet::Inc(T::from_canonical_u32(pc_inc)),
311 Some(to_pc) => PcIncOrSet::Set(to_pc),
312 }
313 }
314}
315
316pub trait PhantomSubExecutor<F>: Send {
324 fn phantom_execute(
325 &mut self,
326 memory: &MemoryController<F>,
327 streams: &mut Streams<F>,
328 discriminant: PhantomDiscriminant,
329 a: F,
330 b: F,
331 c_upper: u16,
332 ) -> eyre::Result<()>;
333}