Native U64 and I64: the 64-bit word as a machine number, with its laws - #931
phenomenon0 wants to merge 1 commit into
Conversation
- base.bend: the U64 block (Word(64n) slot, u64_ family) and the signed twin I64 (two's complement; four ops read the sign), both with add_comm citing the width-generic Word.add_comm - comp.ts: WORDS rows, u64_/i64_ templates, W64 match-scrutinee detection, word_to_u64/u64_to_word in the JS runtime - tests/base: u64_ops (15 checks) + i64_ops (20 checks), four lanes identical - measured: 2,085 -> 0.62 ms and 14,420 -> 4.88 ms per 10M on C - cap measured for this tree: comp.ts 65000 -> 66047 ttok
|
This would make the entire language and libs to be rewritten. Mine Math would need to adapt. I will do it if it was merged. |
|
Thanks for looking at this, Víctor — one thing that makes it easier: it's additive, not a break. |
|
One more data point while this sits in review: both words lower to the device program. A 2²⁰-leaf fork tree over |
|
Ok, I will adapt it in less than 2 hours if it was merged and my math too. |
|
My math library is the basic one, it will not use GPU. It was a port of math.h from the C23 lib it runs even in a i386. |
After the strings review asked for smaller steps, here is one: the 64-bit word as a machine number, with its laws.
What changed
base.bend:U64(theWord(64n)slot F64 uses, with theu64_op family) and its signed twinI64(two's complement; only four ops read the sign — the ordered compare,neg, the arithmetic shift,is_neg). Both carryadd_comm, filled by the same width-genericWord.add_commproofU32cites — the signed filler is the unsigned one, repacked.comp.ts:WORDSrows for the two types; theu64_/i64_templates; the match-scrutinee word detection generalized to W64 words (Natkeeps its own path);word_to_u64/u64_to_wordadded to the JS runtime — the 32-bit pair's 64-bit siblings.tests/base/u64_ops.bend(15 checks) andtests/base/i64_ops.bend(20 checks): four lanes identical — check, interpret, JS, C — including structural-construction cross-checks (a conversion that packed only 32 bits answers 0 forhi32and fails checks 13 and 15).Why
A
Word(64n)used to be a 64-cell structure. It stays exactly as it is — this is additive:U64/I64are new typed views over the sameWord(64n)slot, and datatypes outsideWORDScompile byte-identically (the bench keeps a structural control,S64/T64, precisely so that old path stays exercised). Measured, per 10M operations on the C lane:I64nodes remain in the emitted C; the structural 64-cell node is gone from both lanes.Against native C
The hand-written C baseline for the same loops (
clang -O3,volatile-fenced counter so nothing folds away): 2.49 ms per 10M increments. The native Bend op measures 0.62 ms against a 0.60 ms empty program — one machine+, the same instruction C would emit. The floor is a fold, and the fold is real: 1,000,000,000 increments run in 0.57 ms and print the right answer (1000000000). The signed bench is the stricter measurement — its loop does not fold (4.88 ms against a 0.70 ms floor is measured work) — and net of the floor it is 3,450× the old lowering on C, 165× on JS, 147× on interpret.Size
comp.ts+82/−8;base.bend+270 (mostly the two documented blocks); cap re-measured for this tree:comp.ts65,000 → 66,047 ttok. Gate: PASS 46/46.The signed half is small by construction — two's complement makes
add/sub/mul/the bitwise ops bit-identical to the unsigned ones, so they share lowering; only what reads the sign is new.Measurement reports (longer, with the lane methodology and the bench programs —
bench/nbench*.bend,bench/nbench.c— kept out of this PR to stay lean) live on our fork:github.com/phenomenon0/bend—docs/omen/lanes/native-u64.md,native-i64.md.Happy to split, reshape, or cut anything that fights the budget.