Skip to content

D-DOMINO-MOV-0: bit flip, positional substitution and lazy FULL carry (probe) - #391

Merged
AdaWorldAPI merged 8 commits into
masterfrom
claude/u9-domino
Oct 29, 2026
Merged

AdaWorldAPI merged 8 commits into
masterfrom
claude/u9-domino

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Oct 29, 2026 •

Copy link
Copy Markdown
Owner

Summary

D-DOMINO-MOV-0 tests whether a bit-flip counter (a half adder per column, 64 lanes in a u64) can replace integer carry handling in the U9 abacus, and whether two refinements pay off: positional substitution (assign into a known-empty column) and a lazy FULL carry (a FULL column absorbs a carry as a state change).

Four candidates are implemented in plain Rust and measured on one AVX-512 VNNI Xeon, against native per-lane u64 sums and against D from #389 (merged): F (eager flip), P (substitution), L (lazy FULL), PL (both).

Verdicts

  • F: ADOPT-LAB. Exact and the cheapest bit-plane counter. It beats native per-lane adds on dense and single-lane masks. At the 64K boundary it beats D only on AVX2, where D's lowering is poor.
  • P: REFUSE. Each assignment into an EMPTY column still compiles to a store, and the flag test costs more than the XOR it replaces.
  • L: REFUSE. The FULL flag check and update cost more per column than the ripple step it skips.
  • PL: REFUSE. The combination is slower than F in every measured L1 case, and the gap grows with the length of the FULL chain.

The bit flip crosses 64K exactly, and two u64 planes (R1) and one u128 (R2) cost the same. A two-plane abacus does not make carry escalation cheaper than native addition in general. The exception is dense or single-lane masks fed one at a time.

Review fix

Codex found that PL's ordinary ripple stored an all-ones result without marking the column FULL, unlike L. A later all-ones carry therefore read and wrote the word instead of passing it as FULL -> EMPTY, and the L-vs-PL comparison used two different FULL policies. PL now marks the column FULL the same way L does. check_all asserts after every add that L, PL and PL-stale hold exactly the logically all-ones columns as FULL. A new test builds a column from complementary masks and checks the state-only carry. Restoring the old code turns both red (Y7). Every table below is a second full measurement taken after the fix.

Key measurements

ns per 64 products, 64K boundary, L1 dense (products 65,024-65,025). Ranges are min - max of 3 runs of medians.

L1 dense AVX-512 VNNI AVX-512 (v4) AVX2 (v3)
native u64 lanes 4.81 - 4.83 4.83 - 4.84 4.83 - 4.84
D (#389) 2.28 - 2.56 2.30 - 2.66 9.39 - 9.42
F 2.70 - 2.88 3.05 - 3.14 5.66 - 5.67
P 5.44 - 5.47 5.78 - 5.95 7.61 - 7.81
L 5.89 - 6.22 6.55 - 6.61 8.50 - 8.56
PL 6.36 - 9.58 6.67 - 6.91 8.80 - 8.98

Counters alone, ns per add (B), and one add through n FULL columns (B'), VNNI:

B workload native F P L PL
dense all-ones at 2^0 6.99 - 7.04 1.93 - 1.96 2.92 - 2.93 3.42 - 3.49 3.22 - 3.29
random masks at 2^0 6.99 8.12 - 8.21 10.5 - 10.8 12.4 15.5 - 15.6
sparse one lane at 2^0 6.99 5.77 - 6.41 6.17 - 6.33 6.14 - 7.05 7.14 - 7.65
B' (clone+add, n FULL columns) F P L PL
8 11.8 18.0 - 18.4 24.0 - 24.4 24.6 - 25.2
64 38.6 - 60.4 108 - 143 127 - 168 146 - 150

Clone-only cost is about 5.1 - 5.5 ns (F), 4.9 - 5.0 (P), 5.5 - 5.7 (L), 5.7 (PL). Per FULL column (medians): F about 0.52 ns, P about 1.6 ns, L about 1.9 ns, PL about 2.2 ns (1.85 before the fix).

Normalizing P or PL after every block costs 74.7 - 74.8 and 107 - 110 ns per 64 products on VNNI, so only the deferred form is usable.

Some rows moved between the two measurements even though their code did not change. F on random masks went from 6.4 to 8.1 ns on VNNI, and the streamed rows rose for every counter alike. The report attributes these to run and code-layout variance, not the fix. One conclusion follows from it: F is now about 16% slower than native on random masks on every tier, where the first run had it equal on VNNI.

Assembly findings

  • flip is 5 scalar instructions, no ternlog.
  • R1 pair_add and R2 packed_add compile to the same 10 instructions on native and v3. LLVM sees through the u128 packing.
  • F's ripple is a branching loop over a column array in memory: 47 - 59 instructions, 10 - 12 branches. P is 99 - 123 instructions with 12 - 16 shifts for the u128 bitmap tests. L is 51 - 88 and PL is 86 (counted before the fix).
  • Assignment into an EMPTY column compiles to a store. The column index is a runtime value, so LLVM does not rename or eliminate it.
  • At the 64K block on AVX-512, D is 20 instructions with 4 vpternlogq and no shuffle. F is 68 - 79 with 4 vpternlogq and a branching ripple.
  • At the 64K block on AVX2, D is 107 instructions with 13 shuffle-class ops from the array-backed U16x32, which explains why F is 1.6 - 1.7x faster than D in L1 there.

Coverage findings

The #388/#389 tests (15 in tests/u9_abacus.rs, 5 in tests/u9_head_to_head.rs) were audited from source. Gaps found:

  • Flipping the bit-16 position: MISSING. D keeps high as a u16 count, so no bit-16 plane exists or is tested.
  • Two 64-bit planes in one 128-bit carrier: MISSING.
  • Carry mask forwarded without moving lanes: PARTIAL. Every C test compares only lane-blind totals, so a carry rotated into another lane would pass every C test.
  • Consecutive FULL columns: PARTIAL. Up to about 20 columns, all-lane masks only; no 32/64 chains and no single-lane chains.
  • Carry into bit 17 with bit 16 set, repeated carries not cancelling, eager vs deferred normalization, assignment into an empty destination, and read-after-write between adds: PARTIAL.
  • Crossing 2^16 is PROVEN.

Correctness

tests/u9_domino.rs adds 8 tests, passing on native, v4, v3, v2 (qemu) and aarch64 (qemu); clippy and the shuffle guard are clean. The tests are: half-adder per lane, random adds against a per-lane u128 oracle in both orders for six variants (F, P, L, PL, P-stale, PL-stale) with the FULL invariant checked after every add, FULL chains of 1 to 64 columns, the 64K boundary (bit 16 flips on crossing XOR preset, bit 17 on crossing AND preset), product sums against native and D, overflow refusal, an XOR-made FULL column carrying by state, and R1/R2 agreement.

Seven disable runs each turn tests red:

  • Y1 F carry not propagated: red (5 tests).
  • Y2 PL lost FULL->EMPTY: green at first, red after adding stale-word runs (2 tests). With zero-initialized words the word under a flag is always 0, so the lost transition was invisible until P and PL were also run over stale words.
  • Y3 P assigns into a non-empty column: red (4 tests).
  • Y4 L treats FULL as unchanged after a partial carry: red (3 tests).
  • Y5 carry mask rotated one lane (R1): red (1 test).
  • Y6 P reads the stale word instead of assigning: red (2 tests).
  • Y7 PL's ordinary ripple does not mark an all-ones result FULL (the code before review): red (2 tests).

Not in this PR

  • No facade, backend or storage change, and no intrinsics.
  • No NEON or wasm timings, no unrolled or vectorized ripple, no hand scheduling.

Run:

cargo run --release --example u9_domino --features std
cargo test --release --test u9_domino

🤖 Generated with Claude Code

https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v

… counters

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
The packed-u128 anchor was called once with a known-zero high half, so LLVM
specialized it and the disassembly did not show the general function.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
…eeping

The plain flip ripple (F) is exact and beats native per-lane adds on dense
and single-lane masks. Positional substitution and lazy FULL forwarding (P,
L, PL) are exact but slower than F everywhere: their flag tests cost more
than the XOR/AND they avoid. Two u64 planes and one u128 compile to the
same code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
@coderabbitai

coderabbitai Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c23944ea-d918-4af8-ba94-8d6a88aad306
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review October 29, 2026 21:13
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-29T21:15:27.396249Z 23d6773 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 23d6773b07

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread examples/u9_domino/domino.rs Resolved
PL's ordinary ripple stored an all-ones result without setting FULL, unlike
L, so an all-ones carry into it read and wrote the word instead of passing
it as FULL -> EMPTY. The tests now check, after every add, that L and PL
hold every logically all-ones column as FULL, plus a targeted
complementary-mask case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
All tables re-measured on three tiers. PL's per-FULL-column cost is about
2.2 ns (was 1.85); verdicts are unchanged. Differences in rows whose code
did not change are recorded as run and layout variance. Adds the new test
and disable run Y7 to the report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
@AdaWorldAPI
AdaWorldAPI changed the base branch from claude/u9-abacus to master October 29, 2026 22:14
@AdaWorldAPI
AdaWorldAPI merged commit 44e8022 into master Oct 29, 2026
31 checks passed
AdaWorldAPI added a commit that referenced this pull request Oct 29, 2026
AdaWorldAPI pushed a commit that referenced this pull request Oct 30, 2026
…y for U32x16 shifts

half_add_mask(a, carry) = (a ^ carry, a & carry) and
full_add_mask(a, b, c) = (a ^ b ^ c, majority) are the flip and
carry_save3 laws proven in #391 and #389, now in simd_masking_ops and
re-exported through ndarray::simd. Each u64 is one significance column of
64 independent counters; the carry belongs one column up in the same lane.

Tests: both truth tables in every lane position, lanes 0/31/32/63 alone,
2000 random words (with zero, all-ones and alternating masks) against
per-lane addition, and a ripple counter that keeps lanes apart while
moving the carry to a neighbouring bit corrupts it.

The wasm parity harness now also checks U32x16 << and >> with a distinct
count per lane.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPh2Seh2WLccCksAdY
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants