ADR-371: Vector Lanes, crc32c and Prefetch Are Portable std; an Instruction of One Architecture Is a Safe Function of Its Backend Module

Status: Accepted (the direction; the API is decided by a record of its own) Date: October 19, 2026 Answers: how a Nikaia program uses what the fastest hand-written programs use - comparing 16 to 64 bytes in one instruction, a hash in one instruction, gathering scattered bits, telling the memory system what comes next - without unsafe and without the language core taking on an architecture Related: ADR-296 D6 (hardware instructions leave the core; assembly is a library DSL) and D12 (the grammar’s scans are word-at-a-time, SIMD a layer above), ADR-320 D12 (a Bytes reads a word), #692 (the CPU level a build targets), Part III 16

1. Question

let semis = lanes.equal(';').first()     // where is the next `;` in 16 bytes
let h = hash::crc32c(word)               // a hash in one instruction
let digits = x86::pext(word, 0x0F000F0F) // the temperature's digits, no branch
prefetch(table, at)                      // the slot will be read next

A hand-written 1BRC program in Rust that uses these runs about 90 instructions a row, where the same algorithm without them runs 140 to 260. A Nikaia program can write none of them. ADR-296 D6 moved assembly into a backend DSL that is not built, and D12 rejected a SIMD construct in the core.

2. How others do it

A portable, safe vector type in the standard library is what the systems languages are adding; crc32c is a standard library’s in many; prefetch is a portable hint in Zig and GCC; an instruction like pext is architecture specific nearly everywhere.

3. Decision

D1 — Portable lanes, crc32c and prefetch are std’s.

std offers a vector of lanes (a fixed number of u8, and of wider integers), loaded from a Bytes at a position and checked as the cut it reads, so a proof drops the check (ADR-320 D7); comparisons that answer a mask; and the mask’s first set lane. crc32c joins std’s hashing; a prefetch hint asks that memory be brought close, means nothing to the program, and outside its buffer does nothing. On every target each of them has a meaning: an instruction where the target has one, a loop or nothing where it has not. They are a library’s, not the core’s: ADR-296 D6 holds.

D2 — An instruction of one architecture is a safe function of its module.

std::backend::x86, and its siblings for other architectures, offer such an instruction as a function with a safe signature, x86::pext(x, mask). It can be called only in a build for a CPU level that has it (the build command names the level, --cpu, ADR-002 D6); elsewhere the call is refused at compile time, so a program that wants to build for both writes the other path. No unsafe is written for any of it.

D3 — Assembly stays the last way out.

ADR-296 D6’s backend DSL remains for what D1 and D2 do not offer. Neither D1 nor D2 waits for it.

D4 — The API is a record of its own.

Its first part, a search over a whole buffer, is ADR-375.

The type’s name and lane widths, the operations, the mask’s type, the module of crc32c (D5), which instructions D2 offers first, and how a program asks which level it was built for (D6), are decided after this, from a draft written against the programs that need them.

D5 — crc32c is hash::crc32c(crc: u32, word: u64) -> u32, one step.

It is the CRC-32C (the Castagnoli polynomial) of word’s eight bytes, lowest first, continued from crc, without the inversions a checksum adds before and after: the step SSE 4.2’s crc32 and ARMv8’s crc32cx take, so on those targets it is one instruction, and elsewhere a table gives the same value. The start value is the caller’s: a hash of two words is hash::crc32c(a.truncating_u32(), b), a checksum starts from 0xFFFFFFFF and inverts at the end. It is in hash, beside the hashing std already does for maps.

Rejected: a checksum over a whole Bytes as the only form (a hash of a word in a register would have to be written to memory first); the inversions inside (a hash would pay two instructions for nothing); a value that differs by target, an instruction’s where there is one and another hash elsewhere (a program’s output would depend on the CPU level).

D6 — A program asks the CPU level it is built for with target::has, in a comptime.

target::has(target::Instruction::Crc32c) is a bool: whether the CPU level the program is built for (--cpu, ADR-002 D6) has that instruction. Its natural place is a comptime, so that the choice is made while the program is built and the path not taken is not in the binary:

comptime CRC_IN_ONE: bool = target::has(target::Instruction::Crc32c)

let h = if CRC_IN_ONE { hash::crc32c(a.truncating_u32(), b) } else { (a ^ b).wrapping_mul(K) }

A comptime runs on the machine that builds, but target::has answers for the level the build names, not for that machine: without --cpu that is the target’s baseline, where Crc32c is false on x86-64, and only --cpu=native asks the machine that builds. Called at run time it gives the same answer, the level the binary was built for, not the CPU it runs on. The first instructions are Crc32c (SSE 4.2 on x86-64, crc on AArch64), Bmi2 and Avx2; another is added when a program needs it.

4. Rejected alternatives

5. What this does not decide

D4’s list; a vector wider than the target’s widest register; floating-point lanes. </content> </invoke>

This site is open source. Improve this page.