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}