openvm_rv32im_circuit/auipc/
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, program::DEFAULT_PC_STEP, riscv::RV32_REGISTER_AS,
10};
11use openvm_stark_backend::p3_field::PrimeField32;
12
13use super::{run_auipc, Rv32AuipcExecutor};
14#[cfg(feature = "aot")]
15use crate::common::*;
16
17#[derive(AlignedBytesBorrow, Clone)]
18#[repr(C)]
19struct AuiPcPreCompute {
20 imm: u32,
21 a: u8,
22}
23
24impl<A> Rv32AuipcExecutor<A> {
25 fn pre_compute_impl<F: PrimeField32>(
26 &self,
27 pc: u32,
28 inst: &Instruction<F>,
29 data: &mut AuiPcPreCompute,
30 ) -> Result<(), StaticProgramError> {
31 let Instruction { a, c: imm, d, .. } = inst;
32 if d.as_canonical_u32() != RV32_REGISTER_AS {
33 return Err(StaticProgramError::InvalidInstruction(pc));
34 }
35 let imm = imm.as_canonical_u32();
36 let data: &mut AuiPcPreCompute = data.borrow_mut();
37 *data = AuiPcPreCompute {
38 imm,
39 a: a.as_canonical_u32() as u8,
40 };
41 Ok(())
42 }
43}
44
45impl<F, A> InterpreterExecutor<F> for Rv32AuipcExecutor<A>
46where
47 F: PrimeField32,
48{
49 #[inline(always)]
50 fn pre_compute_size(&self) -> usize {
51 size_of::<AuiPcPreCompute>()
52 }
53
54 #[cfg(not(feature = "tco"))]
55 #[inline(always)]
56 fn pre_compute<Ctx: ExecutionCtxTrait>(
57 &self,
58 pc: u32,
59 inst: &Instruction<F>,
60 data: &mut [u8],
61 ) -> Result<ExecuteFunc<F, Ctx>, StaticProgramError> {
62 let data: &mut AuiPcPreCompute = data.borrow_mut();
63 self.pre_compute_impl(pc, inst, data)?;
64 Ok(execute_e1_impl)
65 }
66
67 #[cfg(feature = "tco")]
68 fn handler<Ctx>(
69 &self,
70 pc: u32,
71 inst: &Instruction<F>,
72 data: &mut [u8],
73 ) -> Result<Handler<F, Ctx>, StaticProgramError>
74 where
75 Ctx: ExecutionCtxTrait,
76 {
77 let data: &mut AuiPcPreCompute = data.borrow_mut();
78 self.pre_compute_impl(pc, inst, data)?;
79 Ok(execute_e1_handler)
80 }
81}
82
83#[cfg(feature = "aot")]
84impl<F, A> AotExecutor<F> for Rv32AuipcExecutor<A>
85where
86 F: PrimeField32,
87{
88 fn generate_x86_asm(&self, inst: &Instruction<F>, pc: u32) -> Result<String, AotError> {
89 use openvm_instructions::riscv::RV32_CELL_BITS;
90
91 let to_i16 = |c: F| -> i16 {
92 let c_u24 = (c.as_canonical_u64() & 0xFFFFFF) as u32;
93 let c_i24 = ((c_u24 << 8) as i32) >> 8;
94 c_i24 as i16
95 };
96 let mut asm_str = String::new();
97 let a: i16 = to_i16(inst.a);
98 let c: i16 = to_i16(inst.c);
99 let d: i16 = to_i16(inst.d);
100 let rd = pc.wrapping_add((c as u32) << RV32_CELL_BITS);
101
102 if d as u32 != RV32_REGISTER_AS {
103 return Err(AotError::InvalidInstruction);
104 }
105
106 let a_reg = a / 4;
107
108 if let Some(override_reg) = RISCV_TO_X86_OVERRIDE_MAP[a_reg as usize] {
109 asm_str += &format!(" mov {override_reg}, {rd}\n");
110 } else {
111 asm_str += &format!(" mov {REG_A_W}, {rd}\n");
112 asm_str += &gpr_to_xmm(REG_A_W, a_reg as u8);
113 }
114
115 Ok(asm_str)
116 }
117
118 fn is_aot_supported(&self, _inst: &Instruction<F>) -> bool {
119 true
120 }
121}
122
123impl<F, A> InterpreterMeteredExecutor<F> for Rv32AuipcExecutor<A>
124where
125 F: PrimeField32,
126{
127 fn metered_pre_compute_size(&self) -> usize {
128 size_of::<E2PreCompute<AuiPcPreCompute>>()
129 }
130
131 #[cfg(not(feature = "tco"))]
132 fn metered_pre_compute<Ctx>(
133 &self,
134 chip_idx: usize,
135 pc: u32,
136 inst: &Instruction<F>,
137 data: &mut [u8],
138 ) -> Result<ExecuteFunc<F, Ctx>, StaticProgramError>
139 where
140 Ctx: MeteredExecutionCtxTrait,
141 {
142 let data: &mut E2PreCompute<AuiPcPreCompute> = data.borrow_mut();
143 data.chip_idx = chip_idx as u32;
144 self.pre_compute_impl(pc, inst, &mut data.data)?;
145 Ok(execute_e2_impl)
146 }
147
148 #[cfg(feature = "tco")]
149 fn metered_handler<Ctx>(
150 &self,
151 chip_idx: usize,
152 pc: u32,
153 inst: &Instruction<F>,
154 data: &mut [u8],
155 ) -> Result<Handler<F, Ctx>, StaticProgramError>
156 where
157 Ctx: MeteredExecutionCtxTrait,
158 {
159 let data: &mut E2PreCompute<AuiPcPreCompute> = data.borrow_mut();
160 data.chip_idx = chip_idx as u32;
161 self.pre_compute_impl(pc, inst, &mut data.data)?;
162 Ok(execute_e2_handler)
163 }
164}
165
166#[cfg(feature = "aot")]
167impl<F, A> AotMeteredExecutor<F> for Rv32AuipcExecutor<A>
168where
169 F: PrimeField32,
170{
171 fn is_aot_metered_supported(&self, _inst: &Instruction<F>) -> bool {
172 true
173 }
174 fn generate_x86_metered_asm(
175 &self,
176 inst: &Instruction<F>,
177 pc: u32,
178 chip_idx: usize,
179 config: &SystemConfig,
180 ) -> Result<String, AotError> {
181 let mut asm_str = update_height_change_asm(chip_idx, 1)?;
182 asm_str += &update_adapter_heights_asm(config, RV32_REGISTER_AS)?;
184 asm_str += &self.generate_x86_asm(inst, pc)?;
185 Ok(asm_str)
186 }
187}
188
189#[inline(always)]
190unsafe fn execute_e12_impl<F: PrimeField32, CTX: ExecutionCtxTrait>(
191 pre_compute: &AuiPcPreCompute,
192 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
193) {
194 let pc = exec_state.pc();
195 let rd = run_auipc(pc, pre_compute.imm);
196 exec_state.vm_write(RV32_REGISTER_AS, pre_compute.a as u32, &rd);
197
198 exec_state.set_pc(pc.wrapping_add(DEFAULT_PC_STEP));
199}
200
201#[create_handler]
202#[inline(always)]
203unsafe fn execute_e1_impl<F: PrimeField32, CTX: ExecutionCtxTrait>(
204 pre_compute: *const u8,
205 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
206) {
207 let pre_compute: &AuiPcPreCompute =
208 std::slice::from_raw_parts(pre_compute, size_of::<AuiPcPreCompute>()).borrow();
209 execute_e12_impl(pre_compute, exec_state);
210}
211
212#[create_handler]
213#[inline(always)]
214unsafe fn execute_e2_impl<F: PrimeField32, CTX: MeteredExecutionCtxTrait>(
215 pre_compute: *const u8,
216 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
217) {
218 let pre_compute: &E2PreCompute<AuiPcPreCompute> =
219 std::slice::from_raw_parts(pre_compute, size_of::<E2PreCompute<AuiPcPreCompute>>())
220 .borrow();
221 exec_state
222 .ctx
223 .on_height_change(pre_compute.chip_idx as usize, 1);
224 execute_e12_impl(&pre_compute.data, exec_state);
225}