1use openvm_algebra_transpiler::{Fp2Opcode, Rv32ModularArithmeticOpcode};
10use openvm_circuit::{
11 arch::{ExecutionError, PreflightExecutor, RecordArena, VmStateMut},
12 system::memory::online::TracingMemory,
13};
14use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP};
15use openvm_mod_circuit_builder::{
16 run_field_expression_precomputed, FieldExpressionCoreRecordMut, FieldExpressionRecordLayout,
17};
18use openvm_rv32_adapters::Rv32VecHeapAdapterExecutor;
19use openvm_stark_backend::p3_field::PrimeField32;
20use strum::EnumCount;
21
22use crate::{
23 fields::{field_operation, fp2_operation, FieldType, Operation},
24 FieldExprVecHeapExecutor,
25};
26
27macro_rules! dispatch_field_op {
30 ($fn:ident, $field_type:expr, $op:expr, $read_data:expr,
32 [$(($curve:ident, $operation:ident)),* $(,)?]) => {
33 match ($field_type, $op) {
34 $(
35 (FieldType::$curve, Operation::$operation) => $fn::<
36 { FieldType::$curve as u8 },
37 BLOCKS,
38 BLOCK_SIZE,
39 { Operation::$operation as u8 },
40 >($read_data),
41 )*
42 #[allow(unreachable_patterns)]
43 _ => unreachable!(),
44 }
45 };
46}
47
48#[inline]
51fn compute_output_fast<const BLOCKS: usize, const BLOCK_SIZE: usize, const IS_FP2: bool>(
52 field_type: Option<FieldType>,
53 operation: Option<Operation>,
54 read_data: [[[u8; BLOCK_SIZE]; BLOCKS]; 2],
55) -> Option<[[u8; BLOCK_SIZE]; BLOCKS]> {
56 let field_type = field_type?;
57 let op = operation?;
58
59 if IS_FP2 {
60 Some(compute_fp2_fast::<BLOCKS, BLOCK_SIZE>(
61 field_type, op, read_data,
62 ))
63 } else {
64 Some(compute_field_fast::<BLOCKS, BLOCK_SIZE>(
65 field_type, op, read_data,
66 ))
67 }
68}
69
70#[inline]
72fn compute_field_fast<const BLOCKS: usize, const BLOCK_SIZE: usize>(
73 field_type: FieldType,
74 op: Operation,
75 read_data: [[[u8; BLOCK_SIZE]; BLOCKS]; 2],
76) -> [[u8; BLOCK_SIZE]; BLOCKS] {
77 dispatch_field_op!(
78 field_operation,
79 field_type,
80 op,
81 read_data,
82 [
83 (K256Coordinate, Add),
84 (K256Coordinate, Sub),
85 (K256Coordinate, Mul),
86 (K256Coordinate, Div),
87 (K256Scalar, Add),
88 (K256Scalar, Sub),
89 (K256Scalar, Mul),
90 (K256Scalar, Div),
91 (P256Coordinate, Add),
92 (P256Coordinate, Sub),
93 (P256Coordinate, Mul),
94 (P256Coordinate, Div),
95 (P256Scalar, Add),
96 (P256Scalar, Sub),
97 (P256Scalar, Mul),
98 (P256Scalar, Div),
99 (BN254Coordinate, Add),
100 (BN254Coordinate, Sub),
101 (BN254Coordinate, Mul),
102 (BN254Coordinate, Div),
103 (BN254Scalar, Add),
104 (BN254Scalar, Sub),
105 (BN254Scalar, Mul),
106 (BN254Scalar, Div),
107 (BLS12_381Coordinate, Add),
108 (BLS12_381Coordinate, Sub),
109 (BLS12_381Coordinate, Mul),
110 (BLS12_381Coordinate, Div),
111 (BLS12_381Scalar, Add),
112 (BLS12_381Scalar, Sub),
113 (BLS12_381Scalar, Mul),
114 (BLS12_381Scalar, Div),
115 ]
116 )
117}
118
119#[inline]
121fn compute_fp2_fast<const BLOCKS: usize, const BLOCK_SIZE: usize>(
122 field_type: FieldType,
123 op: Operation,
124 read_data: [[[u8; BLOCK_SIZE]; BLOCKS]; 2],
125) -> [[u8; BLOCK_SIZE]; BLOCKS] {
126 dispatch_field_op!(
127 fp2_operation,
128 field_type,
129 op,
130 read_data,
131 [
132 (BN254Coordinate, Add),
133 (BN254Coordinate, Sub),
134 (BN254Coordinate, Mul),
135 (BN254Coordinate, Div),
136 (BLS12_381Coordinate, Add),
137 (BLS12_381Coordinate, Sub),
138 (BLS12_381Coordinate, Mul),
139 (BLS12_381Coordinate, Div),
140 ]
141 )
142}
143
144#[inline]
146fn local_opcode_to_modular_operation(local_opcode: usize) -> Option<Operation> {
147 let base_opcode = local_opcode % Rv32ModularArithmeticOpcode::COUNT;
148 match base_opcode {
149 x if x == Rv32ModularArithmeticOpcode::ADD as usize => Some(Operation::Add),
150 x if x == Rv32ModularArithmeticOpcode::SUB as usize => Some(Operation::Sub),
151 x if x == Rv32ModularArithmeticOpcode::MUL as usize => Some(Operation::Mul),
152 x if x == Rv32ModularArithmeticOpcode::DIV as usize => Some(Operation::Div),
153 _ => None,
154 }
155}
156
157#[inline]
159fn local_opcode_to_fp2_operation(local_opcode: usize) -> Option<Operation> {
160 let base_opcode = local_opcode % Fp2Opcode::COUNT;
161 match base_opcode {
162 x if x == Fp2Opcode::ADD as usize => Some(Operation::Add),
163 x if x == Fp2Opcode::SUB as usize => Some(Operation::Sub),
164 x if x == Fp2Opcode::MUL as usize => Some(Operation::Mul),
165 x if x == Fp2Opcode::DIV as usize => Some(Operation::Div),
166 _ => None,
167 }
168}
169
170impl<F, RA, const BLOCKS: usize, const BLOCK_SIZE: usize, const IS_FP2: bool>
171 PreflightExecutor<F, RA> for FieldExprVecHeapExecutor<BLOCKS, BLOCK_SIZE, IS_FP2>
172where
173 F: PrimeField32,
174 for<'buf> RA: RecordArena<
175 'buf,
176 FieldExpressionRecordLayout<
177 F,
178 Rv32VecHeapAdapterExecutor<2, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE>,
179 >,
180 (
181 <Rv32VecHeapAdapterExecutor<2, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE> as openvm_circuit::arch::AdapterTraceExecutor<F>>::RecordMut<'buf>,
182 FieldExpressionCoreRecordMut<'buf>,
183 ),
184 >,
185{
186 fn execute(
187 &self,
188 state: VmStateMut<F, TracingMemory, RA>,
189 instruction: &Instruction<F>,
190 ) -> Result<(), ExecutionError> {
191 use openvm_circuit::arch::AdapterTraceExecutor;
192
193 let (mut adapter_record, mut core_record) =
194 state.ctx.alloc(self.inner.get_record_layout());
195
196 <Rv32VecHeapAdapterExecutor<2, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE> as AdapterTraceExecutor<F>>::start(
197 *state.pc,
198 state.memory,
199 &mut adapter_record,
200 );
201
202 let read_data: [[[u8; BLOCK_SIZE]; BLOCKS]; 2] = self
203 .inner
204 .adapter()
205 .read(state.memory, instruction, &mut adapter_record);
206
207 let local_opcode = instruction.opcode.local_opcode_idx(self.inner.offset);
208
209 core_record
210 .fill_from_execution_data(local_opcode as u8, read_data.as_flattened().as_flattened());
211
212 let operation = if IS_FP2 {
214 local_opcode_to_fp2_operation(local_opcode)
215 } else {
216 local_opcode_to_modular_operation(local_opcode)
217 };
218
219 let output: [[u8; BLOCK_SIZE]; BLOCKS] =
220 if let Some(output) = compute_output_fast::<BLOCKS, BLOCK_SIZE, IS_FP2>(
221 self.cached_field_type,
222 operation,
223 read_data,
224 ) {
225 output
226 } else {
227 let flag_idx = self
229 .inner
230 .local_opcode_idx
231 .iter()
232 .position(|&idx| idx == local_opcode)
233 .and_then(|pos| {
234 if pos < self.inner.opcode_flag_idx.len() {
235 Some(self.inner.opcode_flag_idx[pos])
236 } else {
237 None
238 }
239 })
240 .unwrap_or_else(|| self.inner.expr.num_flags());
241
242 run_field_expression_precomputed::<true>(
243 &self.inner.expr,
244 flag_idx,
245 read_data.as_flattened().as_flattened(),
246 )
247 .into()
248 };
249
250 self.inner.adapter().write(
251 state.memory,
252 instruction,
253 output,
254 &mut adapter_record,
255 );
256
257 *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP);
258 Ok(())
259 }
260
261 fn get_opcode_name(&self, _opcode: usize) -> String {
262 self.inner.name.clone()
263 }
264}