Blog

October 7, 2025 · Technical · 10 min read

Designing ZK IR, a custom instruction set for zero-knowledge proofs

How we designed ZK IR, the 32-bit instruction set of OpenZKVM, for STARK proving efficiency and minimal constraint counts, and how OpenZKVM compares with SP1 and RISC Zero.

The problem with general-purpose zkVMs

Traditional ISAs were designed to maximize throughput on physical hardware, not to minimize the cost of cryptographic proving. A zkVM that reuses one ends up with needlessly complex constraint systems and slower proof generation.

OpenZKVM does not adapt an existing ISA. We designed its instruction set, ZK IR, from first principles for STARK-based proving systems.

The theoretical foundations and full formal analysis of ZK IR, including security proofs, are in our peer-reviewed paper: ZK IR: A Minimalist Instruction Set Architecture for Efficient Zero-Knowledge Proof Generation

Design principles

Principle 1: minimize constraints

Each instruction is designed to need as few algebraic constraints in the STARK proof as possible. To that end, all ZK IR instructions share one 32-bit format:

[opcode:6][rd:5][rs1:5][rs2:5][imm:11]

The constraint system therefore needs no instruction decoding logic, saving approximately 20 constraints per instruction compared with variable-length encodings.

Constraint counts by instruction class:

Instruction TypeZK IR ConstraintsRISC-V Constraints
ALU (add, sub, xor)5-612-15
Memory Load8-1018-22
Memory Store7-916-20
Branch12-1425-30

Principle 2: field-native arithmetic

ZK IR works natively over the Baby Bear prime field (p = 2³¹ - 2²⁷ + 1), so there is no overflow handling and modular arithmetic is efficient:

Field characteristics:
- Prime: p = 2013265921
- 32-bit native representation
- Efficient Montgomery multiplication
- Two's complement compatible for signed operations

This matches Plonky3's field requirements exactly and minimizes conversion overhead.

Principle 3: deterministic execution

ZK IR removes all the sources of non-determinism found in traditional architectures:

  • no interrupts or exceptions during proving
  • deterministic memory allocation
  • fixed instruction timing (no caches or speculation)
  • bounded loop iterations

Registers

ZK IR has 32 general-purpose registers, with a calling convention optimized for common cryptographic patterns:

r0       : Zero register (hardwired to 0)
r1-r8    : Argument/return registers
r9-r16   : Caller-saved temporaries
r17-r24  : Callee-saved registers
r25-r28  : Reserved for runtime
r29 (fp) : Frame pointer
r30 (lr) : Link register
r31 (sp) : Stack pointer

Compilation pipeline

OpenZKVM integrates with standard toolchains through our LLVM backend:

┌─────────────────┐
│  Source Code    │  Rust, C, C++, Go
│  (High-level)   │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│    LLVM IR      │  Platform-independent
│                 │  intermediate representation
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   zkir-llvm     │  Our custom LLVM backend
│                 │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  ZK IR Bytecode │  Optimized for proving
│                 │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  zkir-prover    │  Plonky3-based proof generation
│                 │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   STARK Proof   │  Succinct, verifiable proof
└─────────────────┘

Benchmarks

We benchmarked OpenZKVM against SP1 and RISC Zero, two leading zkVMs, on standard cryptographic workloads:

BenchmarkOpenZKVMSP1RISC Zero
SHA-256 (1KB)1.0x1.3x1.6x
Keccak-256 (1KB)1.0x1.4x1.7x
ECDSA Verify1.0x1.2x1.4x
Merkle Proof (depth 20)1.0x1.5x1.8x

Lower is better. OpenZKVM normalized to 1.0x.

Proof size and verification time

SystemProof SizeVerification Time
OpenZKVM~100 KB12ms
SP1~150 KB18ms
RISC Zero~200 KB25ms

Implementation details

Memory model

OpenZKVM memory is word-addressed, with separate instruction and data regions:

  • Instruction memory: read-only, loaded at initialization
  • Data memory: read-write, initialized to zero
  • Stack: grows downward from high addresses
  • Heap: grows upward from low addresses

Cryptographic precompiles

OpenZKVM provides native precompiled functions for performance-critical operations:

// Native hash computation (single constraint)
let hash = zkir::precompile::sha256(data);

// Native field arithmetic
let result = zkir::precompile::field_mul(a, b);

Source code

The code is open source:

  • openzkvm: ISA specification, VM implementation and documentation
  • zkir-llvm: LLVM backend for compiling high-level languages
  • zkir-prover: Plonky3-based proving system

Questions about this post, or working on something similar? Book a meeting.