openvm_circuit/arch/
integration_api.rs

1use std::{array::from_fn, borrow::Borrow, marker::PhantomData};
2
3use openvm_circuit_primitives::{ColumnsAir, StructReflection, StructReflectionHelper};
4use openvm_circuit_primitives_derive::AlignedBorrow;
5use openvm_cpu_backend::CpuBackend;
6use openvm_instructions::{instruction::Instruction, LocalOpcode};
7use openvm_stark_backend::{
8    p3_air::{Air, AirBuilder, BaseAir},
9    p3_field::PrimeCharacteristicRing,
10    p3_matrix::{dense::RowMajorMatrix, Matrix},
11    p3_maybe_rayon::prelude::*,
12    prover::AirProvingContext,
13    BaseAirWithPublicValues, PartitionedBaseAir, StarkProtocolConfig, Val,
14};
15use serde::{Deserialize, Serialize};
16
17use crate::{
18    arch::RowMajorMatrixArena,
19    primitives::Chip,
20    system::memory::{online::TracingMemory, MemoryAuxColsFactory, SharedMemoryHelper},
21};
22
23/// The interface between primitive AIR and machine adapter AIR.
24pub trait VmAdapterInterface<T> {
25    /// The memory read data that should be exposed for downstream use
26    type Reads;
27    /// The memory write data that are expected to be provided by the integrator
28    type Writes;
29    /// The parts of the instruction that should be exposed to the integrator.
30    /// This will typically include `is_valid`, which indicates whether the trace row
31    /// is being used and `opcode` to indicate which opcode is being executed if the
32    /// VmChip supports multiple opcodes.
33    type ProcessedInstruction;
34}
35
36pub trait VmAdapterAir<AB: AirBuilder>: BaseAir<AB::F> {
37    type Interface: VmAdapterInterface<AB::Expr>;
38
39    /// [Air](openvm_stark_backend::p3_air::Air) constraints owned by the adapter.
40    /// The `interface` is given as abstract expressions so it can be directly used in other AIR
41    /// constraints.
42    ///
43    /// Adapters should document the max constraint degree as a function of the constraint degrees
44    /// of `reads, writes, instruction`.
45    fn eval(
46        &self,
47        builder: &mut AB,
48        local: &[AB::Var],
49        interface: AdapterAirContext<AB::Expr, Self::Interface>,
50    );
51
52    /// Return the `from_pc` expression.
53    fn get_from_pc(&self, local: &[AB::Var]) -> AB::Var;
54}
55
56pub trait VmCoreAir<AB, I>: BaseAirWithPublicValues<AB::F>
57where
58    AB: AirBuilder,
59    I: VmAdapterInterface<AB::Expr>,
60{
61    /// Returns `(to_pc, interface)`.
62    fn eval(
63        &self,
64        builder: &mut AB,
65        local_core: &[AB::Var],
66        from_pc: AB::Var,
67    ) -> AdapterAirContext<AB::Expr, I>;
68
69    /// The offset the opcodes by this chip start from.
70    /// This is usually just `CorrespondingOpcode::CLASS_OFFSET`,
71    /// but sometimes (for modular chips, for example) it also depends on something else.
72    fn start_offset(&self) -> usize;
73
74    fn start_offset_expr(&self) -> AB::Expr {
75        AB::Expr::from_usize(self.start_offset())
76    }
77
78    fn expr_to_global_expr(&self, local_expr: impl Into<AB::Expr>) -> AB::Expr {
79        self.start_offset_expr() + local_expr.into()
80    }
81
82    fn opcode_to_global_expr(&self, local_opcode: impl LocalOpcode) -> AB::Expr {
83        self.expr_to_global_expr(AB::Expr::from_usize(local_opcode.local_usize()))
84    }
85}
86
87pub struct AdapterAirContext<T, I: VmAdapterInterface<T>> {
88    /// Leave as `None` to allow the adapter to decide the `to_pc` automatically.
89    pub to_pc: Option<T>,
90    pub reads: I::Reads,
91    pub writes: I::Writes,
92    pub instruction: I::ProcessedInstruction,
93}
94
95/// Helper trait for CPU tracegen.
96pub trait TraceFiller<F>: Send + Sync {
97    /// Populates `trace`. This function will always be called after
98    /// [`PreflightExecutor::execute`](crate::arch::execution::PreflightExecutor::execute), so the
99    /// `trace` should already contain the records necessary to fill in the rest of it.
100    fn fill_trace(
101        &self,
102        mem_helper: &MemoryAuxColsFactory<F>,
103        trace: &mut RowMajorMatrix<F>,
104        rows_used: usize,
105    ) where
106        F: Send + Sync + Clone,
107    {
108        let width = trace.width();
109        trace.values[..rows_used * width]
110            .par_chunks_exact_mut(width)
111            .for_each(|row_slice| {
112                self.fill_trace_row(mem_helper, row_slice);
113            });
114        trace.values[rows_used * width..]
115            .par_chunks_exact_mut(width)
116            .for_each(|row_slice| {
117                self.fill_dummy_trace_row(row_slice);
118            });
119    }
120
121    /// Populates `row_slice`. This function will always be called after
122    /// [`PreflightExecutor::execute`](crate::arch::execution::PreflightExecutor::execute), so the
123    /// `row_slice` should already contain context necessary to fill in the rest of the row.
124    /// This function will be called for each row in the trace which is being used, and for all
125    /// other rows in the trace see `fill_dummy_trace_row`.
126    ///
127    /// The provided `row_slice` will have length equal to the width of the AIR.
128    fn fill_trace_row(&self, _mem_helper: &MemoryAuxColsFactory<F>, _row_slice: &mut [F]) {
129        unreachable!("fill_trace_row is not implemented")
130    }
131
132    /// Populates `row_slice`. This function will be called on dummy rows.
133    /// By default the trace is padded with empty (all 0) rows to make the height a power of 2.
134    ///
135    /// The provided `row_slice` will have length equal to the width of the AIR.
136    fn fill_dummy_trace_row(&self, _row_slice: &mut [F]) {
137        // By default, the row is filled with zeroes
138    }
139
140    /// Returns a list of public values to publish.
141    fn generate_public_values(&self) -> Vec<F> {
142        vec![]
143    }
144}
145
146/// We want a blanket implementation of `Chip<MatrixRecordArena, CpuBackend>` on any struct that
147/// implements [TraceFiller] but due to Rust orphan rules, we need a wrapper struct.
148// @dev: You could make a macro, but it's hard to handle generics in the struct definition.
149#[derive(derive_new::new)]
150pub struct VmChipWrapper<F, FILLER> {
151    pub inner: FILLER,
152    pub mem_helper: SharedMemoryHelper<F>,
153}
154
155impl<SC, FILLER, RA> Chip<RA, CpuBackend<SC>> for VmChipWrapper<Val<SC>, FILLER>
156where
157    SC: StarkProtocolConfig,
158    FILLER: TraceFiller<Val<SC>>,
159    RA: RowMajorMatrixArena<Val<SC>>,
160{
161    fn generate_proving_ctx(&self, arena: RA) -> AirProvingContext<CpuBackend<SC>> {
162        let rows_used = arena.trace_offset() / arena.width();
163        let mut trace = arena.into_matrix();
164        let mem_helper = self.mem_helper.as_borrowed();
165        self.inner.fill_trace(&mem_helper, &mut trace, rows_used);
166
167        AirProvingContext::simple(trace, self.inner.generate_public_values())
168    }
169}
170
171/// A helper trait for expressing generic state accesses within the implementation of
172/// [PreflightExecutor](crate::arch::execution::PreflightExecutor). Note that this is only a helper
173/// trait when the same interface of state access is reused or shared by multiple implementations.
174/// It is not required to implement this trait if it is easier to implement the
175/// [PreflightExecutor](crate::arch::execution::PreflightExecutor) trait directly without this
176/// trait.
177pub trait AdapterTraceExecutor<F>: Clone {
178    const WIDTH: usize;
179    type ReadData;
180    type WriteData;
181    // @dev This can either be a &mut _ type or a struct with &mut _ fields.
182    // The latter is helpful if we want to directly write certain values in place into a trace
183    // matrix.
184    type RecordMut<'a>
185    where
186        Self: 'a;
187
188    fn start(pc: u32, memory: &TracingMemory, record: &mut Self::RecordMut<'_>);
189
190    fn read(
191        &self,
192        memory: &mut TracingMemory,
193        instruction: &Instruction<F>,
194        record: &mut Self::RecordMut<'_>,
195    ) -> Self::ReadData;
196
197    fn write(
198        &self,
199        memory: &mut TracingMemory,
200        instruction: &Instruction<F>,
201        data: Self::WriteData,
202        record: &mut Self::RecordMut<'_>,
203    );
204}
205
206// NOTE[jpw]: cannot reuse `TraceSubRowGenerator` trait because we need associated constant
207// `WIDTH`.
208pub trait AdapterTraceFiller<F>: Send + Sync {
209    const WIDTH: usize;
210    /// Post-execution filling of rest of adapter row.
211    fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory<F>, adapter_row: &mut [F]);
212}
213
214// ============================== Adapter|Core Air Wrapper ===============================
215
216#[derive(Clone, Copy, derive_new::new)]
217pub struct VmAirWrapper<A, C> {
218    pub adapter: A,
219    pub core: C,
220}
221
222impl<F, A, C> BaseAir<F> for VmAirWrapper<A, C>
223where
224    A: BaseAir<F>,
225    C: BaseAir<F>,
226{
227    fn width(&self) -> usize {
228        self.adapter.width() + self.core.width()
229    }
230}
231
232impl<F, A, M> BaseAirWithPublicValues<F> for VmAirWrapper<A, M>
233where
234    A: BaseAir<F>,
235    M: BaseAirWithPublicValues<F>,
236{
237    fn num_public_values(&self) -> usize {
238        self.core.num_public_values()
239    }
240}
241
242// Current cached trace is not supported
243impl<F, A, M> PartitionedBaseAir<F> for VmAirWrapper<A, M>
244where
245    A: BaseAir<F>,
246    M: BaseAir<F>,
247{
248}
249
250impl<A, M> ColumnsAir for VmAirWrapper<A, M>
251where
252    A: ColumnsAir,
253    M: ColumnsAir,
254{
255    fn columns(&self) -> Option<Vec<String>> {
256        let adapter_cols = self.adapter.columns()?;
257        let core_cols = self.core.columns()?;
258        Some(adapter_cols.into_iter().chain(core_cols).collect())
259    }
260}
261
262impl<AB, A, M> Air<AB> for VmAirWrapper<A, M>
263where
264    AB: AirBuilder,
265    A: VmAdapterAir<AB>,
266    M: VmCoreAir<AB, A::Interface>,
267{
268    fn eval(&self, builder: &mut AB) {
269        let main = builder.main();
270        let local = main.row_slice(0).expect("window should have two elements");
271        let local: &[AB::Var] = (*local).borrow();
272        let (local_adapter, local_core) = local.split_at(self.adapter.width());
273
274        let ctx = self
275            .core
276            .eval(builder, local_core, self.adapter.get_from_pc(local_adapter));
277        self.adapter.eval(builder, local_adapter, ctx);
278    }
279}
280
281// =================================================================================================
282// Concrete adapter interfaces
283// =================================================================================================
284
285/// The most common adapter interface.
286/// Performs `NUM_READS` batch reads of size `READ_SIZE` and
287/// `NUM_WRITES` batch writes of size `WRITE_SIZE`.
288pub struct BasicAdapterInterface<
289    T,
290    PI,
291    const NUM_READS: usize,
292    const NUM_WRITES: usize,
293    const READ_SIZE: usize,
294    const WRITE_SIZE: usize,
295>(PhantomData<T>, PhantomData<PI>);
296
297impl<
298        T,
299        PI,
300        const NUM_READS: usize,
301        const NUM_WRITES: usize,
302        const READ_SIZE: usize,
303        const WRITE_SIZE: usize,
304    > VmAdapterInterface<T>
305    for BasicAdapterInterface<T, PI, NUM_READS, NUM_WRITES, READ_SIZE, WRITE_SIZE>
306{
307    type Reads = [[T; READ_SIZE]; NUM_READS];
308    type Writes = [[T; WRITE_SIZE]; NUM_WRITES];
309    type ProcessedInstruction = PI;
310}
311
312pub struct VecHeapAdapterInterface<
313    T,
314    const NUM_READS: usize,
315    const BLOCKS_PER_READ: usize,
316    const BLOCKS_PER_WRITE: usize,
317    const READ_SIZE: usize,
318    const WRITE_SIZE: usize,
319>(PhantomData<T>);
320
321impl<
322        T,
323        const NUM_READS: usize,
324        const BLOCKS_PER_READ: usize,
325        const BLOCKS_PER_WRITE: usize,
326        const READ_SIZE: usize,
327        const WRITE_SIZE: usize,
328    > VmAdapterInterface<T>
329    for VecHeapAdapterInterface<
330        T,
331        NUM_READS,
332        BLOCKS_PER_READ,
333        BLOCKS_PER_WRITE,
334        READ_SIZE,
335        WRITE_SIZE,
336    >
337{
338    type Reads = [[[T; READ_SIZE]; BLOCKS_PER_READ]; NUM_READS];
339    type Writes = [[T; WRITE_SIZE]; BLOCKS_PER_WRITE];
340    type ProcessedInstruction = MinimalInstruction<T>;
341}
342
343/// Adapter interface for branch operations that read from heap via vec-style blocks.
344/// Similar to `VecHeapAdapterInterface` but without writes (branch operations only compare values).
345pub struct VecHeapBranchAdapterInterface<
346    T,
347    const NUM_READS: usize,
348    const BLOCKS_PER_READ: usize,
349    const READ_SIZE: usize,
350>(PhantomData<T>);
351
352impl<T, const NUM_READS: usize, const BLOCKS_PER_READ: usize, const READ_SIZE: usize>
353    VmAdapterInterface<T>
354    for VecHeapBranchAdapterInterface<T, NUM_READS, BLOCKS_PER_READ, READ_SIZE>
355{
356    type Reads = [[[T; READ_SIZE]; BLOCKS_PER_READ]; NUM_READS];
357    type Writes = ();
358    type ProcessedInstruction = ImmInstruction<T>;
359}
360
361/// Similar to `BasicAdapterInterface`, but it flattens the reads and writes into a single flat
362/// array for each
363pub struct FlatInterface<T, PI, const READ_CELLS: usize, const WRITE_CELLS: usize>(
364    PhantomData<T>,
365    PhantomData<PI>,
366);
367
368impl<T, PI, const READ_CELLS: usize, const WRITE_CELLS: usize> VmAdapterInterface<T>
369    for FlatInterface<T, PI, READ_CELLS, WRITE_CELLS>
370{
371    type Reads = [T; READ_CELLS];
372    type Writes = [T; WRITE_CELLS];
373    type ProcessedInstruction = PI;
374}
375
376/// An interface that is fully determined during runtime. This should **only** be used as a last
377/// resort when static compile-time guarantees cannot be made.
378#[derive(Serialize, Deserialize)]
379pub struct DynAdapterInterface<T>(PhantomData<T>);
380
381impl<T> VmAdapterInterface<T> for DynAdapterInterface<T> {
382    /// Any reads can be flattened into a single vector.
383    type Reads = DynArray<T>;
384    /// Any writes can be flattened into a single vector.
385    type Writes = DynArray<T>;
386    /// Any processed instruction can be flattened into a single vector.
387    type ProcessedInstruction = DynArray<T>;
388}
389
390/// Newtype to implement `From`.
391#[derive(Clone, Debug, Default)]
392pub struct DynArray<T>(pub Vec<T>);
393
394// =================================================================================================
395// Definitions of ProcessedInstruction types for use in integration API
396// =================================================================================================
397
398#[repr(C)]
399#[derive(AlignedBorrow, StructReflection)]
400pub struct MinimalInstruction<T> {
401    pub is_valid: T,
402    /// Absolute opcode number
403    pub opcode: T,
404}
405
406// This ProcessedInstruction is used by rv32_rdwrite
407#[repr(C)]
408#[derive(AlignedBorrow, StructReflection)]
409pub struct ImmInstruction<T> {
410    pub is_valid: T,
411    /// Absolute opcode number
412    pub opcode: T,
413    pub immediate: T,
414}
415
416// This ProcessedInstruction is used by rv32_jalr
417#[repr(C)]
418#[derive(AlignedBorrow, StructReflection)]
419pub struct SignedImmInstruction<T> {
420    pub is_valid: T,
421    /// Absolute opcode number
422    pub opcode: T,
423    pub immediate: T,
424    /// Sign of the immediate (1 if negative, 0 if positive)
425    pub imm_sign: T,
426}
427
428// =================================================================================================
429// Conversions between adapter interfaces
430// =================================================================================================
431
432mod conversions {
433    use super::*;
434
435    // AdapterAirContext: VecHeapAdapterInterface -> DynInterface
436    impl<
437            T,
438            const NUM_READS: usize,
439            const BLOCKS_PER_READ: usize,
440            const BLOCKS_PER_WRITE: usize,
441            const READ_SIZE: usize,
442            const WRITE_SIZE: usize,
443        >
444        From<
445            AdapterAirContext<
446                T,
447                VecHeapAdapterInterface<
448                    T,
449                    NUM_READS,
450                    BLOCKS_PER_READ,
451                    BLOCKS_PER_WRITE,
452                    READ_SIZE,
453                    WRITE_SIZE,
454                >,
455            >,
456        > for AdapterAirContext<T, DynAdapterInterface<T>>
457    {
458        fn from(
459            ctx: AdapterAirContext<
460                T,
461                VecHeapAdapterInterface<
462                    T,
463                    NUM_READS,
464                    BLOCKS_PER_READ,
465                    BLOCKS_PER_WRITE,
466                    READ_SIZE,
467                    WRITE_SIZE,
468                >,
469            >,
470        ) -> Self {
471            AdapterAirContext {
472                to_pc: ctx.to_pc,
473                reads: ctx.reads.into(),
474                writes: ctx.writes.into(),
475                instruction: ctx.instruction.into(),
476            }
477        }
478    }
479
480    // AdapterAirContext: DynInterface -> VecHeapAdapterInterface
481    impl<
482            T,
483            const NUM_READS: usize,
484            const BLOCKS_PER_READ: usize,
485            const BLOCKS_PER_WRITE: usize,
486            const READ_SIZE: usize,
487            const WRITE_SIZE: usize,
488        > From<AdapterAirContext<T, DynAdapterInterface<T>>>
489        for AdapterAirContext<
490            T,
491            VecHeapAdapterInterface<
492                T,
493                NUM_READS,
494                BLOCKS_PER_READ,
495                BLOCKS_PER_WRITE,
496                READ_SIZE,
497                WRITE_SIZE,
498            >,
499        >
500    {
501        fn from(ctx: AdapterAirContext<T, DynAdapterInterface<T>>) -> Self {
502            AdapterAirContext {
503                to_pc: ctx.to_pc,
504                reads: ctx.reads.into(),
505                writes: ctx.writes.into(),
506                instruction: ctx.instruction.into(),
507            }
508        }
509    }
510
511    // AdapterAirContext: BasicInterface -> VecHeapAdapterInterface
512    impl<
513            T,
514            PI: Into<MinimalInstruction<T>>,
515            const BASIC_NUM_READS: usize,
516            const BASIC_NUM_WRITES: usize,
517            const NUM_READS: usize,
518            const BLOCKS_PER_READ: usize,
519            const BLOCKS_PER_WRITE: usize,
520            const READ_SIZE: usize,
521            const WRITE_SIZE: usize,
522        >
523        From<
524            AdapterAirContext<
525                T,
526                BasicAdapterInterface<
527                    T,
528                    PI,
529                    BASIC_NUM_READS,
530                    BASIC_NUM_WRITES,
531                    READ_SIZE,
532                    WRITE_SIZE,
533                >,
534            >,
535        >
536        for AdapterAirContext<
537            T,
538            VecHeapAdapterInterface<
539                T,
540                NUM_READS,
541                BLOCKS_PER_READ,
542                BLOCKS_PER_WRITE,
543                READ_SIZE,
544                WRITE_SIZE,
545            >,
546        >
547    {
548        fn from(
549            ctx: AdapterAirContext<
550                T,
551                BasicAdapterInterface<
552                    T,
553                    PI,
554                    BASIC_NUM_READS,
555                    BASIC_NUM_WRITES,
556                    READ_SIZE,
557                    WRITE_SIZE,
558                >,
559            >,
560        ) -> Self {
561            assert_eq!(BASIC_NUM_READS, NUM_READS * BLOCKS_PER_READ);
562            let mut reads_it = ctx.reads.into_iter();
563            let reads = from_fn(|_| from_fn(|_| reads_it.next().unwrap()));
564            assert_eq!(BASIC_NUM_WRITES, BLOCKS_PER_WRITE);
565            let mut writes_it = ctx.writes.into_iter();
566            let writes = from_fn(|_| writes_it.next().unwrap());
567            AdapterAirContext {
568                to_pc: ctx.to_pc,
569                reads,
570                writes,
571                instruction: ctx.instruction.into(),
572            }
573        }
574    }
575
576    // AdapterAirContext: FlatInterface -> BasicInterface
577    impl<
578            T,
579            PI,
580            const NUM_READS: usize,
581            const NUM_WRITES: usize,
582            const READ_SIZE: usize,
583            const WRITE_SIZE: usize,
584            const READ_CELLS: usize,
585            const WRITE_CELLS: usize,
586        >
587        From<
588            AdapterAirContext<
589                T,
590                BasicAdapterInterface<T, PI, NUM_READS, NUM_WRITES, READ_SIZE, WRITE_SIZE>,
591            >,
592        > for AdapterAirContext<T, FlatInterface<T, PI, READ_CELLS, WRITE_CELLS>>
593    {
594        /// ## Panics
595        /// If `READ_CELLS != NUM_READS * READ_SIZE` or `WRITE_CELLS != NUM_WRITES * WRITE_SIZE`.
596        /// This is a runtime assertion until Rust const generics expressions are stabilized.
597        fn from(
598            ctx: AdapterAirContext<
599                T,
600                BasicAdapterInterface<T, PI, NUM_READS, NUM_WRITES, READ_SIZE, WRITE_SIZE>,
601            >,
602        ) -> AdapterAirContext<T, FlatInterface<T, PI, READ_CELLS, WRITE_CELLS>> {
603            assert_eq!(READ_CELLS, NUM_READS * READ_SIZE);
604            assert_eq!(WRITE_CELLS, NUM_WRITES * WRITE_SIZE);
605            let mut reads_it = ctx.reads.into_iter().flatten();
606            let reads = from_fn(|_| reads_it.next().unwrap());
607            let mut writes_it = ctx.writes.into_iter().flatten();
608            let writes = from_fn(|_| writes_it.next().unwrap());
609            AdapterAirContext {
610                to_pc: ctx.to_pc,
611                reads,
612                writes,
613                instruction: ctx.instruction,
614            }
615        }
616    }
617
618    // AdapterAirContext: BasicInterface -> FlatInterface
619    impl<
620            T,
621            PI,
622            const NUM_READS: usize,
623            const NUM_WRITES: usize,
624            const READ_SIZE: usize,
625            const WRITE_SIZE: usize,
626            const READ_CELLS: usize,
627            const WRITE_CELLS: usize,
628        > From<AdapterAirContext<T, FlatInterface<T, PI, READ_CELLS, WRITE_CELLS>>>
629        for AdapterAirContext<
630            T,
631            BasicAdapterInterface<T, PI, NUM_READS, NUM_WRITES, READ_SIZE, WRITE_SIZE>,
632        >
633    {
634        /// ## Panics
635        /// If `READ_CELLS != NUM_READS * READ_SIZE` or `WRITE_CELLS != NUM_WRITES * WRITE_SIZE`.
636        /// This is a runtime assertion until Rust const generics expressions are stabilized.
637        fn from(
638            AdapterAirContext {
639                to_pc,
640                reads,
641                writes,
642                instruction,
643            }: AdapterAirContext<T, FlatInterface<T, PI, READ_CELLS, WRITE_CELLS>>,
644        ) -> AdapterAirContext<
645            T,
646            BasicAdapterInterface<T, PI, NUM_READS, NUM_WRITES, READ_SIZE, WRITE_SIZE>,
647        > {
648            assert_eq!(READ_CELLS, NUM_READS * READ_SIZE);
649            assert_eq!(WRITE_CELLS, NUM_WRITES * WRITE_SIZE);
650            let mut reads_it = reads.into_iter();
651            let reads: [[T; READ_SIZE]; NUM_READS] =
652                from_fn(|_| from_fn(|_| reads_it.next().unwrap()));
653            let mut writes_it = writes.into_iter();
654            let writes: [[T; WRITE_SIZE]; NUM_WRITES] =
655                from_fn(|_| from_fn(|_| writes_it.next().unwrap()));
656            AdapterAirContext {
657                to_pc,
658                reads,
659                writes,
660                instruction,
661            }
662        }
663    }
664
665    impl<T> From<Vec<T>> for DynArray<T> {
666        fn from(v: Vec<T>) -> Self {
667            Self(v)
668        }
669    }
670
671    impl<T> From<DynArray<T>> for Vec<T> {
672        fn from(v: DynArray<T>) -> Vec<T> {
673            v.0
674        }
675    }
676
677    impl<T, const N: usize, const M: usize> From<[[T; N]; M]> for DynArray<T> {
678        fn from(v: [[T; N]; M]) -> Self {
679            Self(v.into_iter().flatten().collect())
680        }
681    }
682
683    impl<T, const N: usize, const M: usize> From<DynArray<T>> for [[T; N]; M] {
684        fn from(v: DynArray<T>) -> Self {
685            assert_eq!(v.0.len(), N * M, "Incorrect vector length {}", v.0.len());
686            let mut it = v.0.into_iter();
687            from_fn(|_| from_fn(|_| it.next().unwrap()))
688        }
689    }
690
691    impl<T, const N: usize, const M: usize, const R: usize> From<[[[T; N]; M]; R]> for DynArray<T> {
692        fn from(v: [[[T; N]; M]; R]) -> Self {
693            Self(
694                v.into_iter()
695                    .flat_map(|x| x.into_iter().flatten())
696                    .collect(),
697            )
698        }
699    }
700
701    impl<T, const N: usize, const M: usize, const R: usize> From<DynArray<T>> for [[[T; N]; M]; R] {
702        fn from(v: DynArray<T>) -> Self {
703            assert_eq!(
704                v.0.len(),
705                N * M * R,
706                "Incorrect vector length {}",
707                v.0.len()
708            );
709            let mut it = v.0.into_iter();
710            from_fn(|_| from_fn(|_| from_fn(|_| it.next().unwrap())))
711        }
712    }
713
714    impl<T, const N: usize, const M1: usize, const M2: usize> From<([[T; N]; M1], [[T; N]; M2])>
715        for DynArray<T>
716    {
717        fn from(v: ([[T; N]; M1], [[T; N]; M2])) -> Self {
718            let vec =
719                v.0.into_iter()
720                    .flatten()
721                    .chain(v.1.into_iter().flatten())
722                    .collect();
723            Self(vec)
724        }
725    }
726
727    impl<T, const N: usize, const M1: usize, const M2: usize> From<DynArray<T>>
728        for ([[T; N]; M1], [[T; N]; M2])
729    {
730        fn from(v: DynArray<T>) -> Self {
731            assert_eq!(
732                v.0.len(),
733                N * (M1 + M2),
734                "Incorrect vector length {}",
735                v.0.len()
736            );
737            let mut it = v.0.into_iter();
738            (
739                from_fn(|_| from_fn(|_| it.next().unwrap())),
740                from_fn(|_| from_fn(|_| it.next().unwrap())),
741            )
742        }
743    }
744
745    // AdapterAirContext: BasicInterface -> DynInterface
746    impl<
747            T,
748            PI: Into<DynArray<T>>,
749            const NUM_READS: usize,
750            const NUM_WRITES: usize,
751            const READ_SIZE: usize,
752            const WRITE_SIZE: usize,
753        >
754        From<
755            AdapterAirContext<
756                T,
757                BasicAdapterInterface<T, PI, NUM_READS, NUM_WRITES, READ_SIZE, WRITE_SIZE>,
758            >,
759        > for AdapterAirContext<T, DynAdapterInterface<T>>
760    {
761        fn from(
762            ctx: AdapterAirContext<
763                T,
764                BasicAdapterInterface<T, PI, NUM_READS, NUM_WRITES, READ_SIZE, WRITE_SIZE>,
765            >,
766        ) -> Self {
767            AdapterAirContext {
768                to_pc: ctx.to_pc,
769                reads: ctx.reads.into(),
770                writes: ctx.writes.into(),
771                instruction: ctx.instruction.into(),
772            }
773        }
774    }
775
776    // AdapterAirContext: DynInterface -> BasicInterface
777    impl<
778            T,
779            PI,
780            const NUM_READS: usize,
781            const NUM_WRITES: usize,
782            const READ_SIZE: usize,
783            const WRITE_SIZE: usize,
784        > From<AdapterAirContext<T, DynAdapterInterface<T>>>
785        for AdapterAirContext<
786            T,
787            BasicAdapterInterface<T, PI, NUM_READS, NUM_WRITES, READ_SIZE, WRITE_SIZE>,
788        >
789    where
790        PI: From<DynArray<T>>,
791    {
792        fn from(ctx: AdapterAirContext<T, DynAdapterInterface<T>>) -> Self {
793            AdapterAirContext {
794                to_pc: ctx.to_pc,
795                reads: ctx.reads.into(),
796                writes: ctx.writes.into(),
797                instruction: ctx.instruction.into(),
798            }
799        }
800    }
801
802    // AdapterAirContext: FlatInterface -> DynInterface
803    impl<T: Clone, PI: Into<DynArray<T>>, const READ_CELLS: usize, const WRITE_CELLS: usize>
804        From<AdapterAirContext<T, FlatInterface<T, PI, READ_CELLS, WRITE_CELLS>>>
805        for AdapterAirContext<T, DynAdapterInterface<T>>
806    {
807        fn from(ctx: AdapterAirContext<T, FlatInterface<T, PI, READ_CELLS, WRITE_CELLS>>) -> Self {
808            AdapterAirContext {
809                to_pc: ctx.to_pc,
810                reads: ctx.reads.to_vec().into(),
811                writes: ctx.writes.to_vec().into(),
812                instruction: ctx.instruction.into(),
813            }
814        }
815    }
816
817    impl<T> From<MinimalInstruction<T>> for DynArray<T> {
818        fn from(m: MinimalInstruction<T>) -> Self {
819            Self(vec![m.is_valid, m.opcode])
820        }
821    }
822
823    impl<T> From<DynArray<T>> for MinimalInstruction<T> {
824        fn from(m: DynArray<T>) -> Self {
825            let mut m = m.0.into_iter();
826            MinimalInstruction {
827                is_valid: m.next().unwrap(),
828                opcode: m.next().unwrap(),
829            }
830        }
831    }
832
833    impl<T> From<DynArray<T>> for ImmInstruction<T> {
834        fn from(m: DynArray<T>) -> Self {
835            let mut m = m.0.into_iter();
836            ImmInstruction {
837                is_valid: m.next().unwrap(),
838                opcode: m.next().unwrap(),
839                immediate: m.next().unwrap(),
840            }
841        }
842    }
843
844    impl<T> From<ImmInstruction<T>> for DynArray<T> {
845        fn from(instruction: ImmInstruction<T>) -> Self {
846            DynArray::from(vec![
847                instruction.is_valid,
848                instruction.opcode,
849                instruction.immediate,
850            ])
851        }
852    }
853
854    // AdapterAirContext: BasicInterface -> VecHeapBranchAdapterInterface
855    impl<
856            T,
857            const BASIC_NUM_READS: usize,
858            const NUM_READS: usize,
859            const BLOCKS_PER_READ: usize,
860            const READ_SIZE: usize,
861        >
862        From<
863            AdapterAirContext<
864                T,
865                BasicAdapterInterface<T, ImmInstruction<T>, BASIC_NUM_READS, 0, READ_SIZE, 0>,
866            >,
867        >
868        for AdapterAirContext<
869            T,
870            VecHeapBranchAdapterInterface<T, NUM_READS, BLOCKS_PER_READ, READ_SIZE>,
871        >
872    {
873        fn from(
874            ctx: AdapterAirContext<
875                T,
876                BasicAdapterInterface<T, ImmInstruction<T>, BASIC_NUM_READS, 0, READ_SIZE, 0>,
877            >,
878        ) -> Self {
879            assert_eq!(BASIC_NUM_READS, NUM_READS * BLOCKS_PER_READ);
880            let mut reads_it = ctx.reads.into_iter();
881            let reads = from_fn(|_| from_fn(|_| reads_it.next().unwrap()));
882            AdapterAirContext {
883                to_pc: ctx.to_pc,
884                reads,
885                writes: (),
886                instruction: ctx.instruction,
887            }
888        }
889    }
890}