zkaluvm/gfa/
instr.rs

1// AluVM extensions for zero knowledge, STARKs and SNARKs"
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Designed in 2024-2025 by Dr Maxim Orlovsky <orlovsky@ubideco.org>
6// Written in 2024-2025 by Dr Maxim Orlovsky <orlovsky@ubideco.org>
7//
8// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO),
9//                         Institute for Distributed and Cognitive Systems (InDCS), Switzerland.
10// Copyright (C) 2024-2025 Dr Maxim Orlovsky.
11// All rights under the above copyrights are reserved.
12//
13// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
14// in compliance with the License. You may obtain a copy of the License at
15//
16//        http://www.apache.org/licenses/LICENSE-2.0
17//
18// Unless required by applicable law or agreed to in writing, software distributed under the License
19// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
20// or implied. See the License for the specific language governing permissions and limitations under
21// the License.
22
23use aluvm::isa::{CtrlInstr, ReservedInstr};
24use aluvm::SiteId;
25use amplify::num::{u2, u3};
26
27use crate::{fe256, RegE};
28
29#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Display, From)]
30#[display(inner)]
31#[non_exhaustive]
32pub enum Instr<Id: SiteId> {
33    /// Control flow instructions.
34    #[from]
35    Ctrl(CtrlInstr<Id>),
36
37    #[from]
38    Gfa(FieldInstr),
39
40    /// Reserved instruction for future use in core `ALU` ISAs.
41    #[from]
42    Reserved(ReservedInstr),
43}
44
45/// Arithmetic instructions for finite fields.
46#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Display)]
47#[non_exhaustive]
48pub enum FieldInstr {
49    /// Tests if register contains a value and is not set to `None`.
50    ///
51    /// Sets `CO` register to [`Status::Ok`] if a register contains a value, and to [`Status::Fail`]
52    /// otherwise.
53    ///
54    /// Doesn't affect value in `CK` register.
55    #[display("test    {src}")]
56    Test { src: RegE },
57
58    /// Clears register value by setting it to `None`.
59    ///
60    /// Doesn't affect values in `CO` and `CK` registers.
61    #[display("clr     {dst}")]
62    Clr { dst: RegE },
63
64    /// Puts value into a register, replacing previous value in it, if there was any.
65    ///
66    /// Doesn't affect values in `CO` and `CK` registers.
67    #[display("mov     {dst}, {data}")]
68    PutD { dst: RegE, data: fe256 },
69
70    /// Puts zero (`0`) value into a register, replacing previous value in it, if there was any.
71    ///
72    /// Doesn't affect values in `CO` and `CK` registers.
73    #[display("mov     {dst}, 0")]
74    PutZ { dst: RegE },
75
76    /// Puts `val` value, which is a power of 2, into a register, replacing previous value in it, if
77    /// there was any.
78    ///
79    /// Doesn't affect values in `CO` and `CK` registers.
80    #[display("mov     {dst}, {val}")]
81    PutV { dst: RegE, val: ConstVal },
82
83    /// Test whether a value in a register fits in the provided number of bits.
84    ///
85    /// Sets `CO` register to [`Status::Ok`] if the value fits given number of bits, and to
86    /// [`Status::Fail`] otherwise.
87    ///
88    /// If `src` is set to `None`, sets both `CO` and `CK` to [`Status::Fail`]; otherwise leaves
89    /// value in `CK` unchanged.
90    #[display("fits    {src}, {bits}")]
91    Fits { src: RegE, bits: Bits },
92
93    /// Moves (copies) value from `src` to `dst` register, overwriting previous value in `dst`. If
94    /// `src` has no value (i.e. set to `None`), sets `dst` to `None`. State of `src` register
95    /// remains unaffected.
96    ///
97    /// Doesn't affect values in `CO` and `CK` registers.
98    #[display("mov     {dst}, {src}")]
99    Mov { dst: RegE, src: RegE },
100
101    /// Checks whether `src1` and `src2` registers are equal. If both `src1` and `src2` registers
102    /// contain no value, considers them equal.
103    ///
104    /// Sets `CO` register to represent equivalence of the registers.
105    ///
106    /// Doesn't affect value in `CK` register.
107    #[display("eq      {src1}, {src2}")]
108    Eq { src1: RegE, src2: RegE },
109
110    /// Negate value in `src` using finite-field arithmetics, and put result into `dst`.
111    ///
112    /// Doesn't affect values in `CO` register.
113    ///
114    /// If `src` is set to `None`, sets `CK` to [`Status::Fail`]; otherwise leaves value in  `CK`
115    /// unchanged.
116    #[display("neg     {dst}, {src}")]
117    Neg { dst: RegE, src: RegE },
118
119    /// Add `src` value to `dst_src` value using finite-field (modulo) arithmetics of the `order`,
120    /// putting result to `dst_src`.
121    ///
122    /// Doesn't affect values in `CO` register.
123    ///
124    /// If either `src` or `dst_src` (or both) is set to `None`, sets `CK` to [`Status::Fail`];
125    /// otherwise leaves value in  `CK` unchanged.
126    #[display("add     {dst_src}, {src}")]
127    Add { dst_src: RegE, src: RegE },
128
129    /// Multiply `src` value to `dst_src` value using finite-field (modulo) arithmetics of the
130    /// `order`, putting result to `dst_src`.
131    ///
132    /// Doesn't affect values in `CO` register.
133    ///
134    /// If either `src` or `dst_src` (or both) is set to `None`, sets `CK` to [`Status::Fail`];
135    /// otherwise leaves value in  `CK` unchanged.
136    #[display("mul     {dst_src}, {src}")]
137    Mul { dst_src: RegE, src: RegE },
138}
139
140#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Display)]
141#[repr(u8)]
142pub enum ConstVal {
143    #[display("1")]
144    Val1 = 0,
145
146    #[display("ffff_ffff_ffff_ffff#h")]
147    ValU64Max = 1,
148
149    #[display("ffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff#h")]
150    ValU128Max = 2,
151
152    #[display("-1#fe")]
153    ValFeMAX = 3,
154}
155
156impl From<u2> for ConstVal {
157    fn from(val: u2) -> Self {
158        match val {
159            x if x == ConstVal::Val1.to_u2() => ConstVal::Val1,
160            x if x == ConstVal::ValU64Max.to_u2() => ConstVal::ValU64Max,
161            x if x == ConstVal::ValU128Max.to_u2() => ConstVal::ValU128Max,
162            x if x == ConstVal::ValFeMAX.to_u2() => ConstVal::ValFeMAX,
163            _ => unreachable!(),
164        }
165    }
166}
167
168impl ConstVal {
169    #[inline]
170    pub const fn to_u2(self) -> u2 { u2::with(self as u8) }
171
172    pub fn to_fe256(self) -> Option<fe256> {
173        let val = match self {
174            ConstVal::Val1 => 1u128,
175            ConstVal::ValU64Max => u64::MAX as u128,
176            ConstVal::ValU128Max => u128::MAX,
177            ConstVal::ValFeMAX => return None,
178        };
179        Some(fe256::from(val))
180    }
181}
182
183#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Display)]
184#[repr(u8)]
185pub enum Bits {
186    #[display("8:bits")]
187    Bits8,
188
189    #[display("16:bits")]
190    Bits16,
191
192    #[display("24:bits")]
193    Bits24,
194
195    #[display("32:bits")]
196    Bits32,
197
198    #[display("48:bits")]
199    Bits48,
200
201    #[display("64:bits")]
202    Bits64,
203
204    #[display("96:bits")]
205    Bits96,
206
207    #[display("128:bits")]
208    Bits128,
209}
210
211impl From<u3> for Bits {
212    fn from(val: u3) -> Self {
213        match val {
214            x if x == Bits::Bits8.to_u3() => Bits::Bits8,
215            x if x == Bits::Bits16.to_u3() => Bits::Bits16,
216            x if x == Bits::Bits24.to_u3() => Bits::Bits24,
217            x if x == Bits::Bits32.to_u3() => Bits::Bits32,
218            x if x == Bits::Bits48.to_u3() => Bits::Bits48,
219            x if x == Bits::Bits64.to_u3() => Bits::Bits64,
220            x if x == Bits::Bits96.to_u3() => Bits::Bits96,
221            x if x == Bits::Bits128.to_u3() => Bits::Bits96,
222            _ => unreachable!(),
223        }
224    }
225}
226
227impl Bits {
228    #[inline]
229    pub const fn to_u3(self) -> u3 { u3::with(self as u8) }
230
231    pub fn from_bit_len(len: usize) -> Self {
232        match len {
233            8 => Bits::Bits8,
234            16 => Bits::Bits16,
235            24 => Bits::Bits24,
236            32 => Bits::Bits32,
237            48 => Bits::Bits48,
238            64 => Bits::Bits64,
239            96 => Bits::Bits96,
240            128 => Bits::Bits128,
241            invalid => panic!("unsupported bit length {invalid}"),
242        }
243    }
244
245    pub const fn bit_len(self) -> usize {
246        match self {
247            Bits::Bits8 => 8,
248            Bits::Bits16 => 16,
249            Bits::Bits24 => 24,
250            Bits::Bits32 => 32,
251            Bits::Bits48 => 48,
252            Bits::Bits64 => 64,
253            Bits::Bits96 => 96,
254            Bits::Bits128 => 128,
255        }
256    }
257}