Skip to main content
Version: 0.11 (stable)

u32 Operations

In this section we describe semantics and AIR constraints of operations over u32 values (i.e., 32-bit unsigned integers) as they are implemented in Miden VM.

Range checks

Most operations described below require some number of 16-bit range checks (i.e., verifying that the value of a field element is smaller than 2162^{16}). The number of required range checks varies between 22 and 44, depending on the operation. However, to simplify the constraint system, we force each relevant operation to consume exactly 44 range checks.

To perform these range checks, the prover puts the values to be range-checked into helper registers h0,...,h3h_0, ..., h_3, and then updates the range checker bus column brangeb_{range} according to the LogUp construction described in the range checker documentation, using multiplicity 11 for each value.

This operation is enforced via the following constraint. Note that since constraints cannot include divisions, the actual constraint which is enforced will be expressed equivalently with all denominators multiplied through, resulting in a constraint of degree 5.

brange=brange1(αh0)1(αh1)1(αh2)1(αh3) | degree=5b_{range}' = b_{range} - \frac{1}{(\alpha - h_0)} - \frac{1}{(\alpha - h_1)} - \frac{1}{(\alpha - h_2)} - \frac{1}{(\alpha - h_3)} \text{ | degree} = 5

The above is just a partial constraint as it does not show the range checker's part of the constraint, which adds the required values into the bus column. It also omits the selector flag which is used to turn this constraint on only when executing relevant operations.

Checking element validity

Another primitive which is required by most of the operations described below is checking whether four 16-bit values form a valid field element. Assume t0t_0, t1t_1, t2t_2, and t3t_3 are known to be 16-bit values, and we want to verify that 248t3+232t2+216t1+t02^{48} \cdot t_3 + 2^{32} \cdot t_2 + 2^{16} \cdot t_1 + t_0 is a valid field element.

For simplicity, let's denote:

vhi=216t3+t2vlo=216t1+t0v_{hi} = 2^{16} \cdot t_3 + t_2 v_{lo} = 2^{16} \cdot t_1 + t_0

We can then impose the following constraint to verify element validity:

(1m(2321vhi))vlo=0 | degree=3\left(1 - m \cdot (2^{32} - 1 - v_{hi})\right) \cdot v_{lo} = 0 \text{ | degree} = 3

Where mm is a value set non-deterministically by the prover.

The above constraint should hold only if either of the following hold:

  • vlo=0v_{lo} = 0
  • vhi2321v_{hi} \ne 2^{32} - 1

To satisfy the latter equation, the prover needs to set m=(2321vhi)1m = (2^{32} - 1 - v_{hi})^{-1}, which is possible only when vhi2321v_{hi} \ne 2^{32} - 1.

This constraint is sufficient because modulus 264232+12^{64} - 2^{32} + 1 in binary representation is 32 ones, followed by 31 zeros, followed by a single one:

11111111111111111111111111111111000000000000000000000000000000011111111111111111111111111111111100000000000000000000000000000001

This implies that the largest possible 64-bit value encoding a valid field element would be 32 ones, followed by 32 zeros:

11111111111111111111111111111111000000000000000000000000000000001111111111111111111111111111111100000000000000000000000000000000

Thus, for a 64-bit value to encode a valid field element, either the lower 32 bits must be all zeros, or the upper 32 bits must not be all ones (which is 23212^{32} - 1).

U32SPLIT

Assume aa is the element at the top of the stack. The U32SPLIT operation computes (b,c)a(b,c) \leftarrow a, where bb contains the lower 32 bits of aa, and cc contains the upper 32 bits of aa. The diagram below illustrates this graphically.

u32split

To facilitate this operation, the prover sets values in h0,...,h3h_0, ..., h_3 to 16-bit limbs of aa with h0h_0 being the least significant limb. Thus, stack transition for this operation must satisfy the following constraints:

s0=248h3+232h2+216h1+h0 | degree=1s_{0} = 2^{48} \cdot h_3 + 2^{32} \cdot h_2 + 2^{16} \cdot h_1 + h_0 \text{ | degree} = 1 s1=216h1+h0 | degree=1s_{1}' = 2^{16} \cdot h_1 + h_0 \text{ | degree} = 1 s0=216h3+h2 | degree=1s_{0}' = 2^{16} \cdot h_3 + h_2 \text{ | degree} = 1

In addition to the above constraints, we also need to verify that values in h0,...,h3h_0, ..., h_3 are smaller than 2162^{16}, which we can do using 16-bit range checks as described previously. Also, we need to make sure that values in h0,...,h3h_0, ..., h_3, when combined, form a valid field element, which we can do by putting a nondeterministic value mm into helper register h4h_4 and using the technique described here.

The effect of this operation on the rest of the stack is:

  • Right shift starting from position 11.

U32ASSERT2

Assume aa and bb are the elements at the top of the stack. The U32ASSERT2 verifies that both aa and bb are smaller than 2322^{32}. The diagram below illustrates this graphically.

u32assert2

To facilitate this operation, the prover sets values in h0h_0 and h1h_1 to low and high 16-bit limbs of aa, and values in h2h_2 and h3h_3 to to low and high 16-bit limbs of bb. Thus, stack transition for this operation must satisfy the following constraints:

s0=216h3+h2 | degree=1s_0' = 2^{16} \cdot h_3 + h_2 \text{ | degree} = 1 s1=216h1+h0 | degree=1s_1' = 2^{16} \cdot h_1 + h_0 \text{ | degree} = 1

In addition to the above constraints, we also need to verify that values in h0,...,h3h_0, ..., h_3 are smaller than 2162^{16}, which we can do using 16-bit range checks as described previously.

The effect of this operation on the rest of the stack is:

  • No change starting from position 00 - i.e., the state of the stack does not change.

U32ADD

Assume aa and bb are the values at the top of the stack which are known to be smaller than 2322^{32}. The U32ADD operation computes (c,d)a+b(c,d) \leftarrow a + b, where cc contains the low 32-bits of the result, and dd is the carry bit. The diagram below illustrates this graphically.

u32add

To facilitate this operation, the prover sets values in h0h_0, h1h_1, and h2h_2 to 16-bit limbs of a+ba+b with h0h_0 being the least significant limb. Value in h3h_3 is set to 00. Thus, stack transition for this operation must satisfy the following constraints:

s0+s1=232h2+216h1+h0 | degree=1s_0 + s_1 = 2^{32} \cdot h_2 + 2^{16} \cdot h_1 + h_0 \text{ | degree} = 1 s0=h2 | degree=1s_0' = h_2 \text{ | degree} = 1 s1=216h1+h0 | degree=1s_1' = 2^{16} \cdot h_1 + h_0 \text{ | degree} = 1

In addition to the above constraints, we also need to verify that values in h0,...,h3h_0, ..., h_3 are smaller than 2162^{16}, which we can do using 16-bit range checks as described previously.

The effect of this operation on the rest of the stack is:

  • No change starting from position 22.

U32ADD3

Assume aa, bb, cc are the values at the top of the stack which are known to be smaller than 2322^{32}. The U32ADD3 operation computes (d,e)a+b+c(d, e) \leftarrow a + b + c, where cc and dd contains the low and the high 32-bits of the result respectively. The diagram below illustrates this graphically.

u32add3

To facilitate this operation, the prover sets values in h0h_0, h1h_1, and h2h_2 to 16-bit limbs of a+b+ca+b+c with h0h_0 being the least significant limb. Value in h3h_3 is set to 00. Thus, stack transition for this operation must satisfy the following constraints:

s0+s1+s2=232h2+216h1+h0 | degree=1s_0 + s_1 + s_2 = 2^{32} \cdot h_2 + 2^{16} \cdot h_1 + h_0 \text{ | degree} = 1 s0=h2 | degree=1s_0' = h_2 \text{ | degree} = 1 s1=216h1+h0 | degree=1s_1' = 2^{16} \cdot h_1 + h_0 \text{ | degree} = 1

In addition to the above constraints, we also need to verify that values in h0,...,h3h_0, ..., h_3 are smaller than 2162^{16}, which we can do using 16-bit range checks as described previously.

The effect of this operation on the rest of the stack is:

  • Left shift starting from position 33.

U32SUB

Assume aa and bb are the values at the top of the stack which are known to be smaller than 2322^{32}. The U32SUB operation computes (c,d)ab(c, d) \leftarrow a - b, where cc contains the 32-bit result in two's complement, and dd is the borrow bit. The diagram below illustrates this graphically.

u32sub

To facilitate this operation, the prover sets values in h0h_0 and h1h_1 to the low and the high 16-bit limbs of aba-b respectively. Values in h2h_2 and h3h_3 are set to 00. Thus, stack transition for this operation must satisfy the following constraints:

s1=s0+s1+232s0 | degree=1s_1 = s_0 + s_1' + 2^{32} \cdot s_0' \text{ | degree} = 1 s02s0=0 | degree=2s_0'^2 - s_0' = 0 \text{ | degree} = 2 s1=216h1+h0 | degree=1s_1' = 2^{16} \cdot h_1 + h_0 \text{ | degree} = 1

In addition to the above constraints, we also need to verify that values in h0,...,h3h_0, ..., h_3 are smaller than 2162^{16}, which we can do using 16-bit range checks as described previously.

The effect of this operation on the rest of the stack is:

  • No change starting from position 22.

U32MUL

Assume aa and bb are the values at the top of the stack which are known to be smaller than 2322^{32}. The U32MUL operation computes (c,d)ab(c, d) \leftarrow a \cdot b, where cc and dd contain the low and the high 32-bits of the result respectively. The diagram below illustrates this graphically.

u32mul

To facilitate this operation, the prover sets values in h0,...,h3h_0, ..., h_3 to 16-bit limbs of aba \cdot b with h0h_0 being the least significant limb. Thus, stack transition for this operation must satisfy the following constraints:

s0s1=248h3+232h2+216h1+h0 | degree=2s_0 \cdot s_1 = 2^{48} \cdot h_3 + 2^{32} \cdot h_2 + 2^{16} \cdot h_1 + h_0 \text{ | degree} = 2 s1=216h1+h0 | degree=1s_1' = 2^{16} \cdot h_1 + h_0 \text{ | degree} = 1 s0=216h3+h2 | degree=1s_0' = 2^{16} \cdot h_3 + h_2 \text{ | degree} = 1

In addition to the above constraints, we also need to verify that values in h0,...,h3h_0, ..., h_3 are smaller than 2162^{16}, which we can do using 16-bit range checks as described previously. Also, we need to make sure that values in h0,...,h3h_0, ..., h_3, when combined, form a valid field element, which we can do by putting a nondeterministic value mm into helper register h4h_4 and using the technique described here.

The effect of this operation on the rest of the stack is:

  • No change starting from position 22.

U32MADD

Assume aa, bb, cc are the values at the top of the stack which are known to be smaller than 2322^{32}. The U32MADD operation computes (d,e)a+bc(d, e) \leftarrow a +b \cdot c, where cc and dd contains the low and the high 32-bits of a+bca + b \cdot c. The diagram below illustrates this graphically.

u32madd

To facilitate this operation, the prover sets values in h0,...,h3h_0, ..., h_3 to 16-bit limbs of a+bca + b \cdot c with h0h_0 being the least significant limb. Thus, stack transition for this operation must satisfy the following constraints:

s0s1+s2=248h3+232h2+216h1+h0 | degree=2s_0 \cdot s_1 + s_2 = 2^{48} \cdot h_3 + 2^{32} \cdot h_2 + 2^{16} \cdot h_1 + h_0 \text{ | degree} = 2 s1=216h1+h0 | degree=1s_1' = 2^{16} \cdot h_1 + h_0 \text{ | degree} = 1 s0=216h3+h2 | degree=1s_0' = 2^{16} \cdot h_3 + h_2 \text{ | degree} = 1

In addition to the above constraints, we also need to verify that values in h0,...,h3h_0, ..., h_3 are smaller than 2162^{16}, which we can do using 16-bit range checks as described previously. Also, we need to make sure that values in h0,...,h3h_0, ..., h_3, when combined, form a valid field element, which we can do by putting a nondeterministic value mm into helper register h4h_4 and using the technique described here.

Note: that the above constraints guarantee the correctness of the operation iff a+bca + b \cdot c cannot overflow field modules (which is the case for the field with modulus 264232+12^{64} - 2^{32} + 1).

The effect of this operation on the rest of the stack is:

  • Left shift starting from position 33.

U32DIV

Assume aa and bb are the values at the top of the stack which are known to be smaller than 2322^{32}. The U32DIV operation computes (c,d)a/b(c, d) \leftarrow a / b, where cc contains the quotient and dd contains the remainder. The diagram below illustrates this graphically.

u32div

To facilitate this operation, the prover sets values in h0h_0 and h1h_1 to 16-bit limbs of aca - c, and values in h2h_2 and h3h_3 to 16-bit limbs of bd1b - d - 1. Thus, stack transition for this operation must satisfy the following constraints:

s1=s0s1+s0 | degree=2s_1 = s_0 \cdot s_1' + s_0' \text{ | degree} = 2 s1s1=216h1+h0 | degree=1s_1 - s_1' = 2^{16} \cdot h_1 + h_0 \text{ | degree} = 1 s0s01=216h2+h3 | degree=1s_0 - s_0' - 1= 2^{16} \cdot h_2 + h_3 \text{ | degree} = 1

The second constraint enforces that s1s1s_1' \leq s_1, while the third constraint enforces that s0<s0s_0' < s_0.

The effect of this operation on the rest of the stack is:

  • No change starting from position 22.

U32AND

Assume aa and bb are the values at the top of the stack. The U32AND operation computes c(ab)c \leftarrow (a \land b), where cc is the result of performing a bitwise AND on aa and bb. The diagram below illustrates this graphically.

u32and

To facilitate this operation, we will need to make a request to the chiplet bus bchipb_{chip} by dividing its current value by the value representing bitwise operation request. This can be enforced with the following constraint:

bchip(α0+α1opu32and+α2s0+α3s1+α4s0)=bchip | degree=2b_{chip}' \cdot \left(\alpha_0 + \alpha_1 \cdot op_{u32and} + \alpha_2 \cdot s_0 + \alpha_3 \cdot s_1 + \alpha_4 \cdot s_0' \right) = b_{chip} \text{ | degree} = 2

In the above, opu32andop_{u32and} is the unique operation label of the bitwise AND operation.

Note: unlike for many other u32 operations, bitwise AND operation does not assume that the values at the top of the stack are smaller than 2322^{32}. This is because the lookup will fail for any inputs which are not 32-bit integers.

The effect of this operation on the rest of the stack is:

  • Left shift starting from position 22.

U32XOR

Assume aa and bb are the values at the top of the stack. The U32XOR operation computes c(ab)c \leftarrow (a \oplus b), where cc is the result of performing a bitwise XOR on aa and bb. The diagram below illustrates this graphically.

u32xor

To facilitate this operation, we will need to make a request to the chiplet bus bchipb_{chip} by dividing its current value by the value representing bitwise operation request. This can be enforced with the following constraint:

bchip(α0+α1opu32xor+α2s0+α3s1+α4s0)=bchip | degree=2b_{chip}' \cdot \left(\alpha_0 + \alpha_1 \cdot op_{u32xor} + \alpha_2 \cdot s_0 + \alpha_3 \cdot s_1 + \alpha_4 \cdot s_0' \right) = b_{chip} \text{ | degree} = 2

In the above, opu32xorop_{u32xor} is the unique operation label of the bitwise XOR operation.

Note: unlike for many other u32 operations, bitwise XOR operation does not assume that the values at the top of the stack are smaller than 2322^{32}. This is because the lookup will fail for any inputs which are not 32-bit integers.

The effect of this operation on the rest of the stack is:

  • Left shift starting from position 22.