Skip to content

Commit 9402e57

Browse files
authored
Merge pull request #349 from AdaWorldAPI/ccr-9f3b8075-74u47l
simd: AVX-without-AVX2 realization (simd_avx.rs) + three x86 fixes
2 parents 4546b82 + 2853e34 commit 9402e57

14 files changed

Lines changed: 1308 additions & 64 deletions

File tree

‎.cargo/config-avx.toml‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# AVX WITHOUT AVX2 (Sandy Bridge class) — the `simd_avx.rs` realization.
2+
#
3+
# Usage:
4+
# env -u RUSTFLAGS cargo --config .cargo/config-avx.toml <cmd>
5+
# bash scripts/masking-parity.sh avx-qemu (runs under qemu -cpu SandyBridge)
6+
#
7+
# Selects the arm `all(target_feature = "avx", not(target_feature = "avx2"))`:
8+
# Sandy Bridge / Ivy Bridge, AMD Bulldozer–Jaguar, VMs that hide AVX2. Plan:
9+
# `.claude/plans/simd-avx1-backend-v1.md`. Overlay semantics as in
10+
# `config-v3.toml`: only the target-cpu is set here; the crypto cfgs come from
11+
# `.cargo/config.toml` through cargo's rustflags join. `env -u RUSTFLAGS` is
12+
# load-bearing for the same reason.
13+
[target.'cfg(target_arch = "x86_64")']
14+
rustflags = ["-Ctarget-cpu=sandybridge"]

‎.claude/blackboard.md‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
## 2026-10-10 — `simd_avx.rs`: AVX-without-AVX2 realization (Sandy/Ivy Bridge, Bulldozer–Jaguar), plus three pre-existing x86 fixes
2+
3+
- **What:** a seventh realization, selected at compile time by `all(target_feature = "avx", not(target_feature = "avx2"))`. You get it with `target-cpu=native` on such a CPU, or with `.cargo/config-avx.toml` (`sandybridge`). The types and the `simd.rs` exports are unchanged; the 49 AVX2-only items in `simd_avx2.rs` and 19 in `simd_avx512.rs` are cfg-gated, and `simd_avx.rs` supplies the same methods from SSE2–SSE4.1 halves plus AVX1 float-domain ops. There are no runtime feature checks. The `cpu_guard` AVX2 startup floor applies only when the AVX2 arm is compiled in. Baseline builds (no `avx` cfg) are UNCHANGED: they keep the AVX2 arm and the floor. Whether the SSE tier should take them over is an open operator question.
4+
- **Evidence:**
5+
- `scripts/masking-parity.sh avx-qemu` passes all 15 groups under `qemu-x86_64-static -cpu SandyBridge`, with header `avx2=false avx512f=false`.
6+
- 422 `simd` lib tests pass on that arm under qemu.
7+
- The CI `realization/avx` row runs both, plus an objdump witness. The witness checks the parity binary for AVX2 integer ymm ops, FMA3 and F16C, and finds none. It can fire: the AVX2-op check finds 20,162 hits on a v3 binary. Its FMA/F16C check has never been seen to fire, because the parity program never calls `mul_add`; the unit test under qemu covers that case instead.
8+
- Clippy `-D warnings` is clean on v3, v4, avx and baseline. Lib tests pass on v3 (2510) and v4 (2561). Codegen witness avx2/avx512 and parity v3/v4 PASS.
9+
- **sentinel-qa BLOCK, fixed:** `F32x8::mul_add` called `_mm256_fmadd_ps` ungated, and F32x8 is exported on this arm, so a Sandy Bridge build faulted. It now has `cfg(target_feature = "fma")` with a per-lane `f32::mul_add` fallback (fused, one rounding). Test `f32x8_mul_add_is_fused` separates fused from unfused rounding. Disable: forcing the FMA body on the AVX build gives SIGILL under qemu. The F16C sites in `simd_avx512.rs` are runtime-detected and need nothing.
10+
- **Pre-existing bugs found and fixed:**
11+
- `simd_avx2::dot_i8`, a safe `pub fn` with no in-tree callers: `vpmaddubsw` saturated at `a = b = 127` (4080 instead of 516128), and a shorter `b` was read past its end. This is inventory row 410. Both arms now use `pmovsxbw` + `pmaddwd`, fold into an i64 every 16,384 chunks, and sum over `min` length. Test `simd_avx2_dot_i8_is_exact` is red on the old code on both arms.
12+
- Five x86 256-bit methods had **no test on any stable arm**: `U16x16::{mullo, blend_epi32}`, `U8x32::shuffle_bytes`, `I8x32::cmp_gt` and `I16x16::cmp_gt`. Found because their disable runs stayed green. Test `x86_256bit_integer_methods_match_scalar` now checks them against scalar references on every x86 arm.
13+
- **Disable runs on `simd_avx.rs`, all red, each restored:** cmpgt bias dropped (6 tests), `reduce_min` shuffle, saturating sub → wrapping, `transpose8` unpack swap, blend operand swap, I8 cmp_gt operand swap, I16 cmp_gt half swap, shuffle operand swap, mullo → mulhi. The first run of the I16 half swap crashed qemu itself (`QEMU internal SIGSEGV`) under the multithreaded harness. That is not a detection; single-threaded, the assertion fails as it should.
14+
- **Unsafe:** 29 new blocks in `simd_avx.rs`, all SAFETY-commented, verdict IRREDUCIBLE. They are appended to `.claude/knowledge/unsafe-inventory/sites.tsv` with a README addendum.
15+
- **Open:**
16+
- The SSE-tier scope decision (all non-AVX builds vs only v2 builds).
17+
- The `saturating_abs` SSSE3 patch, awaiting approval.
18+
- `dot_i8` changed its results; it has no in-tree callers, so the effect on external consumers is unknown.
19+
- **Plan:** `.claude/plans/simd-avx1-backend-v1.md`.
20+
121
## 2026-10-08 — `PillarReport::deferred` + `certified()`; `Cascade::query` documents that Stroke 1 can drop true hits
222

323
- **Pillars:** Pillar-15/16/17 run no probe but report `passed = true`. A gate on `passed` counted them as certified. `PillarReport` now has `deferred: bool` (true only for those three) and `certified() = passed && !deferred`. `print()` shows `DEFERRED`. `passed` keeps its meaning, so existing callers do not change.
@@ -3630,3 +3650,11 @@ qemu `-cpu Nehalem` 132, `SandyBridge` 132, `IvyBridge` 132, `Haswell` 0,
36303650
`max` 0. Side effect: a baseline build on a PRE-AVX CPU now gets the message
36313651
too (the guard is not VEX-encoded there). The optional pre-AVX to-do above
36323652
remains only for builds compiled with `target-cpu` v3/v4/native.
3653+
3654+
## simd_avx.rs (AVX-without-AVX2 backend) — started 2026-10-10, plan `.claude/plans/simd-avx1-backend-v1.md`
3655+
3656+
Operator chose Option A after #348. P0 landed: `.cargo/config-avx.toml`
3657+
(`-Ctarget-cpu=sandybridge`) and `scripts/masking-parity.sh avx-qemu` (run under
3658+
`qemu-x86_64-static -cpu SandyBridge`). Before the backend exists the arm FAILS
3659+
with exit 132 from `cpu_guard`'s AVX2 floor (measured) — the expected red.
3660+
No CI row yet; it is added when the arm goes green.

‎.claude/knowledge/unsafe-inventory/README.md‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,32 @@ A flag is a lead, not a verdict. The first four were re-read at source and hold.
144144
per type to two.
145145
4. SAFETY comments, macro-first.
146146
5. `SAFE-API` swaps; `SAFE-CRATE` only with approval.
147+
148+
## Addendum 2026-10-10 — `src/simd_avx.rs` (AVX-without-AVX2 arm)
149+
150+
The snapshot above predates this file. Its **29 sites** are appended to
151+
`sites.tsv` (rows at the end, line numbers from the commit that added them).
152+
All are `unsafe { }` blocks over SSE2–SSE4.1 / AVX1 intrinsics, all carry a
153+
`// SAFETY:` comment, and all get verdict **IRREDUCIBLE**: per the probe table,
154+
an x86 intrinsic in a plain fn is still E0133 on rustc 1.99 even when the
155+
feature is compiled in. Their footing is the arm predicate
156+
`all(target_feature = "avx", not(target_feature = "avx2"))`: the module only
157+
exists in builds where AVX (and, through it, SSE4.2) is enabled for the whole
158+
binary, and `cpu_guard` checks those features at startup.
159+
160+
Also changed by the same arc: `F32x8::mul_add` in `src/simd_avx512.rs` now
161+
issues `_mm256_fmadd_ps` only under `cfg(target_feature = "fma")` and otherwise
162+
uses `f32::mul_add` per lane. Before, it was an ungated FMA3 intrinsic in a
163+
type exported on the AVX arm (sentinel-qa BLOCK; a Sandy Bridge build faulted,
164+
verified under `qemu -cpu SandyBridge`). The site count for that file is
165+
unchanged; its line numbers in `sites.tsv` have moved by a few rows.
166+
167+
**Resolved by the same arc — `src/simd_avx2.rs` row 410 (`dot_i8`).** The
168+
bounds flag (a shorter `b` read 32 bytes past its end inside a safe `pub fn`)
169+
is fixed: both x86 arms now sum over `min(a.len(), b.len())` bytes with
170+
`chunks_exact`, so every load is in bounds. The same commit fixes a wrong-value
171+
bug the flag did not name: the `vpmaddubsw` form saturated i16 at
172+
`a = b = 127` (returned 4080 instead of 516128 over 32 bytes). The rows' second
173+
flag ("safe pub fn uses AVX2 with no cfg") is now covered: the AVX2 bodies are
174+
cfg-gated off the AVX-without-AVX2 arm, and a baseline (no `avx` cfg) build
175+
keeps `cpu_guard`'s AVX2 startup floor.

‎.claude/knowledge/unsafe-inventory/sites.tsv‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,3 +1300,32 @@ src/zip/ndproducer.rs 360 UPSTREAM fn src UNSAFE-FN-API missing - unsafe fn: cal
13001300
src/zip/ndproducer.rs 364 UPSTREAM fn src UNSAFE-FN-API missing - unsafe fn: caller upholds pointer/bounds/validity precondition; raw-pointer core design ptr_arith unsafe fn uget_ptr(&self, i: &Self::Dim) -> *const A {
13011301
src/zip/ndproducer.rs 409 UPSTREAM fn src UNSAFE-FN-API missing - unsafe fn: caller upholds pointer/bounds/validity precondition; raw-pointer core design ptr_arith unsafe fn as_ref(&self, ptr: *mut A) -> *mut A {
13021302
src/zip/ndproducer.rs 413 UPSTREAM fn src UNSAFE-FN-API missing - unsafe fn: caller upholds pointer/bounds/validity precondition; raw-pointer core design ptr_arith unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A {
1303+
src/simd_avx.rs 42 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic unsafe { (_mm256_castsi256_si128(v), _mm256_extractf128_si256::<1>(v)) }
1304+
src/simd_avx.rs 49 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic unsafe { _mm256_set_m128i(hi, lo) }
1305+
src/simd_avx.rs 59 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic unsafe { join($op(al, bl), $op(ah, bh)) }
1306+
src/simd_avx.rs 69 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic unsafe { _mm256_castps_si256($op(_mm256_castsi256_ps($a), _mm256_castsi256_ps($b))) }
1307+
src/simd_avx.rs 79 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic unsafe { (_mm_movemask_epi8(lo) as u32 & 0xFFFF) | ((_mm_movemask_epi8(hi) as u32) << 16)
1308+
src/simd_avx.rs 93 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic let mut sum = unsafe {
1309+
src/simd_avx.rs 133 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic let mut result = unsafe {
1310+
src/simd_avx.rs 174 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic unsafe {
1311+
src/simd_avx.rs 192 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic,raw_load_store,ptr_arith unsafe {
1312+
src/simd_avx.rs 212 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic,raw_load_store,ptr_arith unsafe { _mm256_loadu_pd(v.0.as_ptr().add(4 * half) as *const f64) }
1313+
src/simd_avx.rs 219 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic let c = unsafe {
1314+
src/simd_avx.rs 235 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic,raw_load_store,ptr_arith unsafe { _mm256_storeu_pd(out[4 * bj + k].0.as_mut_ptr().add(4 * bi) as *mut f64, v) };
1315+
src/simd_avx.rs 276 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic,raw_load_store,ptr_arith core::array::from_fn(|k| unsafe { _mm_loadu_si128(self.0.as_ptr().add(4 * k) as *const __m
1316+
src/simd_avx.rs 284 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic unsafe {
1317+
src/simd_avx.rs 297 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic unsafe {
1318+
src/simd_avx.rs 314 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic let q = unsafe { _mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(a[k], b[k]))) as u32 };
1319+
src/simd_avx.rs 332 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic unsafe {
1320+
src/simd_avx.rs 343 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic unsafe {
1321+
src/simd_avx.rs 360 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic Self(unsafe { _mm256_permute2f128_si256::<IMM>(self.0, other.0) })
1322+
src/simd_avx.rs 370 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic Self(unsafe {
1323+
src/simd_avx.rs 394 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic unsafe {
1324+
src/simd_avx.rs 478 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic let ones = unsafe { _mm256_set1_epi16(-1) };
1325+
src/simd_avx.rs 496 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic,raw_load_store,ptr_arith unsafe {
1326+
src/simd_avx.rs 527 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic let bias = unsafe { _mm256_set1_epi8(i8::MIN) };
1327+
src/simd_avx.rs 562 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic unsafe {
1328+
src/simd_avx.rs 573 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic unsafe {
1329+
src/simd_avx.rs 605 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic Self(unsafe { join(_mm_blendv_epi8(bl, al, ml), _mm_blendv_epi8(bh, ah, mh)) })
1330+
src/simd_avx.rs 688 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic unsafe {
1331+
src/simd_avx.rs 755 FORK block src IRREDUCIBLE present - SSE2-SSE4.1/AVX1 intrinsics on the AVX-without-AVX2 arm; still E0133 on rustc 1.99 (see probe table), so the block cannot be removed x86_intrinsic unsafe {
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Plan: `simd_avx.rs` — AVX-without-AVX2 backend (v1)
2+
3+
Operator decision 2026-10-10 (after PR #348): Option A, a real backend for CPUs
4+
with AVX but not AVX2 (Sandy Bridge / Ivy Bridge, AMD Bulldozer–Jaguar, VMs that
5+
hide AVX2), selected at compile time. No runtime feature checks in the SIMD code.
6+
7+
## Selection
8+
9+
* Arm predicate: `all(target_arch = "x86_64", target_feature = "avx",
10+
not(target_feature = "avx2"))` — written out at each gate (no build.rs, no
11+
cfg-alias crate).
12+
* Reached by: `target-cpu=native` on such a CPU, or the new
13+
`.cargo/config-avx.toml` (`-Ctarget-cpu=sandybridge`).
14+
* Unchanged: AVX-512 builds, AVX2 builds, and BASELINE builds (no `avx` cfg).
15+
A baseline build keeps the AVX2 arm and the `cpu_guard` AVX2 startup floor —
16+
switching it to this arm would slow every baseline build on AVX2 hosts.
17+
(Open question for the operator; default is "unchanged".)
18+
* `cpu_guard::BACKEND_FLOOR` applies only when the AVX2 arm is compiled in. On
19+
this arm the normal compiled-feature table already requires `avx`.
20+
21+
## Shape: impl blocks, not types
22+
23+
Measured: the AVX2-only intrinsics sit in impl blocks; the TYPES are shared and
24+
referenced across the arm (`F32x16`→`I32x16`/`U32x16`, `F64x8`→`U64x8`,
25+
`U16x32`→`U8x64`, `U8x32`→`U16x16`). Redefining types would cascade, so:
26+
27+
* Type definitions and `simd.rs` export lists do not change.
28+
* In `simd_avx2.rs` / `simd_avx512.rs`, every method that calls an AVX2-only
29+
intrinsic moves into an impl block gated `not(<arm predicate>)`. Mixed blocks
30+
are split; their portable methods stay shared (no duplication).
31+
* `simd_avx.rs` provides the same methods, same signatures, gated on the arm:
32+
two `__m128i` halves via SSE2/SSSE3/SSE4.1 (all present on every AVX CPU),
33+
plus AVX1 for loads/stores/float-domain bit ops.
34+
35+
Affected — ⊘ the first table here counted doc-comment mentions and listed
36+
`U8x64`, `U32x16`, `U32x8`, `U16x8`, `palette_lookup_u8x8` and a 1-op `F32x8`
37+
item. Re-measured with comments stripped (`tools/avx2_gate.py`): those are
38+
scalar or comment-only and needed nothing. What actually needed a gate:
39+
40+
| item | gated in | notes |
41+
|---|---|---|
42+
| `U8x32` | `simd_avx2.rs` | 15 methods + 5 traits; biggest |
43+
| `U16x16` | `simd_avx2.rs` | 8 methods + 12 traits |
44+
| `U64x8` | `simd_avx2.rs` | `rotl_half`, `mul_lo32`, `transpose8`, `Shl`/`Shr<Self>` (per-lane: no SSE `sllv`) |
45+
| `I32x16` | `simd_avx2.rs` | `reduce_min/max`, `gt_bitmask` |
46+
| `popcount`, `dot_i8` (free fns) | `simd_avx2.rs` | re-exported from `simd_avx` on the arm |
47+
| `I8x32`, `I16x16` | `simd_avx512.rs` | 6 + 5 methods, 4 traits each |
48+
| `F32x8::mul_add` | `simd_avx512.rs` | FMA3, not AVX2 — missed by the AVX2-only scan, caught by sentinel-qa; now `cfg(target_feature = "fma")` with a per-lane `f32::mul_add` fallback |
49+
50+
49 gates in `simd_avx2.rs`, 19 in `simd_avx512.rs`. F16C sites in
51+
`simd_avx512.rs` are runtime-detected (`is_x86_feature_detected!`) and need none.
52+
53+
## Work items
54+
55+
- [x] P0 TESTS FIRST: a `native-avx` arm in `crates/simd-masking-parity`
56+
(`--config .cargo/config-avx.toml`, run under `qemu-x86_64-static -cpu
57+
SandyBridge`), wired into `scripts/masking-parity.sh` and the
58+
`simd-matrix` CI workflow. Expect it to SIGILL before the backend exists.
59+
- [x] P1 `.cargo/config-avx.toml`; arm predicate; `cpu_guard` floor gated.
60+
- [x] P2 split the affected impl blocks (no behaviour change on other arms;
61+
codegen-witness avx2/avx512 must stay byte-for-byte green).
62+
- [x] P3 `simd_avx.rs` per type, one type per chunk (U8x32, U16x16, U64x8,
63+
U32x8, U32x16, I32x16, U8x64, popcount/dot_i8, I8x32, I16x16, U16x8,
64+
palette_lookup_u8x8). Every `unsafe` gets SAFETY + sentinel-qa audit.
65+
- [x] P4 gates: lib tests on the avx arm under qemu SandyBridge; parity on all
66+
six existing arms + the new one; v3/v4 clippy; disable-run per type.
67+
- [x] P5 docs: CLAUDE.md realization list, contract doc, blackboard, unsafe
68+
inventory rows for the new file.
69+
70+
Estimate: ~1,000–1,500 lines, 2–3 sessions.

0 commit comments

Comments
 (0)