Flow Control
zkMIPS enforces MIPS32r2 control flow verification via dedicated Branch and Jump chips, ensuring precise execution of program control instructions.
Branch Chip
MIPS branch instructions execute conditional jumps through register comparisons (BEQ/BNE for equality, BGTZ/BLEZ etc. for sign checks). They calculate targets using 16-bit offsets shifted left twice (enabling ±128KB jumps) and feature a mandatory branch delay slot that always executes the next instruction—simplifying pipelining by allowing compiler-controlled optimizations.
Structure Description
Branch chip uses columns to record the following information.
-
Control Flow Management
- Tracks current and future program counter states across sequential and branching execution paths (
pc, next_pc,target_pc,next_next_pc
). - Implements 32-bit address validation through dedicated range-checking components(
next_pc_range_checker, target_pc_range_checker, next_next_pc_range_checker
).
- Tracks current and future program counter states across sequential and branching execution paths (
-
Operand Handling System
- Stores three register/immediate values following MIPS three-operand convention (
op_a_value, op_b_value, op_c_value
). - Contains special flag detection for zero-register operand scenarios (
op_a_0
).
- Stores three register/immediate values following MIPS three-operand convention (
-
Instruction Semantics Encoding
Embeds five mutually exclusive flags corresponding to MIPS branch opcodes (
is_beq, is_bltz, is_blez, is_bgtz, is_bgez
). -
Execution State Tracking
Maintains dual execution path indicators for taken/not-taken branch conditions(
is_branching, not_branching
). -
Comparison Logic Core
Evaluates signed integer relationships between primary operands, generating equality, greater-than, and less-than condition flags (
a_eq_b, a_gt_b, a_lt_b
).
Major Constraints
We use the following key constraints to validate the branch chip:
-
Program Counter Validation
- Range check for all PC values (
pc
,next_pc
,target_pc
,next_next_pc
, etc.). - Branching case:
next_pc
must equaltarget_pc
. - Non-branching case:
next_next_pc
must equalnext_pc + 4
. is_branching
andnot_branching
are mutually exclusive and exhaustive for real instructions.
- Range check for all PC values (
-
Instruction Validity
- Exactly one branch instruction flag must be active per row (
1 = is_beq + ... + is_bgtz
). - Instruction flags are strictly boolean values (0/1).
- Opcode validity is enforced through linear combination verification.
- Exactly one branch instruction flag must be active per row (
-
Branch Condition Logic
is_branching
andnot_branching
consistent whti condition flags.
Jump Chip
MIPS jump instructions force unconditional PC changes via absolute or register-based targets. They calculate 256MB-range addresses by combining PC's upper bits with 26-bit immediates or use full 32-bit register values. All jumps enforce a mandatory delay slot executing the next instruction—enabling compiler-driven pipeline optimizations without speculative execution.
Structure Description
Jump chip uses columns to record the following information:
-
Control Flow Management
- Tracks current program counter and jump targets (
pc, next_pc, target_pc
). - Implements 32-bit address validation via dedicated range checkers (
next_pc_range_checker, target_pc_range_checker, op_a_range_checker
).
- Tracks current program counter and jump targets (
-
Operand System
- Stores three operands for jump address calculation (
op_a_value, op_b_value, op_c_value
). - Contains zero-register flag detection for first operand register (
op_a_0
).
- Stores three operands for jump address calculation (
-
Instruction Semantics
Embeds three mutually exclusive jump-type flags (
is_jump, is_jumpi, is_jumpdirect
).
Major Constraints
We use the fllowing key constraints to validate the jump chip:
-
Instruction Validity
-
Exactly one jump instruction flag must be active per row:
#![allow(unused)] fn main() { 1 = is_jump + is_jumpi + is_jumpdirect }
-
Instruction flags are strictly boolean (0/1).
-
Opcode validity enforced through linear combination verification:
#![allow(unused)] fn main() { opcode = is_jump*JUMP + is_jumpi*JUMPI + is_jumpdirect*JUMPDIRECT }
-
-
Return Address Handling
- For non-X0 register targets (
op_a_0
= 0):#![allow(unused)] fn main() { op_a_value = next_pc + 4 }
- When jumping to X0 (
op_a_0
= 1), return address validation is skipped.
- For non-X0 register targets (
-
Range Checking
All critical values (
op_a_value, next_pc, target_pc
) are range-checked, ensuring values are valid 32-bit words. -
PC Transition Logic
- Target_pc calculation via ALU operation:
#![allow(unused)] fn main() { send_alu( Opcode::ADD, target_pc = next_pc + op_b_value, is_jumpdirect ) }
- Direct jumps (
is_jumpdirect
) use immediate operand addition.
- Target_pc calculation via ALU operation: