miden_core/operations/mod.rs
1use core::fmt;
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6mod debug_metadata;
7pub use debug_metadata::AssemblyOp;
8
9use crate::{
10 Felt,
11 serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
12};
13
14// OPERATIONS AND CONTROL FLOW OPCODES
15// ================================================================================================
16
17/// Opcode patterns have the following meanings:
18/// - 00xxxxx operations do not shift the stack; constraint degree can be up to 2.
19/// - 010xxxx operations shift the stack the left; constraint degree can be up to 2.
20/// - 011xxxx operations shift the stack to the right; constraint degree can be up to 2.
21/// - 100xxx-: operations consume 4 range checks; constraint degree can be up to 3. These are used
22/// to encode most u32 operations.
23/// - 101xxx-: operations where constraint degree can be up to 3. These include control flow
24/// operations and some other operations requiring high degree constraints.
25/// - 11xxx--: operations where constraint degree can be up to 5. These include control flow
26/// operations and some other operations requiring very high degree constraints.
27#[rustfmt::skip]
28pub mod opcodes {
29 pub const NOOP: u8 = 0b0000_0000;
30 pub const EQZ: u8 = 0b0000_0001;
31 pub const NEG: u8 = 0b0000_0010;
32 pub const INV: u8 = 0b0000_0011;
33 pub const INCR: u8 = 0b0000_0100;
34 pub const NOT: u8 = 0b0000_0101;
35 /* unused 0b0000_0110 */
36 pub const MLOAD: u8 = 0b0000_0111;
37 pub const SWAP: u8 = 0b0000_1000;
38 pub const CALLER: u8 = 0b0000_1001;
39 pub const MOVUP2: u8 = 0b0000_1010;
40 pub const MOVDN2: u8 = 0b0000_1011;
41 pub const MOVUP3: u8 = 0b0000_1100;
42 pub const MOVDN3: u8 = 0b0000_1101;
43 pub const ADVPOPW: u8 = 0b0000_1110;
44 pub const EXPACC: u8 = 0b0000_1111;
45
46 pub const MOVUP4: u8 = 0b0001_0000;
47 pub const MOVDN4: u8 = 0b0001_0001;
48 pub const MOVUP5: u8 = 0b0001_0010;
49 pub const MOVDN5: u8 = 0b0001_0011;
50 pub const MOVUP6: u8 = 0b0001_0100;
51 pub const MOVDN6: u8 = 0b0001_0101;
52 pub const MOVUP7: u8 = 0b0001_0110;
53 pub const MOVDN7: u8 = 0b0001_0111;
54 pub const SWAPW: u8 = 0b0001_1000;
55 pub const EXT2MUL: u8 = 0b0001_1001;
56 pub const MOVUP8: u8 = 0b0001_1010;
57 pub const MOVDN8: u8 = 0b0001_1011;
58 pub const SWAPW2: u8 = 0b0001_1100;
59 pub const SWAPW3: u8 = 0b0001_1101;
60 pub const SWAPDW: u8 = 0b0001_1110;
61 pub const EMIT: u8 = 0b0001_1111;
62
63 pub const ASSERT: u8 = 0b0010_0000;
64 pub const EQ: u8 = 0b0010_0001;
65 pub const ADD: u8 = 0b0010_0010;
66 pub const MUL: u8 = 0b0010_0011;
67 pub const AND: u8 = 0b0010_0100;
68 pub const OR: u8 = 0b0010_0101;
69 pub const U32AND: u8 = 0b0010_0110;
70 pub const U32XOR: u8 = 0b0010_0111;
71 pub const FRIE2F4: u8 = 0b0010_1000;
72 pub const DROP: u8 = 0b0010_1001;
73 pub const CSWAP: u8 = 0b0010_1010;
74 pub const CSWAPW: u8 = 0b0010_1011;
75 pub const MLOADW: u8 = 0b0010_1100;
76 pub const MSTORE: u8 = 0b0010_1101;
77 pub const MSTOREW: u8 = 0b0010_1110;
78 /* unused 0b0010_1111 */
79
80 pub const PAD: u8 = 0b0011_0000;
81 pub const DUP0: u8 = 0b0011_0001;
82 pub const DUP1: u8 = 0b0011_0010;
83 pub const DUP2: u8 = 0b0011_0011;
84 pub const DUP3: u8 = 0b0011_0100;
85 pub const DUP4: u8 = 0b0011_0101;
86 pub const DUP5: u8 = 0b0011_0110;
87 pub const DUP6: u8 = 0b0011_0111;
88 pub const DUP7: u8 = 0b0011_1000;
89 pub const DUP9: u8 = 0b0011_1001;
90 pub const DUP11: u8 = 0b0011_1010;
91 pub const DUP13: u8 = 0b0011_1011;
92 pub const DUP15: u8 = 0b0011_1100;
93 pub const ADVPOP: u8 = 0b0011_1101;
94 pub const SDEPTH: u8 = 0b0011_1110;
95 pub const CLK: u8 = 0b0011_1111;
96
97 pub const U32ADD: u8 = 0b0100_0000;
98 pub const U32SUB: u8 = 0b0100_0010;
99 pub const U32MUL: u8 = 0b0100_0100;
100 pub const U32DIV: u8 = 0b0100_0110;
101 pub const U32SPLIT: u8 = 0b0100_1000;
102 pub const U32ASSERT2: u8 = 0b0100_1010;
103 pub const U32ADD3: u8 = 0b0100_1100;
104 pub const U32MADD: u8 = 0b0100_1110;
105
106 pub const HPERM: u8 = 0b0101_0000;
107 pub const MPVERIFY: u8 = 0b0101_0001;
108 pub const PIPE: u8 = 0b0101_0010;
109 pub const MSTREAM: u8 = 0b0101_0011;
110 pub const SPLIT: u8 = 0b0101_0100;
111 pub const LOOP: u8 = 0b0101_0101;
112 pub const SPAN: u8 = 0b0101_0110;
113 pub const JOIN: u8 = 0b0101_0111;
114 pub const DYN: u8 = 0b0101_1000;
115 pub const HORNERBASE: u8 = 0b0101_1001;
116 pub const HORNEREXT: u8 = 0b0101_1010;
117 pub const PUSH: u8 = 0b0101_1011;
118 pub const DYNCALL: u8 = 0b0101_1100;
119 pub const EVALCIRCUIT: u8 = 0b0101_1101;
120 pub const LOGDEFERRED: u8 = 0b0101_1110;
121
122 pub const MRUPDATE: u8 = 0b0110_0000;
123 pub const CRYPTOSTREAM: u8 = 0b0110_0100;
124 pub const SYSCALL: u8 = 0b0110_1000;
125 pub const CALL: u8 = 0b0110_1100;
126 pub const END: u8 = 0b0111_0000;
127 pub const REPEAT: u8 = 0b0111_0100;
128 pub const RESPAN: u8 = 0b0111_1000;
129 pub const HALT: u8 = 0b0111_1100;
130}
131
132// OPERATIONS
133// ================================================================================================
134
135/// The set of native VM basic block operations executable which take exactly one cycle to execute.
136///
137/// Specifically, the operations encoded here are only those which can be executed within basic
138/// blocks, i.e., they exclude all control flow operations (e.g., `Loop`, `Span`, `Join`, etc.).
139/// Note though that those operations have their own unique opcode which lives in the same 7-bit
140/// opcode space as the basic block operations.
141#[derive(Copy, Clone, Debug, Eq, PartialEq)]
142#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
143#[repr(u8)]
144pub enum Operation {
145 // ----- system operations -------------------------------------------------------------------
146 /// Advances cycle counter, but does not change the state of user stack.
147 Noop = opcodes::NOOP,
148
149 /// Pops the stack; if the popped value is not 1, execution fails.
150 ///
151 /// The internal value specifies an error code associated with the error in case when the
152 /// execution fails.
153 Assert(Felt) = opcodes::ASSERT,
154
155 /// Pushes the current depth of the stack onto the stack.
156 SDepth = opcodes::SDEPTH,
157
158 /// Overwrites the top four stack items with the hash of a function which initiated the current
159 /// SYSCALL. Thus, this operation can be executed only inside a SYSCALL code block.
160 Caller = opcodes::CALLER,
161
162 /// Pushes the current value of the clock cycle onto the stack. This operation can be used to
163 /// measure the number of cycles it has taken to execute the program up to the current
164 /// instruction.
165 Clk = opcodes::CLK,
166
167 /// Emits an event to the host.
168 ///
169 /// Semantics:
170 /// - Reads the event id from the top of the stack (as a `Felt`) without consuming it; the
171 /// caller is responsible for pushing and later dropping the id.
172 /// - User-defined events are conventionally derived from strings via
173 /// `hash_string_to_word(name)[0]` (Blake3-based) and may be emitted via immediate forms in
174 /// assembly (`emit.event("...")` or `emit.CONST` where `CONST=event("...")`).
175 /// - System events are identified by reserved [`SystemEvent`](crate::events::SystemEvent) IDs.
176 /// Most are handled by the VM; `SystemEvent::TraceEvent` triggers the host's optional
177 /// read-only trace handler for the trace event id at stack position 1.
178 /// - Any non system event ID is forwarded to the host's regular event handler.
179 ///
180 /// This operation does not change the state of the user stack aside from reading the value.
181 Emit = opcodes::EMIT,
182
183 // ----- field operations --------------------------------------------------------------------
184 /// Pops two elements off the stack, adds them, and pushes the result back onto the stack.
185 Add = opcodes::ADD,
186
187 /// Pops an element off the stack, negates it, and pushes the result back onto the stack.
188 Neg = opcodes::NEG,
189
190 /// Pops two elements off the stack, multiplies them, and pushes the result back onto the
191 /// stack.
192 Mul = opcodes::MUL,
193
194 /// Pops an element off the stack, computes its multiplicative inverse, and pushes the result
195 /// back onto the stack.
196 Inv = opcodes::INV,
197
198 /// Pops an element off the stack, adds 1 to it, and pushes the result back onto the stack.
199 Incr = opcodes::INCR,
200
201 /// Pops two elements off the stack, multiplies them, and pushes the result back onto the
202 /// stack.
203 ///
204 /// If either of the elements is greater than 1, execution fails. This operation is equivalent
205 /// to boolean AND.
206 And = opcodes::AND,
207
208 /// Pops two elements off the stack and subtracts their product from their sum.
209 ///
210 /// If either of the elements is greater than 1, execution fails. This operation is equivalent
211 /// to boolean OR.
212 Or = opcodes::OR,
213
214 /// Pops an element off the stack and subtracts it from 1.
215 ///
216 /// If the element is greater than one, the execution fails. This operation is equivalent to
217 /// boolean NOT.
218 Not = opcodes::NOT,
219
220 /// Pops two elements off the stack and compares them. If the elements are equal, pushes 1
221 /// onto the stack, otherwise pushes 0 onto the stack.
222 Eq = opcodes::EQ,
223
224 /// Pops an element off the stack and compares it to 0. If the element is 0, pushes 1 onto
225 /// the stack, otherwise pushes 0 onto the stack.
226 Eqz = opcodes::EQZ,
227
228 /// Computes a single turn of exponent accumulation for the given inputs. This operation can be
229 /// be used to compute a single turn of power of a field element.
230 ///
231 /// The top 4 elements of the stack are expected to be arranged as follows (form the top):
232 /// - least significant bit of the exponent in the previous trace if there's an expacc call,
233 /// otherwise ZERO
234 /// - exponent of base number `a` for this turn
235 /// - accumulated power of base number `a` so far
236 /// - number which needs to be shifted to the right
237 ///
238 /// At the end of the operation, exponent is replaced with its square, current value of power
239 /// of base number `a` on exponent is incorporated into the accumulator and the number is
240 /// shifted to the right by one bit.
241 Expacc = opcodes::EXPACC,
242
243 // ----- ext2 operations ---------------------------------------------------------------------
244 /// Computes the product of two elements in the extension field of degree 2 and pushes the
245 /// result back onto the stack as the third and fourth elements. Pushes 0 onto the stack as
246 /// the first and second elements.
247 ///
248 /// The extension field is defined as 𝔽ₚ\[x\]/(x² - 7), i.e. using the
249 /// irreducible quadratic polynomial x² - 7 over the base field.
250 Ext2Mul = opcodes::EXT2MUL,
251
252 // ----- u32 operations ----------------------------------------------------------------------
253 /// Pops an element off the stack, splits it into upper and lower 32-bit values, and pushes
254 /// these values back onto the stack.
255 U32split = opcodes::U32SPLIT,
256
257 /// Pops two elements off the stack, adds them, and splits the result into upper and lower
258 /// 32-bit values. Then pushes these values back onto the stack.
259 ///
260 /// If either of these elements is greater than or equal to 2^32, the result of this
261 /// operation is undefined.
262 U32add = opcodes::U32ADD,
263
264 /// Pops two elements off the stack and checks if each of them represents a 32-bit value.
265 /// If both of them are, they are pushed back onto the stack, otherwise an error is returned.
266 ///
267 /// The internal value specifies an error code associated with the error in case when the
268 /// assertion fails.
269 U32assert2(Felt) = opcodes::U32ASSERT2,
270
271 /// Pops three elements off the stack, adds them together, and splits the result into upper
272 /// and lower 32-bit values. Then pushes the result back onto the stack.
273 U32add3 = opcodes::U32ADD3,
274
275 /// Pops two elements off the stack and subtracts the first element from the second. Then,
276 /// the result, together with a flag indicating whether subtraction underflowed is pushed
277 /// onto the stack.
278 ///
279 /// If their of the values is greater than or equal to 2^32, the result of this operation is
280 /// undefined.
281 U32sub = opcodes::U32SUB,
282
283 /// Pops two elements off the stack, multiplies them, and splits the result into upper and
284 /// lower 32-bit values. Then pushes these values back onto the stack.
285 ///
286 /// If their of the values is greater than or equal to 2^32, the result of this operation is
287 /// undefined.
288 U32mul = opcodes::U32MUL,
289
290 /// Pops two elements off the stack and multiplies them. Then pops the third element off the
291 /// stack, and adds it to the result. Finally, splits the result into upper and lower 32-bit
292 /// values, and pushes them onto the stack.
293 ///
294 /// If any of the three values is greater than or equal to 2^32, the result of this operation
295 /// is undefined.
296 U32madd = opcodes::U32MADD,
297
298 /// Pops two elements off the stack and divides the second element by the first. Then pushes
299 /// the integer result of the division, together with the remainder, onto the stack.
300 ///
301 /// If their of the values is greater than or equal to 2^32, the result of this operation is
302 /// undefined.
303 U32div = opcodes::U32DIV,
304
305 /// Pops two elements off the stack, computes their binary AND, and pushes the result back
306 /// onto the stack.
307 ///
308 /// If either of the elements is greater than or equal to 2^32, execution fails.
309 U32and = opcodes::U32AND,
310
311 /// Pops two elements off the stack, computes their binary XOR, and pushes the result back
312 /// onto the stack.
313 ///
314 /// If either of the elements is greater than or equal to 2^32, execution fails.
315 U32xor = opcodes::U32XOR,
316
317 // ----- stack manipulation ------------------------------------------------------------------
318 /// Pushes 0 onto the stack.
319 Pad = opcodes::PAD,
320
321 /// Removes to element from the stack.
322 Drop = opcodes::DROP,
323
324 /// Pushes a copy of stack element 0 onto the stack.
325 Dup0 = opcodes::DUP0,
326
327 /// Pushes a copy of stack element 1 onto the stack.
328 Dup1 = opcodes::DUP1,
329
330 /// Pushes a copy of stack element 2 onto the stack.
331 Dup2 = opcodes::DUP2,
332
333 /// Pushes a copy of stack element 3 onto the stack.
334 Dup3 = opcodes::DUP3,
335
336 /// Pushes a copy of stack element 4 onto the stack.
337 Dup4 = opcodes::DUP4,
338
339 /// Pushes a copy of stack element 5 onto the stack.
340 Dup5 = opcodes::DUP5,
341
342 /// Pushes a copy of stack element 6 onto the stack.
343 Dup6 = opcodes::DUP6,
344
345 /// Pushes a copy of stack element 7 onto the stack.
346 Dup7 = opcodes::DUP7,
347
348 /// Pushes a copy of stack element 9 onto the stack.
349 Dup9 = opcodes::DUP9,
350
351 /// Pushes a copy of stack element 11 onto the stack.
352 Dup11 = opcodes::DUP11,
353
354 /// Pushes a copy of stack element 13 onto the stack.
355 Dup13 = opcodes::DUP13,
356
357 /// Pushes a copy of stack element 15 onto the stack.
358 Dup15 = opcodes::DUP15,
359
360 /// Swaps stack elements 0 and 1.
361 Swap = opcodes::SWAP,
362
363 /// Swaps stack elements 0, 1, 2, and 3 with elements 4, 5, 6, and 7.
364 SwapW = opcodes::SWAPW,
365
366 /// Swaps stack elements 0, 1, 2, and 3 with elements 8, 9, 10, and 11.
367 SwapW2 = opcodes::SWAPW2,
368
369 /// Swaps stack elements 0, 1, 2, and 3, with elements 12, 13, 14, and 15.
370 SwapW3 = opcodes::SWAPW3,
371
372 /// Swaps the top two words pair wise.
373 ///
374 /// Input: [D, C, B, A, ...]
375 /// Output: [B, A, D, C, ...]
376 SwapDW = opcodes::SWAPDW,
377
378 /// Moves stack element 2 to the top of the stack.
379 MovUp2 = opcodes::MOVUP2,
380
381 /// Moves stack element 3 to the top of the stack.
382 MovUp3 = opcodes::MOVUP3,
383
384 /// Moves stack element 4 to the top of the stack.
385 MovUp4 = opcodes::MOVUP4,
386
387 /// Moves stack element 5 to the top of the stack.
388 MovUp5 = opcodes::MOVUP5,
389
390 /// Moves stack element 6 to the top of the stack.
391 MovUp6 = opcodes::MOVUP6,
392
393 /// Moves stack element 7 to the top of the stack.
394 MovUp7 = opcodes::MOVUP7,
395
396 /// Moves stack element 8 to the top of the stack.
397 MovUp8 = opcodes::MOVUP8,
398
399 /// Moves the top stack element to position 2 on the stack.
400 MovDn2 = opcodes::MOVDN2,
401
402 /// Moves the top stack element to position 3 on the stack.
403 MovDn3 = opcodes::MOVDN3,
404
405 /// Moves the top stack element to position 4 on the stack.
406 MovDn4 = opcodes::MOVDN4,
407
408 /// Moves the top stack element to position 5 on the stack.
409 MovDn5 = opcodes::MOVDN5,
410
411 /// Moves the top stack element to position 6 on the stack.
412 MovDn6 = opcodes::MOVDN6,
413
414 /// Moves the top stack element to position 7 on the stack.
415 MovDn7 = opcodes::MOVDN7,
416
417 /// Moves the top stack element to position 8 on the stack.
418 MovDn8 = opcodes::MOVDN8,
419
420 /// Pops an element off the stack, and if the element is 1, swaps the top two remaining
421 /// elements on the stack. If the popped element is 0, the stack remains unchanged.
422 ///
423 /// If the popped element is neither 0 nor 1, execution fails.
424 CSwap = opcodes::CSWAP,
425
426 /// Pops an element off the stack, and if the element is 1, swaps the remaining elements
427 /// 0, 1, 2, and 3 with elements 4, 5, 6, and 7. If the popped element is 0, the stack
428 /// remains unchanged.
429 ///
430 /// If the popped element is neither 0 nor 1, execution fails.
431 CSwapW = opcodes::CSWAPW,
432
433 // ----- input / output ----------------------------------------------------------------------
434 /// Pushes the immediate value onto the stack.
435 Push(Felt) = opcodes::PUSH,
436
437 /// Removes the next element from the advice stack and pushes it onto the operand stack.
438 AdvPop = opcodes::ADVPOP,
439
440 /// Removes a word (4 elements) from the advice stack and overwrites the top four operand
441 /// stack elements with it.
442 AdvPopW = opcodes::ADVPOPW,
443
444 /// Pops an element off the stack, interprets it as a memory address, and replaces the
445 /// remaining 4 elements at the top of the stack with values located at the specified address.
446 MLoadW = opcodes::MLOADW,
447
448 /// Pops an element off the stack, interprets it as a memory address, and writes the remaining
449 /// 4 elements at the top of the stack into memory at the specified address.
450 MStoreW = opcodes::MSTOREW,
451
452 /// Pops an element off the stack, interprets it as a memory address, and pushes the first
453 /// element of the word located at the specified address to the stack.
454 MLoad = opcodes::MLOAD,
455
456 /// Pops an element off the stack, interprets it as a memory address, and writes the remaining
457 /// element at the top of the stack into the first element of the word located at the specified
458 /// memory address. The remaining 3 elements of the word are not affected.
459 MStore = opcodes::MSTORE,
460
461 /// Loads two words from memory, and replaces the top 8 elements of the stack with them,
462 /// element-wise, in stack order.
463 ///
464 /// The operation works as follows:
465 /// - The memory address of the first word is retrieved from 13th stack element (position 12).
466 /// - Two consecutive words, starting at this address, are loaded from memory.
467 /// - The top 8 elements of the stack are overwritten with these words (element-wise, in stack
468 /// order).
469 /// - Memory address (in position 12) is incremented by 2.
470 /// - All other stack elements remain the same.
471 MStream = opcodes::MSTREAM,
472
473 /// Pops two words from the advice stack, writes them to memory, and replaces the top 8
474 /// elements of the stack with them, element-wise, in stack order.
475 ///
476 /// The operation works as follows:
477 /// - Two words are popped from the advice stack.
478 /// - The destination memory address for the first word is retrieved from the 13th stack element
479 /// (position 12).
480 /// - The two words are written to memory consecutively, starting at this address.
481 /// - The top 8 elements of the stack are overwritten with these words (element-wise, in stack
482 /// order).
483 /// - Memory address (in position 12) is incremented by 2.
484 /// - All other stack elements remain the same.
485 Pipe = opcodes::PIPE,
486
487 /// Encrypts data from source memory to destination memory using the Poseidon2 sponge keystream.
488 ///
489 /// Two consecutive words (8 elements) are loaded from source memory, each element is added
490 /// to the corresponding element in the rate (top 8 stack elements), and the resulting
491 /// ciphertext is written to destination memory and replaces the rate. Source and destination
492 /// addresses are incremented by 8.
493 ///
494 /// Stack transition:
495 /// ```text
496 /// [rate(8), cap(4), src, dst, ...]
497 /// ↓
498 /// [ct(8), cap(4), src+8, dst+8, ...]
499 /// ```
500 /// where `ct = mem[src..src+8] + rate`, where addition is element-wise.
501 ///
502 /// After this operation, `hperm` should be applied to refresh the keystream for the next block.
503 CryptoStream = opcodes::CRYPTOSTREAM,
504
505 // ----- cryptographic operations ------------------------------------------------------------
506 /// Performs a Poseidon2 permutation on the top 3 words of the operand stack,
507 /// where the top 2 words are the rate (words C and B), the deepest word is the capacity (word
508 /// A), and the digest output is the middle word E.
509 ///
510 /// Stack transition:
511 /// [C, B, A, ...] -> [F, E, D, ...]
512 HPerm = opcodes::HPERM,
513
514 /// Verifies that a Merkle path from the specified node resolves to the specified root. This
515 /// operation can be used to prove that the prover knows a path in the specified Merkle tree
516 /// which starts with the specified node.
517 ///
518 /// The stack is expected to be arranged as follows (from the top):
519 /// - value of the node, 4 elements.
520 /// - depth of the path, 1 element.
521 /// - index of the node, 1 element.
522 /// - root of the tree, 4 elements.
523 ///
524 /// The Merkle path itself is expected to be provided by the prover non-deterministically (via
525 /// merkle sets). If the prover is not able to provide the required path, the operation fails.
526 /// The state of the stack does not change.
527 ///
528 /// The internal value specifies an error code associated with the error in case when the
529 /// assertion fails.
530 MpVerify(Felt) = opcodes::MPVERIFY,
531
532 /// Computes a new root of a Merkle tree where a node at the specified position is updated to
533 /// the specified value.
534 ///
535 /// The stack is expected to be arranged as follows (from the top):
536 /// - old value of the node, 4 element
537 /// - depth of the node, 1 element
538 /// - index of the node, 1 element
539 /// - current root of the tree, 4 elements
540 /// - new value of the node, 4 element
541 ///
542 /// The Merkle path for the node is expected to be provided by the prover non-deterministically
543 /// via the advice provider. At the end of the operation, the old node value is replaced with
544 /// the new root value, that is computed based on the provided path. Everything else on the
545 /// stack remains the same.
546 ///
547 /// The tree will always be copied into a new instance, meaning the advice provider will keep
548 /// track of both the old and new Merkle trees.
549 MrUpdate = opcodes::MRUPDATE,
550
551 /// Performs FRI (Fast Reed-Solomon Interactive Oracle Proofs) layer folding by a factor of 4
552 /// for FRI protocol executed in a degree 2 extension of the base field.
553 ///
554 /// This operation:
555 /// - Folds 4 query values (v0, v1), (v2, v3), (v4, v5), (v6, v7) into a single value (ne0, ne1)
556 /// - Computes new value of the domain generator power: poe' = poe^4
557 /// - Increments layer pointer (cptr) by 2
558 /// - Checks that the previous folding was done correctly
559 /// - Shifts the stack to move an item from the overflow table to stack position 15
560 ///
561 /// Stack transition:
562 /// Input: [v7, v6, v5, v4, v3, v2, v1, v0, f_pos, d_seg, poe, pe1, pe0, a1, a0, cptr, ...]
563 /// Output: [t1, t0, s1, s0, df3, df2, df1, df0, poe^2, f_tau, cptr+2, poe^4, f_pos, ne1, ne0,
564 /// eptr, ...] where eptr is moved from the stack overflow table and is the address of the
565 /// final FRI layer.
566 FriE2F4 = opcodes::FRIE2F4,
567
568 /// Performs 8 steps of the Horner evaluation method on a polynomial with coefficients over
569 /// the base field, i.e., it computes
570 ///
571 /// acc' = (((acc_tmp * alpha + c3) * alpha + c2) * alpha + c1) * alpha + c0
572 ///
573 /// where
574 ///
575 /// acc_tmp := (((acc * alpha + c7) * alpha + c6) * alpha + c5) * alpha + c4
576 ///
577 ///
578 /// In other words, the intsruction computes the evaluation at alpha of the polynomial
579 ///
580 /// P(X) := c7 * X^7 + c6 * X^6 + ... + c1 * X + c0
581 HornerBase = opcodes::HORNERBASE,
582
583 /// Performs 4 steps of the Horner evaluation method on a polynomial with coefficients over
584 /// the extension field, i.e., it computes
585 ///
586 /// acc' = (((acc * alpha + c3) * alpha + c2) * alpha + c1) * alpha + c0
587 ///
588 /// In other words, the intsruction computes the evaluation at alpha of the polynomial
589 ///
590 /// P(X) := c3 * X^3 + c2 * X^2 + c1 * X + c0
591 HornerExt = opcodes::HORNEREXT,
592
593 /// Evaluates an arithmetic circuit given a pointer to its description in memory, the number
594 /// of arithmetic gates, and the sum of the input and constant gates.
595 EvalCircuit = opcodes::EVALCIRCUIT,
596
597 /// Logs a precompile event. This instruction is used to signal that a precompile computation
598 /// was requested.
599 LogDeferred = opcodes::LOGDEFERRED,
600}
601
602impl Operation {
603 pub const OP_BITS: usize = 7;
604
605 /// Returns the opcode of this operation.
606 #[rustfmt::skip]
607 pub fn op_code(&self) -> u8 {
608 // SAFETY: This is safe because we have given this enum a primitive representation with
609 // #[repr(u8)], with the first field of the underlying union-of-structs the discriminant.
610 //
611 // See the section on "accessing the numeric value of the discriminant"
612 // here: https://doc.rust-lang.org/std/mem/fn.discriminant.html
613 unsafe { *<*const _>::from(self).cast::<u8>() }
614 }
615
616 /// Returns an immediate value carried by this operation.
617 // Proptest generators for operations in crate::mast::node::basic_block_node::tests discriminate
618 // on this flag, please update them when you modify the semantics of this method.
619 pub fn imm_value(&self) -> Option<Felt> {
620 match *self {
621 Self::Push(imm) => Some(imm),
622 _ => None,
623 }
624 }
625
626 /// Returns true if this basic block operation increases the stack depth by one.
627 ///
628 /// Note: this only applies to operations within basic blocks (i.e. those executed via
629 /// `ResumeBasicBlock` continuations). Control flow operations that affect stack size
630 /// (e.g. Split, Loop, Dyn) are handled separately.
631 pub fn increments_stack_size(&self) -> bool {
632 matches!(
633 self,
634 Self::Push(_)
635 | Self::Pad
636 | Self::Dup0
637 | Self::Dup1
638 | Self::Dup2
639 | Self::Dup3
640 | Self::Dup4
641 | Self::Dup5
642 | Self::Dup6
643 | Self::Dup7
644 | Self::Dup9
645 | Self::Dup11
646 | Self::Dup13
647 | Self::Dup15
648 | Self::U32split
649 | Self::SDepth
650 | Self::Clk
651 | Self::AdvPop
652 )
653 }
654
655 /// Returns true if this basic block operation decreases the stack depth by one.
656 ///
657 /// Note: this only applies to operations within basic blocks (i.e. those executed via
658 /// `ResumeBasicBlock` continuations). Control flow operations that affect stack size
659 /// (e.g. Split, Loop, Dyn) are handled separately.
660 pub fn decrements_stack_size(&self) -> bool {
661 matches!(
662 self,
663 Self::Drop
664 | Self::Assert(_)
665 | Self::Add
666 | Self::Mul
667 | Self::And
668 | Self::Or
669 | Self::Eq
670 | Self::U32add3
671 | Self::U32madd
672 | Self::U32and
673 | Self::U32xor
674 | Self::CSwap
675 | Self::CSwapW
676 | Self::MLoadW
677 | Self::MStoreW
678 | Self::MStore
679 | Self::FriE2F4
680 )
681 }
682}
683
684impl crate::prettier::PrettyPrint for Operation {
685 fn render(&self) -> crate::prettier::Document {
686 crate::prettier::display(self)
687 }
688}
689
690impl fmt::Display for Operation {
691 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
692 match self {
693 // ----- system operations ------------------------------------------------------------
694 Self::Noop => write!(f, "noop"),
695 Self::Assert(err_code) => write!(f, "assert({err_code})"),
696
697 Self::SDepth => write!(f, "sdepth"),
698 Self::Caller => write!(f, "caller"),
699
700 Self::Clk => write!(f, "clk"),
701
702 // ----- field operations -------------------------------------------------------------
703 Self::Add => write!(f, "add"),
704 Self::Neg => write!(f, "neg"),
705 Self::Mul => write!(f, "mul"),
706 Self::Inv => write!(f, "inv"),
707 Self::Incr => write!(f, "incr"),
708
709 Self::And => write!(f, "and"),
710 Self::Or => write!(f, "or"),
711 Self::Not => write!(f, "not"),
712
713 Self::Eq => write!(f, "eq"),
714 Self::Eqz => write!(f, "eqz"),
715
716 Self::Expacc => write!(f, "expacc"),
717
718 // ----- ext2 operations --------------------------------------------------------------
719 Self::Ext2Mul => write!(f, "ext2mul"),
720
721 // ----- u32 operations ---------------------------------------------------------------
722 Self::U32assert2(err_code) => write!(f, "u32assert2({err_code})"),
723 Self::U32split => write!(f, "u32split"),
724 Self::U32add => write!(f, "u32add"),
725 Self::U32add3 => write!(f, "u32add3"),
726 Self::U32sub => write!(f, "u32sub"),
727 Self::U32mul => write!(f, "u32mul"),
728 Self::U32madd => write!(f, "u32madd"),
729 Self::U32div => write!(f, "u32div"),
730
731 Self::U32and => write!(f, "u32and"),
732 Self::U32xor => write!(f, "u32xor"),
733
734 // ----- stack manipulation -----------------------------------------------------------
735 Self::Drop => write!(f, "drop"),
736 Self::Pad => write!(f, "pad"),
737
738 Self::Dup0 => write!(f, "dup0"),
739 Self::Dup1 => write!(f, "dup1"),
740 Self::Dup2 => write!(f, "dup2"),
741 Self::Dup3 => write!(f, "dup3"),
742 Self::Dup4 => write!(f, "dup4"),
743 Self::Dup5 => write!(f, "dup5"),
744 Self::Dup6 => write!(f, "dup6"),
745 Self::Dup7 => write!(f, "dup7"),
746 Self::Dup9 => write!(f, "dup9"),
747 Self::Dup11 => write!(f, "dup11"),
748 Self::Dup13 => write!(f, "dup13"),
749 Self::Dup15 => write!(f, "dup15"),
750
751 Self::Swap => write!(f, "swap"),
752 Self::SwapW => write!(f, "swapw"),
753 Self::SwapW2 => write!(f, "swapw2"),
754 Self::SwapW3 => write!(f, "swapw3"),
755 Self::SwapDW => write!(f, "swapdw"),
756
757 Self::MovUp2 => write!(f, "movup2"),
758 Self::MovUp3 => write!(f, "movup3"),
759 Self::MovUp4 => write!(f, "movup4"),
760 Self::MovUp5 => write!(f, "movup5"),
761 Self::MovUp6 => write!(f, "movup6"),
762 Self::MovUp7 => write!(f, "movup7"),
763 Self::MovUp8 => write!(f, "movup8"),
764
765 Self::MovDn2 => write!(f, "movdn2"),
766 Self::MovDn3 => write!(f, "movdn3"),
767 Self::MovDn4 => write!(f, "movdn4"),
768 Self::MovDn5 => write!(f, "movdn5"),
769 Self::MovDn6 => write!(f, "movdn6"),
770 Self::MovDn7 => write!(f, "movdn7"),
771 Self::MovDn8 => write!(f, "movdn8"),
772
773 Self::CSwap => write!(f, "cswap"),
774 Self::CSwapW => write!(f, "cswapw"),
775
776 // ----- input / output ---------------------------------------------------------------
777 Self::Push(value) => write!(f, "push({value})"),
778
779 Self::AdvPop => write!(f, "advpop"),
780 Self::AdvPopW => write!(f, "advpopw"),
781
782 Self::MLoadW => write!(f, "mloadw"),
783 Self::MStoreW => write!(f, "mstorew"),
784
785 Self::MLoad => write!(f, "mload"),
786 Self::MStore => write!(f, "mstore"),
787
788 Self::MStream => write!(f, "mstream"),
789 Self::Pipe => write!(f, "pipe"),
790 Self::CryptoStream => write!(f, "crypto_stream"),
791
792 Self::Emit => write!(f, "emit"),
793
794 // ----- cryptographic operations -----------------------------------------------------
795 Self::HPerm => write!(f, "hperm"),
796 Self::MpVerify(err_code) => write!(f, "mpverify({err_code})"),
797 Self::MrUpdate => write!(f, "mrupdate"),
798
799 // ----- STARK proof verification -----------------------------------------------------
800 Self::FriE2F4 => write!(f, "frie2f4"),
801 Self::HornerBase => write!(f, "horner_eval_base"),
802 Self::HornerExt => write!(f, "horner_eval_ext"),
803 Self::EvalCircuit => write!(f, "eval_circuit"),
804 Self::LogDeferred => write!(f, "log_deferred"),
805 }
806 }
807}
808
809impl Serializable for Operation {
810 fn write_into<W: ByteWriter>(&self, target: &mut W) {
811 target.write_u8(self.op_code());
812
813 // For operations that have extra data, encode it in `data`.
814 match self {
815 Operation::Assert(err_code)
816 | Operation::MpVerify(err_code)
817 | Operation::U32assert2(err_code) => {
818 err_code.write_into(target);
819 },
820 Operation::Push(value) => value.as_canonical_u64().write_into(target),
821
822 // Note: we explicitly write out all the operations so that whenever we make a
823 // modification to the `Operation` enum, we get a compile error here. This
824 // should help us remember to properly encode/decode each operation variant.
825 Operation::Noop
826 | Operation::SDepth
827 | Operation::Caller
828 | Operation::Clk
829 | Operation::Add
830 | Operation::Neg
831 | Operation::Mul
832 | Operation::Inv
833 | Operation::Incr
834 | Operation::And
835 | Operation::Or
836 | Operation::Not
837 | Operation::Eq
838 | Operation::Eqz
839 | Operation::Expacc
840 | Operation::Ext2Mul
841 | Operation::U32split
842 | Operation::U32add
843 | Operation::U32add3
844 | Operation::U32sub
845 | Operation::U32mul
846 | Operation::U32madd
847 | Operation::U32div
848 | Operation::U32and
849 | Operation::U32xor
850 | Operation::Pad
851 | Operation::Drop
852 | Operation::Dup0
853 | Operation::Dup1
854 | Operation::Dup2
855 | Operation::Dup3
856 | Operation::Dup4
857 | Operation::Dup5
858 | Operation::Dup6
859 | Operation::Dup7
860 | Operation::Dup9
861 | Operation::Dup11
862 | Operation::Dup13
863 | Operation::Dup15
864 | Operation::Swap
865 | Operation::SwapW
866 | Operation::SwapW2
867 | Operation::SwapW3
868 | Operation::SwapDW
869 | Operation::Emit
870 | Operation::MovUp2
871 | Operation::MovUp3
872 | Operation::MovUp4
873 | Operation::MovUp5
874 | Operation::MovUp6
875 | Operation::MovUp7
876 | Operation::MovUp8
877 | Operation::MovDn2
878 | Operation::MovDn3
879 | Operation::MovDn4
880 | Operation::MovDn5
881 | Operation::MovDn6
882 | Operation::MovDn7
883 | Operation::MovDn8
884 | Operation::CSwap
885 | Operation::CSwapW
886 | Operation::AdvPop
887 | Operation::AdvPopW
888 | Operation::MLoadW
889 | Operation::MStoreW
890 | Operation::MLoad
891 | Operation::MStore
892 | Operation::MStream
893 | Operation::Pipe
894 | Operation::CryptoStream
895 | Operation::HPerm
896 | Operation::MrUpdate
897 | Operation::FriE2F4
898 | Operation::HornerBase
899 | Operation::HornerExt
900 | Operation::EvalCircuit
901 | Operation::LogDeferred => (),
902 }
903 }
904}
905
906impl Operation {
907 /// Returns the serialized size of this operation in bytes.
908 pub(crate) fn encoded_size(&self) -> usize {
909 let mut size = size_of::<u8>();
910 match self {
911 Operation::Assert(err_code)
912 | Operation::MpVerify(err_code)
913 | Operation::U32assert2(err_code) => {
914 size += err_code.get_size_hint();
915 },
916 Operation::Push(value) => {
917 size += value.as_canonical_u64().get_size_hint();
918 },
919 Operation::Noop
920 | Operation::SDepth
921 | Operation::Caller
922 | Operation::Clk
923 | Operation::Add
924 | Operation::Neg
925 | Operation::Mul
926 | Operation::Inv
927 | Operation::Incr
928 | Operation::And
929 | Operation::Or
930 | Operation::Not
931 | Operation::Eq
932 | Operation::Eqz
933 | Operation::Expacc
934 | Operation::Ext2Mul
935 | Operation::U32split
936 | Operation::U32add
937 | Operation::U32add3
938 | Operation::U32sub
939 | Operation::U32mul
940 | Operation::U32madd
941 | Operation::U32div
942 | Operation::U32and
943 | Operation::U32xor
944 | Operation::Pad
945 | Operation::Drop
946 | Operation::Dup0
947 | Operation::Dup1
948 | Operation::Dup2
949 | Operation::Dup3
950 | Operation::Dup4
951 | Operation::Dup5
952 | Operation::Dup6
953 | Operation::Dup7
954 | Operation::Dup9
955 | Operation::Dup11
956 | Operation::Dup13
957 | Operation::Dup15
958 | Operation::Swap
959 | Operation::SwapW
960 | Operation::SwapW2
961 | Operation::SwapW3
962 | Operation::SwapDW
963 | Operation::Emit
964 | Operation::MovUp2
965 | Operation::MovUp3
966 | Operation::MovUp4
967 | Operation::MovUp5
968 | Operation::MovUp6
969 | Operation::MovUp7
970 | Operation::MovUp8
971 | Operation::MovDn2
972 | Operation::MovDn3
973 | Operation::MovDn4
974 | Operation::MovDn5
975 | Operation::MovDn6
976 | Operation::MovDn7
977 | Operation::MovDn8
978 | Operation::CSwap
979 | Operation::CSwapW
980 | Operation::AdvPop
981 | Operation::AdvPopW
982 | Operation::MLoadW
983 | Operation::MStoreW
984 | Operation::MLoad
985 | Operation::MStore
986 | Operation::MStream
987 | Operation::Pipe
988 | Operation::CryptoStream
989 | Operation::HPerm
990 | Operation::MrUpdate
991 | Operation::FriE2F4
992 | Operation::HornerBase
993 | Operation::HornerExt
994 | Operation::EvalCircuit
995 | Operation::LogDeferred => (),
996 }
997 size
998 }
999}
1000
1001impl Deserializable for Operation {
1002 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
1003 let op_code = source.read_u8()?;
1004
1005 let operation = match op_code {
1006 opcodes::NOOP => Self::Noop,
1007 opcodes::EQZ => Self::Eqz,
1008 opcodes::NEG => Self::Neg,
1009 opcodes::INV => Self::Inv,
1010 opcodes::INCR => Self::Incr,
1011 opcodes::NOT => Self::Not,
1012 opcodes::MLOAD => Self::MLoad,
1013 opcodes::SWAP => Self::Swap,
1014 opcodes::CALLER => Self::Caller,
1015 opcodes::MOVUP2 => Self::MovUp2,
1016 opcodes::MOVDN2 => Self::MovDn2,
1017 opcodes::MOVUP3 => Self::MovUp3,
1018 opcodes::MOVDN3 => Self::MovDn3,
1019 opcodes::ADVPOPW => Self::AdvPopW,
1020 opcodes::EXPACC => Self::Expacc,
1021
1022 opcodes::MOVUP4 => Self::MovUp4,
1023 opcodes::MOVDN4 => Self::MovDn4,
1024 opcodes::MOVUP5 => Self::MovUp5,
1025 opcodes::MOVDN5 => Self::MovDn5,
1026 opcodes::MOVUP6 => Self::MovUp6,
1027 opcodes::MOVDN6 => Self::MovDn6,
1028 opcodes::MOVUP7 => Self::MovUp7,
1029 opcodes::MOVDN7 => Self::MovDn7,
1030 opcodes::SWAPW => Self::SwapW,
1031 opcodes::EXT2MUL => Self::Ext2Mul,
1032 opcodes::MOVUP8 => Self::MovUp8,
1033 opcodes::MOVDN8 => Self::MovDn8,
1034 opcodes::SWAPW2 => Self::SwapW2,
1035 opcodes::SWAPW3 => Self::SwapW3,
1036 opcodes::SWAPDW => Self::SwapDW,
1037 opcodes::EMIT => Self::Emit,
1038
1039 opcodes::ASSERT => Self::Assert(Felt::read_from(source)?),
1040 opcodes::EQ => Self::Eq,
1041 opcodes::ADD => Self::Add,
1042 opcodes::MUL => Self::Mul,
1043 opcodes::AND => Self::And,
1044 opcodes::OR => Self::Or,
1045 opcodes::U32AND => Self::U32and,
1046 opcodes::U32XOR => Self::U32xor,
1047 opcodes::FRIE2F4 => Self::FriE2F4,
1048 opcodes::DROP => Self::Drop,
1049 opcodes::CSWAP => Self::CSwap,
1050 opcodes::CSWAPW => Self::CSwapW,
1051 opcodes::MLOADW => Self::MLoadW,
1052 opcodes::MSTORE => Self::MStore,
1053 opcodes::MSTOREW => Self::MStoreW,
1054
1055 opcodes::PAD => Self::Pad,
1056 opcodes::DUP0 => Self::Dup0,
1057 opcodes::DUP1 => Self::Dup1,
1058 opcodes::DUP2 => Self::Dup2,
1059 opcodes::DUP3 => Self::Dup3,
1060 opcodes::DUP4 => Self::Dup4,
1061 opcodes::DUP5 => Self::Dup5,
1062 opcodes::DUP6 => Self::Dup6,
1063 opcodes::DUP7 => Self::Dup7,
1064 opcodes::DUP9 => Self::Dup9,
1065 opcodes::DUP11 => Self::Dup11,
1066 opcodes::DUP13 => Self::Dup13,
1067 opcodes::DUP15 => Self::Dup15,
1068 opcodes::ADVPOP => Self::AdvPop,
1069 opcodes::SDEPTH => Self::SDepth,
1070 opcodes::CLK => Self::Clk,
1071
1072 opcodes::U32ADD => Self::U32add,
1073 opcodes::U32SUB => Self::U32sub,
1074 opcodes::U32MUL => Self::U32mul,
1075 opcodes::U32DIV => Self::U32div,
1076 opcodes::U32SPLIT => Self::U32split,
1077 opcodes::U32ASSERT2 => Self::U32assert2(Felt::read_from(source)?),
1078 opcodes::U32ADD3 => Self::U32add3,
1079 opcodes::U32MADD => Self::U32madd,
1080
1081 opcodes::HPERM => Self::HPerm,
1082 opcodes::MPVERIFY => Self::MpVerify(Felt::read_from(source)?),
1083 opcodes::PIPE => Self::Pipe,
1084 opcodes::MSTREAM => Self::MStream,
1085 opcodes::CRYPTOSTREAM => Self::CryptoStream,
1086 opcodes::HORNERBASE => Self::HornerBase,
1087 opcodes::HORNEREXT => Self::HornerExt,
1088 opcodes::LOGDEFERRED => Self::LogDeferred,
1089 opcodes::EVALCIRCUIT => Self::EvalCircuit,
1090
1091 opcodes::MRUPDATE => Self::MrUpdate,
1092 opcodes::PUSH => Self::Push(Felt::read_from(source)?),
1093 _ => {
1094 return Err(DeserializationError::InvalidValue(format!(
1095 "Invalid opcode '{op_code}'"
1096 )));
1097 },
1098 };
1099
1100 Ok(operation)
1101 }
1102
1103 /// Returns the minimum serialized size: 1 byte opcode.
1104 ///
1105 /// Some operations have additional payload (e.g., Push has 8 bytes for Felt),
1106 /// but the minimum is just the opcode byte.
1107 fn min_serialized_size() -> usize {
1108 1
1109 }
1110}
1111
1112#[cfg(test)]
1113mod tests;