openvm_sha2_circuit/sha2_chips/
execution.rs1use std::borrow::{Borrow, BorrowMut};
2
3use openvm_circuit::{arch::*, system::memory::online::GuestMemory};
4use openvm_circuit_primitives::AlignedBytesBorrow;
5use openvm_instructions::{
6 instruction::Instruction,
7 program::DEFAULT_PC_STEP,
8 riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS},
9 LocalOpcode,
10};
11use openvm_stark_backend::p3_field::PrimeField32;
12
13use super::{Sha2Config, Sha2VmExecutor, SHA2_READ_SIZE};
14use crate::SHA2_WRITE_SIZE;
15
16#[derive(AlignedBytesBorrow, Clone)]
17#[repr(C)]
18struct Sha2PreCompute {
19 a: u8,
20 b: u8,
21 c: u8,
22}
23
24impl<F: PrimeField32, C: Sha2Config> InterpreterExecutor<F> for Sha2VmExecutor<C> {
25 fn pre_compute_size(&self) -> usize {
26 size_of::<Sha2PreCompute>()
27 }
28
29 #[cfg(not(feature = "tco"))]
30 fn pre_compute<Ctx>(
31 &self,
32 pc: u32,
33 inst: &Instruction<F>,
34 data: &mut [u8],
35 ) -> Result<ExecuteFunc<F, Ctx>, StaticProgramError>
36 where
37 Ctx: ExecutionCtxTrait,
38 {
39 let data: &mut Sha2PreCompute = data.borrow_mut();
40 self.pre_compute_impl(pc, inst, data)?;
41 Ok(execute_e1_impl::<_, _, C>)
42 }
43
44 #[cfg(feature = "tco")]
45 fn handler<Ctx>(
46 &self,
47 pc: u32,
48 inst: &Instruction<F>,
49 data: &mut [u8],
50 ) -> Result<Handler<F, Ctx>, StaticProgramError>
51 where
52 Ctx: ExecutionCtxTrait,
53 {
54 let data: &mut Sha2PreCompute = data.borrow_mut();
55 self.pre_compute_impl(pc, inst, data)?;
56 Ok(execute_e1_handler::<_, _, C>)
57 }
58}
59
60impl<F: PrimeField32, C: Sha2Config> InterpreterMeteredExecutor<F> for Sha2VmExecutor<C> {
61 fn metered_pre_compute_size(&self) -> usize {
62 size_of::<E2PreCompute<Sha2PreCompute>>()
63 }
64
65 #[cfg(not(feature = "tco"))]
66 fn metered_pre_compute<Ctx>(
67 &self,
68 chip_idx: usize,
69 pc: u32,
70 inst: &Instruction<F>,
71 data: &mut [u8],
72 ) -> Result<ExecuteFunc<F, Ctx>, StaticProgramError>
73 where
74 Ctx: MeteredExecutionCtxTrait,
75 {
76 let data: &mut E2PreCompute<Sha2PreCompute> = data.borrow_mut();
77 data.chip_idx = chip_idx as u32;
78 self.pre_compute_impl(pc, inst, &mut data.data)?;
79 Ok(execute_e2_impl::<_, _, C>)
80 }
81
82 #[cfg(feature = "tco")]
83 fn metered_handler<Ctx>(
84 &self,
85 chip_idx: usize,
86 pc: u32,
87 inst: &Instruction<F>,
88 data: &mut [u8],
89 ) -> Result<Handler<F, Ctx>, StaticProgramError>
90 where
91 Ctx: MeteredExecutionCtxTrait,
92 {
93 let data: &mut E2PreCompute<Sha2PreCompute> = data.borrow_mut();
94 data.chip_idx = chip_idx as u32;
95 self.pre_compute_impl(pc, inst, &mut data.data)?;
96 Ok(execute_e2_handler::<_, _, C>)
97 }
98}
99
100#[inline(always)]
101unsafe fn execute_e12_impl<
102 F: PrimeField32,
103 C: Sha2Config,
104 CTX: ExecutionCtxTrait,
105 const IS_E1: bool,
106>(
107 pre_compute: &Sha2PreCompute,
108 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
109) -> u32 {
110 let dst = exec_state.vm_read(RV32_REGISTER_AS, pre_compute.a as u32);
111 let state = exec_state.vm_read(RV32_REGISTER_AS, pre_compute.b as u32);
112 let input = exec_state.vm_read(RV32_REGISTER_AS, pre_compute.c as u32);
113 let dst_u32 = u32::from_le_bytes(dst);
114 let state_u32 = u32::from_le_bytes(state);
115 let input_u32 = u32::from_le_bytes(input);
116
117 let mut state_data = Vec::with_capacity(C::STATE_BYTES);
119 for i in 0..C::STATE_READS {
120 state_data.extend_from_slice(&exec_state.vm_read::<u8, SHA2_READ_SIZE>(
121 RV32_MEMORY_AS,
122 state_u32 + (i * SHA2_READ_SIZE) as u32,
123 ));
124 }
125 let mut input_block = Vec::with_capacity(C::BLOCK_BYTES);
126 for i in 0..C::BLOCK_READS {
127 input_block.extend_from_slice(&exec_state.vm_read::<u8, SHA2_READ_SIZE>(
128 RV32_MEMORY_AS,
129 input_u32 + (i * SHA2_READ_SIZE) as u32,
130 ));
131 }
132
133 C::compress(&mut state_data, &input_block);
134
135 for i in 0..C::STATE_WRITES {
136 exec_state.vm_write::<u8, SHA2_WRITE_SIZE>(
137 RV32_MEMORY_AS,
138 dst_u32 + (i * SHA2_WRITE_SIZE) as u32,
139 &state_data[i * SHA2_WRITE_SIZE..(i + 1) * SHA2_WRITE_SIZE]
140 .try_into()
141 .unwrap(),
142 );
143 }
144
145 let pc = exec_state.pc();
146 exec_state.set_pc(pc.wrapping_add(DEFAULT_PC_STEP));
147
148 1 }
150
151#[create_handler]
152#[inline(always)]
153unsafe fn execute_e1_impl<F: PrimeField32, CTX: ExecutionCtxTrait, C: Sha2Config>(
154 pre_compute: *const u8,
155 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
156) {
157 let pre_compute: &Sha2PreCompute =
158 std::slice::from_raw_parts(pre_compute, size_of::<Sha2PreCompute>()).borrow();
159 execute_e12_impl::<F, C, CTX, true>(pre_compute, exec_state);
160}
161
162#[create_handler]
163#[inline(always)]
164unsafe fn execute_e2_impl<F: PrimeField32, CTX: MeteredExecutionCtxTrait, C: Sha2Config>(
165 pre_compute: *const u8,
166 exec_state: &mut VmExecState<F, GuestMemory, CTX>,
167) {
168 let pre_compute: &E2PreCompute<Sha2PreCompute> =
169 std::slice::from_raw_parts(pre_compute, size_of::<E2PreCompute<Sha2PreCompute>>()).borrow();
170
171 let main_air_idx = pre_compute.chip_idx as usize;
172
173 let height = execute_e12_impl::<F, C, CTX, false>(&pre_compute.data, exec_state);
175 exec_state.ctx.on_height_change(main_air_idx, height);
176
177 let block_hasher_air_idx = main_air_idx + 1;
181
182 exec_state
184 .ctx
185 .on_height_change(block_hasher_air_idx, C::ROWS_PER_BLOCK as u32);
186}
187
188#[cfg(feature = "aot")]
189impl<F: PrimeField32, C: Sha2Config> AotExecutor<F> for Sha2VmExecutor<C> {}
190
191#[cfg(feature = "aot")]
192impl<F: PrimeField32, C: Sha2Config> AotMeteredExecutor<F> for Sha2VmExecutor<C> {}
193
194impl<C: Sha2Config> Sha2VmExecutor<C> {
195 fn pre_compute_impl<F: PrimeField32>(
196 &self,
197 pc: u32,
198 inst: &Instruction<F>,
199 data: &mut Sha2PreCompute,
200 ) -> Result<(), StaticProgramError> {
201 let Instruction {
202 opcode,
203 a,
204 b,
205 c,
206 d,
207 e,
208 ..
209 } = inst;
210 let e_u32 = e.as_canonical_u32();
211 if d.as_canonical_u32() != RV32_REGISTER_AS || e_u32 != RV32_MEMORY_AS {
212 return Err(StaticProgramError::InvalidInstruction(pc));
213 }
214 *data = Sha2PreCompute {
215 a: a.as_canonical_u32() as u8,
216 b: b.as_canonical_u32() as u8,
217 c: c.as_canonical_u32() as u8,
218 };
219 assert_eq!(&C::OPCODE.global_opcode(), opcode);
220 Ok(())
221 }
222}