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