Skip to content

feat(hpc): codec8 phase (b) — keep ties-away encode vector on wasm - #371

Closed
AdaWorldAPI wants to merge 1 commit into
masterfrom
research/codec8-ties-away-vector-20261024
Closed

AdaWorldAPI wants to merge 1 commit into
masterfrom
research/codec8-ties-away-vector-20261024

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

What

Phase (b) of .claude/plans/quantized-codec8-v2.md, follow-up to #370. Before adding SIMD primitives, I checked each arm's generated code. The vector contract's codegen-oracle rule says a loop counts as scalar only if the assembly shows it is.

arm ties-even encode (lane_even, onnx) ties-away encode (tflite_act)
x86-64-v3 / v4 vector (vdivps, vroundps) vector
aarch64 vector (fdiv v.4s, frintx) vector
wasm32 + simd128 vector (f32x4.div, f32x4.nearest) 4 roundf calls per f32x4

wasm has no vector round-half-away instruction. So TiesAway::round now uses trunc plus a half test on wasm32 only, chosen by cfg with no runtime dispatch. Every other target keeps f32::round. After the change, the wasm loop is f32x4.trunc / sub / abs / ge / bitselect, with no roundf calls and no lane extracts.

No new SIMD primitives. Every arm is already vectorised by LLVM, so adding primitives under W1a would add surface area without adding speed.

Evidence

  • Equality with f32::round: 0 mismatches over all 2³² bit patterns natively. Run in node's wasm engine (simd128 build), also 0 mismatches over 2³²−1; the skipped pattern 0xFFFFFFFF is a NaN.
  • Unit test: a strided pin plus half-integer neighbourhoods runs on every target. Disable-checked: >= → > fails it.
  • x86 assembly unchanged: scripts/codec8_asm_diff.sh gives v3 283/295 and v4 278/295 lines, generic vs hand-written IDENTICAL, and the negative control differs.
  • wasm: generic and hand-written lane_even are identical. For tflite_act the instruction multisets are identical; only the placement of the length-check error block differs. That difference was there before this change.
  • cargo test --release --lib hpc::codec8: 17 passed. Clippy (-D warnings, lib + example) and fmt are clean.

Not measured: wasm timing. The claim here is codegen (vector, no libm calls), not speed.


Generated by Claude Code

The codegen check for phase (b) of .claude/plans/quantized-codec8-v2.md
found one arm that leaves the vector unit: on wasm32+simd128,
f32::round (round half away) has no vector instruction, so the TFLite
encode loop made four roundf calls per f32x4. x86 v3/v4 and aarch64
were already fully vector and generic == hand-written.

TiesAway::round now uses trunc plus a half test on wasm32 only (cfg,
no runtime dispatch); other targets keep f32::round, so their assembly
is unchanged (scripts/codec8_asm_diff.sh: v3 283/295, v4 278/295,
IDENTICAL). On wasm the loop is now f32x4.trunc/sub/abs/ge/bitselect,
zero roundf calls.

Equality of the trunc form with f32::round: 0 mismatches over all 2^32
bit patterns natively, and over 2^32 - 1 run in node's wasm engine
(simd128 build). A strided unit test plus half-integer neighbourhoods
keeps it pinned on every target.

No new SIMD primitives: every other arm is already vectorised by LLVM.

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

coderabbitai Bot commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.

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.

Copy link
Copy Markdown
Owner Author

Closing this PR because its premise is wrong. It treats LLVM auto-vectorisation of a scalar loop as the vector implementation, and wins that vectorisation back on wasm with a cfg trick inside codec8.rs. The vector path must instead be explicit ndarray::simd methods on every backend: a mode-parameterised round, a saturating i32→i8 narrow mirroring the existing low/high widening, and the requantize pair. Compiler output is evidence about the generated code, never the implementation. The exhaustive 2³² equality test for ties-away carries over as the oracle for the new polyfill method.


Generated by Claude Code

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