From 95db84e9cf29d0f9f49ef8f169fb6f352f7f9e76 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 16 Sep 2026 21:04:49 +0000 Subject: [PATCH 1/2] fix: COPY .cargo/ into both image builders; correct the RUSTFLAGS fallback tier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two findings from a CodeRabbit review that posted at 20:56 and that I merged past at 21:02. Both are correct. The first is a regression I introduced in #313 and it breaks both image builds. ## CRITICAL — both Dockerfiles referenced a file the image does not contain #313 converted the Dockerfiles from `ENV RUSTFLAGS="-C target-cpu=..."` to `cargo --config .cargo/config-vN.toml` to stop the RUSTFLAGS env from wiping the crypto-backend cfgs. That fix is right and stands. What it missed: both Dockerfiles COPY selectively BY DESIGN (the Dockerfile says so at its own COPY block), and neither names `.cargo/`. The old env form had no file dependency; the new form does, and the file was never in the image. Verified, two-sided, in a scratch crate: cargo --config .cargo/config-v3.toml build (absent) exit 101 cargo --config .cargo/config-v3.toml build (present) exit 0, Finished So the first RUN in each image would have failed before compiling anything. Fixed with `COPY .cargo/ .cargo/` ahead of the RUN block in both files. Checked rather than assumed: the COPY precedes every `RUN cargo` (Dockerfile 82 < 98, Dockerfile.avx512 56 < 68); `.dockerignore` does not exclude `.cargo/`; all nine config files are git-tracked and therefore in the build context. Root cause worth keeping: I changed a build I cannot run — there is no Docker daemon in this container — and did not check that the file the new flag names is present in the image. A `--config` path is a build INPUT, not just a flag. ## The fallback tier was misstated, and my own measurement disproves it `CLAUDE.md` read: RUSTFLAGS "silently drops `-Ctarget-cpu=x86-64-v4` and the arm measures v3". Wrong. RUSTFLAGS replaces EVERY config rustflags entry, so it drops the DEFAULT config's target-cpu too, not only the overlay's. The measurement from this same session is the disproof: `RUSTFLAGS="-D warnings"` produced ZERO `-Ctarget-cpu` flags against 65 with the env unset. The build gets rustc's own default for the target, the `x86-64` baseline (SSE2), which is LOWER than v3 and is precisely the tier `simd_avx2.rs`'s intrinsics SIGILL on. The sentence was wrong before the native flip too; the flip only changed which config gets discarded. Corrected in place with the numbers. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01DCfrD5y19cvFc4AoyydXYv --- CLAUDE.md | 15 +++++++++++++-- Dockerfile | 9 +++++++++ Dockerfile.avx512 | 5 +++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 62ae0a2c..bb4c8f6f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -141,8 +141,19 @@ src/ `env -u RUSTFLAGS` is load-bearing: a RUSTFLAGS env var REPLACES every cargo-config rustflags entry, so it silently drops `-Ctarget-cpu=x86-64-v4` - and the arm measures v3 while claiming v4 (the trap `scripts/masking-parity.sh` - documents). + and the arm does NOT measure v4 while claiming to (the trap + `scripts/masking-parity.sh` documents). + + > ⊘ **CORRECTED 2026-09-16 (coderabbit, #313).** This read "*and the arm + > measures v3*". Wrong, and this session's own measurement is what disproves + > it: RUSTFLAGS replaces **every** config rustflags entry, so it drops the + > DEFAULT config's target-cpu too, not just the overlay's. Measured on one + > unit — `RUSTFLAGS="-D warnings"` produced **zero** `-Ctarget-cpu` flags, + > against 65 with the env unset. What you actually get is rustc's own default + > for the target, i.e. the `x86-64` baseline (SSE2), which is LOWER than v3 + > and is the tier `simd_avx2.rs`'s intrinsics SIGILL on. The sentence was + > wrong before the native flip as well; the flip only changed which config + > gets discarded. **That same mechanism had silently disabled the whole config in CI, and it is the more serious half (found 2026-09-16).** `.github/workflows/ci.yaml` sets a diff --git a/Dockerfile b/Dockerfile index 08e979aa..b8bd2bd4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -72,6 +72,15 @@ COPY ndarray-rand/benches/ ndarray-rand/benches/ # detects AVX-512 at runtime via LazyLock even when compiled for v3; # compile-time v3 just means the scalar/AVX2 fallback paths are used when the # runtime check fails. Both paths produce identical results. +# The cargo CONFIG DIRECTORY, required by the `--config` flags below and easy to +# forget: this Dockerfile COPYs selectively by design (see the note above), so a +# file that is not named here does not exist in the image. Adding `--config +# .cargo/config-v3.toml` without this line makes cargo fail on a missing +# configuration file BEFORE it compiles anything — which is exactly what +# happened on #313 and was caught in review after merge, not by a build (there +# is no Docker daemon in the dev container, so neither image is built here). +COPY .cargo/ .cargo/ + # The tier is passed as a CONFIG, not as `ENV RUSTFLAGS` (changed 2026-09-16). # A RUSTFLAGS env REPLACES every cargo-config `rustflags` entry rather than # joining it, so `ENV RUSTFLAGS="-C target-cpu=x86-64-v3"` did set the tier — diff --git a/Dockerfile.avx512 b/Dockerfile.avx512 index 21d2e1e9..f53f1802 100644 --- a/Dockerfile.avx512 +++ b/Dockerfile.avx512 @@ -50,6 +50,11 @@ COPY examples/ examples/ COPY benches/ benches/ COPY ndarray-rand/benches/ ndarray-rand/benches/ +# The cargo CONFIG DIRECTORY, required by the `--config` flags below. This +# Dockerfile COPYs selectively, so a file not named here is absent from the +# image and cargo fails on a missing configuration file before compiling. +COPY .cargo/ .cargo/ + # AVX-512 pinned: compile-time dispatch, everything inlined. # # Passed as a CONFIG, not `ENV RUSTFLAGS` (changed 2026-09-16): a RUSTFLAGS env From a08bd408cd663f058ff931903b68ecf3ecb51757 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 16 Sep 2026 21:49:14 +0000 Subject: [PATCH 2/2] masking-parity: the same wrong fallback claim, at its source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit on #314. `scripts/masking-parity.sh:26-30` carried the identical claim this PR already corrected in CLAUDE.md: that a set RUSTFLAGS makes the arm "measure v3 while claiming v4". Sharper than a duplicate: CLAUDE.md's correction CITES this script as the place the trap is documented, so the doc was fixed while the source it points at still taught the wrong number. An exhaustive grep confirms these were the only two instances outside the blackboard's historical record. The correction, with this session's own measurement: RUSTFLAGS replaces EVERY config rustflags entry, including the DEFAULT config's own `-Ctarget-cpu`, so no target-cpu reaches rustc and the build lands on the generic `x86-64` baseline (SSE2) — BELOW v3, and the tier `simd_avx2.rs`'s intrinsics SIGILL on. Measured: `RUSTFLAGS="-D warnings"` emitted ZERO `-Ctarget-cpu` flags against 65 with the env unset. Also states what the arm now IS, since the default flip: it names no tier and builds whatever config wins, by default `target-cpu=native`. Read the program's own `avx512f=` header for the tier, or pin config-v3 for AVX2. Verified the script still runs both ways: bash scripts/masking-parity.sh native PASS, avx512f=true CARGO_ARGS='--config .cargo/config-v3.toml' ... native PASS, avx512f=false Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01DCfrD5y19cvFc4AoyydXYv --- scripts/masking-parity.sh | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/scripts/masking-parity.sh b/scripts/masking-parity.sh index 4dff97cb..787c652b 100755 --- a/scripts/masking-parity.sh +++ b/scripts/masking-parity.sh @@ -26,8 +26,21 @@ case "$ARM" in # `env -u RUSTFLAGS`: a workflow-global RUSTFLAGS (CI sets "-D warnings") # REPLACES every cargo-config rustflags entry, so a `--config # .cargo/config-v4.toml` passed through CARGO_ARGS would silently lose its - # `-Ctarget-cpu=x86-64-v4` and this arm would measure v3 while claiming - # v4 — the exact trap the tier4 CI job hit. Clearing it lets the config win. + # `-Ctarget-cpu=x86-64-v4` — the exact trap the tier4 CI job hit. Clearing + # it lets the config win. + # + # What you get INSTEAD is not v3 (corrected 2026-09-16, coderabbit on #314; + # this comment said "would measure v3"). RUSTFLAGS replaces EVERY entry, + # including the DEFAULT `.cargo/config.toml`'s own `-Ctarget-cpu`, so no + # target-cpu reaches rustc at all and the build lands on rustc's generic + # `x86-64` baseline — SSE2, BELOW v3, and the tier `simd_avx2.rs`'s + # intrinsics SIGILL on. Measured on one unit: `RUSTFLAGS="-D warnings"` + # emitted ZERO `-Ctarget-cpu` flags against 65 with the env unset. + # + # This arm NAMES NO TIER by design: it builds with whatever config wins, + # which by default is `target-cpu=native` (the host). Read the program's + # own `avx512f=` header line for the tier; pin `.cargo/config-v3.toml` + # through CARGO_ARGS when you specifically want AVX2. env -u RUSTFLAGS cargo ${CARGO_ARGS:-} build --release --manifest-path "$MANIFEST" --bin simd-masking-parity "$TD/release/simd-masking-parity" ;;