openvm_bigint_circuit/extension/
mod.rs

1use std::sync::Arc;
2
3use derive_more::derive::From;
4use openvm_bigint_transpiler::{
5    Rv32BaseAlu256Opcode, Rv32BranchEqual256Opcode, Rv32BranchLessThan256Opcode,
6    Rv32LessThan256Opcode, Rv32Mul256Opcode, Rv32Shift256Opcode,
7};
8use openvm_circuit::{
9    arch::{
10        AirInventory, AirInventoryError, ChipInventory, ChipInventoryError, ExecutionBridge,
11        ExecutorInventoryBuilder, ExecutorInventoryError, MatrixRecordArena, RowMajorMatrixArena,
12        VmBuilder, VmChipComplex, VmCircuitExtension, VmExecutionExtension, VmProverExtension,
13    },
14    system::{memory::SharedMemoryHelper, SystemChipInventory, SystemCpuBuilder, SystemPort},
15};
16use openvm_circuit_derive::{AnyEnum, Executor, MeteredExecutor, PreflightExecutor};
17use openvm_circuit_primitives::{
18    bitwise_op_lookup::{
19        BitwiseOperationLookupAir, BitwiseOperationLookupBus, BitwiseOperationLookupChip,
20        SharedBitwiseOperationLookupChip,
21    },
22    range_tuple::{
23        RangeTupleCheckerAir, RangeTupleCheckerBus, RangeTupleCheckerChip,
24        SharedRangeTupleCheckerChip,
25    },
26};
27use openvm_instructions::{program::DEFAULT_PC_STEP, LocalOpcode};
28use openvm_rv32im_circuit::Rv32ImCpuProverExt;
29use openvm_stark_backend::{
30    config::{StarkGenericConfig, Val},
31    engine::StarkEngine,
32    p3_field::PrimeField32,
33    prover::cpu::{CpuBackend, CpuDevice},
34};
35use serde::{Deserialize, Serialize};
36
37use crate::*;
38
39cfg_if::cfg_if! {
40    if #[cfg(feature = "cuda")] {
41        mod cuda;
42        pub use self::cuda::*;
43        pub use self::cuda::{
44            Int256GpuProverExt as Int256ProverExt,
45            Int256Rv32GpuBuilder as Int256Rv32Builder,
46        };
47    } else {
48        pub use self::{
49            Int256CpuProverExt as Int256ProverExt,
50            Int256Rv32CpuBuilder as Int256Rv32Builder,
51        };
52    }
53}
54
55// =================================== VM Extension Implementation =================================
56#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
57pub struct Int256 {
58    #[serde(default = "default_range_tuple_checker_sizes")]
59    pub range_tuple_checker_sizes: [u32; 2],
60}
61
62impl Default for Int256 {
63    fn default() -> Self {
64        Self {
65            range_tuple_checker_sizes: default_range_tuple_checker_sizes(),
66        }
67    }
68}
69
70fn default_range_tuple_checker_sizes() -> [u32; 2] {
71    [1 << 8, 32 * (1 << 8)]
72}
73
74#[derive(Clone, From, AnyEnum, Executor, MeteredExecutor, PreflightExecutor)]
75#[cfg_attr(
76    feature = "aot",
77    derive(
78        openvm_circuit_derive::AotExecutor,
79        openvm_circuit_derive::AotMeteredExecutor
80    )
81)]
82pub enum Int256Executor {
83    BaseAlu256(Rv32BaseAlu256Executor),
84    LessThan256(Rv32LessThan256Executor),
85    BranchEqual256(Rv32BranchEqual256Executor),
86    BranchLessThan256(Rv32BranchLessThan256Executor),
87    Multiplication256(Rv32Multiplication256Executor),
88    Shift256(Rv32Shift256Executor),
89}
90
91impl<F: PrimeField32> VmExecutionExtension<F> for Int256 {
92    type Executor = Int256Executor;
93
94    fn extend_execution(
95        &self,
96        inventory: &mut ExecutorInventoryBuilder<F, Int256Executor>,
97    ) -> Result<(), ExecutorInventoryError> {
98        let pointer_max_bits = inventory.pointer_max_bits();
99
100        let alu = Rv32BaseAlu256Executor::new(
101            Rv32HeapAdapterExecutor::new(pointer_max_bits),
102            Rv32BaseAlu256Opcode::CLASS_OFFSET,
103        );
104        inventory.add_executor(alu, Rv32BaseAlu256Opcode::iter().map(|x| x.global_opcode()))?;
105
106        let lt = Rv32LessThan256Executor::new(
107            Rv32HeapAdapterExecutor::new(pointer_max_bits),
108            Rv32LessThan256Opcode::CLASS_OFFSET,
109        );
110        inventory.add_executor(lt, Rv32LessThan256Opcode::iter().map(|x| x.global_opcode()))?;
111
112        let beq = Rv32BranchEqual256Executor::new(
113            Rv32HeapBranchAdapterExecutor::new(pointer_max_bits),
114            Rv32BranchEqual256Opcode::CLASS_OFFSET,
115            DEFAULT_PC_STEP,
116        );
117        inventory.add_executor(
118            beq,
119            Rv32BranchEqual256Opcode::iter().map(|x| x.global_opcode()),
120        )?;
121
122        let blt = Rv32BranchLessThan256Executor::new(
123            Rv32HeapBranchAdapterExecutor::new(pointer_max_bits),
124            Rv32BranchLessThan256Opcode::CLASS_OFFSET,
125        );
126        inventory.add_executor(
127            blt,
128            Rv32BranchLessThan256Opcode::iter().map(|x| x.global_opcode()),
129        )?;
130
131        let mult = Rv32Multiplication256Executor::new(
132            Rv32HeapAdapterExecutor::new(pointer_max_bits),
133            Rv32Mul256Opcode::CLASS_OFFSET,
134        );
135        inventory.add_executor(mult, Rv32Mul256Opcode::iter().map(|x| x.global_opcode()))?;
136
137        let shift = Rv32Shift256Executor::new(
138            Rv32HeapAdapterExecutor::new(pointer_max_bits),
139            Rv32Shift256Opcode::CLASS_OFFSET,
140        );
141        inventory.add_executor(shift, Rv32Shift256Opcode::iter().map(|x| x.global_opcode()))?;
142
143        Ok(())
144    }
145}
146
147impl<SC: StarkGenericConfig> VmCircuitExtension<SC> for Int256 {
148    fn extend_circuit(&self, inventory: &mut AirInventory<SC>) -> Result<(), AirInventoryError> {
149        let SystemPort {
150            execution_bus,
151            program_bus,
152            memory_bridge,
153        } = inventory.system().port();
154
155        let exec_bridge = ExecutionBridge::new(execution_bus, program_bus);
156        let range_checker = inventory.range_checker().bus;
157        let pointer_max_bits = inventory.pointer_max_bits();
158
159        let bitwise_lu = {
160            // A trick to get around Rust's borrow rules
161            let existing_air = inventory.find_air::<BitwiseOperationLookupAir<8>>().next();
162            if let Some(air) = existing_air {
163                air.bus
164            } else {
165                let bus = BitwiseOperationLookupBus::new(inventory.new_bus_idx());
166                let air = BitwiseOperationLookupAir::<8>::new(bus);
167                inventory.add_air(air);
168                air.bus
169            }
170        };
171
172        let range_tuple_checker = {
173            let existing_air = inventory.find_air::<RangeTupleCheckerAir<2>>().find(|c| {
174                c.bus.sizes[0] >= self.range_tuple_checker_sizes[0]
175                    && c.bus.sizes[1] >= self.range_tuple_checker_sizes[1]
176            });
177            if let Some(air) = existing_air {
178                air.bus
179            } else {
180                let bus = RangeTupleCheckerBus::new(
181                    inventory.new_bus_idx(),
182                    self.range_tuple_checker_sizes,
183                );
184                let air = RangeTupleCheckerAir { bus };
185                inventory.add_air(air);
186                air.bus
187            }
188        };
189
190        let alu = Rv32BaseAlu256Air::new(
191            Rv32HeapAdapterAir::new(exec_bridge, memory_bridge, bitwise_lu, pointer_max_bits),
192            BaseAluCoreAir::new(bitwise_lu, Rv32BaseAlu256Opcode::CLASS_OFFSET),
193        );
194        inventory.add_air(alu);
195
196        let lt = Rv32LessThan256Air::new(
197            Rv32HeapAdapterAir::new(exec_bridge, memory_bridge, bitwise_lu, pointer_max_bits),
198            LessThanCoreAir::new(bitwise_lu, Rv32LessThan256Opcode::CLASS_OFFSET),
199        );
200        inventory.add_air(lt);
201
202        let beq = Rv32BranchEqual256Air::new(
203            Rv32HeapBranchAdapterAir::new(exec_bridge, memory_bridge, bitwise_lu, pointer_max_bits),
204            BranchEqualCoreAir::new(Rv32BranchEqual256Opcode::CLASS_OFFSET, DEFAULT_PC_STEP),
205        );
206        inventory.add_air(beq);
207
208        let blt = Rv32BranchLessThan256Air::new(
209            Rv32HeapBranchAdapterAir::new(exec_bridge, memory_bridge, bitwise_lu, pointer_max_bits),
210            BranchLessThanCoreAir::new(bitwise_lu, Rv32BranchLessThan256Opcode::CLASS_OFFSET),
211        );
212        inventory.add_air(blt);
213
214        let mult = Rv32Multiplication256Air::new(
215            Rv32HeapAdapterAir::new(exec_bridge, memory_bridge, bitwise_lu, pointer_max_bits),
216            MultiplicationCoreAir::new(range_tuple_checker, Rv32Mul256Opcode::CLASS_OFFSET),
217        );
218        inventory.add_air(mult);
219
220        let shift = Rv32Shift256Air::new(
221            Rv32HeapAdapterAir::new(exec_bridge, memory_bridge, bitwise_lu, pointer_max_bits),
222            ShiftCoreAir::new(bitwise_lu, range_checker, Rv32Shift256Opcode::CLASS_OFFSET),
223        );
224        inventory.add_air(shift);
225
226        Ok(())
227    }
228}
229
230pub struct Int256CpuProverExt;
231// This implementation is specific to CpuBackend because the lookup chips (VariableRangeChecker,
232// BitwiseOperationLookupChip) are specific to CpuBackend.
233impl<E, SC, RA> VmProverExtension<E, RA, Int256> for Int256CpuProverExt
234where
235    SC: StarkGenericConfig,
236    E: StarkEngine<SC = SC, PB = CpuBackend<SC>, PD = CpuDevice<SC>>,
237    RA: RowMajorMatrixArena<Val<SC>>,
238    Val<SC>: PrimeField32,
239{
240    fn extend_prover(
241        &self,
242        extension: &Int256,
243        inventory: &mut ChipInventory<SC, RA, CpuBackend<SC>>,
244    ) -> Result<(), ChipInventoryError> {
245        let range_checker = inventory.range_checker()?.clone();
246        let timestamp_max_bits = inventory.timestamp_max_bits();
247        let mem_helper = SharedMemoryHelper::new(range_checker.clone(), timestamp_max_bits);
248        let pointer_max_bits = inventory.airs().config().memory_config.pointer_max_bits;
249
250        let bitwise_lu = {
251            let existing_chip = inventory
252                .find_chip::<SharedBitwiseOperationLookupChip<8>>()
253                .next();
254            if let Some(chip) = existing_chip {
255                chip.clone()
256            } else {
257                let air: &BitwiseOperationLookupAir<8> = inventory.next_air()?;
258                let chip = Arc::new(BitwiseOperationLookupChip::new(air.bus));
259                inventory.add_periphery_chip(chip.clone());
260                chip
261            }
262        };
263
264        let range_tuple_checker = {
265            let existing_chip = inventory
266                .find_chip::<SharedRangeTupleCheckerChip<2>>()
267                .find(|c| {
268                    c.bus().sizes[0] >= extension.range_tuple_checker_sizes[0]
269                        && c.bus().sizes[1] >= extension.range_tuple_checker_sizes[1]
270                });
271            if let Some(chip) = existing_chip {
272                chip.clone()
273            } else {
274                let air: &RangeTupleCheckerAir<2> = inventory.next_air()?;
275                let chip = SharedRangeTupleCheckerChip::new(RangeTupleCheckerChip::new(air.bus));
276                inventory.add_periphery_chip(chip.clone());
277                chip
278            }
279        };
280
281        inventory.next_air::<Rv32BaseAlu256Air>()?;
282        let alu = Rv32BaseAlu256Chip::new(
283            BaseAluFiller::new(
284                Rv32HeapAdapterFiller::new(pointer_max_bits, bitwise_lu.clone()),
285                bitwise_lu.clone(),
286                Rv32BaseAlu256Opcode::CLASS_OFFSET,
287            ),
288            mem_helper.clone(),
289        );
290        inventory.add_executor_chip(alu);
291
292        inventory.next_air::<Rv32LessThan256Air>()?;
293        let lt = Rv32LessThan256Chip::new(
294            LessThanFiller::new(
295                Rv32HeapAdapterFiller::new(pointer_max_bits, bitwise_lu.clone()),
296                bitwise_lu.clone(),
297                Rv32LessThan256Opcode::CLASS_OFFSET,
298            ),
299            mem_helper.clone(),
300        );
301        inventory.add_executor_chip(lt);
302
303        inventory.next_air::<Rv32BranchEqual256Air>()?;
304        let beq = Rv32BranchEqual256Chip::new(
305            BranchEqualFiller::new(
306                Rv32HeapBranchAdapterFiller::new(pointer_max_bits, bitwise_lu.clone()),
307                Rv32BranchEqual256Opcode::CLASS_OFFSET,
308                DEFAULT_PC_STEP,
309            ),
310            mem_helper.clone(),
311        );
312        inventory.add_executor_chip(beq);
313
314        inventory.next_air::<Rv32BranchLessThan256Air>()?;
315        let blt = Rv32BranchLessThan256Chip::new(
316            BranchLessThanFiller::new(
317                Rv32HeapBranchAdapterFiller::new(pointer_max_bits, bitwise_lu.clone()),
318                bitwise_lu.clone(),
319                Rv32BranchLessThan256Opcode::CLASS_OFFSET,
320            ),
321            mem_helper.clone(),
322        );
323        inventory.add_executor_chip(blt);
324
325        inventory.next_air::<Rv32Multiplication256Air>()?;
326        let mult = Rv32Multiplication256Chip::new(
327            MultiplicationFiller::new(
328                Rv32HeapAdapterFiller::new(pointer_max_bits, bitwise_lu.clone()),
329                range_tuple_checker.clone(),
330                Rv32Mul256Opcode::CLASS_OFFSET,
331            ),
332            mem_helper.clone(),
333        );
334        inventory.add_executor_chip(mult);
335
336        inventory.next_air::<Rv32Shift256Air>()?;
337        let shift = Rv32Shift256Chip::new(
338            ShiftFiller::new(
339                Rv32HeapAdapterFiller::new(pointer_max_bits, bitwise_lu.clone()),
340                bitwise_lu.clone(),
341                range_checker.clone(),
342                Rv32Shift256Opcode::CLASS_OFFSET,
343            ),
344            mem_helper.clone(),
345        );
346        inventory.add_executor_chip(shift);
347        Ok(())
348    }
349}
350
351#[derive(Clone)]
352pub struct Int256Rv32CpuBuilder;
353
354impl<E, SC> VmBuilder<E> for Int256Rv32CpuBuilder
355where
356    SC: StarkGenericConfig,
357    E: StarkEngine<SC = SC, PB = CpuBackend<SC>, PD = CpuDevice<SC>>,
358    Val<SC>: PrimeField32,
359{
360    type VmConfig = Int256Rv32Config;
361    type SystemChipInventory = SystemChipInventory<SC>;
362    type RecordArena = MatrixRecordArena<Val<SC>>;
363
364    fn create_chip_complex(
365        &self,
366        config: &Int256Rv32Config,
367        circuit: AirInventory<E::SC>,
368    ) -> Result<
369        VmChipComplex<E::SC, Self::RecordArena, E::PB, Self::SystemChipInventory>,
370        ChipInventoryError,
371    > {
372        let mut chip_complex =
373            VmBuilder::<E>::create_chip_complex(&SystemCpuBuilder, &config.system, circuit)?;
374        let inventory = &mut chip_complex.inventory;
375        VmProverExtension::<E, _, _>::extend_prover(&Rv32ImCpuProverExt, &config.rv32i, inventory)?;
376        VmProverExtension::<E, _, _>::extend_prover(&Rv32ImCpuProverExt, &config.rv32m, inventory)?;
377        VmProverExtension::<E, _, _>::extend_prover(&Rv32ImCpuProverExt, &config.io, inventory)?;
378        VmProverExtension::<E, _, _>::extend_prover(
379            &Int256CpuProverExt,
380            &config.bigint,
381            inventory,
382        )?;
383        Ok(chip_complex)
384    }
385}