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