openvm_rv32im_circuit/hintstore/
execution.rs1use std::{
2 borrow::{Borrow, BorrowMut},
3 mem::size_of,
4};
5
6use openvm_circuit::{arch::*, system::memory::online::GuestMemory};
7use openvm_circuit_primitives_derive::AlignedBytesBorrow;
8use openvm_instructions::{
9 instruction::Instruction,
10 program::DEFAULT_PC_STEP,
11 riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS, RV32_REGISTER_NUM_LIMBS},
12 LocalOpcode,
13};
14use openvm_rv32im_transpiler::{
15 Rv32HintStoreOpcode,
16 Rv32HintStoreOpcode::{HINT_BUFFER, HINT_STOREW},
17 MAX_HINT_BUFFER_WORDS,
18};
19use openvm_stark_backend::p3_field::PrimeField32;
20
21use super::Rv32HintStoreExecutor;
22
23#[derive(AlignedBytesBorrow, Clone)]
24#[repr(C)]
25struct HintStorePreCompute {
26 c: u32,
27 a: u8,
28 b: u8,
29}
30
31impl Rv32HintStoreExecutor {
32 #[inline(always)]
33 fn pre_compute_impl<F: PrimeField32>(
34 &self,
35 pc: u32,
36 inst: &Instruction<F>,
37 data: &mut HintStorePreCompute,
38 ) -> Result<Rv32HintStoreOpcode, StaticProgramError> {
39 let &Instruction {
40 opcode,
41 a,
42 b,
43 c,
44 d,
45 e,
46 ..
47 } = inst;
48 if d.as_canonical_u32() != RV32_REGISTER_AS || e.as_canonical_u32() != RV32_MEMORY_AS {
49 return Err(StaticProgramError::InvalidInstruction(pc));
50 }
51 *data = {
52 HintStorePreCompute {
53 c: c.as_canonical_u32(),
54 a: a.as_canonical_u32() as u8,
55 b: b.as_canonical_u32() as u8,
56 }
57 };
58 Ok(Rv32HintStoreOpcode::from_usize(
59 opcode.local_opcode_idx(self.offset),
60 ))
61 }
62}
63
64macro_rules! dispatch {
65 ($execute_impl:ident, $local_opcode:ident) => {
66 match $local_opcode {
67 HINT_STOREW => Ok($execute_impl::<_, _, true>),
68 HINT_BUFFER => Ok($execute_impl::<_, _, false>),
69 }
70 };
71}
72
73impl<F> InterpreterExecutor<F> for Rv32HintStoreExecutor
74where
75 F: PrimeField32,
76{
77 #[inline(always)]
78 fn pre_compute_size(&self) -> usize {
79 size_of::<HintStorePreCompute>()
80 }
81
82 #[cfg(not(feature = "tco"))]
83 fn pre_compute<Ctx: ExecutionCtxTrait>(
84 &self,
85 pc: u32,
86 inst: &Instruction<F>,
87 data: &mut [u8],
88 ) -> Result<ExecuteFunc<F, Ctx>, StaticProgramError> {
89 let pre_compute: &mut HintStorePreCompute = data.borrow_mut();
90 let local_opcode = self.pre_compute_impl(pc, inst, pre_compute)?;
91 dispatch!(execute_e1_handler, local_opcode)
92 }
93
94 #[cfg(feature = "tco")]
95 fn handler<Ctx>(
96 &self,
97 pc: u32,
98 inst: &Instruction<F>,
99 data: &mut [u8],
100 ) -> Result<Handler<F, Ctx>, StaticProgramError>
101 where
102 Ctx: ExecutionCtxTrait,
103 {
104 let pre_compute: &mut HintStorePreCompute = data.borrow_mut();
105 let local_opcode = self.pre_compute_impl(pc, inst, pre_compute)?;
106 dispatch!(execute_e1_handler, local_opcode)
107 }
108}
109
110#[cfg(feature = "aot")]
111impl<F> AotExecutor<F> for Rv32HintStoreExecutor where F: PrimeField32 {}
112
113impl<F> InterpreterMeteredExecutor<F> for Rv32HintStoreExecutor
114where
115 F: PrimeField32,
116{
117 fn metered_pre_compute_size(&self) -> usize {
118 size_of::<E2PreCompute<HintStorePreCompute>>()
119 }
120
121 #[cfg(not(feature = "tco"))]
122 fn metered_pre_compute<Ctx>(
123 &self,
124 chip_idx: usize,
125 pc: u32,
126 inst: &Instruction<F>,
127 data: &mut [u8],
128 ) -> Result<ExecuteFunc<F, Ctx>, StaticProgramError>
129 where
130 Ctx: MeteredExecutionCtxTrait,
131 {
132 let pre_compute: &mut E2PreCompute<HintStorePreCompute> = data.borrow_mut();
133 pre_compute.chip_idx = chip_idx as u32;
134 let local_opcode = self.pre_compute_impl(pc, inst, &mut pre_compute.data)?;
135 dispatch!(execute_e2_handler, local_opcode)
136 }
137
138 #[cfg(feature = "tco")]
139 fn metered_handler<Ctx>(
140 &self,
141 chip_idx: usize,
142 pc: u32,
143 inst: &Instruction<F>,
144 data: &mut [u8],
145 ) -> Result<Handler<F, Ctx>, StaticProgramError>
146 where
147 Ctx: MeteredExecutionCtxTrait,
148 {
149 let pre_compute: &mut E2PreCompute<HintStorePreCompute> = data.borrow_mut();
150 pre_compute.chip_idx = chip_idx as u32;
151 let local_opcode = self.pre_compute_impl(pc, inst, &mut pre_compute.data)?;
152 dispatch!(execute_e2_handler, local_opcode)
153 }
154}
155
156#[cfg(feature = "aot")]
157impl<F> AotMeteredExecutor<F> for Rv32HintStoreExecutor where F: PrimeField32 {}
158#[inline(always)]
160unsafe fn execute_e12_impl<F: PrimeField32, CTX: ExecutionCtxTrait, const IS_HINT_STOREW: bool>(
161 pre_compute: &HintStorePreCompute,
162 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
163) -> Result<u32, ExecutionError> {
164 let pc = exec_state.pc();
165 let mem_ptr_limbs = exec_state.vm_read::<u8, 4>(RV32_REGISTER_AS, pre_compute.b as u32);
166 let mem_ptr = u32::from_le_bytes(mem_ptr_limbs);
167
168 let num_words = if IS_HINT_STOREW {
169 1
170 } else {
171 let num_words_limbs = exec_state.vm_read::<u8, 4>(RV32_REGISTER_AS, pre_compute.a as u32);
172 u32::from_le_bytes(num_words_limbs)
173 };
174 if num_words == 0 {
176 return Err(ExecutionError::HintBufferZeroWords { pc });
177 }
178 if num_words > MAX_HINT_BUFFER_WORDS as u32 {
179 return Err(ExecutionError::HintBufferTooLarge {
180 pc,
181 num_words,
182 max_hint_buffer_words: MAX_HINT_BUFFER_WORDS as u32,
183 });
184 }
185
186 if exec_state.streams.hint_stream.len() < RV32_REGISTER_NUM_LIMBS * num_words as usize {
187 let err = ExecutionError::HintOutOfBounds { pc };
188 return Err(err);
189 }
190
191 for word_index in 0..num_words {
192 let data: [u8; RV32_REGISTER_NUM_LIMBS] = std::array::from_fn(|_| {
193 exec_state
194 .streams
195 .hint_stream
196 .pop_front()
197 .unwrap()
198 .as_canonical_u32() as u8
199 });
200 exec_state.vm_write(
201 RV32_MEMORY_AS,
202 mem_ptr + (RV32_REGISTER_NUM_LIMBS as u32 * word_index),
203 &data,
204 );
205 }
206
207 exec_state.set_pc(pc.wrapping_add(DEFAULT_PC_STEP));
208 Ok(num_words)
209}
210
211#[create_handler]
212#[inline(always)]
213unsafe fn execute_e1_impl<F: PrimeField32, CTX: ExecutionCtxTrait, const IS_HINT_STOREW: bool>(
214 pre_compute: *const u8,
215 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
216) -> Result<(), ExecutionError> {
217 let pre_compute: &HintStorePreCompute =
218 std::slice::from_raw_parts(pre_compute, size_of::<HintStorePreCompute>()).borrow();
219 execute_e12_impl::<F, CTX, IS_HINT_STOREW>(pre_compute, exec_state)?;
220 Ok(())
221}
222
223#[create_handler]
224#[inline(always)]
225unsafe fn execute_e2_impl<
226 F: PrimeField32,
227 CTX: MeteredExecutionCtxTrait,
228 const IS_HINT_STOREW: bool,
229>(
230 pre_compute: *const u8,
231 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
232) -> Result<(), ExecutionError> {
233 let pre_compute: &E2PreCompute<HintStorePreCompute> =
234 std::slice::from_raw_parts(pre_compute, size_of::<E2PreCompute<HintStorePreCompute>>())
235 .borrow();
236 let height_delta = execute_e12_impl::<F, CTX, IS_HINT_STOREW>(&pre_compute.data, exec_state)?;
237 exec_state
238 .ctx
239 .on_height_change(pre_compute.chip_idx as usize, height_delta);
240 Ok(())
241}