openvm_rv32im_circuit/hintstore/
execution.rs

1use 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/// Return the number of used rows.
153#[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}