Skip to content

v1.15.0 — stream_store family completion - #38

Merged
petlukk merged 10 commits into
mainfrom
feat/v1.15-stream-store-family
May 20, 2026
Merged

v1.15.0 — stream_store family completion#38
petlukk merged 10 commits into
mainfrom
feat/v1.15-stream-store-family

Conversation

@petlukk

@petlukk petlukk commented May 20, 2026

Copy link
Copy Markdown
Owner

Summary

  • Scalar stream_store overloads (i16/u16/i32/u32/i64/u64) — closes the surface gap blocking Olorin's q4k_repack.ea kernel. Lowers to movnti on x86 SSE2 for i32/i64.
  • fence_nt() intrinsic — zero-arg store-store barrier (sfence on x86, dmb ishst on aarch64). Completes the prefetch_nta + stream_store + fence_nt non-temporal memory-hint family.
  • Vector stream_store alignment fix (pre-existing bug surfaced by new objdump tests) — alignment was set to element width, causing LLVM to silently decompose 128/256-bit NT stores to scalar non-temporal-hinted sequences. Now set to element_size * lane_count. Behavior change: vector-aligned buffers get the fast path; misaligned buffers now SIGSEGV per the documented contract.
  • stream_store reference docs upgraded to prefetch_nta parity — target-lowering table, alignment contract, ordering contract, and "When NOT to use" anti-pattern guidance (softmax accumulators, FWHT scratch).
  • Multicore + sub-byte bit-packing deferred with consumer-audit rationale (Olorin SpinBarrier pool, Cougar pool.run pattern, Cougar BitNet fused unpack, eakv Q4_1 split packing). Both have ROADMAP entries explaining why a generic primitive would be strictly less capable than what consumers already built.

Test Plan

  • 17 new v1.15 tests pass on x86 (10 stream_store + 7 fence_nt: typeck, runtime, IR, objdump assertions, alignment-failure crash)
  • 1,001 total project tests pass; clippy clean with -D warnings
  • mdbook docs build cleanly
  • Pi 5 manual ritual (per feedback_pi5_access_constraint): check out branch on peter@10.46.0.27, run cargo test --features llvm --release --test stream_store_tests --test fence_nt_tests — the 3 aarch64-gated tests must pass on real silicon.
  • Capture aarch64 assembly: confirm fence_nt() lowers to dmb ishst and scalar stream_store(*mut i64, ..., v) lowers to str/stnp. If mnemonics differ from docs, amend before tagging.
  • Tag v1.15.0 after Pi 5 verification passes.

petlukk and others added 10 commits May 20, 2026 11:28
Adds check_stream_store separate from check_store. Allows
i16/u16/i32/u32/i64/u64 scalar values in addition to vectors,
prerequisite for q4k_repack-style scalar streaming writes that
cannot use the vector form.

Codegen still scalarizes through .into_vector_value() — next
commit completes the scalar codegen path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
compile_stream_store now branches on scalar vs. vector value type.
Scalar path stores a single i16/u16/i32/u32/i64/u64 with the same
!nontemporal metadata as the vector form. Lowers to movnti on x86
SSE2 for i32/i64; i16/u16 fall through to regular mov on x86 since
no dedicated NT scalar instruction exists at that width.

Closes the q4k_repack scalar-write surface gap identified during
v1.15 brainstorm consumer audit (Olorin v2.0.3).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New zero-arg intrinsic providing intra-kernel store ordering after
stream_store writes. Lowers to:
  - x86:    @llvm.x86.sse.sfence()     -> sfence
  - aarch64: @llvm.aarch64.dmb(i32 10) -> dmb ishst

Explicit target intrinsics rather than IR fence release, which would
lower to mfence (x86) / dmb ish (aarch64) — heavier than needed for
store-only ordering.

Cross-thread visibility comes from host-side sync primitives
(pthread_join, rayon::scope, WaitGroup.Wait) which already provide
release semantics. fence_nt() is for the rare intra-kernel case where
the same kernel writes via stream_store and reads the same memory
back before returning.

Completes the prefetch_nta + stream_store + fence_nt non-temporal
memory-hint family.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Documents what LLVM 18 emits on aarch64 for !nontemporal scalar
stores and fence_nt via runtime + IR assertions. Tests are
target_arch-gated; require Pi 5 (per feedback_pi5_access_constraint)
for full run before release-cut.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds assert_intrinsic_in_disassembly checks for movnti (scalar i32/i64),
movntps / movntdq (vector 128-bit), vmovntps / vmovntdq (vector 256-bit),
and sfence (fence_nt). Per feedback_llvm_intrinsic_linktest, IR-level
!nontemporal does not guarantee the emitted assembly uses an NT mnemonic
— LLVM can drop the hint silently.

Also adds a deliberate alignment-failure crash test pinning the
documented alignment contract: vmovntps with a 1-byte-misaligned pointer
must raise SIGSEGV. Uses child-process pattern so the parent test runner
survives.

Fixes vector stream_store codegen: alignment was incorrectly set to
element size (4 bytes for f32), causing LLVM to emit scalar movntsd
instead of vector movntps/vmovntps. Now computed as element_size *
element_count to signal vector-width intent to LLVM's backend.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
stream_store reference brought to prefetch_nta parity:
  - Target-specific lowering table covering vector + scalar widths
  - Explicit alignment contract (GPF on x86 misalignment)
  - Explicit ordering contract (weak ordering, caller-side fence)
  - "When NOT to use" section listing working-buffer anti-patterns
    (softmax accumulators, FWHT scratch) to prevent regressions
    during consumer adoption

New fence_nt reference section covering target lowering (sfence /
dmb ishst), intended-use (intra-kernel ordering only), and
anti-patterns (cross-thread fencing is the caller's sync primitive's
job; store-to-load ordering requires a full barrier).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps version to 1.15.0. CHANGELOG documents the additions (scalar
stream_store overloads, fence_nt), the fixed pre-existing vector
alignment bug, the docs upgrade, and the test hardening.

ROADMAP amends the Multi-core / parallel_for entry with the v1.15
audit finding (Olorin + Cougar already ship custom SpinBarrier-based
pools strictly more capable than any generic primitive eacompute
could provide).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
VectorType::get_size() already returns u32; the explicit cast triggers
clippy::unnecessary_cast. Caught by the v1.15.0 release-cut clippy gate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Remove stale "Task 2 lands later" comment in stream_store scalar
  typeck test; Task 2 has shipped on this branch.
- Fix misleading "Read back after fence to verify visibility" comment
  in test_fence_nt_runtime_x86 — the kernel writes + fences; the C
  host reads back, not the kernel.
- Replace "scalar movntsd sequences" with the more accurate "sequence
  of scalar (non-temporal-hinted) stores" in CHANGELOG and ROADMAP —
  movntsd is SSE4a/AMD-specific, the actual LLVM fallback for under-
  aligned NT stores is not guaranteed to be that specific mnemonic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pi 5 (Cortex-A76, LLVM 18.1.8) capture revealed the original v1.15.0
aarch64 lowering claims were wrong for 4 of the 6 stream_store rows.
Root cause: aarch64 has no scalar non-temporal store instruction. The
only NT store is stnp (Store Non-temporal Pair), which requires two
operands. LLVM 18 honors !nontemporal only when it can synthesize stnp:
  - i64 self-pairs to a w-pair via lsr — NT preserved
  - 128-bit vector q-register self-pairs to a d-pair — NT preserved
  - i32 / i16 scalars have no pair-friendly form — NT hint dropped,
    plain str / strh emitted

Adjacent stream_store(*mut i32, ...) calls fuse to regular stp on
aarch64, not stnp — LLVM does not synthesize NT pairs from sequential
non-NT-fusable stores.

Doc and metadata changes:
  - docs/src/reference/intrinsics.md: target-lowering table corrected;
    added explanatory paragraph about aarch64-no-scalar-NT semantics
    and the "prefer 64-bit-or-wider on aarch64" guidance.
  - CHANGELOG.md: scalar overloads entry now lists the actual per-target
    lowering matrix instead of x86-only mnemonics.
  - ROADMAP.md: shipped-in-v1.15.0 entry expanded with the aarch64
    semantics summary and pointer to the reference for the full matrix.

Test additions (aarch64-gated, pinning observed LLVM 18 behavior):
  - test_stream_store_scalar_i64_emits_stnp_aarch64
  - test_stream_store_f32x4_emits_stnp_aarch64
  - test_stream_store_scalar_i32_emits_plain_str_aarch64 (PIN: hint dropped)
  - test_stream_store_scalar_i16_emits_plain_strh_aarch64 (PIN: hint dropped)
  - test_fence_nt_emits_dmb_ishst_aarch64

Mirrors the x86 objdump discipline so a future LLVM upgrade that
changes any of these mnemonics will fail loudly instead of silently
drifting away from the reference docs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@petlukk
petlukk merged commit 464e717 into main May 20, 2026
4 checks passed
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.

1 participant