Skip to content
Announcing PVM2: Reimagining PVM towards standard RISC-V

Announcing PVM2: Reimagining PVM towards standard RISC-V

May 28, 2026·sorpaas
sorpaas

Background

PVM is the instruction set architecture (ISA) of PolkaVM. This is the planned ISA to be deployed on the JAM Chain, advertised to be fast. In March, we started Grey (later known as JAR). JAR’s virtual machine JAVM is based on PVM ISA, and we managed to accomplish a 2x speed up for many of our PVM workloads compared with PolkaVM.

Our design has diverged a lot from JAM, challenging many of the JAM’s design rationales. We have since had, notably, a capability-based system and a real kvm microkernel which give us some impressive performance results and security properties. But until recently, we still kept PVM ISA almost unmodified.

The PVM ISA is the part we think that is also worth some deep thinking for us. PVM ISA has an entirely custom encoding definition. So we asked ourselves a simple question: do we really need it? If we move PVM towards a mostly standard-compliant RISC-V, can we get the same performance?

Specification: Defining PVM2

So we designed PVM2. A new ISA that is kept as close to the RISC-V spec as possible. How we define it is like this. We define a new base ISA. Supposedly, this will be the only thing we want to modify. Then we apply RISC-V extensions cleanly on top.

Our entire base ISA is defined as a short differential from RV64E, with just the following changes:

  • Memory address space
  • New meaning of pc
  • Disabled auipc, jalr (and c.jalr).
  • Standard-compliant custom-0 RISC-V instructions, trap, ecall.jar, ecalli.jar, br_table and fallthrough.

That’s basically it.

We then apply unmodified standard RISC-V extensions: m, c, zbb, zba, zbs, zicond, zicclsm.

Rationale: Future-proofing the spec

Some of the designs in PVM is questionable, or at least, debatable. An example is bitmask, which bloats the binary size by 12.5%. Supposedly, this is for random access to the program. Yet, the new gas metering requires a pre-validation round, completely defeating the purpose. There’s no flexibility in any of this: as an ISA spec, you either have it, or you do not. The middle ground is the worst of all places.

One of the rationales for PVM2 is to side-step this. We use the standard whenever possible. Avoid reinventing the wheels when it’s not necessary.

A mostly-standard RISC-V ISA also has significant advantage in its tooling: if LLVM ships any optimizations, we get it immediately. No bikeshedding with the transpiler to “re-support”. In addition, even our custom-0 instructions are defined in a standard-compliant way. This means we can easily utilize, for example, Rust’s asm macro, in a way that is not straightforward on PVM.

Our design also allows us to support new RISC-V extensions at ease.

Findings: You don’t need PVM

Our findings are quite clear: you don’t need PVM – we implemented the PVM2 ISA for JAVM, and managed to get a recompiler that is as fast as the old custom ISA. Our benchmark results are as follows.

Benchmarking comparison

We measured recompile + execute time. JAVM (PVM2) runs as fast as JAVM (PVM) on most workloads. And JAVM (PVM) itself is around 2x faster than PolkaVM.

JAVM (PVM2) JAVM (PVM) PolkaVM (PVM)
Workload
RuntimeTime
× vs PolkaVM
prime_sievearithmetic
JAVM (PVM)
190.2µs
JAVM (PVM2)
281.5µs
PolkaVM (PVM)
353.0µs
1.25×
keccakhash
JAVM (PVM)
61.4µs
JAVM (PVM2)
60.5µs
PolkaVM (PVM)
140.0µs
2.31×
blake2bhash
JAVM (PVM)
101.4µs
JAVM (PVM2)
104.0µs
PolkaVM (PVM)
276.0µs
2.65×
goldilocks_mulfield
JAVM (PVM)
521.4µs
JAVM (PVM2)
452.9µs
PolkaVM (PVM)
531.0µs
1.17×
ed25519signature
JAVM (PVM)
1.02ms
JAVM (PVM2)
644.6µs
PolkaVM (PVM)
1.35ms
2.09×
ecrecoversignature
JAVM (PVM)
1.49ms
JAVM (PVM2)
1.61ms
PolkaVM (PVM)
3.32ms
2.06×
poseidon2_permhash
JAVM (PVM)
1.82ms
JAVM (PVM2)
1.69ms
PolkaVM (PVM)
2.02ms
1.19×
mini_verifierzk-proof
JAVM (PVM)
788.3µs
JAVM (PVM2)
727.7µs
PolkaVM (PVM)
924.0µs
1.27×
poly_evalfield
JAVM (PVM)
1.71ms
JAVM (PVM2)
1.62ms
PolkaVM (PVM)
1.74ms
1.08×
fri_fold_treezk-proof
JAVM (PVM)
788.5µs
JAVM (PVM2)
728.2µs
PolkaVM (PVM)
962.0µs
1.32×

In-house delta

The below table shows the in-house delta between JAVM (PVM2) and JAVM (PVM).

WorkloadJAVM (PVM)JAVM (PVM2)ΔPVM2 / PVM
prime_sieve190.2µs281.5µs+91.3µs · +48.0%148.00%
keccak61.4µs60.5µs−0.9µs · −1.5%98.53%
blake2b101.4µs104.0µs+2.6µs · +2.6%102.56%
goldilocks_mul521.4µs452.9µs−68.5µs · −13.1%86.86%
ed255191.02ms644.6µs−374.7µs · −36.8%63.24%
ecrecover1.49ms1.61ms+116.5µs · +7.8%107.79%
poseidon2_perm1.82ms1.69ms−126.0µs · −6.9%93.07%
mini_verifier788.3µs727.7µs−60.6µs · −7.7%92.31%
poly_eval1.71ms1.62ms−96.0µs · −5.6%94.40%
fri_fold_tree788.5µs728.2µs−60.3µs · −7.6%92.35%
total8.50ms7.92ms−576.6µs · −6.8%93.21%

Binary size comparison

The binary size of PVM2 is also competitive. This is probably mainly due to PVM’s insistence of keeping bitmask, a 12.5% overhead. The reason that PVM2 can effortlessly enable a few more standard RISC-V extensions also helped.

WorkloadJAVM (PVM)JAVM (PVM2)ΔPVM2 / PVM
prime_sieve158,497B158,399B−98B99.94%
keccak15,289B12,320B−2,969B80.58%
blake2b30,704B22,014B−8,690B71.70%
goldilocks_mul5,581B5,526B−55B99.01%
ed25519229,041B94,136B−134,905B41.10%
ecrecover261,330B203,894B−57,436B78.02%
poseidon2_perm16,435B13,328B−3,107B81.10%
mini_verifier20,152B15,632B−4,520B77.57%
poly_eval72,821B72,404B−417B99.43%
fri_fold_tree281,375B277,120B−4,255B98.49%
total1,091,225B874,773B−216,452B80.16%