Skip to content

fix(ci): clear the remaining Rust 1.98.0 clippy failures (qmoe, ep-cpu) - #1609

Merged
justinchuby merged 1 commit into
mainfrom
justinchuby-repair-main-ci
Aug 20, 2026
Merged

justinchuby merged 1 commit into
mainfrom
justinchuby-repair-main-ci

Conversation

@justinchuby

Copy link
Copy Markdown
Owner

Finishes the job #1604 started. Refs #1600.

main is still red at a429538f. #1604 fixed the fmt drift, manual_slice_fill, needless_late_init, and centrally allowed chunks_exact_to_as_chunks, which turned Rust quality, Fast (Linux), CLI ORT (Linux) and all three coverage jobs green. Two things it could not have seen are left.

What was still failing

Clippy CUDA execution provider — the only non-Windows red left on main.

Not chunks_exact and not approx_constant, both of which were guesses on record in #1600. I pulled the job log rather than infer. Run 32406806834 (main @ eb8ce595) and run 32406772158 (#1604's own branch) agree exactly:

error: this `if` statement can be collapsed
 --> crates/onnx-runtime-ep-cuda/src/kernels/qmoe.rs:1768:9
 --> crates/onnx-runtime-ep-cuda/src/kernels/qmoe.rs:1780:9
 --> crates/onnx-runtime-ep-cuda/src/kernels/qmoe.rs:1792:9
 --> crates/onnx-runtime-ep-cuda/src/kernels/qmoe.rs:1804:9
 --> crates/onnx-runtime-ep-cuda/src/kernels/qmoe.rs:1805:13
error: could not compile `onnx-runtime-ep-cuda` (lib) due to 5 previous errors

clippy::collapsible_if now fires across if let chains in 1.98.0. The float_widen_entry block it lands on arrived with #1602, which merged before the 1.98.0 bump was understood — so this is a second, independent 1.98.0 casualty, not a regression from #1604.

Fixed by collapsing into let chains, which is clippy's own suggested form and is available on edition 2024. 1804 and 1805 are one three-deep nest reported twice; it becomes a single chain.

Three onnx-runtime-ep-cpu sites that no CI job can currently see.

#1600 attributed these to Fast (Linux) and Rust quality. That attribution is wrong, and the correction matters for the ledger:

  • sdpa.rs ×6 unusual_byte_groupings are inside #[cfg(all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")))]
  • accelerate_gemm.rs:943 manual_range_contains is in the Accelerate-framework module
  • matmul.rs:3424 collapsible_if is inside #[cfg(any(target_os = "macos", target_os = "ios"))]

The Linux gate never compiles any of them, and ci.yml runs no clippy on macOS at all — Rust coverage (macOS arm64) sets RUSTFLAGS: -D warnings, which catches rustc warnings but not clippy lints. So these are latent, not currently-red. They still break cargo clippy for anyone developing on Apple silicon, which is how they surfaced. I've fixed them here; the underlying gap is noted in #1600 rather than papered over.

How I verified

I could reproduce all of this locally, including the CUDA gate that #1600 expected to need CI as its only oracle. The unlock: CI is on rustc 1.98.0 (88d9e12ae 2026-08-18) — readable from the failing job log — and rustup install 1.98.0 yields that exact build. cargo clippy -p onnx-runtime-ep-cuda --features cuda needs no CUDA toolkit to run.

Every check below is a negative control: confirmed failing before the change and passing after.

check before after
cargo clippy -p onnx-runtime-ep-cuda --features cuda -- -D warnings (CI's exact command) 5 errors, identical line:col to CI clean
sdpa.rs lint set, -p onnx-runtime-ep-cpu --all-targets 6 errors at 2542/2543/2544/2587/2588/2589 clean
ci.yml's full offline-crate gate, package list verbatim, --all-targets -- -D warnings 2 errors (accelerate_gemm, matmul) clean, exit 0
cargo test -p onnx-runtime-ep-cpu — 1525 passed, 0 failed
cargo fmt --all -- --check — clean

The hex regrouping is asserted numerically, not by eye — I compiled both spellings and compared:

DEC0DE_A: old=233573866 (0xDEC0DEA)  new=233573866  identical=true
DEC0DE_B: old=233573867 (0xDEC0DEB)  new=233573867  identical=true
DEC0DE_C: old=233573868 (0xDEC0DEC)  new=233573868  identical=true
A1CE_0A:  old=10604042  (0xA1CE0A)   new=10604042   identical=true
A1CE_0B:  old=10604043  (0xA1CE0B)   new=10604043   identical=true
A1CE_0C:  old=10604044  (0xA1CE0C)   new=10604044   identical=true

Those are seeds for deterministic_values, so an unchanged value is what keeps the expected test outputs valid. The 1525-test run is the confirmation. The matmul.rs thin-M NEON path I collapsed is aarch64/macOS, so it is genuinely exercised on this machine rather than merely compiled.

What I could not verify

  • Nothing was run on Windows. CUDA compile (Windows x86_64) fails on the identical five qmoe.rs errors as Linux, so the same fix should clear it, but I am asserting that from log equality, not from a Windows run.
  • CUDA is lint-checked, never built or executed here. No GPU and no CUDA toolkit. --features cuda type-checks the crate; it does not prove the QMoE kernel still runs. The change is a pure if-nesting collapse with no reordering of conditions and no change to short-circuit behaviour, but that is an argument, not a measurement.
  • This does not address Test cross-platform offline crates on Rust (Windows ARM64) / Rust coverage (Windows x86_64). Deliberately kept separate per CI on main is red across 8 jobs, and has been since 08-03 — attribution table + reproductions #1600's ordering.
  • This does not pin the toolchain. See below.

On pinning — deliberately not done here

#1600 calls the missing pin the root cause. Before changing it I found this in ci.yml, above every one of the 17 rustup install sites:

Keep CI on Rust stable deliberately: this follows Rust's stability promise without freezing security fixes; the cache key records the resolved rustc release.

So the unpinned toolchain is an explicit, reasoned decision, not an oversight — and reversing a documented policy does not belong inside an unrelated repair PR. There are also two concrete traps that make a bare rust-toolchain.toml unsafe: rustup component add llvm-tools-preview and any rustup target add apply to the default toolchain, not to a directory-pinned one, so a naive pin would quietly break the two coverage jobs and the aarch64-pc-windows-msvc cross-compile. (Miri is safe — it invokes cargo +nightly explicitly.)

Worth doing, worth doing on purpose, and worth its own PR with that policy comment updated to match. Raised in #1600 with the tradeoff spelled out.

Conflict risk

Flagging for #1579 (the #1186 memory stack): this touches crates/onnx-runtime-ep-cpu/src/kernels/{sdpa,accelerate_gemm,matmul}.rs and crates/onnx-runtime-ep-cuda/src/kernels/qmoe.rs. All four hunks are small and local — six literals, one boolean, two if-nesting collapses — so textual conflicts should be trivial to resolve. Nothing in crates/onnx-genai-engine/src/{decode,native_decode}/ is touched.

#1604 repaired most of the 1.98.0 fallout but could not see two things:

- `onnx-runtime-ep-cuda/src/kernels/qmoe.rs` trips `collapsible_if` at five
  sites. This is the only thing keeping `main` red outside Windows: it fails
  `Clippy CUDA execution provider` in both `CUDA compile (Linux x86_64)` and
  `CUDA compile (Windows x86_64)`. The block came in with #1602, which merged
  before the 1.98.0 bump was understood. Collapsed into `let` chains (edition
  2024), which is exactly clippy's own suggestion.

- Three `onnx-runtime-ep-cpu` lint sites sit behind
  `cfg(all(target_arch = "aarch64", any(target_os = "macos", ...)))`, so the
  Linux quality gate never compiles them and no CI job lints macOS at all.
  They are latent rather than currently-red, but they break `cargo clippy` for
  anyone developing on Apple silicon:
    * `sdpa.rs` x6 — `unusual_byte_groupings` on RNG seeds
    * `accelerate_gemm.rs:943` — `manual_range_contains`
    * `matmul.rs:3424` — `collapsible_if`

The six `sdpa.rs` literals are regrouped only; their values are asserted
numerically identical (0xDEC0DE_A == 0xDEC_0DEA == 233573866, and so on), not
eyeballed, so the deterministic test seeds are unchanged.

Verified with rustc/clippy 1.98.0 (88d9e12ae 2026-08-18), the same build CI
reports, each with a negative control confirming the check fails before the
change and passes after:

- `cargo clippy -p onnx-runtime-ep-cuda --features cuda -- -D warnings`
  reproduces CI's five errors at identical line:col, and is clean after.
- the full offline-crate clippy gate from ci.yml, verbatim, `--all-targets`
  `-- -D warnings`: clean.
- `cargo test -p onnx-runtime-ep-cpu`: 1525 passed, 0 failed. The thin-M NEON
  path changed in `matmul.rs` is aarch64/macOS, so it is actually exercised.
- `cargo fmt --all -- --check`: clean.

Refs #1600.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@justinchuby

Copy link
Copy Markdown
Owner Author

Reviewed. I re-derived the claims I could check independently rather than reading them, and they hold. Two of them correct me, which is the part worth recording.

Independently verified

The six seed values are unchanged. I did not take the table on trust — I extracted every hex literal from both sides of the diff, stripped separators, and parsed base 16:

0xDEC0DE_A -> 233573866   0xDEC_0DEA -> 233573866
0xDEC0DE_B -> 233573867   0xDEC_0DEB -> 233573867
0xDEC0DE_C -> 233573868   0xDEC_0DEC -> 233573868
0xA1CE_0A  -> 10604042    0xA1_CE0A  -> 10604042
0xA1CE_0B  -> 10604043    0xA1_CE0B  -> 10604043
0xA1CE_0C  -> 10604044    0xA1_CE0C  -> 10604044

Multisets identical. Since these feed deterministic_values, that is the property that keeps the expected outputs valid, and your 1525-test run confirms it end to end.

The qmoe.rs collapses are semantics-preserving. All five are if let A { if let B { X } } with no else on either level, becoming if let A && let B { X }. && short-circuits exactly where the nesting did, so float_widen_entry(..)? is still only evaluated when the outer pattern matched, and the ? propagates from the same set of states. The three-deep fc3 bias nest collapsing to one chain is the same argument applied twice. Nothing is reordered.

The cfg-gating claim. This is the one I most wanted to disprove, and could not. sdpa.rs's sites at 2542-2544 and 2587-2589 sit under #[cfg(all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")))] inside the test module; matmul.rs:3424 is likewise macOS-gated. Linux never compiles any of them.

Where that corrects me, and why the correction matters more than the fix

In #1600 I attributed the ep-cpu lint set to Fast (Linux) and Rust quality, and I claimed strong evidence for it because I had reproduced all ten errors verbatim in a clean origin/main worktree. I even ranked that evidence above my onnx-runtime-ir attribution on the grounds that one was reproduced and the other was not.

The reproduction was real. The inference from it was wrong. I reproduced them on macOS, because that is the machine I have — so what I actually established was "pre-existing on main", not "this is what makes the Linux job red". Those are different claims, and my host's cfg profile silently supplied the difference. Reproducing on a clean tree controls for my branch; it does not control for my platform. I had no business calling that evidence strong for a Linux job.

Your finding that ci.yml runs no clippy on macOS at all — Rust coverage (macOS arm64) sets RUSTFLAGS: -D warnings, which catches rustc warnings but not clippy lints — is the more valuable half of this PR. It means these were never red and could not have been: they are latent, reachable only by a human developing on Apple silicon. Please keep that gap on #1600 as its own item rather than letting the fix close it; a lint class with no gate will simply refill.

Second correction: I had told you CUDA compile / Clippy CUDA execution provider was the chunks_exact lint arriving via onnx-runtime-ir, after earlier telling #1600 it was approx_constant in optimizer.rs. Both were guesses, and you were right to pull the job log instead of inheriting either. It is collapsible_if in qmoe.rs, from #1602. Note that approx_constant in optimizer.rs is nonetheless a real pre-existing failure — I hit it today under cargo clippy -p onnx-runtime-ep-cuda --lib --all-targets and reproduced it verbatim on clean origin/main. It is not what reddens that CI job, but it does break --all-targets locally. Worth a line in #1600 so the next person does not rediscover it.

rustup install 1.98.0 was the move I missed. I recorded "no local reproduction possible, CI is the only oracle" for the CUDA gate. That was defeatism dressed as rigour: the exact toolchain build is printed in the failing job log and is one command away, and --features cuda type-checks with no CUDA toolkit present. Getting the oracle onto the developer's machine beats reasoning carefully without one.

Scope

No conflict with #1579. It touches none of these four files, and I confirmed that mechanically rather than by inspection (git diff --numstat origin/main...HEAD is empty for each).

Your three "could not verify" items are stated at the right strength, particularly declining to claim the QMoE kernel still runs. A nesting collapse with no reordering is a strong argument, and you correctly labelled it an argument.

The thing still not done

Pinning the toolchain. This is now the third PR in two days spent on symptoms of one unpinned toolchain, and the ledger of misattributions above — mine, twice — is the real cost: nobody could tell platform-gated from toolchain-gated from branch-introduced, because the toolchain was a free variable in every comparison. Land this, then pin it.

@codecov

codecov Bot commented Aug 20, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.94%. Comparing base (a429538) to head (6e38849).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1609      +/-   ##
==========================================
+ Coverage   80.79%   80.94%   +0.14%     
==========================================
  Files         380      382       +2     
  Lines      175073   178555    +3482     
  Branches   175073   178555    +3482     
==========================================
+ Hits       141450   144528    +3078     
- Misses      28727    29088     +361     
- Partials     4896     4939      +43     
Flag Coverage Δ
cli-ort-linux 82.60% <ø> (ø)
cli-ort-windows 82.19% <ø> (+0.09%) ⬆️
mlas 85.09% <ø> (?)
offline 80.81% <100.00%> (+0.07%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...onnx-runtime-ep-cpu/src/kernels/accelerate_gemm.rs 91.43% <100.00%> (ø)
crates/onnx-runtime-ep-cpu/src/kernels/matmul.rs 82.09% <100.00%> (+0.02%) ⬆️
crates/onnx-runtime-ep-cpu/src/kernels/sdpa.rs 98.25% <100.00%> (ø)

... and 16 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@justinchuby
justinchuby merged commit 78058a4 into main Aug 20, 2026
19 checks passed
@justinchuby
justinchuby deleted the justinchuby-repair-main-ci branch August 20, 2026 20:34
justinchuby added a commit that referenced this pull request Aug 20, 2026
…hosts cannot take

`Test cross-platform offline crates` fails on
`half_prefill_gebp_agrees_with_the_blocked_half_gemm_and_is_the_route`:

    assertion `left == right` failed: BFloat16 m=2: prefill did not take the
    fused widen-pack GEBP
      left: 0
     right: 1

The library is right and the test is wrong. `half_gemm_tile` tries the native
AVX-512 BF16 kernel *before* the fused widen-pack GEBP:

    if format == HalfFormat::Bf16 && x86_bf16::native_available() { ...; return }
    if half_prefill_gebp_selected(..) && half_prefill_gebp_enabled() { ... }

`native_available()` is a runtime `is_x86_feature_detected!("avx512bf16")`
probe, so on a host that has AVX-512 BF16 a bf16 tile legitimately never
reaches the GEBP and the counter stays 0. The test carved out the analogous
MLAS/f16 case but not this one.

That explains the shape of the failure, which otherwise looks like a
regression from whatever merged most recently: it tracks the runner's CPU, not
the commit. It passed on `eb8ce595` and failed on `a429538f` on Linux while
also failing on Windows x86_64 — different hardware, same tree. Nothing in
#1608 (CUDA VMM) could have touched a CPU bf16 GEBP route.

The precedence is already documented and already encoded for the decode side
in `half_decode_prefers_gebp_when`, which declines bf16 exactly when
`native_available()`. Only the prefill guardrail was missing it.

Rather than switch the assertion off on AVX-512 hosts, count both arms and
assert exactly one ran. A bare "GEBP was not taken" skip would stop catching a
silent fall-through to the portable blocked half GEMM on precisely the newest
hardware; the disjunction keeps the dispatch guarded on every CPU. The numeric
agreement check against `naive_matmul` runs either way, as before.

Verification, and its limits:

- `cargo fmt --all -- --check` clean, and `cargo clippy -p onnx-runtime-ep-cpu
  --all-targets` reports only the pre-existing macOS lints fixed in #1609 —
  none from this change.
- **The changed code is not compiled locally at all.** It is entirely
  `#[cfg(target_arch = "x86_64")]` and this host is aarch64-apple-darwin. I
  tried both x86_64 targets: `x86_64-apple-darwin` fails because upstream
  publishes no `onnxruntime-osx-x86_64-1.28.0` asset, and
  `x86_64-unknown-linux-gnu` fails in ort-sys bindgen with `'stdlib.h' file not
  found` — the same environmental blocker #1604 hit on
  `aarch64-pc-windows-msvc`. `onnx-genai-ort-sys` is a normal dependency of
  `onnx-runtime-ep-cpu` via `onnx-runtime-ep-api`, so there is no `--lib`-only
  escape. CI is the oracle for this one.
- Even a green CI run only proves the non-AVX-512 arm unless the runner
  happens to have `avx512bf16`. The `(0, 1)` branch may go unexercised.

Refs #1600.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
justinchuby added a commit that referenced this pull request Aug 20, 2026
…1381) (#1613)

## The finding: the in-tree gate was the bug, not the missing one

`MatMul` sent every `m == 1` `f16`/`bf16` decode with `k * n >=
HALF_PREFILL_GEBP_MIN_WEIGHT`
(1 048 576) to the fused widen-pack GEBP. `Gemm` never did — its `m ==
1` `f16` path stayed on the
decode GEMV at every weight. That divergence is #1381, and the obvious
reading was that `Gemm` was
missing a gate. I implemented that reading first, benchmarked it, and it
reversed: at the model
shapes the GEBP was 19–26% **slower**. Escalating to the authoritative
production harness showed
why — it is `MatMul` that was carrying a bad gate.

**This PR retires the handover.** `#1381` closes with `MatMul` adopting
`Gemm`'s route rather than
the reverse.

## Evidence

All numbers through
`crates/onnx-runtime-ep-cpu/benches/half_decode_gemv_ab.rs`, which
drives
`ExecutionProvider::get_kernel` + `Kernel::execute` (dispatch and output
narrowing included). Arms
are selected by `ONNX_GENAI_CPU_MM_HALF_GEMV`/`_GEBP`, which are
process-wide `OnceLock`s, so one
arm per process. Every ratio is divided by an `f32` control measured in
the same two processes,
because this host is heavily shared.

The original gate's evidence was **32 threads and `k <= 2048` only**.

- **8 threads — a loss at every `full`-set shape at or above the gate**,
`/ctl` 0.26–0.76
(1.3x–3.8x), and in **20 of the 22 `f16` cells** of the
threshold-neighbourhood `band` set over
**two independent repetitions**, `/ctl` 0.20–1.14 (up to 5.0x). The
GEBP's weight bandwidth is
pinned at 20–34 GB/s independent of shape; the GEMV reaches 24–155 GB/s.
- Reported against myself: the band re-run **does not reproduce** an
earlier "no row above 1.00"
claim. `w1.05M_k1024` — *exactly* at the retired threshold, at `k =
1024` — measured 1.14 then
0.89, and five `bf16` cells reach 1.00+ with their `f32` controls
drifted to 0.55–0.94, which
makes those cells unusable rather than favourable. Both are tabulated in
the doc rather than
dropped. The earlier 6.7x figure is likewise not reproducible and has
been walked back to the
    supported 5.0x everywhere it was cited.
- **32 threads — still a loss at every shape a 7B-class decode issues.**
`k = 4096` qkv/mlp `/ctl`
0.51–0.89; a 136M `lm_head` 0.86. (The original table reported 1.18/1.37
for the last two; re-run
  they are 0.88/0.86.) `bf16` tracks `f16` at 0.41–0.88.
- **The one corner it wins** — `k = 1024`, 1.05M–4.2M, 32 threads,
`/ctl` 0.95–1.49 — is not a shape
any such model emits, and its `k = 2048` neighbours are not reproducible
run to run (6.3M measured
  1.31 then 0.70 from the same binary).

## Why there was nothing to retune

The GEBP earns its packing by reusing a `KC x NR` panel of `B` across
the rows of `A`
(`MR = 6, NR = 16, KC = 256`). At `m == 1`, `m_panels = 1` — every panel
is consumed by exactly one
microkernel pass, so **none** of the packing is repaid.

Where the cost actually lands: the widen-pack writes `k*n` `f32` and
reads it back, but per strip
that is a `bpack` of at most `KC * 16 * NR * 4 B` = 256 KiB, i.e.
**L2-resident** on this host — so
that traffic is not a DRAM story. What does reach DRAM is **line
amplification**: `pack_b_half`
walks `B` column-strip-major and at the usual `panels_per_strip = 1`
touches `NR * 2` = 32 bytes of
every 64-byte line, ~2x the GEMV's read traffic. The rest is L2
bandwidth, the widening compute, and
a fork/join over strips that one row of `A` cannot amortise. Both axes
follow: pack work per unit of
reuse rises with `k`, and it can only be overlapped if there are workers
to overlap it with — which
is why the arm collapses at 8 threads and merely loses at 32.

A decode-specialised GEBP that skips packing `B` **is** the GEMV. The
finding is structural.

## Changes

- `matmul.rs`: dropped `&& !half_decode_prefers_gebp(..)` from the
decode arm; deleted
`half_decode_prefers_gebp` and `half_decode_prefers_gebp_when` (94
lines, incl. the superseded
  evidence table).
- `gemm.rs`: **behaviourally unchanged** — it was right. Adds a
test-only route counter and the
  comment recording #1381's resolution.
- **Deliberately untouched:** `HALF_PREFILL_GEBP_MIN_WEIGHT` /
`half_prefill_gebp_selected`. They are
the *prefill* gate and they still serve a **batched** `m == 1` half
MatMul, which the non-batched
GEMV declines and whose only alternative is the row-blocked half GEMM at
16x–21x. Pinned by
  `a_batched_single_row_half_matmul_still_reaches_the_gebp`.
- **Coverage repaired in the same change:** new `PROBE_SHAPE=band` puts
11 rows immediately below, at
and above the retired threshold at three different `k`, so a
`k`-dependent effect cannot hide
inside a `k * n` gate again. Pins are now by **execution** rather than
by asking a predicate — the
  predicate is what was deleted.
- Docs: new `docs/benchmarks/2026-08-21-half-decode-gebp-retired.md`,
new §14 in
`docs/performance/CPU_MATMUL_ASSIGNMENT.md`, and a superseded banner on
the 2026-08-19 bf16 record
  whose second finding this reverses.

## Review

Reviewed twice by `claude-opus-4.8`, both times with an explicit
instruction to argue the opposing
case (that retiring the divert is wrong, or that a narrower gate should
have been kept). It did, and
concluded the case does not survive: a `k * n`-only gate — the only kind
the retired predicate could
express — cannot see thread count, and reinstating it to capture the `k
= 1024` corner net-loses on
the `k = 4096` and `lm_head` shapes that models actually issue. All five
defects from the first pass
are fixed in this head; the second pass found one further Low (a loss
range that no longer matched
its own table after rescoping), which is fixed, and prompted the band
re-run above.

## Mutation test

Re-inserting the divert (`&& geom.k * geom.n < 1_048_576` on the decode
arm) fails exactly three
tests — `no_half_decode_is_diverted_off_the_gemv_on_weight`,
`f16_decode_at_the_retired_weight_gate_keeps_the_gemv`,
`gemm_and_matmul_take_the_same_decode_route` — so the pins are not
vacuous.

## Validation (local, on this head merged with `origin/main` 78058a4)

`PASS=19 FAIL=1` over the full matrix: `fmt --all`, offline build, `-p
onnx-runtime-ep-cpu --lib`
(**1552 passed / 0 failed / 21 ignored**), clippy `-D warnings`,
`--features mlas`, native-be
clippy, `aarch64-unknown-linux-gnu` clippy, `--no-default-features`,
`--all-features`, **no-MLAS
default-artifact symbol scan**, `cross-compile.sh`, and all eight
`scripts/` conformance checks
(dispatch reachability, dispatch manifest, feature-gate coverage,
documented env vars, publish
order, profile table, platform naming, workspace test packages).

The one failure is step **H** `aarch64-pc-windows-msvc`, which is
environmental and pre-existing:
`ort-sys`' bindgen cannot find `stdlib.h` without the Windows SDK
headers. No code path in this diff
is Windows-specific.

Additionally, because the repo's quality lane is wider than that step's
package list, I ran it
separately: clippy `-D warnings --all-targets` over the full 31-package
quality set — **green** — and
`-p onnx-genai-cli` — **green**.

> Out-of-scope observation: `-p onnx-runtime-ep-cuda --features cuda`
clippy is **red on `main`
> today** (`unusual_byte_groupings` in
`device_argmax.rs`/`matmul_nbits.rs`, `type_complexity` in
> `gqa_shared_prefix_parity_gpu.rs`, a manual `div_ceil`). Pre-existing
1.98.0 fallout in the lane
> #1609 did not cover; this diff touches no CUDA file.

Miri is **not** meaningful coverage for this EP's SIMD and this diff
adds no `unsafe` — reporting it
as *no coverage*, not as a pass.

## Remaining losses

- The `k = 1024` / 32-thread corner gives up 1.1x–1.5x. Recovering it
needs a thread-count- **and**
`k`-dependent predicate; not attempted, because the measurement that
would justify it is the least
reproducible one on this host — and §12 already records a per-op result
that inverted under the
  real pool.
- The GEMV itself is not at the roofline: 24–155 GB/s against a 75.8
GB/s DRAM ceiling puts the
large shapes at ~77% and leaves the small ones latency-bound. Separate
line of work.
- `Gemm`'s half fast path is still **`f16`-only**: `bf16` operands
return `None` at the dtype check
and fall into the portable blocked half GEMM, where `MatMul` serves them
from the same GEMV. A
distinct, separately mergeable gap — documented as open, not addressed
here.

Closes #1381.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
justinchuby added a commit that referenced this pull request Aug 20, 2026
…hosts cannot take (#1610)

Fixes the `Rust coverage` failure in
`kernels::matmul::tests::half_prefill_gebp_agrees_with_the_blocked_half_gemm_and_is_the_route`.

## What the failure was

```
matmul.rs:5434: assertion `left == right` failed: BFloat16 m=2: prefill did not take the fused widen-pack GEBP
  left: 0   right: 1
```

**This is a latent test defect, not a regression, and it is not
attributable to any commit.** A naive bisect says otherwise — `Rust
coverage (Linux x86_64)` is green at `eb8ce595` (#1604) and red at
`a429538f` (#1608) — but #1608 changes exactly one file,
`crates/onnx-runtime-ep-cuda/src/provider.rs`, and that coverage job's
package list does not contain `-p onnx-runtime-ep-cuda` at all. The
commit's only changed file is not compiled by the job that went red. The
red tracks **which runner picked up the job**, not the code.

`half_gemm_tile` tries the native AVX-512 BF16 kernel *before* the GEBP
and returns:

```rust
if format == HalfFormat::Bf16 && x86_bf16::native_available() {
    x86_bf16::gemm(a, b, c, m, k, n);
    return;                       // never reaches the GEBP route
}
if half_prefill_gebp_selected(format, m, k, n) && half_prefill_gebp_enabled() { ... }
```

On a host with `avx512bf16` (Cooper Lake / Sapphire Rapids and later) a
bf16 tile legitimately never reaches the GEBP and the counter stays 0.
GitHub's Linux x64 pool is a mix of Intel generations, so the same
commit passes or fails depending on the runner. The precedence is
already encoded for decode in `half_decode_prefers_gebp_when`; only
prefill's guardrail lacked it.

## A second, latent hardware assumption in the same test

The test already had `if !crate::backend::has_simd_x86() { continue; }`.
That is a **`continue`**, so on a non-AVX2/FMA host it skipped the
numeric comparison against `naive_matmul` as well as the route assertion
— deleting the half of the test that is valuable on every host. It has
never fired (all current runners have AVX2), so it was latent, but it is
the same class of bug.

## What changed

The expectation is now **derived from the predicates `half_gemm_tile`
actually dispatches on**, rather than restating hardware conditions in
the test:

```rust
let expect_native_bf16 = format == HalfFormat::Bf16 && x86_bf16::native_available();
let expect_gebp = !expect_native_bf16
    && half_prefill_gebp_selected(format, m, k, n)
    && half_prefill_gebp_enabled();
assert_eq!(
    (half_prefill_gebp_calls(), half_native_bf16_calls()),
    (u64::from(expect_gebp), u64::from(expect_native_bf16)), ...);
```

- A test-only `HALF_NATIVE_BF16_CALLS` counter on the native bf16 arm
distinguishes "took the *other* fast route" from "silently fell through
to the portable blocked half GEMM" — which is what the guardrail is for.
Without it, the only way to tolerate the bf16 case is to stop asserting
anything.
- `(0, 0)` on a no-SIMD host is now an asserted-correct outcome instead
of a skip.
- **The numeric comparison against `naive_matmul` now runs
unconditionally on every host.**
- Because the expectation reuses the real predicates, it cannot drift
from the dispatch order the way a transcribed condition would.

## How I verified it

Transcribed `half_gemm_tile`'s dispatch order and the new expectation
into a standalone program and checked them against each other across
**all 16 combinations** of `native_available` × `has_simd_x86` ×
`half_prefill_gebp_enabled` × dtype. They agree on all 16.

## What I could not verify

**I have not reproduced the original failure, and I have not run this
test.** Stating that plainly rather than implying otherwise.

This host is aarch64 macOS; the test is `#[cfg(target_arch = "x86_64")]`
and `x86_bf16::native_available()` does not exist here. Cross-compiling
is blocked in both directions: `x86_64-apple-darwin` because upstream
publishes no `onnxruntime-osx-x86_64-1.28.0.tgz` (HTTP 404), and
`x86_64-unknown-linux-gnu` because ort-sys bindgen fails on `'stdlib.h'
file not found`. `onnx-genai-ort-sys` is a normal dependency via
`onnx-runtime-ep-api`, so `--lib` does not avoid it. No Docker on this
machine.

So the 16-configuration check verifies the *logic* is consistent with
the dispatch order. It does **not** verify that the code compiles under
`target_arch = "x86_64"`, nor that the counter increments where I placed
it. CI is the oracle for both.

**A green run on this PR will not prove the fix.** It only exercises the
`(1, 0)` branch unless the runner happens to have `avx512bf16`. The `(0,
1)` branch — the one that was actually failing — is reachable only on
Cooper Lake or newer, and a runner cannot be requested. Corroborating
evidence for the mechanism, though: #1609's run went green on `Rust
coverage (Linux x86_64)` *and* `Rust coverage (Windows x86_64)` with the
**old** assertion still in place, which is exactly what the
CPU-dependence diagnosis predicts and is not evidence that the old test
was fine.

## Notes

- Touches only `crates/onnx-runtime-ep-cpu/src/kernels/matmul.rs`. No
overlap with #1579's file set; nothing in
`crates/onnx-genai-engine/src/{decode,native_decode}/`.
- Based on `a429538f`, which predates #1609, so this branch inherits
`main`'s `CUDA compile (Linux x86_64)` red at `Clippy CUDA execution
provider`. That failure is **not** from this change — #1609 fixes it.

Refs #1600.

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
justinchuby added a commit that referenced this pull request Aug 20, 2026
fix(ci): collapse the NVRTC cache lookup's nested `if let`

`main` is red on both `CUDA compile (Linux x86_64)` and `CUDA compile
(Windows x86_64)` at `81855858`. This is the third independent casualty
of the Rust 1.98.0 clippy bump, after #1604 and #1609. Refs #1600.

## What is failing

```
error: this `if` statement can be collapsed
   --> crates/onnx-runtime-ep-cuda/src/runtime.rs:942:9
    = note: `-D clippy::collapsible-if` implied by `-D warnings`
error: could not compile `onnx-runtime-ep-cuda` (lib) due to 1 previous error
```

`clippy::collapsible_if` fires across `if let` chains in 1.98.0. The
block
it lands on is the PTX disk-cache lookup added by #1612, which merged
before this lint's reach was understood -- the same way #1602's
`float_widen_entry` block became #1609's problem. Nothing here is a
regression from #1612's logic; the code is correct, the lint is new.

I did not infer the location. I pulled the job log from run

[32421805546](https://github.com/justinchuby/onnx-genai/actions/runs/32421805546)
and read it.

## The fix

Collapse to a let-chain, matching the form #1609 adopted in `qmoe.rs`. A
let-chain is semantically identical to the nested form -- same
short-circuit, same bindings, same scope -- so cache behaviour is
unchanged. `kernel_cache::load` returning `None`, or the bytes failing
UTF-8, still both fall through to a real NVRTC compile.

Only one occurrence exists. The other `kernel_cache::load` call site in
this file (line 973, the cubin path) is a `match`, not a nested `if
let`,
and does not trip the lint.

## Verified on this macOS host

- `cargo clippy -p onnx-runtime-ep-cuda --features cuda --lib -- -D
warnings`
  is clean with this change.
- **Reverse control, which is the part that matters:** I reverted the
file
  to `origin/main` and re-ran the same command. It fails with the exact
  CI error at the exact line --
`error: this if statement can be collapsed -->
crates/onnx-runtime-ep-cuda/src/runtime.rs:942:9`
-- then passes again once the change is restored. So this reproduces the
  CI failure locally and demonstrably clears it, rather than merely
  compiling.
- `cargo fmt --all -- --check` clean.

## Not verified

No CUDA hardware here, so nothing was executed -- but this job is a
compile/lint gate, and the gate itself is what I reproduced. The runtime
path is untouched.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c80f8522-983c-47f7-8241-2155a823aabe

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c80f8522-983c-47f7-8241-2155a823aabe
justinchuby added a commit that referenced this pull request Aug 21, 2026
Closes the root cause behind the 1.98.0 CI incidents tracked in #1600.

## What the failure was

Every blocking gate in `ci.yml` runs under `-D warnings` — either as
`RUSTFLAGS` or as a trailing `-- -D warnings` on clippy. CI resolved
`stable` at job time, so when runner images rolled forward to `rustc
1.98.0 (88d9e12ae 2026-08-18)`, every lint that release made
warn-by-default became an instant failure on code that was clean the day
before. No commit to blame, and a contributor on 1.97.0 could not
reproduce it — clippy 0.1.97 does not carry those lints at all, so it
does not even print them.

Four consecutive incidents, all the same cause:

| PR | Lint | Code from |
|---|---|---|
| #1604 | `cargo fmt` drift, `manual_slice_fill`, `needless_late_init` |
assorted |
| #1609 | `collapsible_if` ×5 in `qmoe.rs` + ep-cpu lints | #1602 |
| #1615 | `collapsible_if` in `runtime.rs` | #1612 |
| #1603 | `chunks_exact_to_as_chunks`, 12 crates | assorted |

Each fixed symptoms; none could prevent the next. Formatting is the
self-perpetuating one — an unpinned rustfmt means whoever formats next
re-flows the file back the other way, so the tree oscillates.

## How I reproduced it

I read the version out of a failing job log rather than guessing, then
installed it:

```
rustup toolchain install 1.98.0    # -> rustc 1.98.0 (88d9e12ae 2026-08-18), byte-identical build hash
```

That is what turned "unreproducible locally" into reproducible, and it
is what made #1609 and #1603 diagnosable at all.

## What I changed

**`rust-toolchain.toml`** (new) — `channel = "1.98.0"`, `profile =
"minimal"`, `components = ["clippy", "rustfmt", "llvm-tools-preview"]`.
No `targets` key: `Rust (Windows ARM64)` runs natively on
`windows-11-arm` and needs no cross target, and
`scripts/check_cross_compile.sh` adds its own.

**`ci.yml`** — the eight setup steps collapse from `rustup toolchain
install stable --profile minimal --component …` + `rustup default
stable` to a bare `rustup toolchain install`, which reads the file and
installs the declared components. The `version=` cache-key output is
unchanged. Policy comment rewritten.

**`ci.yml`, `changes` job** — separate bug, fixed while here. It was
gated on `github.event_name == 'pull_request' || 'push'`, with a comment
claiming schedule and `workflow_dispatch` runs would skip only that job
and still get full CI. **They did not.** Every job below is `needs:
changes` with a plain `if:` (no `always()`/`!cancelled()`), and GitHub
skips a job whose `needs` dependency was skipped regardless of its own
`if:`. So the gate skipped the *entire workflow*: the nightly cron and
every manual dispatch reported success without running a single check —
a green with no verdict behind it, which is the same class of problem
this PR is about. The classify step already leaves `docs_only=false` for
any event it does not diff, so removing the gate makes the documented
behaviour real.

## How I verified it

All local, on this branch, with my rustup default left at 1.97.0 so the
file is doing the work:

**Negative control — the pin is what changes the resolution:**

```
without rust-toolchain.toml:  rustc 1.97.0   clippy 0.1.97   rustfmt 1.9.0 (2d8144b788)
with    rust-toolchain.toml:  rustc 1.98.0   clippy 0.1.98   rustfmt 1.9.0 (88d9e12ae1)
```

clippy `0.1.97` → `0.1.98` is precisely the gap that made these failures
invisible to contributors.

**Both gates, run with bare commands** (no `+1.98.0`), which is the
point — a 1.97.0 contributor now gets CI's behaviour by default:

- `cargo fmt --all -- --check` → clean
- the verbatim 30-package clippy gate from `ci.yml` ~326, including the
trailing `-- -D warnings` → **exit 0**

**Component auto-install:** `llvm-tools` was absent from my 1.98.0
install and rustup fetched it on first use inside the repo, so dropping
the per-job `--component` flags is safe. Confirmed end-to-end with
`cargo llvm-cov --locked -p onnx-runtime-cpuinfo` → exit 0, which is the
one job whose component requirement changed.

**rustup override semantics, empirically checked** (I had this wrong
earlier and said so on #1600): `rustup component add` and `rustup target
add` **respect the directory pin** — I expected them to hit the default
toolchain and silently break the coverage and cross-compile jobs. They
do not; both landed on 1.98.0. Explicit `+toolchain` still beats the
file (`rustc +stable -Vv` → 1.97.0 inside the repo), so miri's `cargo
+nightly` is unaffected.

**YAML:** all workflow files parse; `changes` confirmed to have no `if:`
key.

## What I could not verify

- **Everything about the CI runners themselves.** I am on macOS aarch64.
That a Linux or Windows runner materializes 1.98.0 from this file, and
that the cache key still resolves correctly there, is only checkable in
CI. This PR's own run is the oracle.
- **The `workflow_dispatch`/`schedule` fix.** I verified the mechanism
by reading the gating (all 8 downstream jobs are `needs: changes` with
plain `if:`), but I have not observed a dispatch run execute the matrix.
Worth triggering one after merge to confirm.
- **Other workflows.** `audit.yml`, `publish.yml`,
`publish-ep-plugins.yml`, `wheels.yml` still say `rustup default
stable`. They inherit the pin anyway — any `cargo` run inside the repo
resolves through the file — so their behaviour is already correct and
their explicit install is merely redundant. I left them rather than
widen the blast radius onto release workflows. **One exception worth
flagging:** `benchmark.yml` uses `dtolnay/rust-toolchain@stable`, which
sets `RUSTUP_TOOLCHAIN` — that env var *overrides* the file, so
benchmark is genuinely not pinned. It is non-blocking, but it is a real
gap, not an oversight.

## Related, not fixed here

**No CI job runs clippy on macOS.** `rust-coverage` matrixes macOS but
only *installs* clippy (~line 458) and never runs it; all six
clippy-running jobs are Linux/Windows. That means #1609's macOS-only
fixes in `accelerate_gemm.rs` and `matmul.rs` (both `#[cfg(target_os =
"macos")]`) were structurally unverifiable by CI. I ran CI's exact
clippy package list on macOS under 1.98.0 → **exit 0**, so this is a
prevention gap rather than an outstanding defect. Filing separately.

## Conflict risk with #1579

None. This PR touches only `.github/workflows/ci.yml` and a new root
file. No overlap with `crates/onnx-runtime-ep-cpu`,
`crates/onnx-runtime-ir`, or
`crates/onnx-genai-engine/src/{decode,native_decode}/`.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 08760f2f-160f-41e5-828d-9d9b6045c00d
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