Issue #876: Measure named wide operations (full multiply, carry chains, 128÷64, carry-less multiply) before deciding on them

Open and answer this issue on GitHub.

ready · opened 2026-10-27 · updated 2026-10-27 · 0 comments

Question

Nikaia has no i128/u128 (NK1135, ADR-315). The hardware does not offer a general 128-bit integer unit either. What it offers is single instructions with 128-bit results, and today no Nikaia program can reach them:

Instruction Where Use
64×64 → 128 (MUL/MULX, UMULH, MULHU) x86-64, ARM64, RISC-V bigint, a·b mod m, hashes (wyhash), PRNGs (PCG64)
ADCX/ADOX (two carry chains) x86 bigint multiplication
128 ÷ 64 → 64 (DIV r64, DLGR) x86, IBM z a·b mod m without a library call
carry-less 64×64 → 128 (PCLMULQDQ, PMULL, clmul) x86, ARM64, RISC-V Zbc CRC32/64, GHASH, Reed-Solomon
quadword vector ops (vadduqm, vdivuq, VAQ) POWER10, IBM z decimal and bigint

The instruction facts come from memory and are not checked; the measurement is what decides.

A native u128 would not reach most of these: LLVM lowers u128 / u64 to __udivti3, carry-less multiply is not integer multiplication at all, and LLVM seldom emits ADCX/ADOX. So the candidate is named operations, in the style of wrapping_/saturating_/checked_, implemented in std with Rust underneath and a software fallback where the CPU lacks the instruction (the ADR-296 kernel pattern).

A sketch (names open, see below):

let (lo, hi) = a.widening_mul(b)                // u64 × u64
let (sum, carry) = a.carrying_add(b, carry_in)
let r = lo.wide_rem(hi, m)                      // 128 ÷ 64
let (lo, hi) = x.carryless_mul(k)

Measure first (owner, 2026-10-27)

This is not to wait until a program asks for it: find out now whether these operations pay off for known workloads, then decide. Measure before any ADR, following CLAUDE.md Measuring performance: callgrind instruction and branch counts, against tuned Rust, same output byte for byte, release builds on both sides.

Workloads, each as today’s Nikaia (emulated with u32 halves or bit loops) against tuned Rust (u128, core::arch):

  1. bigint add and multiply, 4096 bits
  2. mul_mod for a 64-bit modulus, Miller-Rabin over it
  3. wyhash mix, PCG64
  4. CRC64 and GHASH (carry-less multiply)

To build

No language change before that decision.

Back to Open Issues