perf: prove U256 multiply, shifts and compare with Int256 instructions - #679
Draft
Qumeric wants to merge 1 commit into
Draft
perf: prove U256 multiply, shifts and compare with Int256 instructions#679Qumeric wants to merge 1 commit into
Qumeric wants to merge 1 commit into
Conversation
Patches the guest's ruint to dispatch those operations to OpenVM's 256-bit instructions instead of limb loops, and links the shims that define them. Addition, subtraction and the bitwise operations stay portable: the instructions address memory while ruint's methods take their operands by value, so the operand staging outweighs their few register operations.
Qumeric
force-pushed
the
valery/ruint-int256
branch
from
July 28, 2026 06:33
9098b93 to
bec9d6f
Compare
Qumeric
changed the base branch from
valery/guest-keccak-sponge
to
develop-v2.1.0
July 28, 2026 06:33
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
OpenVM proves a 256-bit arithmetic operation in a single instruction, and the bigint extension is
already enabled in
openvm.toml— but nothing was reaching those instructions, becauseruintdoesthe arithmetic in software limb loops. This patches the guest's
ruintto dispatch to them.Benchmark results
Both blocks measured against
develop-v2.1.0at the same commit, execute-metered.execute_metered_insnsmetered_rows_unpaddedmetered_main_cells_unpaddedmetered_interaction_cells_unpaddedmetered_memory_unpadded_bytesRuns: block 24001988 baseline / this PR; block 24002549 baseline / this PR.
Three things worth knowing about the shape of it.
Not every operation is worth dispatching. The instructions address memory while
ruint's methodstake operands by value, so LLVM has to spill both operands and the result to stack slots the
instruction can point at — about twenty loads and stores it cannot elide, because the inline
asm!may read any memory. Measured per operation on riscv64, in the shape revm calls them:
arithmetic_shrwrapping_shlwrapping_shrwrapping_mulcmp/ltwrapping_addwrapping_subSo multiply, the shifts and comparison are dispatched; addition, subtraction and the bitwise
operations deliberately are not — a four-limb carry chain is cheaper than the staging. Comparison
avoids the staging entirely because its operands arrive as references.
The shifts dispatch from the
Shl/Shroperator impls, not the inherent methods.extern "C"cannot be called from a
const fn, andnybblesbuilds lookup tables instatics withwrapping_shl, so making that non-const breaks a downstream crate. Trait methods are never const,and revm's handlers use the operators, so this keeps the win with no
constfallout.It requires
openvm-bigint-guestlinked for its symbols. Nothing references it in source, so itneeds
use openvm_bigint_guest as _;or the rlib is dropped before its#[no_mangle]exports reachthe link — the same idiom
stateless-executoralready uses foropenvm-guest-mem.Correctness: 2.8M differential cases against stock
ruint, with the instruction shims mocked fromthe reference implementation the circuit itself is checked against, so a disagreement would be a
disagreement with the circuit. That caught one real trap — the shift instructions read only the low
byte of the shift amount, so amounts >= 256 wrap instead of clearing, and the wrappers resolve that
themselves.
Open question before this leaves draft. It depends on a
ruintfork(
Qumeric/uint, branchperf/openvm-int256), patched into this workspace only.wrapping_mulandarithmetic_shrhave to dropconstthere, which is a breaking change for otherruintusers, sothat part is not upstreamable as written. Upstream is already unifying
const_*helpers with runtimetwins (alloy-rs/ruint#615), which is the shape an upstreamable version would take — but until then
this is a fork we carry. That trade is the only thing blocking this PR: the measurement is the
largest in the current queue, and the segment reduction is the largest of any change in flight.