openvm_ecc_circuit/weierstrass_chip/
preflight.rs1use openvm_algebra_circuit::fields::FieldType;
5use openvm_circuit::{
6 arch::{AdapterTraceExecutor, ExecutionError, PreflightExecutor, RecordArena, VmStateMut},
7 system::memory::online::TracingMemory,
8};
9use openvm_ecc_transpiler::Rv32WeierstrassOpcode;
10use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP};
11use openvm_mod_circuit_builder::{
12 run_field_expression_precomputed, FieldExpressionCoreRecordMut, FieldExpressionRecordLayout,
13};
14use openvm_rv32_adapters::Rv32VecHeapAdapterExecutor;
15use openvm_stark_backend::p3_field::PrimeField32;
16use strum::EnumCount;
17
18use super::{
19 add_ne::EcAddNeExecutor,
20 curves::{ec_add_ne, ec_double, CurveType},
21 double::EcDoubleExecutor,
22};
23
24macro_rules! dispatch_enum {
26 ($fn:ident, $val:expr, $read_data:expr,
27 [$(($variant_type:ident :: $variant:ident)),* $(,)?]) => {
28 match $val {
29 $(
30 $variant_type::$variant => $fn::<
31 { $variant_type::$variant as u8 },
32 BLOCKS,
33 BLOCK_SIZE,
34 >($read_data),
35 )*
36 #[allow(unreachable_patterns)]
37 _ => unreachable!(),
38 }
39 };
40}
41
42#[inline]
44fn compute_ec_add_ne_fast<const BLOCKS: usize, const BLOCK_SIZE: usize>(
45 field_type: Option<FieldType>,
46 read_data: [[[u8; BLOCK_SIZE]; BLOCKS]; 2],
47) -> Option<[[u8; BLOCK_SIZE]; BLOCKS]> {
48 let field_type = field_type?;
49
50 Some(match field_type {
51 FieldType::K256Coordinate
52 | FieldType::P256Coordinate
53 | FieldType::BN254Coordinate
54 | FieldType::BLS12_381Coordinate => {
55 dispatch_enum!(
56 ec_add_ne,
57 field_type,
58 read_data,
59 [
60 (FieldType::K256Coordinate),
61 (FieldType::P256Coordinate),
62 (FieldType::BN254Coordinate),
63 (FieldType::BLS12_381Coordinate),
64 ]
65 )
66 }
67 _ => return None,
69 })
70}
71
72#[inline]
74fn compute_ec_double_fast<const BLOCKS: usize, const BLOCK_SIZE: usize>(
75 curve_type: Option<CurveType>,
76 read_data: [[u8; BLOCK_SIZE]; BLOCKS],
77) -> Option<[[u8; BLOCK_SIZE]; BLOCKS]> {
78 let curve_type = curve_type?;
79
80 Some(dispatch_enum!(
81 ec_double,
82 curve_type,
83 read_data,
84 [
85 (CurveType::K256),
86 (CurveType::P256),
87 (CurveType::BN254),
88 (CurveType::BLS12_381),
89 ]
90 ))
91}
92
93#[inline]
95fn is_setup_opcode(local_opcode: usize) -> bool {
96 let base_opcode = local_opcode % Rv32WeierstrassOpcode::COUNT;
97 base_opcode == Rv32WeierstrassOpcode::SETUP_EC_ADD_NE as usize
98 || base_opcode == Rv32WeierstrassOpcode::SETUP_EC_DOUBLE as usize
99}
100
101#[inline]
103fn compute_ec_slow<const BLOCKS: usize, const BLOCK_SIZE: usize>(
104 expr: &openvm_mod_circuit_builder::FieldExpr,
105 local_opcode: usize,
106 read_bytes: &[u8],
107) -> [[u8; BLOCK_SIZE]; BLOCKS] {
108 let flag_idx = if is_setup_opcode(local_opcode) {
109 expr.num_flags()
111 } else {
112 0
114 };
115 run_field_expression_precomputed::<true>(expr, flag_idx, read_bytes).into()
116}
117
118impl<F, RA, const BLOCKS: usize, const BLOCK_SIZE: usize> PreflightExecutor<F, RA>
120 for EcAddNeExecutor<BLOCKS, BLOCK_SIZE>
121where
122 F: PrimeField32,
123 for<'buf> RA: RecordArena<
124 'buf,
125 FieldExpressionRecordLayout<
126 F,
127 Rv32VecHeapAdapterExecutor<2, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE>,
128 >,
129 (
130 <Rv32VecHeapAdapterExecutor<2, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE> as AdapterTraceExecutor<F>>::RecordMut<'buf>,
131 FieldExpressionCoreRecordMut<'buf>,
132 ),
133 >,
134{
135 fn execute(
136 &self,
137 state: VmStateMut<F, TracingMemory, RA>,
138 instruction: &Instruction<F>,
139 ) -> Result<(), ExecutionError> {
140 let (mut adapter_record, mut core_record) =
141 state.ctx.alloc(self.inner.get_record_layout());
142
143 <Rv32VecHeapAdapterExecutor<2, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE> as AdapterTraceExecutor<F>>::start(
144 *state.pc,
145 state.memory,
146 &mut adapter_record,
147 );
148
149 let read_data: [[[u8; BLOCK_SIZE]; BLOCKS]; 2] = self
150 .inner
151 .adapter()
152 .read(state.memory, instruction, &mut adapter_record);
153
154 let local_opcode = instruction.opcode.local_opcode_idx(self.inner.offset);
155
156 core_record.fill_from_execution_data(
157 local_opcode as u8,
158 read_data.as_flattened().as_flattened(),
159 );
160
161 let output: [[u8; BLOCK_SIZE]; BLOCKS] =
163 if !is_setup_opcode(local_opcode) {
164 compute_ec_add_ne_fast::<BLOCKS, BLOCK_SIZE>(self.cached_field_type, read_data)
165 .unwrap_or_else(|| {
166 compute_ec_slow::<BLOCKS, BLOCK_SIZE>(
167 &self.inner.expr,
168 local_opcode,
169 read_data.as_flattened().as_flattened(),
170 )
171 })
172 } else {
173 compute_ec_slow::<BLOCKS, BLOCK_SIZE>(
174 &self.inner.expr,
175 local_opcode,
176 read_data.as_flattened().as_flattened(),
177 )
178 };
179
180 self.inner.adapter().write(
181 state.memory,
182 instruction,
183 output,
184 &mut adapter_record,
185 );
186
187 *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP);
188 Ok(())
189 }
190
191 fn get_opcode_name(&self, _opcode: usize) -> String {
192 self.inner.name.clone()
193 }
194}
195
196impl<F, RA, const BLOCKS: usize, const BLOCK_SIZE: usize> PreflightExecutor<F, RA>
198 for EcDoubleExecutor<BLOCKS, BLOCK_SIZE>
199where
200 F: PrimeField32,
201 for<'buf> RA: RecordArena<
202 'buf,
203 FieldExpressionRecordLayout<
204 F,
205 Rv32VecHeapAdapterExecutor<1, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE>,
206 >,
207 (
208 <Rv32VecHeapAdapterExecutor<1, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE> as AdapterTraceExecutor<F>>::RecordMut<'buf>,
209 FieldExpressionCoreRecordMut<'buf>,
210 ),
211 >,
212{
213 fn execute(
214 &self,
215 state: VmStateMut<F, TracingMemory, RA>,
216 instruction: &Instruction<F>,
217 ) -> Result<(), ExecutionError> {
218 let (mut adapter_record, mut core_record) =
219 state.ctx.alloc(self.inner.get_record_layout());
220
221 <Rv32VecHeapAdapterExecutor<1, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE> as AdapterTraceExecutor<F>>::start(
222 *state.pc,
223 state.memory,
224 &mut adapter_record,
225 );
226
227 let read_data_arr: [[[u8; BLOCK_SIZE]; BLOCKS]; 1] = self
228 .inner
229 .adapter()
230 .read(state.memory, instruction, &mut adapter_record);
231 let read_data: [[u8; BLOCK_SIZE]; BLOCKS] = read_data_arr[0];
232
233 let local_opcode = instruction.opcode.local_opcode_idx(self.inner.offset);
234
235 core_record.fill_from_execution_data(local_opcode as u8, read_data.as_flattened());
236
237 let output: [[u8; BLOCK_SIZE]; BLOCKS] =
239 if !is_setup_opcode(local_opcode) {
240 compute_ec_double_fast::<BLOCKS, BLOCK_SIZE>(self.cached_curve_type, read_data)
241 .unwrap_or_else(|| {
242 compute_ec_slow::<BLOCKS, BLOCK_SIZE>(
243 &self.inner.expr,
244 local_opcode,
245 read_data.as_flattened(),
246 )
247 })
248 } else {
249 compute_ec_slow::<BLOCKS, BLOCK_SIZE>(
250 &self.inner.expr,
251 local_opcode,
252 read_data.as_flattened(),
253 )
254 };
255
256 self.inner.adapter().write(
257 state.memory,
258 instruction,
259 output,
260 &mut adapter_record,
261 );
262
263 *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP);
264 Ok(())
265 }
266
267 fn get_opcode_name(&self, _opcode: usize) -> String {
268 self.inner.name.clone()
269 }
270}