sp1_recursion_compiler/ir/
instructions.rs

1use std::{
2    borrow::Cow,
3    ops::{Deref, Range},
4};
5
6#[cfg(feature = "debug")]
7use backtrace::Backtrace;
8use sp1_recursion_core::air::RecursionPublicValues;
9use sp1_stark::septic_curve::SepticCurve;
10
11use super::{
12    Array, CircuitV2FriFoldInput, CircuitV2FriFoldOutput, Config, Ext, Felt, FriFoldInput,
13    MemIndex, Ptr, Usize, Var,
14};
15
16/// An intermeddiate instruction set for implementing programs.
17///
18/// Programs written in the DSL can compile both to the recursive zkVM and the R1CS or Plonk-ish
19/// circuits.
20#[derive(Debug, Clone)]
21pub enum DslIr<C: Config> {
22    // Immediates.
23    /// Assigns an immediate to a variable (var = imm).
24    ImmV(Var<C::N>, C::N),
25    /// Assigns a field immediate to a field element (felt = field imm).
26    ImmF(Felt<C::F>, C::F),
27    /// Assigns an ext field immediate to an extension field element (ext = ext field imm).
28    ImmE(Ext<C::F, C::EF>, C::EF),
29
30    // Additions.
31    /// Add two variables (var = var + var).
32    AddV(Var<C::N>, Var<C::N>, Var<C::N>),
33    /// Add a variable and an immediate (var = var + imm).
34    AddVI(Var<C::N>, Var<C::N>, C::N),
35    /// Add two field elements (felt = felt + felt).
36    AddF(Felt<C::F>, Felt<C::F>, Felt<C::F>),
37    /// Add a field element and a field immediate (felt = felt + field imm).
38    AddFI(Felt<C::F>, Felt<C::F>, C::F),
39    /// Add two extension field elements (ext = ext + ext).
40    AddE(Ext<C::F, C::EF>, Ext<C::F, C::EF>, Ext<C::F, C::EF>),
41    /// Add an extension field element and an ext field immediate (ext = ext + ext field imm).
42    AddEI(Ext<C::F, C::EF>, Ext<C::F, C::EF>, C::EF),
43    /// Add an extension field element and a field element (ext = ext + felt).
44    AddEF(Ext<C::F, C::EF>, Ext<C::F, C::EF>, Felt<C::F>),
45    /// Add an extension field element and a field immediate (ext = ext + field imm).
46    AddEFI(Ext<C::F, C::EF>, Ext<C::F, C::EF>, C::F),
47    /// Add a field element and an ext field immediate (ext = felt + ext field imm).
48    AddEFFI(Ext<C::F, C::EF>, Felt<C::F>, C::EF),
49
50    // Subtractions.
51    /// Subtracts two variables (var = var - var).
52    SubV(Var<C::N>, Var<C::N>, Var<C::N>),
53    /// Subtracts a variable and an immediate (var = var - imm).
54    SubVI(Var<C::N>, Var<C::N>, C::N),
55    /// Subtracts an immediate and a variable (var = imm - var).
56    SubVIN(Var<C::N>, C::N, Var<C::N>),
57    /// Subtracts two field elements (felt = felt - felt).
58    SubF(Felt<C::F>, Felt<C::F>, Felt<C::F>),
59    /// Subtracts a field element and a field immediate (felt = felt - field imm).
60    SubFI(Felt<C::F>, Felt<C::F>, C::F),
61    /// Subtracts a field immediate and a field element (felt = field imm - felt).
62    SubFIN(Felt<C::F>, C::F, Felt<C::F>),
63    /// Subtracts two extension field elements (ext = ext - ext).
64    SubE(Ext<C::F, C::EF>, Ext<C::F, C::EF>, Ext<C::F, C::EF>),
65    /// Subtrancts an extension field element and an extension field immediate (ext = ext - ext
66    /// field imm).
67    SubEI(Ext<C::F, C::EF>, Ext<C::F, C::EF>, C::EF),
68    /// Subtracts an extension field immediate and an extension field element (ext = ext field imm
69    /// - ext).
70    SubEIN(Ext<C::F, C::EF>, C::EF, Ext<C::F, C::EF>),
71    /// Subtracts an extension field element and a field immediate (ext = ext - field imm).
72    SubEFI(Ext<C::F, C::EF>, Ext<C::F, C::EF>, C::F),
73    /// Subtracts an extension field element and a field element (ext = ext - felt).
74    SubEF(Ext<C::F, C::EF>, Ext<C::F, C::EF>, Felt<C::F>),
75
76    // Multiplications.
77    /// Multiplies two variables (var = var * var).
78    MulV(Var<C::N>, Var<C::N>, Var<C::N>),
79    /// Multiplies a variable and an immediate (var = var * imm).
80    MulVI(Var<C::N>, Var<C::N>, C::N),
81    /// Multiplies two field elements (felt = felt * felt).
82    MulF(Felt<C::F>, Felt<C::F>, Felt<C::F>),
83    /// Multiplies a field element and a field immediate (felt = felt * field imm).
84    MulFI(Felt<C::F>, Felt<C::F>, C::F),
85    /// Multiplies two extension field elements (ext = ext * ext).
86    MulE(Ext<C::F, C::EF>, Ext<C::F, C::EF>, Ext<C::F, C::EF>),
87    /// Multiplies an extension field element and an extension field immediate (ext = ext * ext
88    /// field imm).
89    MulEI(Ext<C::F, C::EF>, Ext<C::F, C::EF>, C::EF),
90    /// Multiplies an extension field element and a field immediate (ext = ext * field imm).
91    MulEFI(Ext<C::F, C::EF>, Ext<C::F, C::EF>, C::F),
92    /// Multiplies an extension field element and a field element (ext = ext * felt).
93    MulEF(Ext<C::F, C::EF>, Ext<C::F, C::EF>, Felt<C::F>),
94
95    // Divisions.
96    /// Divides two variables (var = var / var).
97    DivF(Felt<C::F>, Felt<C::F>, Felt<C::F>),
98    /// Divides a field element and a field immediate (felt = felt / field imm).
99    DivFI(Felt<C::F>, Felt<C::F>, C::F),
100    /// Divides a field immediate and a field element (felt = field imm / felt).
101    DivFIN(Felt<C::F>, C::F, Felt<C::F>),
102    /// Divides two extension field elements (ext = ext / ext).
103    DivE(Ext<C::F, C::EF>, Ext<C::F, C::EF>, Ext<C::F, C::EF>),
104    /// Divides an extension field element and an extension field immediate (ext = ext / ext field
105    /// imm).
106    DivEI(Ext<C::F, C::EF>, Ext<C::F, C::EF>, C::EF),
107    /// Divides and extension field immediate and an extension field element (ext = ext field imm /
108    /// ext).
109    DivEIN(Ext<C::F, C::EF>, C::EF, Ext<C::F, C::EF>),
110    /// Divides an extension field element and a field immediate (ext = ext / field imm).
111    DivEFI(Ext<C::F, C::EF>, Ext<C::F, C::EF>, C::F),
112    /// Divides a field immediate and an extension field element (ext = field imm / ext).
113    DivEFIN(Ext<C::F, C::EF>, C::F, Ext<C::F, C::EF>),
114    /// Divides an extension field element and a field element (ext = ext / felt).
115    DivEF(Ext<C::F, C::EF>, Ext<C::F, C::EF>, Felt<C::F>),
116
117    // Negations.
118    /// Negates a variable (var = -var).
119    NegV(Var<C::N>, Var<C::N>),
120    /// Negates a field element (felt = -felt).
121    NegF(Felt<C::F>, Felt<C::F>),
122    /// Negates an extension field element (ext = -ext).
123    NegE(Ext<C::F, C::EF>, Ext<C::F, C::EF>),
124    /// Inverts a variable (var = 1 / var).
125    InvV(Var<C::N>, Var<C::N>),
126    /// Inverts a field element (felt = 1 / felt).
127    InvF(Felt<C::F>, Felt<C::F>),
128    /// Inverts an extension field element (ext = 1 / ext).
129    InvE(Ext<C::F, C::EF>, Ext<C::F, C::EF>),
130
131    /// Selects order of felts based on a bit (should_swap, first result, second result, first
132    /// input, second input)
133    Select(Felt<C::F>, Felt<C::F>, Felt<C::F>, Felt<C::F>, Felt<C::F>),
134
135    // Control flow.
136    /// Executes a for loop with the parameters (start step value, end step value, step size, step
137    /// variable, body).
138    For(Box<(Usize<C::N>, Usize<C::N>, C::N, Var<C::N>, Vec<DslIr<C>>)>),
139    /// Executes an equal conditional branch with the parameters (lhs var, rhs var, then body, else
140    /// body).
141    IfEq(Box<(Var<C::N>, Var<C::N>, Vec<DslIr<C>>, Vec<DslIr<C>>)>),
142    /// Executes a not equal conditional branch with the parameters (lhs var, rhs var, then body,
143    /// else body).
144    IfNe(Box<(Var<C::N>, Var<C::N>, Vec<DslIr<C>>, Vec<DslIr<C>>)>),
145    /// Executes an equal conditional branch with the parameters (lhs var, rhs imm, then body, else
146    /// body).
147    IfEqI(Box<(Var<C::N>, C::N, Vec<DslIr<C>>, Vec<DslIr<C>>)>),
148    /// Executes a not equal conditional branch with the parameters (lhs var, rhs imm, then body,
149    /// else body).
150    IfNeI(Box<(Var<C::N>, C::N, Vec<DslIr<C>>, Vec<DslIr<C>>)>),
151    /// Break out of a for loop.
152    Break,
153
154    // Assertions.
155    /// Assert that two variables are equal (var == var).
156    AssertEqV(Var<C::N>, Var<C::N>),
157    /// Assert that two variables are not equal (var != var).
158    AssertNeV(Var<C::N>, Var<C::N>),
159    /// Assert that two field elements are equal (felt == felt).
160    AssertEqF(Felt<C::F>, Felt<C::F>),
161    /// Assert that two field elements are not equal (felt != felt).
162    AssertNeF(Felt<C::F>, Felt<C::F>),
163    /// Assert that two extension field elements are equal (ext == ext).
164    AssertEqE(Ext<C::F, C::EF>, Ext<C::F, C::EF>),
165    /// Assert that two extension field elements are not equal (ext != ext).
166    AssertNeE(Ext<C::F, C::EF>, Ext<C::F, C::EF>),
167    /// Assert that a variable is equal to an immediate (var == imm).
168    AssertEqVI(Var<C::N>, C::N),
169    /// Assert that a variable is not equal to an immediate (var != imm).
170    AssertNeVI(Var<C::N>, C::N),
171    /// Assert that a field element is equal to a field immediate (felt == field imm).
172    AssertEqFI(Felt<C::F>, C::F),
173    /// Assert that a field element is not equal to a field immediate (felt != field imm).
174    AssertNeFI(Felt<C::F>, C::F),
175    /// Assert that an extension field element is equal to an extension field immediate (ext == ext
176    /// field imm).
177    AssertEqEI(Ext<C::F, C::EF>, C::EF),
178    /// Assert that an extension field element is not equal to an extension field immediate (ext !=
179    /// ext field imm).
180    AssertNeEI(Ext<C::F, C::EF>, C::EF),
181
182    // Memory instructions.
183    /// Allocate (ptr, len, size) a memory slice of length len
184    Alloc(Ptr<C::N>, Usize<C::N>, usize),
185    /// Load variable (var, ptr, index)
186    LoadV(Var<C::N>, Ptr<C::N>, MemIndex<C::N>),
187    /// Load field element (var, ptr, index)
188    LoadF(Felt<C::F>, Ptr<C::N>, MemIndex<C::N>),
189    /// Load extension field
190    LoadE(Ext<C::F, C::EF>, Ptr<C::N>, MemIndex<C::N>),
191    /// Store variable at address
192    StoreV(Var<C::N>, Ptr<C::N>, MemIndex<C::N>),
193    /// Store field element at address
194    StoreF(Felt<C::F>, Ptr<C::N>, MemIndex<C::N>),
195    /// Store extension field at address
196    StoreE(Ext<C::F, C::EF>, Ptr<C::N>, MemIndex<C::N>),
197
198    /// Force reduction of field elements in circuit.
199    ReduceE(Ext<C::F, C::EF>),
200
201    // Bits.
202    /// Decompose a variable into size bits (bits = num2bits(var, size)). Should only be used when
203    /// target is a gnark circuit.
204    CircuitNum2BitsV(Var<C::N>, usize, Vec<Var<C::N>>),
205    /// Decompose a field element into bits (bits = num2bits(felt)). Should only be used when
206    /// target is a gnark circuit.
207    CircuitNum2BitsF(Felt<C::F>, Vec<Var<C::N>>),
208    /// Convert a Felt to a Var in a circuit. Avoids decomposing to bits and then reconstructing.
209    CircuitFelt2Var(Felt<C::F>, Var<C::N>),
210
211    // Hashing.
212    /// Permutes an array of baby bear elements using Poseidon2 (output = p2_permute(array)).
213    Poseidon2PermuteBabyBear(Box<(Array<C, Felt<C::F>>, Array<C, Felt<C::F>>)>),
214    /// Compresses two baby bear element arrays using Poseidon2 (output = p2_compress(array1,
215    /// array2)).
216    Poseidon2CompressBabyBear(
217        Box<(Array<C, Felt<C::F>>, Array<C, Felt<C::F>>, Array<C, Felt<C::F>>)>,
218    ),
219    /// Absorb an array of baby bear elements for a specified hash instance.
220    Poseidon2AbsorbBabyBear(Var<C::N>, Array<C, Felt<C::F>>),
221    /// Finalize and return the hash digest of a specified hash instance.
222    Poseidon2FinalizeBabyBear(Var<C::N>, Array<C, Felt<C::F>>),
223    /// Permutes an array of Bn254 elements using Poseidon2 (output = p2_permute(array)). Should
224    /// only be used when target is a gnark circuit.
225    CircuitPoseidon2Permute([Var<C::N>; 3]),
226    /// Permutates an array of BabyBear elements in the circuit.
227    CircuitPoseidon2PermuteBabyBear(Box<[Felt<C::F>; 16]>),
228    /// Permutates an array of BabyBear elements in the circuit using the skinny precompile.
229    CircuitV2Poseidon2PermuteBabyBear(Box<([Felt<C::F>; 16], [Felt<C::F>; 16])>),
230    /// Commits the public values.
231    CircuitV2CommitPublicValues(Box<RecursionPublicValues<Felt<C::F>>>),
232
233    // Miscellaneous instructions.
234    /// Decompose hint operation of a usize into an array. (output = num2bits(usize)).
235    HintBitsU(Array<C, Var<C::N>>, Usize<C::N>),
236    /// Decompose hint operation of a variable into an array. (output = num2bits(var)).
237    HintBitsV(Array<C, Var<C::N>>, Var<C::N>),
238    /// Decompose hint operation of a field element into an array. (output = num2bits(felt)).
239    HintBitsF(Array<C, Var<C::N>>, Felt<C::F>),
240    /// Decompose hint operation of a field element into an array. (output = num2bits(felt)).
241    CircuitV2HintBitsF(Vec<Felt<C::F>>, Felt<C::F>),
242    /// Prints a variable.
243    PrintV(Var<C::N>),
244    /// Prints a field element.
245    PrintF(Felt<C::F>),
246    /// Prints an extension field element.
247    PrintE(Ext<C::F, C::EF>),
248    /// Throws an error.
249    Error(),
250
251    /// Converts an ext to a slice of felts.  
252    HintExt2Felt(Array<C, Felt<C::F>>, Ext<C::F, C::EF>),
253    /// Hint the length of the next array.  
254    HintLen(Var<C::N>),
255    /// Hint an array of variables.
256    HintVars(Array<C, Var<C::N>>),
257    /// Hint an array of field elements.
258    HintFelts(Array<C, Felt<C::F>>),
259    /// Hint an array of extension field elements.
260    HintExts(Array<C, Ext<C::F, C::EF>>),
261    /// Hint an array of field elements.
262    CircuitV2HintFelts(Felt<C::F>, usize),
263    /// Hint an array of extension field elements.
264    CircuitV2HintExts(Ext<C::F, C::EF>, usize),
265    /// Witness a variable. Should only be used when target is a gnark circuit.
266    WitnessVar(Var<C::N>, u32),
267    /// Witness a field element. Should only be used when target is a gnark circuit.
268    WitnessFelt(Felt<C::F>, u32),
269    /// Witness an extension field element. Should only be used when target is a gnark circuit.
270    WitnessExt(Ext<C::F, C::EF>, u32),
271    /// Label a field element as the ith public input.
272    Commit(Felt<C::F>, Var<C::N>),
273    /// Registers a field element to the public inputs.
274    RegisterPublicValue(Felt<C::F>),
275    /// Operation to halt the program. Should be the last instruction in the program.  
276    Halt,
277
278    // Public inputs for circuits.
279    /// Asserts that the inputted var is equal the circuit's vkey hash public input. Should only be
280    /// used when target is a gnark circuit.
281    CircuitCommitVkeyHash(Var<C::N>),
282    /// Asserts that the inputted var is equal the circuit's committed values digest public input.
283    /// Should only be used when target is a gnark circuit.
284    CircuitCommitCommittedValuesDigest(Var<C::N>),
285
286    /// Adds two elliptic curve points. (sum, point_1, point_2).
287    CircuitV2HintAddCurve(
288        Box<(SepticCurve<Felt<C::F>>, SepticCurve<Felt<C::F>>, SepticCurve<Felt<C::F>>)>,
289    ),
290
291    // FRI specific instructions.
292    /// Executes a FRI fold operation. 1st field is the size of the fri fold input array.  2nd
293    /// field is the fri fold input array.  See [`FriFoldInput`] for more details.
294    FriFold(Var<C::N>, Array<C, FriFoldInput<C>>),
295    // FRI specific instructions.
296    /// Executes a FRI fold operation. Input is the fri fold input array.  See [`FriFoldInput`] for
297    /// more details.
298    CircuitV2FriFold(Box<(CircuitV2FriFoldOutput<C>, CircuitV2FriFoldInput<C>)>),
299    // FRI specific instructions.
300    /// Executes a Batch FRI loop. Input is the power of alphas, evaluations at z, and evaluations
301    /// at x.
302    CircuitV2BatchFRI(
303        Box<(Ext<C::F, C::EF>, Vec<Ext<C::F, C::EF>>, Vec<Ext<C::F, C::EF>>, Vec<Felt<C::F>>)>,
304    ),
305    /// Select's a variable based on a condition. (select(cond, true_val, false_val) => output).
306    /// Should only be used when target is a gnark circuit.
307    CircuitSelectV(Var<C::N>, Var<C::N>, Var<C::N>, Var<C::N>),
308    /// Select's a field element based on a condition. (select(cond, true_val, false_val) =>
309    /// output). Should only be used when target is a gnark circuit.
310    CircuitSelectF(Var<C::N>, Felt<C::F>, Felt<C::F>, Felt<C::F>),
311    /// Select's an extension field element based on a condition. (select(cond, true_val,
312    /// false_val) => output). Should only be used when target is a gnark circuit.
313    CircuitSelectE(Var<C::N>, Ext<C::F, C::EF>, Ext<C::F, C::EF>, Ext<C::F, C::EF>),
314    /// Converts an ext to a slice of felts. Should only be used when target is a gnark circuit.
315    CircuitExt2Felt([Felt<C::F>; 4], Ext<C::F, C::EF>),
316    /// Converts a slice of felts to an ext. Should only be used when target is a gnark circuit.
317    CircuitFelts2Ext([Felt<C::F>; 4], Ext<C::F, C::EF>),
318
319    // Debugging instructions.
320    /// Executes less than (var = var < var).  This operation is NOT constrained.
321    LessThan(Var<C::N>, Var<C::N>, Var<C::N>),
322    /// Tracks the number of cycles used by a block of code annotated by the string input.
323    CycleTracker(String),
324    /// Tracks the number of cycles used by a block of code annotated by the string input.
325    CycleTrackerV2Enter(Cow<'static, str>),
326    /// Tracks the number of cycles used by a block of code annotated by the string input.
327    CycleTrackerV2Exit,
328
329    /// Reverse bits exponentiation.
330    ExpReverseBitsLen(Ptr<C::N>, Var<C::N>, Var<C::N>),
331    /// Reverse bits exponentiation. Output, base, exponent bits.
332    CircuitV2ExpReverseBits(Felt<C::F>, Felt<C::F>, Vec<Felt<C::F>>),
333
334    // Structuring IR constructors.
335    /// Blocks that may be executed in parallel.
336    Parallel(Vec<DslIrBlock<C>>),
337
338    /// Pass a backtrace for debugging.
339    #[cfg(feature = "debug")]
340    DebugBacktrace(Backtrace),
341}
342
343/// A block of instructions.
344#[derive(Clone, Default, Debug)]
345pub struct DslIrBlock<C: Config> {
346    pub ops: Vec<DslIr<C>>,
347    pub addrs_written: Range<u32>,
348}
349
350#[derive(Clone, Debug)]
351pub struct DslIrProgram<C: Config>(DslIrBlock<C>);
352
353impl<C: Config> DslIrProgram<C> {
354    /// # Safety
355    /// The given block must represent a well formed program. This is defined as the following:
356    /// - reads are performed after writes, according to a "happens-before" relation; and
357    /// - an address is written to at most once.
358    ///
359    /// The "happens-before" relation is defined as follows:
360    /// - It is a strict partial order, meaning it is transitive, irreflexive, and asymmetric.
361    /// - Contiguous sequences of instructions that are not [`DslIr::Parallel`] in a [`DslIrBlock`]
362    ///   are linearly ordered. Call these sequences "sequential blocks."
363    /// - For each `DslIrBlock` in the `DslIr::Parallel` variant:
364    ///   - The block's first instruction comes after the last instruction in the parent's previous
365    ///     sequential block. if it exists.
366    ///   - The block's last instruction comes before the first instruction in the parent's next
367    ///     sequential block, if it exists.
368    ///   - If the sequential blocks mentioned in eiither of the previous two rules do not exist,
369    ///     then the situation is that of two consecutive [`DslIr::Parallel`] instructions `x` and
370    ///     `y`. Then each last instruction of `x` comes before each first instruction of `y`.
371    pub unsafe fn new_unchecked(block: DslIrBlock<C>) -> Self {
372        Self(block)
373    }
374
375    pub fn into_inner(self) -> DslIrBlock<C> {
376        self.0
377    }
378}
379
380impl<C: Config> Default for DslIrProgram<C> {
381    fn default() -> Self {
382        // SAFETY: An empty block is always well formed.
383        unsafe { Self::new_unchecked(DslIrBlock::default()) }
384    }
385}
386
387impl<C: Config> Deref for DslIrProgram<C> {
388    type Target = DslIrBlock<C>;
389
390    fn deref(&self) -> &Self::Target {
391        &self.0
392    }
393}