Registers
Jolt proves the correctness of register updates using the Twist memory checking algorithm, specifically utilizing the "local" prover algorithm.
In this Twist instance, because we have 32 RISC-V registers and 32 virtual registers. This is small enough that we can use .
Virtual register layout
The 32 virtual registers (registers 32--63) are partitioned into three regions:
| Register(s) | Name | Purpose |
|---|---|---|
| 32 | reservation_w | Reservation address for LR.W/SC.W |
| 33 | reservation_d | Reservation address for LR.D/SC.D |
| 34 | mtvec | Machine trap-vector base address (CSR 0x305) |
| 35 | mscratch | Machine scratch register (CSR 0x340) |
| 36 | mepc | Machine exception program counter (CSR 0x341) |
| 37 | mcause | Machine trap cause (CSR 0x342) |
| 38 | mtval | Machine trap value (CSR 0x343) |
| 39 | mstatus | Machine status (CSR 0x300) |
| 40--46 | (instruction) | Temporary registers for virtual sequences, allocated by allocate() |
| 47--63 | (inline) | Temporary registers for inline sequences, allocated by allocate_for_inline() |
Reserved registers (32--39) are persistent: they retain their values across instructions and are never handed out by the allocator. The two reservation registers are used by the LR/SC virtual sequences to track atomic reservations. The six CSR registers store M-mode control/status state for trap handling.
Instruction registers (40--46) are a pool of 7 temporary registers used by allocate() within virtual sequences (e.g., for division, SC). They are released back to the pool when the virtual sequence completes.
Inline registers (47--63) are used by allocate_for_inline() for inline sequences. All inline registers must be zeroed at the end of the inline sequence to ensure clean state.
Deviations from the Twist algorithm as described in the paper
Our implementation of the Twist prover algorithm differs from the description given in the Twist and Shout paper in a couple of ways. One such deviation is wv virtualization. Other, register-specific deviations are described below.
Two reads, one write per cycle
The Twist algorithm as described in the paper assumes one read and one write per cycle, with corresponding polynomials (read address) and (write address).
However, in the context of the RV64IMAC instruction set, a single instruction (specifically, an R-type instruction) can read from two source registers (rs1 and rs2) and write to a destination register (rd).
Thus, we have two polynomials corresponding to rs1 and rs2, plus a ) polynomial corresponding to rd.
Similarly, there are two polynomials and one polynomial.
As a result, we have two read-checking sumcheck instances and one write-checking sumcheck instance. In practice, all three are batched into a single sumcheck instance.
Why we don't need one-hot checks
Normally in Twist and Shout, polynomials like and need to be checked for one-hotness using the Booleanity and Hamming weight sumchecks.
In the context of registers, we do not need to perform these one-hot checks for , , and .
Observe that the registers are hardcoded in the program bytecode. Thus we can virtualize , , and using the bytecode read-checking sumcheck. Since the bytecode is known by the verifier and assumed to be "well-formed", the soundness of the read-checking sumcheck effectively ensures the one-hotness of , , and .
A (simplified) sumcheck expression to illustrate how this virtualization works:
where if the instruction at index in the bytecode has .