openvm_rv32im_circuit/mul/
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_REGISTER_AS, RV32_REGISTER_NUM_LIMBS},
12 LocalOpcode,
13};
14use openvm_rv32im_transpiler::MulOpcode;
15use openvm_stark_backend::p3_field::PrimeField32;
16
17use crate::MultiplicationExecutor;
18
19#[derive(AlignedBytesBorrow, Clone)]
20#[repr(C)]
21struct MultiPreCompute {
22 a: u8,
23 b: u8,
24 c: u8,
25}
26
27impl<A, const LIMB_BITS: usize> MultiplicationExecutor<A, { RV32_REGISTER_NUM_LIMBS }, LIMB_BITS> {
28 fn pre_compute_impl<F: PrimeField32>(
29 &self,
30 pc: u32,
31 inst: &Instruction<F>,
32 data: &mut MultiPreCompute,
33 ) -> Result<(), StaticProgramError> {
34 assert_eq!(
35 MulOpcode::from_usize(inst.opcode.local_opcode_idx(self.offset)),
36 MulOpcode::MUL
37 );
38 if inst.d.as_canonical_u32() != RV32_REGISTER_AS {
39 return Err(StaticProgramError::InvalidInstruction(pc));
40 }
41
42 *data = MultiPreCompute {
43 a: inst.a.as_canonical_u32() as u8,
44 b: inst.b.as_canonical_u32() as u8,
45 c: inst.c.as_canonical_u32() as u8,
46 };
47 Ok(())
48 }
49}
50
51impl<F, A, const LIMB_BITS: usize> Executor<F>
52 for MultiplicationExecutor<A, { RV32_REGISTER_NUM_LIMBS }, LIMB_BITS>
53where
54 F: PrimeField32,
55{
56 fn pre_compute_size(&self) -> usize {
57 size_of::<MultiPreCompute>()
58 }
59 fn pre_compute<Ctx>(
60 &self,
61 pc: u32,
62 inst: &Instruction<F>,
63 data: &mut [u8],
64 ) -> Result<ExecuteFunc<F, Ctx>, StaticProgramError>
65 where
66 Ctx: ExecutionCtxTrait,
67 {
68 let pre_compute: &mut MultiPreCompute = data.borrow_mut();
69 self.pre_compute_impl(pc, inst, pre_compute)?;
70 Ok(execute_e1_impl)
71 }
72
73 #[cfg(feature = "tco")]
74 fn handler<Ctx>(
75 &self,
76 pc: u32,
77 inst: &Instruction<F>,
78 data: &mut [u8],
79 ) -> Result<Handler<F, Ctx>, StaticProgramError>
80 where
81 Ctx: ExecutionCtxTrait,
82 {
83 let pre_compute: &mut MultiPreCompute = data.borrow_mut();
84 self.pre_compute_impl(pc, inst, pre_compute)?;
85 Ok(execute_e1_tco_handler)
86 }
87}
88
89impl<F, A, const LIMB_BITS: usize> MeteredExecutor<F>
90 for MultiplicationExecutor<A, { RV32_REGISTER_NUM_LIMBS }, LIMB_BITS>
91where
92 F: PrimeField32,
93{
94 fn metered_pre_compute_size(&self) -> usize {
95 size_of::<E2PreCompute<MultiPreCompute>>()
96 }
97
98 fn metered_pre_compute<Ctx>(
99 &self,
100 chip_idx: usize,
101 pc: u32,
102 inst: &Instruction<F>,
103 data: &mut [u8],
104 ) -> Result<ExecuteFunc<F, Ctx>, StaticProgramError>
105 where
106 Ctx: MeteredExecutionCtxTrait,
107 {
108 let pre_compute: &mut E2PreCompute<MultiPreCompute> = data.borrow_mut();
109 pre_compute.chip_idx = chip_idx as u32;
110 self.pre_compute_impl(pc, inst, &mut pre_compute.data)?;
111 Ok(execute_e2_impl)
112 }
113
114 #[cfg(feature = "tco")]
115 fn metered_handler<Ctx>(
116 &self,
117 chip_idx: usize,
118 pc: u32,
119 inst: &Instruction<F>,
120 data: &mut [u8],
121 ) -> Result<Handler<F, Ctx>, StaticProgramError>
122 where
123 Ctx: MeteredExecutionCtxTrait,
124 {
125 let pre_compute: &mut E2PreCompute<MultiPreCompute> = data.borrow_mut();
126 pre_compute.chip_idx = chip_idx as u32;
127 self.pre_compute_impl(pc, inst, &mut pre_compute.data)?;
128 Ok(execute_e2_tco_handler)
129 }
130}
131
132#[inline(always)]
133unsafe fn execute_e12_impl<F: PrimeField32, CTX: ExecutionCtxTrait>(
134 pre_compute: &MultiPreCompute,
135 vm_state: &mut VmExecState<F, GuestMemory, CTX>,
136) {
137 let rs1: [u8; RV32_REGISTER_NUM_LIMBS] =
138 vm_state.vm_read(RV32_REGISTER_AS, pre_compute.b as u32);
139 let rs2: [u8; RV32_REGISTER_NUM_LIMBS] =
140 vm_state.vm_read(RV32_REGISTER_AS, pre_compute.c as u32);
141 let rs1 = u32::from_le_bytes(rs1);
142 let rs2 = u32::from_le_bytes(rs2);
143 let rd = rs1.wrapping_mul(rs2);
144 vm_state.vm_write(RV32_REGISTER_AS, pre_compute.a as u32, &rd.to_le_bytes());
145
146 vm_state.pc += DEFAULT_PC_STEP;
147 vm_state.instret += 1;
148}
149
150#[create_tco_handler]
151unsafe fn execute_e1_impl<F: PrimeField32, CTX: ExecutionCtxTrait>(
152 pre_compute: &[u8],
153 vm_state: &mut VmExecState<F, GuestMemory, CTX>,
154) {
155 let pre_compute: &MultiPreCompute = pre_compute.borrow();
156 execute_e12_impl(pre_compute, vm_state);
157}
158
159#[create_tco_handler]
160unsafe fn execute_e2_impl<F: PrimeField32, CTX: MeteredExecutionCtxTrait>(
161 pre_compute: &[u8],
162 vm_state: &mut VmExecState<F, GuestMemory, CTX>,
163) {
164 let pre_compute: &E2PreCompute<MultiPreCompute> = pre_compute.borrow();
165 vm_state
166 .ctx
167 .on_height_change(pre_compute.chip_idx as usize, 1);
168 execute_e12_impl(&pre_compute.data, vm_state);
169}