Stack operation constraints
In addition to the constraints described in the previous section, we need to impose constraints to check that each VM operation is executed correctly.
For this purpose the VM exposes a set of operation-specific flags. These flags are set to when a given operation is executed, and to otherwise. The naming convention for these flags is . For example, would be set to when DUP operation is executed, and to otherwise. Operation flags are discussed in detail in the section below.
To describe how operation-specific constraints work, let's use an example with DUP operation. This operation pushes a copy of the top stack item onto the stack. The constraints we need to impose for this operation are as follows:
The first constraint enforces that the top stack item in the next row is the same as the top stack item in the current row. The second constraint enforces that all stack items (starting from item ) are shifted to the right by . We also need to impose all the constraints discussed in the previous section, be we omit them here.
Let's write similar constraints for DUP1 operation, which pushes a copy of the second stack item onto the stack:
It is easy to notice that while the first constraint changed, the second constraint remained the same - i.e., we are still just shifting the stack to the right.
In fact, for most operations it makes sense to make a distinction between constraints unique to the operation vs. more general constraints which enforce correct behavior for the stack items not affected by the operation. In the subsequent sections we describe in detail only the former constraints, and provide high-level descriptions of the more general constraints. Specifically, we indicate how the operation affects the rest of the stack (e.g., shifts right starting from position ).
Operation flags
As mentioned above, operation flags are used as selectors to enforce operation-specific constraints. That is, they turn on relevant constraints for a given operation. The VM defines operation flags over all 7-bit opcodes (with some combinations not valid due to the need for degree reduction of some op flags), though several opcodes are currently unused.
Operation flags are mutually exclusive. That is, if one flag is set to , all other flags are set to . Also, one of the flags is always guaranteed to be set to .
To compute values of operation flags we use op bits registers located in the decoder. These registers contain binary representations of operation codes (opcodes). Each opcode consists of bits, and thus, there are op bits registers. We denote these registers as . The values are computed by multiplying the op bit registers in various combinations. Notice that binary encoding down below is showed in big-endian order, so the flag bits correspond to the reverse order of the op bits registers, from to .
For example, the value of the flag for NOOP, which is encoded as 0000000, is computed as follows:
While the value of the DROP operation, which is encoded as 0101001 is computed as follows:
As can be seen from above, the degree for both of these flags is . Since degree of constraints in Miden VM can go up to , this means that operation-specific constraints cannot exceed degree . However, there are some operations which require constraints of higher degree (e.g., or even ). To support such constraints, we adopt the following scheme.
We organize the opcode slots into groups as shown below and also introduce two extra registers and for degree reduction:
| # of slots | degree | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | x | x | x | x | x | x | 0 | 0 | 64 | 7 |
| 1 | 0 | 0 | x | x | x | - | 0 | 0 | 8 | 6 |
| 1 | 0 | 1 | x | x | x | x | 1 | 0 | 16 | 5 |
| 1 | 1 | x | x | x | - | - | 0 | 1 | 8 | 4 |
In the above:
- Operation flags for operations in the first group (with prefix
0), are computed using all op bits, and thus their degree is . - Operation flags for operations in the second group (with prefix
100), are computed using only the first op bits, and thus their degree is . - Operation flags for opcode slots in the third group (with prefix
101), are computed using all op bits. We use the extra register (which is set to ) to reduce the degree by . Thus, the degree of op flags in this group is . This group has slots, but slot is unused. - Operation flags for operations in the fourth group (with prefix
11), are computed using only the first op bits. We use the extra register (which is set to ) to reduce the degree by . Thus, the degree of op flags in this group is .
The decoder shares one constraint across the three upper groups:
For prefixes 100 and 11, , so must be zero. For prefix 101, ,
and the second term rejects opcode . The two terms select disjoint opcode groups.
How operations are distributed between these groups is described in the sections below.
No stack shift operations
This group contains opcode slots whose AIR route does not shift the stack (this is almost all such operations). Since the op flag degree for these slots is , constraints selected by them cannot exceed degree .
| Operation | Opcode value | Binary encoding | Operation group | Flag degree |
|---|---|---|---|---|
NOOP | 000_0000 | System ops | ||
EQZ | 000_0001 | Field ops | ||
NEG | 000_0010 | Field ops | ||
INV | 000_0011 | Field ops | ||
INCR | 000_0100 | Field ops | ||
NOT | 000_0101 | Field ops | ||
<reserved> | 000_0110 | AIR no-shift alias | ||
MLOAD | 000_0111 | I/O ops | ||
SWAP | 000_1000 | Stack ops | ||
CALLER | 000_1001 | System ops | ||
MOVUP2 | 000_1010 | Stack ops | ||
MOVDN2 | 000_1011 | Stack ops | ||
MOVUP3 | 000_1100 | Stack ops | ||
MOVDN3 | 000_1101 | Stack ops | ||
ADVPOPW | 000_1110 | I/O ops | ||
EXPACC | 000_1111 | Field ops | ||
MOVUP4 | 001_0000 | Stack ops | ||
MOVDN4 | 001_0001 | Stack ops | ||
MOVUP5 | 001_0010 | Stack ops | ||
MOVDN5 | 001_0011 | Stack ops | ||
MOVUP6 | 001_0100 | Stack ops | ||
MOVDN6 | 001_0101 | Stack ops | ||
MOVUP7 | 001_0110 | Stack ops | ||
MOVDN7 | 001_0111 | Stack ops | ||
SWAPW | 001_1000 | Stack ops | ||
EXT2MUL | 001_1001 | Field ops | ||
MOVUP8 | 001_1010 | Stack ops | ||
MOVDN8 | 001_1011 | Stack ops | ||
SWAPW2 | 001_1100 | Stack ops | ||
SWAPW3 | 001_1101 | Stack ops | ||
SWAPDW | 001_1110 | Stack ops | ||
EMIT | 001_1111 | System ops |
Opcode is reserved/non-serializable: no Operation maps to it, but the AIR accepts the slot
as a full no-shift alias so every visible stack position remains constrained.
Left stack shift operations
This group contains operations which shift the stack to the left (i.e., remove an item from the stack). Most of left-shift operations are contained in this group. Since the op flag degree for these operations is , constraints for these operations cannot exceed degree .
| Operation | Opcode value | Binary encoding | Operation group | Flag degree |
|---|---|---|---|---|
ASSERT | 010_0000 | System ops | ||
EQ | 010_0001 | Field ops | ||
ADD | 010_0010 | Field ops | ||
MUL | 010_0011 | Field ops | ||
AND | 010_0100 | Field ops | ||
OR | 010_0101 | Field ops | ||
U32AND | 010_0110 | u32 ops | ||
U32XOR | 010_0111 | u32 ops | ||
FRIE2F4 | 010_1000 | Crypto ops | ||
DROP | 010_1001 | Stack ops | ||
CSWAP | 010_1010 | Stack ops | ||
CSWAPW | 010_1011 | Stack ops | ||
MLOADW | 010_1100 | I/O ops | ||
MSTORE | 010_1101 | I/O ops | ||
MSTOREW | 010_1110 | I/O ops | ||
<unused> | 010_1111 |
Right stack shift operations
This group contains operations which shift the stack to the right (i.e., push a new item onto the stack). Most of right-shift operations are contained in this group. Since the op flag degree for these operations is , constraints for these operations cannot exceed degree .
| Operation | Opcode value | Binary encoding | Operation group | Flag degree |
|---|---|---|---|---|
PAD | 011_0000 | Stack ops | ||
DUP | 011_0001 | Stack ops | ||
DUP1 | 011_0010 | Stack ops | ||
DUP2 | 011_0011 | Stack ops | ||
DUP3 | 011_0100 | Stack ops | ||
DUP4 | 011_0101 | Stack ops | ||
DUP5 | 011_0110 | Stack ops | ||
DUP6 | 011_0111 | Stack ops | ||
DUP7 | 011_1000 | Stack ops | ||
DUP9 | 011_1001 | Stack ops | ||
DUP11 | 011_1010 | Stack ops | ||
DUP13 | 011_1011 | Stack ops | ||
DUP15 | 011_1100 | Stack ops | ||
ADVPOP | 011_1101 | I/O ops | ||
SDEPTH | 011_1110 | I/O ops | ||
CLK | 011_1111 | System ops |
u32 operations
This group contains u32 operations. These operations are grouped together because all of them require range checks. The constraints for range checks are of degree , however, since all these operations require them, we can define a flag with common prefix 100 to serve as a selector for the range check constraints. The value of this flag is computed as follows:
The degree of this flag is , which is acceptable for a selector for degree constraints.
| Operation | Opcode value | Binary encoding | Operation group | Flag degree |
|---|---|---|---|---|
U32ADD | 100_0000 | u32 ops | ||
U32SUB | 100_0010 | u32 ops | ||
U32MUL | 100_0100 | u32 ops | ||
U32DIV | 100_0110 | u32 ops | ||
U32SPLIT | 100_1000 | u32 ops | ||
U32ASSERT2 | 100_1010 | u32 ops | ||
U32ADD3 | 100_1100 | u32 ops | ||
U32MADD | 100_1110 | u32 ops |
The last bit is unused for these operations. The shared constraint above fixes it to zero for
prefix 100.
Putting these operations into a group with flag degree is important for two other reasons:
- Constraints for the
U32SPLIToperation have degree . Thus, the degree of the op flag for this operation cannot exceed . - Operations
U32ADD3andU32MADDshift the stack left. Thus, having these two operations in this group and putting them under the common prefix10011allows us to include them in the degree- aggregate left-shift flag.
High-degree operations
This group contains operations which require constraints with degree up to . All operation bits are used for these flags. The extra column is used for degree reduction of the three high-degree bits.
| Operation | Opcode value | Binary encoding | Operation group | Flag degree |
|---|---|---|---|---|
COMPRESS | 101_0000 | Crypto ops | ||
MPVERIFY | 101_0001 | Crypto ops | ||
PIPE | 101_0010 | I/O ops | ||
MSTREAM | 101_0011 | I/O ops | ||
SPLIT | 101_0100 | Flow control ops | ||
LOOP | 101_0101 | Flow control ops | ||
SPAN | 101_0110 | Flow control ops | ||
JOIN | 101_0111 | Flow control ops | ||
DYN | 101_1000 | Flow control ops | ||
HORNERBASE | 101_1001 | Crypto ops | ||
HORNEREXT | 101_1010 | Crypto ops | ||
PUSH | 101_1011 | I/O ops | ||
DYNCALL | 101_1100 | Flow control ops | ||
EVALCIRCUIT | 101_1101 | Crypto ops | ||
LOGDEFERRED | 101_1110 | Crypto ops | ||
<unused> | 101_1111 |
Note that the SPLIT and LOOP operations share the common prefix 101010 and can be detected together with a flag of degree (using for degree reduction). Only SPLIT shifts the stack to the left, however: LOOP is do-while and reads no stack input — see LOOP block decoding.
The defining constraint for is:
The final slot in this group (101_1111, opcode ) is unused and is rejected by the shared
constraint above.
Very high-degree operations
This group contains operations which require constraints with degree up to .
| Operation | Opcode value | Binary encoding | Operation group | Flag degree |
|---|---|---|---|---|
MRUPDATE | 110_0000 | Crypto ops | ||
CRYPTOSTREAM | 110_0100 | Crypto ops | ||
SYSCALL | 110_1000 | Flow control ops | ||
CALL | 110_1100 | Flow control ops | ||
END | 111_0000 | Flow control ops | ||
REPEAT | 111_0100 | Flow control ops | ||
RESPAN | 111_1000 | Flow control ops | ||
HALT | 111_1100 | Flow control ops |
The last two bits are unused for these operations. The shared constraint fixes ; is fixed by:
The defining constraint for is:
Composite flags
Using the operation flags defined above, we can compute several composite flags which are used by various constraints in the VM.
Shift right flag
The right-shift flag is the low-degree aggregate used by the stack-depth and overflow-table constraints for operations that shift the stack right by one element. It is computed as follows:
In the above, evaluates to for all
right stack shift operations described previously. This works
because all these operations have a common prefix 011. We also add operations outside that group
which have the same net depth effect and overflow behavior: PUSH and U32SPLIT. This aggregate
is a lower-degree expression than the per-position right-shift selectors; U32SPLIT, for example,
overwrites while still increasing stack depth by one.
Shift left flag
The left-shift flag is the low-degree aggregate used by the stack-depth and overflow-table constraints for ordinary one-element left shifts. To simplify its description, we first compute the following intermediate variable:
A flag which is set to when or :
Using the above variable, we compute the left-shift flag as follows:
In the above:
- evaluates to for all left stack shift operations described previously. This works because all these operations have a common prefix
010. - is the SPLIT op flag.
- is the helper register in the decoder which is set to when we are exiting a
LOOPblock, and to otherwise. Because the loop body is always entered, coincides with "the ending node is a loop". - covers
DYN, which consumes the memory address of its target hash from the stack.
This selector deliberately excludes DYNCALL. Although DYNCALL also has a net one-element pop,
call entry resets the ordinary stack-depth and overflow-pointer columns, while the caller's
post-shift depth and overflow address are recorded in decoder helper columns and the overflow-table
removal is constrained separately. DYNCALL is added explicitly to the local selector which fills
from overflow, or sets it to zero when overflow is empty. As with , is
not a per-position selector; specialized operations such as FRIE2F4 constrain their visible
stack positions separately.
Control flow flag
The control flow flag is set to when a control flow operation is being executed by the VM, and to otherwise. Naively, this flag can be computed as follows:
However, this can be computed more efficiently via the common operation prefixes for the two groups of control flow operations as follows.
Immediate value flag
The immediate value flag is set to 1 when an operation has an immediate value, and 0 otherwise:
Note that the ASSERT, MPVERIFY and other operations have immediate values too. However, these immediate values are not included in the MAST digest, and hence are not considered for the flag.