Skip to content

blake3: drop the C toolchain requirement; record measured u32/u64 lane codegen - #264

Merged
AdaWorldAPI merged 4 commits into
masterfrom
claude/simd-codegen-oracle
Jul 29, 2026
Merged

blake3: drop the C toolchain requirement; record measured u32/u64 lane codegen#264
AdaWorldAPI merged 4 commits into
masterfrom
claude/simd-codegen-oracle

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Jul 29, 2026

Copy link
Copy Markdown
Owner

One dependency line changes in the shipped crate. Everything else is documentation under .claude/knowledge/.

The change

blake3's default build compiles hand-written assembly through cc and links it — 33 object files and one static archive. Any vendored or offline build of ndarray therefore requires a C toolchain, and fails without one.

-blake3 = { version = "1", optional = true }
+blake3 = { version = "1", optional = true, default-features = false, features = ["pure"] }

pure routes build.rs to build_sse2_sse41_avx2_rust_intrinsics(), which compiles no C.

object files archives cc invoked
before 33 1 yes
after 0 0 no

Verified after cargo clean -p blake3: the only cfgs set are blake3_{sse2,sse41,avx2}_rust.

Cost: the hand-tuned x86-64 assembly is replaced by Rust intrinsics; the AVX-512 path and the aarch64 NEON C intrinsics are dropped.

cmake, openblas-build and openssl-sys also build C, but are reachable only through the optional blas feature, never through default = ["std", "hpc-extras"]. blake3 was the only C on the default path.

Reviewer note: blake3_*_ffi still appears in build-script output as rustc-check-cfg declarations — cargo enumerating valid cfg names, not setting them. A check must anchor on ^cargo:rustc-cfg= to tell the two apart.

Supporting measurement

Under the -Ctarget-cpu=x86-64-v3 baseline, the macro-generated lane types do not compile to scalar code. 13 probes:

lane operation packed scalar (lane data) lowering
u32 ARX round (ChaCha) 52 0 vpaddd / vpxor / vpshufb
u32 reduce_sum 8 0 logarithmic reduction tree
f32 FMA accumulate 28 0 vfmadd213ps
i8 saturating_abs 4 0 vpxor / vpsubsb / vpblendvb
u16→f32 widening 6 0 vpmovzxwd / vcvtdq2ps
u8x64 cross-lane reverse 9 0 vpshufb / vpermq
u64 rotate 0 8 scalar rorq, one per lane
BLAKE2b G-function 22 ~88 packed add, then scalar through all four rotates
gather / table lookup 0 0 movzbl chain, no arithmetic
loop-carried dependency 0 27 inherently serial

The u64 rotate is the outlier. LLVM lowers u32 rotate_left(12)/(7) via vpslld/vpsrld/vpor and folds byte-granular amounts to vpshufb. At 64-bit width it does neither — including for byte-granular 32/24/16 — despite vpsllq/vpsrlq being available.

This is load-bearing for the crypto surface: BLAKE2b is a 64-bit ARX cipher and argon2 uses BLAKE2b. The crate has 8 rotate_left methods, all u32, and no rotate_right at any width, so argon2's kernel cannot currently be expressed. AVX-512 provides _mm512_rorv_epi64 as a single instruction.

The converse also holds: hand-writing intrinsics for the u32 lanes would lose to the optimizer, which already emits 8 vpaddd for 64 u32 lanes — the AVX2 minimum.

Documentation added

  • .claude/knowledge/simd-codegen-oracle/ — the measurement instrument (probes, analyzer, baseline, README with the runnable recipe and recorded results). Filed as knowledge rather than as a crate or CI job: the question "does this vectorize?" is answered once during design and recorded, not re-answered on every commit.
  • simd-one-spec-design.md — design for consolidating five backend files (31 macro-generated types, 57 hand-written, 13,253 LoC) behind one declaration, with a measured entry criterion for intrinsic overrides.
  • crypto-lane-status.md — per-lane status: u32 ARX and f32 FMA need no work; u64 ARX is absent and needs intrinsics.
  • td-t22-asm-investigation.md — extended with the CI baseline finding below.

Incidental finding: CI does not apply the v3 baseline

.github/workflows/ci.yaml sets RUSTFLAGS: "-D warnings" at workflow level, and cargo's RUSTFLAGS env var replaces [target.'cfg(…)'].rustflags from .cargo/config.toml rather than merging:

$ cargo build -p ndarray --lib -v | grep -o 'target-cpu=[a-z0-9-]*'
target-cpu=x86-64-v3
$ RUSTFLAGS="-D warnings" cargo build -p ndarray --lib -v | grep -o 'target-cpu=[a-z0-9-]*'
(empty)

Every job inheriting that env:tests/{stable,beta,1.95.0}, clippy, native-backend, hpc-stream-parallel — compiles at baseline x86-64 rather than the AVX2 tier. The comment in .cargo/config.toml stating "this is what GitHub CI runs against" is inaccurate. tier4-avx512-check is unaffected; it uses the more specific CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS.

Deliberately not changed here. Switching CI to v3 alters the instruction set every test exercises and carries the SIGILL risk documented in TD-SIMD-1. Separate decision, separate PR.

Out of scope

The u64 rotate implementation, the one-spec migration, and BLAKE3's remaining Rust intrinsics — a second SIMD surface that pure does not remove. Closing that means implementing BLAKE3's compression on ndarray::simd::U32x16, since it is a ChaCha-derived u32 ARX kernel and that lane is already at the instruction floor.

Adds `crates/simd-codegen-oracle` (+ script, baseline, CI job) and removes
blake3's C/asm FFI. The oracle answers "does this need intrinsics?" with an
assembly measurement instead of intuition — and its first run falsified the
design assumptions it was built to check.

## Why

TD-T22 measured that under the pinned `-Ctarget-cpu=x86-64-v3`, the
"scalar polyfill" SIMD types already compile to packed AVX2 at the
instruction floor. A PR had hand-written 700 lines of intrinsics against
that nonexistent gap. Nothing in the repo could have caught it: there is no
codegen check, only correctness parity harnesses.

## What the oracle measured

13 probes, three groups. Group A expected to vectorize, Group B expected
NOT to, Group C (u64 rotate) deliberately unclassified.

Group A — all vectorized, as expected. `arx_rounds_u32x16` (10-round ChaCha
double-round): 52 packed, 0 scalar arithmetic on lane data.

Group B — **3 of 5 predictions were WRONG.** LLVM vectorized:
  * saturating_abs_i8x32   → vpxor/vpsubsb/vpblendvb, i.e. it synthesized
                              the VPABSB abs+clamp trick on its own
  * widening_u16_to_f32    → vpmovzxwd + vcvtdq2ps
  * cross_lane_reverse_u8x64 → vbroadcasti128 + vpshufb + vpermq — a
                              cross-lane permute, from a scalar index loop
Only serial_dependent_chain (loop-carried dependency) and gather_lookup_u8
stayed scalar. The "cross-lane/widening/saturating obviously needs
intrinsics" intuition is measurably false.

Group C — **the u64 rotate does NOT vectorize.** rot_u64x8 / rot_u64x4:
0 packed, one scalar `rorq %cl` per lane. blake2b_g_u64x8: the leading
`a+b` goes packed (vpaddq), then LLVM extracts every lane to a GPR and
stays scalar through all four rotate stages — including the byte-granular
amounts 32/24/16, which at u32 width fold to vpshufb. AVX2 has
vpsllq/vpsrlq and LLVM applies exactly that shift-or to u32 rotate-by-12/7;
it declines to at 64-bit width.

That matters because BLAKE2b is a 64-bit ARX cipher and argon2 uses
BLAKE2b. The crate has 8 `rotate_left` methods (all u32) and zero
`rotate_right` at any width, so argon2's kernel is unexpressible today.
This is the first intrinsic override meeting the entry criterion: a probe
proving the generic form fails.

Same crate, same week, opposite answers for u32 and u64. That contrast is
the argument for the oracle.

## blake3: no more C

Operator directive — C is a contamination of ndarray, and vendored builds
fail without a C toolchain. blake3's default build ran cc over
c/blake3_{sse2,sse41,avx2,avx512}_x86-64_unix.S: measured 33 .o files plus
libblake3_avx512_assembly.a, with blake3_*_ffi cfgs set. Now pinned to
`default-features = false, features = ["pure"]`, which routes build.rs to
build_sse2_sse41_avx2_rust_intrinsics() ("No C code to compile here").

Verified after `cargo clean -p blake3`: the only cfgs actually SET are
blake3_{sse2,sse41,avx2}_rust; 0 object files, 0 archives, no cc. (The
_ffi names still appear in the build output as rustc-check-cfg
DECLARATIONS — cargo listing valid cfg names — not assignments. A grep
that cannot tell the two apart reports a false positive here.)

Remaining cc build-deps in the lockfile (cmake, openblas-build,
openssl-sys) are reachable only through the optional `blas` feature, not
`default = ["std", "hpc-extras"]`. blake3 was the only C on the default
path — which is why vendoring broke for everyone.

## Honesty rule baked into the tool

The analyzer separates "scalar arithmetic on lane data" from loop control,
so a trip-counter `decl` is never reported as scalar lane work. That exact
overclaim was made and corrected earlier this week; the tool cannot
reproduce it.

## Docs

  * .claude/knowledge/simd-one-spec-design.md — design for collapsing 5
    backends (31 macro-generated + 57 hand-written types, 13,253 LoC, three
    authoring strategies) into one spec, with the oracle as the entry
    criterion for intrinsic overrides. Includes the table of my three wrong
    predictions.
  * .claude/knowledge/crypto-lane-status.md — u32 ARX proven optimal
    (ChaCha20/BLAKE3 need no SIMD work); u64 ARX absent and now measured as
    genuinely needing intrinsics.
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a SIMD codegen oracle with probe kernels, assembly classification, target baselines, CI execution, and documentation describing SIMD lane status and a one-spec multi-backend design.

Changes

SIMD codegen verification

Layer / File(s) Summary
Lane status and generation design
.claude/knowledge/*.md, Cargo.toml
Documents measured SIMD capabilities, defines the proposed lane-generation model, records u64 rotate limitations, and configures the optional pure-Rust blake3 dependency.
Probe crate and SIMD kernels
crates/simd-codegen-oracle/Cargo.toml, crates/simd-codegen-oracle/src/main.rs
Adds runtime-seeded SIMD probes covering ARX, FMA, reductions, bitwise operations, scalar-shaped loops, u64 rotates, and BLAKE2b mixing.
Assembly classification and verdicts
scripts/codegen_oracle_analyze.py
Parses emitted assembly, classifies instructions, loads TOML expectations, reports probe results, and returns failure bitmasks.
Baseline execution and CI wiring
crates/simd-codegen-oracle/baselines/*, scripts/codegen-oracle.sh, .github/workflows/ci.yaml
Adds x86_64 expectations, target-aware assembly generation, analyzer invocation, and CI conclusion dependencies.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Poem

I’m a bunny checking lanes in flight,
Packed little arrows, classified right.
Rotates hop through assembly bright,
Baselines guard each SIMD byte.
CI thumps its paws: “All’s tight!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately captures the two main themes: removing the C toolchain dependency via blake3 pure mode and recording measured SIMD lane codegen.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

@cursor

cursor Bot commented Jul 29, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_c2dbd14b-0f5d-482f-bd30-6c0b85426e5e)

@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review July 29, 2026 05:44
The oracle's first CI run failed with packed=0 on every Group-A probe —
arx_rounds_u32x16 came back 0 packed / 146 scalar / 491 memory against
52 packed / 0 scalar locally. Not codegen drift: no AVX2 at all.

Cause, verified empirically:

  $ cargo build -p ndarray --lib -v | grep -o 'target-cpu=[a-z0-9-]*'
  target-cpu=x86-64-v3
  $ RUSTFLAGS="-D warnings" cargo build ... | grep -o 'target-cpu=...'
  (empty)

Cargo's RUSTFLAGS env var REPLACES `[target.'cfg(...)'].rustflags` from
.cargo/config.toml; they do not merge. ci.yaml:23 sets
`RUSTFLAGS: "-D warnings"` at workflow level, so the v3 baseline is
silently dropped for every job that inherits it.

Fix: the script now DECLARES its baseline, passing `-C target-cpu=x86-64-v3`
after the `--` so it lands on the final rustc invocation and wins over
ambient RUSTFLAGS. An oracle that inherits its baseline measures a
different machine depending on where it runs — precisely the class of
error it exists to catch. Verified with RUSTFLAGS="-D warnings" exported:
all 13 probes match.

Also recorded in the TD-T22 artifact, because it narrows that finding:
"the scalar polyfill compiles to packed AVX2" holds UNDER the v3 baseline,
and CI never had it. The baseline does the work, not the source form.

NOT changed here: whether CI itself should build at v3 so tests exercise
the shipped instruction set. That flips every job's codegen and carries the
SIGILL risk TD-SIMD-1 documented — an operator decision, not a drive-by fix
in a tooling PR. tier4-avx512-check is unaffected; it uses the more
specific CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS, which does not
collide.

@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: b0aea59a44

ℹ️ 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 scripts/codegen_oracle_analyze.py Outdated
print("simd-codegen-oracle: ALL PROBES MATCH BASELINE EXPECTATIONS")
else:
print(f"simd-codegen-oracle: FAILURES (exit bitmask 0x{exit_mask:x}) -- see FAIL rows above")
return exit_mask

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Return a bounded nonzero status for late probe failures

Use a bounded nonzero exit status rather than returning the full bitmask. On Unix, process exit codes retain only the low 8 bits, so a failure confined to probe index 8 or later produces 256, 512, etc. and is observed by the shell as status 0. With the current sorted probe list, missing rot_u64x4/rot_u64x8 symbols or regressions in saturating_abs_i8x32, serial_dependent_chain, or widening_u16_to_f32 therefore leave the new CI job green despite printing FAILURES.

Useful? React with 👍 / 👎.

Two changes.

1. The oracle is no longer a CI job (operator ruling). A standing check
   that parses assembly to catch a designer skipping a measurement is
   machinery guarding a lapse the design step should prevent. The question
   "does this vectorize?" is answered once, during design, and recorded;
   it is not re-answered on every commit. The crate and script remain as an
   on-demand instrument. `.github/workflows/ci.yaml` job and its
   `conclusion.needs` entry removed; YAML re-validated.

2. `codegen_oracle_analyze.py` returned the raw failure bitmask as the
   process exit status. Unix truncates exit status to the low 8 bits, so a
   failure confined to probe index >= 8 exits 0x100/0x200/... and the shell
   observes 0 -- the script printed "FAILURES" and reported success.
   Demonstrated: a probe-9-only failure exited 512, seen as 0. With probes
   sorted alphabetically this covered rot_u64x4, rot_u64x8,
   saturating_abs_i8x32, serial_dependent_chain and widening_u16_to_f32 --
   both u64-rotate probes among them, the finding the tool exists to
   protect. Now exits 1 on any failure; the bitmask stays in the printed
   line for diagnosis. Found by Codex review.

Docs updated to stop claiming CI enforcement and to state why the tool is
deliberately on-demand.
@AdaWorldAPI AdaWorldAPI changed the title simd: a codegen oracle, the u64 rotate it found, and blake3 without C simd: measure u32/u64 lane codegen; drop blake3's C toolchain requirement Jul 29, 2026
The tool is knowledge, not a product artifact. It was adding a crate, two
scripts, and a workspace-exclude entry to a published library in order to
answer a question that gets asked during design and then recorded.

Moved crates/simd-codegen-oracle + scripts/codegen-oracle.sh +
scripts/codegen_oracle_analyze.py into
.claude/knowledge/simd-codegen-oracle/ (probes.rs, analyze.py, run.sh,
baseline-x86_64-v3.toml, README.md). Reverted the Cargo.toml exclude entry.

Net effect on the shipped crate: this PR now changes exactly one dependency
line (blake3 -> default-features = false, features = ["pure"]) plus its
comment. Nothing else outside .claude/.

The README carries the runnable recipe, the recorded per-probe results, the
declared-baseline rationale, and the exit-status bug, so the instrument is
reproducible from where it lives.
@AdaWorldAPI AdaWorldAPI changed the title simd: measure u32/u64 lane codegen; drop blake3's C toolchain requirement blake3: drop the C toolchain requirement; record measured u32/u64 lane codegen Jul 29, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/codegen-oracle.sh`:
- Around line 19-47: Update the Cargo invocations in the target-building logic
to execute from $ROOT, such as by wrapping each command with a subshell that
changes to $ROOT before running Cargo. Preserve the existing manifest, target,
and rustc emission arguments while ensuring .cargo/config.toml is resolved from
the repository root.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 47782926-2a41-4027-8f57-71275f9399f9

📥 Commits

Reviewing files that changed from the base of the PR and between 2c8af9a and b0aea59.

⛔ Files ignored due to path filters (1)
  • crates/simd-codegen-oracle/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • .claude/knowledge/crypto-lane-status.md
  • .claude/knowledge/simd-one-spec-design.md
  • .github/workflows/ci.yaml
  • Cargo.toml
  • crates/simd-codegen-oracle/Cargo.toml
  • crates/simd-codegen-oracle/baselines/x86_64-unknown-linux-gnu.toml
  • crates/simd-codegen-oracle/src/main.rs
  • scripts/codegen-oracle.sh
  • scripts/codegen_oracle_analyze.py

Comment thread scripts/codegen-oracle.sh
Comment on lines +19 to +47
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
MANIFEST="$ROOT/crates/simd-codegen-oracle/Cargo.toml"

TARGET="${1:-}"
case "$TARGET" in
"" | -*)
TARGET="$(rustc -vV | sed -n 's/^host: //p')"
;;
*)
shift
;;
esac
# Remaining args (an optional `--` and/or analyzer flags like --verbose) forward as-is.
if [ "${1:-}" = "--" ]; then
shift
fi

BASELINE="$ROOT/crates/simd-codegen-oracle/baselines/$TARGET.toml"
if [ ! -f "$BASELINE" ]; then
echo "==> no baseline for target $TARGET at $BASELINE" >&2
exit 90
fi

echo "==> building simd-codegen-oracle (--emit asm) for $TARGET"
if [ "$TARGET" = "$(rustc -vV | sed -n 's/^host: //p')" ]; then
cargo rustc --release --manifest-path "$MANIFEST" -- --emit asm -C debuginfo=0
else
cargo rustc --release --manifest-path "$MANIFEST" --target "$TARGET" -- --emit asm -C debuginfo=0
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate target files =="
git ls-files | rg '(^|/)(codegen-oracle\.sh|Cargo\.toml|config\.toml|baselines/|Cargo\.lock)$|cargo' | sed -n '1,160p'

echo "== script =="
if [ -f scripts/codegen-oracle.sh ]; then
  cat -n scripts/codegen-oracle.sh
fi

echo "== oracle cargo files =="
for f in crates/simd-codegen-oracle/Cargo.toml crates/simd-codegen-oracle/.cargo/config.toml crates/simd-codegen-oracle/Cargo.lock; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    cat -n "$f"
  fi
done

echo "== all .cargo/config.toml in repo =="
for f in $(git ls-files '*\.cargo/config.toml'); do
  echo "--- $f ---"
  cat -n "$f"
done

echo "== run from outside manifests using temp target (read-only probe for files only) =="
mkdir -p /tmp/cargo-config-test
cat >/tmp/cargo-config-test/Cargo.toml <<'EOF'
[package]
name = "test"
version = "0.0.0"
edition = "2021"
[profile.dev]
EOF
mkdir -p /tmp/cargo-config-test/.cargo
cat >/tmp/cargo-config-test/.cargo/config.toml <<'EOF'
[build]
rustc-args = ["--crate-name", "fromconfig"]
EOF
cargo metadata --format-version 1 --manifest-path /tmp/cargo-config-test/Cargo.toml >/tmp/metadata.json
python3 - <<'PY'
import json
data=json.load(open('/tmp/metadata.json'))
pk=data['packages']
print([p['name']+":"+p['manifest_path'] for p in pk])
print("root:", data.get('workspace_root'))
PY

echo "== relevant cargo references in repo =="
rg -n "codegen-oracle|simd-codegen-oracle|target-cpu|x86-64-v3|CARGO|cargo rustc|\.cargo" scripts README.md .github crates 2>/dev/null | sed -n '1,240p'

Repository: AdaWorldAPI/ndarray

Length of output: 149


🌐 Web query:

Cargo documentation rustc configuration files discovered based on current directory not manifest path .cargo/config.toml

💡 Result:

Cargo configuration files (such as.cargo/config.toml) are discovered by traversing the file system upward, starting from the current working directory (CWD) where the cargo command is executed, not from the directory containing the Cargo.toml manifest file [1][2][3]. This behavior means that if you run a cargo command from a directory other than the project root (e.g., using --manifest-path), Cargo will not automatically locate or respect configuration files located in the project's root directory or its subdirectories [2][3]. This is a long-standing and known limitation in Cargo [4][5][6]. Key aspects of Cargo configuration discovery include: Hierarchy: Cargo searches for.cargo/config.toml (or.cargo/config) files in the current directory and all parent directories up to the root of the file system [1][7][8]. Precedence: Configuration values are merged, with deeper directories (those closer to the current working directory) taking precedence over ancestor directories [1][9][8]. Global configuration files located in $CARGO_HOME (e.g., $HOME/.cargo/config.toml) have the lowest precedence [1][10][8]. Workspaces: When operating within a workspace, Cargo does not automatically read configuration files from individual member crates; it only considers configurations found relative to the CWD [7][9][8]. Recommended Workaround: To ensure Cargo picks up a specific configuration file, the most reliable method is to change your current working directory to the directory containing the desired.cargo/config.toml file before executing the cargo command [3]. Alternatively, you can use the --config CLI flag to provide specific configuration overrides directly [1][11].

Citations:


🌐 Web query:

Cargo command config file discovered current directory current working directory manifest path

💡 Result:

Cargo discovers configuration files (.cargo/config.toml) by traversing upward from the current working directory [1][2]. It searches the current directory and every parent directory until it reaches the root of the filesystem [1][2]. Additionally, Cargo reads a global configuration file located at $CARGO_HOME/config.toml (which defaults to $HOME/.cargo/config.toml on Unix or %USERPROFILE%.cargo\config.toml on Windows) [1][2]. Crucially, this hierarchical search is based on the current working directory at the time Cargo is invoked, not on the location of the project's Cargo.toml manifest file [3][4]. If you use the --manifest-path option to specify a Cargo.toml file in a different directory, Cargo will still search for configuration files starting from your current working directory, which can lead to unexpected behavior if configuration files exist in those directories [3][4]. For manifest path discovery, Cargo searches upward for a file named Cargo.toml starting from the current working directory [5]. You can use the cargo locate-project command to print the path to the Cargo.toml file that Cargo has resolved [5]. Precedence for configuration values (from lowest to highest) is generally as follows [6][7]: 1. Configuration files in parent directories (hierarchical search) [1][6]. 2. Configuration file in the current working directory [6]. 3. Environment variables (CARGO_*) [6][7]. 4. Command-line overrides (e.g., --config) [6][7]. Note that for configuration files, paths are relative to the parent directory of the directory where the config file is defined [1][2]. For environment variables and --config overrides, paths are relative to the current working directory [1][2].

Citations:


Run the Cargo command from $ROOT before using --manifest-path.

Cargo resolves .cargo/config.toml from the current working directory, not from the manifest path. Since rustc flags like target-cpu are configured there, running this script from outside the repo root or a subdirectory can bypass the intended x86-64-v3 pin and produce incompatible baseline output.

🔧 Proposed fix
 ROOT="$(cd "$(dirname "$0")/.." && pwd)"
 MANIFEST="$ROOT/crates/simd-codegen-oracle/Cargo.toml"
+cd "$ROOT"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
MANIFEST="$ROOT/crates/simd-codegen-oracle/Cargo.toml"
TARGET="${1:-}"
case "$TARGET" in
"" | -*)
TARGET="$(rustc -vV | sed -n 's/^host: //p')"
;;
*)
shift
;;
esac
# Remaining args (an optional `--` and/or analyzer flags like --verbose) forward as-is.
if [ "${1:-}" = "--" ]; then
shift
fi
BASELINE="$ROOT/crates/simd-codegen-oracle/baselines/$TARGET.toml"
if [ ! -f "$BASELINE" ]; then
echo "==> no baseline for target $TARGET at $BASELINE" >&2
exit 90
fi
echo "==> building simd-codegen-oracle (--emit asm) for $TARGET"
if [ "$TARGET" = "$(rustc -vV | sed -n 's/^host: //p')" ]; then
cargo rustc --release --manifest-path "$MANIFEST" -- --emit asm -C debuginfo=0
else
cargo rustc --release --manifest-path "$MANIFEST" --target "$TARGET" -- --emit asm -C debuginfo=0
fi
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
MANIFEST="$ROOT/crates/simd-codegen-oracle/Cargo.toml"
cd "$ROOT"
TARGET="${1:-}"
case "$TARGET" in
"" | -*)
TARGET="$(rustc -vV | sed -n 's/^host: //p')"
;;
*)
shift
;;
esac
# Remaining args (an optional `--` and/or analyzer flags like --verbose) forward as-is.
if [ "${1:-}" = "--" ]; then
shift
fi
BASELINE="$ROOT/crates/simd-codegen-oracle/baselines/$TARGET.toml"
if [ ! -f "$BASELINE" ]; then
echo "==> no baseline for target $TARGET at $BASELINE" >&2
exit 90
fi
echo "==> building simd-codegen-oracle (--emit asm) for $TARGET"
if [ "$TARGET" = "$(rustc -vV | sed -n 's/^host: //p')" ]; then
cargo rustc --release --manifest-path "$MANIFEST" -- --emit asm -C debuginfo=0
else
cargo rustc --release --manifest-path "$MANIFEST" --target "$TARGET" -- --emit asm -C debuginfo=0
fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/codegen-oracle.sh` around lines 19 - 47, Update the Cargo invocations
in the target-building logic to execute from $ROOT, such as by wrapping each
command with a subshell that changes to $ROOT before running Cargo. Preserve the
existing manifest, target, and rustc emission arguments while ensuring
.cargo/config.toml is resolved from the repository root.

@AdaWorldAPI
AdaWorldAPI merged commit 8a07459 into master Jul 29, 2026
20 checks passed
AdaWorldAPI added a commit that referenced this pull request Jul 29, 2026
docs: settle what actually compiles vendor/chacha20; board hygiene for #264/#265
AdaWorldAPI pushed a commit that referenced this pull request Jul 29, 2026
Rung 3a of the ladder, landed as the module + the measurement, WITHOUT the
call-site swap -- because the swap turns out to have a price tag.

## The module

src/hpc/blake3.rs, 771 lines, transcribed from upstream's own
reference_impl/reference_impl.rs (the spec-referenced serial implementation).
No unsafe, no core::arch, no new dependencies. Scoped by the finding that
ndarray's blake3 usage is entirely single-input -- 14 call sites across 8
files use only hash / Hasher::{new,new_keyed,update,finalize,finalize_xof} /
Hash::as_bytes, no hash_many -- so the serial core suffices.

Correctness proven against the official vectors, vendored alongside: 35/35
cases, unkeyed AND keyed, each at both 32-byte length and full extended
length via finalize_xof().fill() -- 140 assertions, input lengths 0..102400.
Plus streaming (one update vs many 37-byte updates over 102400 bytes), empty
input, and incremental fill.

Hash::eq is CONSTANT-TIME. The transcription arrived with a plain `==`, which
leaks match-prefix length through timing when a BLAKE3 output is used as a
MAC; upstream uses the constant_time_eq crate, and since this takes no new
deps the XOR-fold is written inline with a black_box on the accumulator. No
call site compares two Hash values today (seal.rs compares the truncated
MerkleRoot), so it is a guard for future consumers rather than a live fix.

## The measurement, and why the swap is not in this commit

First actual TIME number in this arc -- everything prior was
instruction-class. `.claude/knowledge/blake3-ab-bench/`, release, two runs:

  16 B     137 ns vs 100 ns   1.34x
  256 B    445 ns vs 350 ns   1.28x
  2 KB     4.3 us vs 2.7 us   1.34-1.60x
  64 KB    115 us vs  23 us   4.6-5.1x

Two gaps, different causes. The 64 KB gap is the absent hash_many (rung 3b).
The 1.3x small-input gap is NOT -- at 16 B there is one compression and no
parallelism to be had; the crate is faster because it SIMD-accelerates the
single compress itself. Closing that needs a U32x4-shaped compress, a rung
the plan did not have. Call it 3c.

So the earlier framing -- "removes things, needs no benchmark to justify" --
was true about what it removes and silent about what it costs. Dropping the
dependency is a trade, and it is the operator's call, so the call sites still
use the external crate here.

## Four corrections from codex on #268, all valid

- "No CI job compiles the chacha20 ndarray_simd backend" is FALSE.
  ci.yaml:141-142 builds vendor/chacha20 for wasm32+simd128, selecting that
  backend, under a comment calling it the wasm matryoshka guard. Only the
  AVX-512 arm is uncovered. Fixed here AND in the already-merged
  chacha20-vendoring-blast-radius.md. Fourth instance of the same
  scope-quantifier error: checked the x86 path, generalized to all targets.
- 3a does NOT remove a C build. features = ["pure"] removed all C/ASM in
  #264 already. 3a removes the Rust dependency and its intrinsic surface.
  Overstating this matters when the counterweight is transcribing a crypto
  implementation.
- The ordering arrows from 3a to rungs 4-6 contradicted the same section's
  own next sentence and would serialize independent work. Only 3a -> 3b is
  real; chacha20's backend already depends on ndarray, dalek has no cycle,
  and the u64 lane lives inside ndarray.
- [[patch.unused]] is NOT cargo's cycle diagnostic. Cargo reports the cycle
  by naming the chain (cargo update -p blake3 did); an unused patch only
  means the patch was not selected, usually a version mismatch or a stale
  lockfile. Conflating them teaches misdiagnosis and undermines the
  cargo-tree check the entry prescribes.

4 blake3 tests pass; clippy clean; fmt clean.
AdaWorldAPI pushed a commit that referenced this pull request Jul 29, 2026
…nsic

Records what shipped rather than what was planned: the native
VPROLVQ/VPRORVQ override exists on avx512 only; avx2/scalar/nightly carry
per-lane loops that are measured not to vectorize, and the explicit
vpsllq/vpsrlq shift-or for avx2 is NOT written. Stating that as a table
avoids the reading that all six backends got the override.

Also reconciles two lines the rung-6 change made stale: the 'rungs 4-6 are
optional' summary, and the 'only 3a removes a C build' clause, which #264's
features = ["pure"] had already made false and which rung 3a's own section
already corrected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfbkUCBbtZhy3yjSfCDHp
AdaWorldAPI pushed a commit that referenced this pull request Jul 29, 2026
…iming doc

Addresses the CodeRabbit pass on #268. Taking the findings that hold and
saying which do not, rather than applying all nine.

FIXED — correctness:

- **`derive_key` had zero vector coverage.** The parser read `input_len`,
  `hash`, and `keyed_hash` and skipped past the `derive_key` field already
  present in the file. It is the one public mode built as TWO passes
  (DERIVE_KEY_CONTEXT over the context string, whose output keys a
  DERIVE_KEY_MATERIAL pass over the material), so a swapped or collapsed
  transcription would have been invisible while hash/keyed_hash stayed green.
  Now asserted for all 35 cases at both the 32-byte and extended XOF lengths.
  It passed on the first run — the transcription was right; it just wasn't
  proven. Control-tested: swapping the two flags fails ONLY the new assertion.

- **`Hash::eq`'s documentation described the opposite of the code.** The
  doc-comment said `read_volatile` (the code uses `core::hint::black_box`) and
  the not-transcribed list flatly asserted "`PartialEq` here is a normal
  short-circuiting byte-array compare" — stale since the constant-time fold
  landed. On a MAC-comparison path a backwards timing claim is worse than no
  claim: an auditor would have believed this file leaks match-prefix length
  when it does not. Also now states that `black_box` is a best-effort barrier,
  not a guarantee; the data-independent loop is the actual property.

FIXED — hazards:

- `run.sh` copied the bench to a fixed `examples/blake3_ab.rs` and had the EXIT
  trap `rm` it unconditionally: a pre-existing file there would be clobbered
  and then deleted. Now refuses to overwrite, and only removes the directory it
  created.
- `shiftor_rot_u64x8` is `pub` and took arbitrary `u32` into `a >> n` — a debug
  panic and a release wrapping-shift for `n >= 64`. Normalized at the PUBLIC
  wrapper and deliberately NOT in the measured inner body, which would have
  changed the codegen the oracle exists to observe.
- Rotate test extended past 64 (65/127/128/191) against std's `u64::rotate_*`,
  pinning the mod-64 contract rather than only the width case.
- Test-local context const renamed `VECTORS_CONTEXT`; as `DERIVE_KEY_CONTEXT`
  it shadowed the module-scope u32 flag of that name.
- Redundant `.into()` in `derive_key` (`clippy::useless_conversion`).
- The `pub mod blake3` incident history was an OUTER doc comment, so rustdoc
  would publish it as module documentation. Now a plain `//` comment.

Three more stale doc claims, all the same class already corrected elsewhere in
this PR — an assertion outliving the change that falsified it:

- EPIPHANIES said "an error there means no cycle and the work is unblocked"
  one paragraph after explaining the error is ambiguous. Read literally it
  marks blocked work unblocked on the strength of a typo. Now a four-step
  procedure with the error explicitly inconclusive.
- crypto-lane-status prescribed an explicit shift-or for AVX2/NEON/wasm,
  contradicting the measurement directly above it (LLVM folds the two forms
  byte-identically) and misdescribing what shipped.
- blake3-in-tree-measured still credited the swap with removing the C build,
  already removed by `features = ["pure"]` in #264.

NOT taken, with reasons:

- Doctest examples on every public API. Worth doing, but it is a separate
  change across a surface this PR is not otherwise touching, and the vectors
  already smoke-test the public path harder than an example would.
- Caching the compressed block in `OutputReader`. A real inefficiency for
  small repeated `fill` calls, but unmeasured, and the module is explicitly
  not on a hot path pending the swap ruling.
- Deduplicating the rotate body across backends. Every other lane type in this
  crate is duplicated per backend by design; collapsing one method would make
  it the exception, not the rule.
- Secret-scanner allowlist for the vectors file. Correct that the hex is
  published test data, not credentials, but no scanner config lives in this
  repo to add it to.

Full lib suite green: 2200 passed, 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfbkUCBbtZhy3yjSfCDHp
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