Skip to content

chore(mlx): bump pin to e9463bb and rebase CUDA overlays (#625)#642

Merged
inureyes merged 2 commits into
mainfrom
update/issue-625-mlx-pin-bump
Jul 2, 2026
Merged

chore(mlx): bump pin to e9463bb and rebase CUDA overlays (#625)#642
inureyes merged 2 commits into
mainfrom
update/issue-625-mlx-pin-bump

Conversation

@inureyes

@inureyes inureyes commented Jul 2, 2026

Copy link
Copy Markdown
Member

Summary

Bumps the pinned MLX commit from a6ec7123dac814417147e21d4aeed694924ddd4d (2026-06-10) to e9463bbfc1a7cd9e0e6b96aaa3068a316e234a63 (upstream main, 2026-07-01) in both enforcement points, and reconciles all 20 source overlays three-way against the 63 upstream commits between the pins. Part of epic #623; the full GB10 re-baseline sweep and outlier triage verdicts are done separately on top of this (placeholder committed at docs/benchmark_results/mlx-pin-upgrade-2026-07-03.md).

Pin bump

  • src/lib/mlx-cpp/CMakeLists.txt GIT_TAG -> e9463bb...
  • src/lib/mlxcel-core/build.rs MLX_EXPECTED_COMMIT -> e9463bb... (the mismatch check and .mlx-build-commit purge forced the clean MLX rebuild)
  • MLX_CCCL_DIR is now defined on the new mlx_dirs OBJECT target (upstream moved the macro consumer into dirs.cpp to isolate dynamic defines from the compile cache), with a fallback to the mlx target for older trees. Verified in the generated mlx_dirs.dir/flags.make.

Overlay reconciliation

14 of 20 overlay base files are byte-identical between the two pins, so those overlays apply unchanged. The remaining six were reconciled as follows:

Overlay Decision Reason
quantized/qmm/qmm.h dropped Comment-only delta vs old upstream; pristine e9463bb adds the global_scale param (#3723).
quantized/qmm/qmv.cu dropped Comment-only delta (its documented broadcast_w fix had already landed upstream); pristine e9463bb carries qmv global scale (#3723) and the relocated device/cute_dequant.cuh include (#3576).
primitives.cpp dropped No-op overlay (annotation comment only).
quantized/quantized.cpp rebased Re-applied the ensure_row_contiguous fix for w/scales/biases in QuantizedMatmul::eval_gpu (3D batched MLA weights, upstream-rejected) onto the new base, which now passes std::nullopt global_scale to qmv. GatherQMM path returns to pristine upstream.
jit_module.cpp rebased Re-applied /proc/self/exe dir resolution + MLXCEL_CCCL_DIR override + cold-JIT notice onto the new base; adopted upstream's cccl_dir() fallback, extra bundled include path, JIT'd qmm header registration (#3706/#3576), CUTLASS nvrtc flags, and get_jit_module(Device&) signature. MLX_PTX_CACHE_DIR handling is unchanged upstream, so the persistent PTX cache redirect still takes effect (upstream #3587 turned out to be a CI-cache change only).
binary/binary.cuh rebased (adapted) Re-applied the mixed-precision bf16/fp32 kernels + trait + dispatch onto the new base. Also drops stale v0.31.1-era reversions the old overlay silently carried (missing __launch_bounds__), and adopts upstream's large-grid index_rest fix (grid.z) and get_launch_args_general in both same-type and mixed general paths.
patches-cuda/ops.cpp rebased (adapted) Re-applied bf16_mixed_astype() + its use in add/subtract/multiply/divide/maximum/minimum. Drops stale v0.31.1-era reversions (safe_cast overflow checks, arange zero-step check, use-after-move-prone auto& shape bindings) and picks up upstream's new array-API ops unmodified.

Kept unchanged (base identical between pins): device/binary_ops.cuh, gemms/grouped_gemm.h, gemms/grouped_gemm_unaligned.cu, matmul.cpp (the CUTLASS GatherMM rework intentionally still bypasses upstream's JIT gather_gemm; the re-baseline sweep should confirm this remains the right call on MoE models), reduce/*, metal/compiled.cpp, metal/kernels/utils.h, patches-cuda/{dtype,fast}.cpp.

Quantized kernels moved to runtime JIT (deployment impact)

Upstream #3706/#3576 JIT-compile the CUTLASS qmm/gather_gemm kernels with NVRTC at runtime instead of nvcc at build time. Cold build time dropped from the historical 30+ minutes to ~7.5 minutes on GB10, but the JIT'd kernels include <cute/...>/<cutlass/...>, which a statically linked mlxcel binary could not resolve (first quantized run failed with cannot open source file "cute/numeric/numeric_types.hpp"). Fixed in this PR, mirroring the existing CCCL solution:

  • jit_module.cpp overlay resolves CUTLASS headers via MLXCEL_CUTLASS_DIR env override, the bundled <exe>/../include layout, or the compiled-in MLX_CUTLASS_DIR build-tree fallback.
  • mlx-cpp/CMakeLists.txt compiles in the _deps/cutlass-src/include fallback for source builds.
  • release.yml bundles include/cute/ + include/cutlass/ in both Linux CUDA archive jobs (with completeness checks).
  • docs/installation.md documents the new runtime requirement and env var.
  • build.rs now watches ../mlx-cpp/patches-cuda with rerun-if-changed (previously unwatched, so edits to the CUDA-only overlays did not retrigger the MLX build).

First quantized-model runs pay a one-time per-kernel-variant NVRTC compile, amortized by the persistent commit-scoped PTX cache.

Test-suite reconciliation

cargo test --release --features cuda on GB10 surfaced two failures, both understood:

  • sanitize_tests::load_and_sanitize_weights_dequantizes_nvfp4_gemma4_checkpoint: pre-existing CUDA bug, not a pin regression. The CUDA loader widens F16 safetensors to F32 at load (from_bytes_f16, CUDA branch dating to 2026-03), while the NVFP4 dequant parsed the block scales as raw F16 bytes, reading zeros on CUDA. Fixed by normalizing the scales to F32 via astype before the byte parse (dtype-agnostic on both backends). This also corrects real gemma4-nvfp4 block-scale handling on CUDA, relevant to the epic's gemma-4-31b nvfp4 outlier.
  • gemma4_mtp_target::tests::divergent_round_hidden_matches_b1_replay: pin-related property change. Since MLX #3704 (rope without copy) RoPE preserves input layouts instead of canonicalizing them with a copy, so downstream GEMM/SDPA kernels can see different strides for a batched row than for the B=1 replay and pick different reduction orders. Observed max_abs 1.6e-4 on fp32 hidden of magnitude ~8, i.e. reduction-order noise, not a logic bug. The bitwise assertion is now a 2e-3 fp32 tolerance on CUDA (bitwise retained on other backends); the argmax-equality assertion, which is the functional MTP-verify guarantee, is unchanged.

The default parallel cargo test run aborts under the new pin even on warm PTX caches: the JIT'd kernels are compiled and module-loaded on first use, and those first-use paths are not safe against concurrent test threads. Observed as cudaStreamEndCapture ... previous error during capture (module load racing another thread's stream capture) and, with MLX_USE_CUDA_GRAPHS=0, as cuLaunchKernelEx ... invalid argument (kernel-configure race in get_kernel_and_dims). The suite is stable with --test-threads=1; documented in docs/installation.md. Inference binaries are unaffected (parity runs and benches are clean). Worth an upstream issue; also worth watching if mlxcel-server ever evals from multiple threads.

One more pre-existing failure fixed en route: family_order_is_exhaustive (bin target) was failing on main because the recent VLM ports added the GLM VLM, Kimi VLM, and PaddleOCR VLM families without updating FAMILY_ORDER in src/main.rs; the three families are now listed before Other VLM.

Tegra managed-memory finding (#3701)

GB10 reports concurrentManagedAccess=1 (probed with a CUDA attribute query), so upstream's new gate does not trip: managed memory stays enabled on GB10 under both pins, exactly as before. #3701 is a no-op on this platform (it targets Jetson Orin Nano-class boards and WSL, which report 0). The epic hypothesis that the old pin's managed-memory behavior was depressing GB10 numbers is refuted at the gate level.

Test plan

  • Cold cargo build --release --features cuda from purged _deps (fetched e9463bb, all overlays applied; verified in the configured tree and mlx_dirs flags)
  • cargo test --features cuda locally
  • Parity spot-checks (coherent short generations): llama-3.1-8b-4bit, qwen3-8b-4bit, qwen3-30b-a3b-4bit (MoE), qwen2.5-vl-3b-4bit (VLM)
  • Quick decode sanity bench on a small subset (no gross regression)
  • Full GB10 sweep + long-prompt ladder + outlier verdicts (orchestrator, epic epic: CUDA (MLX backend) prefill and decode acceleration program #623)
  • Metal re-bench (follow-up; Metal overlays compile-verified only via CI matrix)

Closes #625

Bump the pinned MLX commit from a6ec7123dac814417147e21d4aeed694924ddd4d (2026-06-10) to e9463bbfc1a7cd9e0e6b96aaa3068a316e234a63 (upstream main, 2026-07-01) in both enforcement points (mlx-cpp/CMakeLists.txt GIT_TAG and mlxcel-core/build.rs MLX_EXPECTED_COMMIT), and reconcile every source overlay three-way against the 63 upstream commits between the pins (qmv global scale #3723, JIT qmm/gather_gemm #3706 and qmm_naive #3576, rope without copy #3704, Tegra managed-memory gate #3701, fused SDPA asymmetric head dims #3637, NAX qmm fixes #3631/#3632, large-uncontiguous-grid fix 4885acd).

git diff --stat (src/lib/mlx-cpp/ overlay rebase plus supporting changes):

 .github/workflows/release.yml                      |  40 +-
 .../mlx-pin-upgrade-2026-07-03.md                  | 139 ++++++
 docs/installation.md                               |  30 +-
 src/bin/mlx_server.rs                              |   5 +
 src/lib/mlx-cpp/CMakeLists.txt                     |  25 +-
 src/lib/mlx-cpp/patches-cuda/ops.cpp               | 362 +++++++++++----
 .../patches/mlx/backend/cuda/binary/binary.cuh     | 136 +++---
 .../patches/mlx/backend/cuda/jit_module.cpp        |  66 ++-
 .../patches/mlx/backend/cuda/primitives.cpp        |  41 --
 .../patches/mlx/backend/cuda/quantized/qmm/qmm.h   | 149 ------
 .../patches/mlx/backend/cuda/quantized/qmm/qmv.cu  | 504 ---------------------
 .../mlx/backend/cuda/quantized/quantized.cpp       |  25 +-
 src/lib/mlxcel-core/build.rs                       |   3 +-
 src/main.rs                                        |   3 +
 src/models/gemma4_mtp_target_tests.rs              |  44 +-
 src/models/sanitize.rs                             |  28 +-
 16 files changed, 707 insertions(+), 893 deletions(-)

Per-file decisions:

- patches/mlx/backend/cuda/quantized/qmm/qmm.h: dropped (superseded), the overlay delta vs the old pin was a documentation comment only and pristine e9463bb adds the global_scale parameter from upstream #3723 which we want unmodified.
- patches/mlx/backend/cuda/quantized/qmm/qmv.cu: dropped (superseded), comment-only delta vs the old pin (the broadcast_w fix it documented had already landed upstream), and pristine e9463bb carries qmv global-scale support (#3723) plus the relocated device/cute_dequant.cuh include (#3576).
- patches/mlx/backend/cuda/primitives.cpp: dropped (no-op), the overlay was byte-identical to upstream apart from an annotation comment.
- patches/mlx/backend/cuda/quantized/quantized.cpp: rebased, re-applied the ensure_row_contiguous fix for w/scales/biases in QuantizedMatmul::eval_gpu (non-contiguous 3D batched MLA weights, upstream-rejected) onto the new base that now passes std::nullopt global_scale to qmv; the GatherQMM path returns to pristine upstream.
- patches/mlx/backend/cuda/jit_module.cpp: rebased, re-applied the /proc/self/exe executable-dir resolution, the MLXCEL_CCCL_DIR override, and the cold-JIT stderr notice onto the new base, adopting upstream's cccl_dir() fallback, the extra bundled include path, the JIT'd qmm/cute_dequant header registration (#3706/#3576), the CUTLASS nvrtc flags, and the get_jit_module(Device&) signature; MLX_PTX_CACHE_DIR handling is unchanged upstream so the persistent PTX cache redirect still takes effect (upstream #3587 turned out to be CI-cache-only, no code conflict).
- patches/mlx/backend/cuda/binary/binary.cuh: rebased (adapted), re-applied the mixed-precision bf16/fp32 kernels, trait, and dispatch onto the new base; this also drops stale v0.31.1-era reversions the old overlay was silently carrying (missing __launch_bounds__/BINARY_MAX_BLOCK_DIM) and adopts upstream's large-grid index_rest fix (grid.z) and get_launch_args_general in both the same-type and the mixed general paths.
- patches-cuda/ops.cpp: rebased (adapted), re-applied bf16_mixed_astype() and its use in add/subtract/multiply/divide/maximum/minimum onto the new base; this drops stale v0.31.1-era reversions the old overlay was silently carrying (safe_cast overflow checks, the arange zero-step check, use-after-move-prone auto& shape bindings) and picks up upstream's new array-API ops unmodified.
- src/lib/mlx-cpp/CMakeLists.txt: pin bump, retargeting the MLX_CCCL_DIR compile definition to the new mlx_dirs OBJECT target (upstream moved the macro consumer into dirs.cpp) with a fallback to the mlx target, and a new MLX_CUTLASS_DIR build-tree fallback because the JIT'd quantized kernels now need CUTLASS/CuTe headers at runtime.
- Runtime JIT deployment: upstream #3706/#3576 moved the CUTLASS qmm/gather_gemm instantiations from build-time nvcc to runtime NVRTC (cold build dropped from 30+ min to ~7.5 min on GB10), so quantized models now need cute/ and cutlass/ headers on the deployment host at first run; the jit_module overlay resolves them via MLXCEL_CUTLASS_DIR, the bundled include/ layout, or the compiled-in build-tree path, release archives bundle include/cute + include/cutlass next to include/cccl, and docs/installation.md documents the requirement.
- src/lib/mlxcel-core/build.rs: also watch ../mlx-cpp/patches-cuda with rerun-if-changed (it was unwatched, so edits to the CUDA-only dtype/fast/ops overlays did not retrigger the MLX build).
- Unchanged overlays (base file identical between the two pins, kept as-is): device/binary_ops.cuh, gemms/grouped_gemm.h, gemms/grouped_gemm_unaligned.cu, matmul.cpp (CUTLASS GatherMM rework intentionally still bypasses upstream's JIT gather_gemm; re-baseline sweep will confirm), reduce/{all,col,init,row}_reduce.cu, reduce/reduce_ops.cuh, metal/compiled.cpp, metal/kernels/utils.h, patches-cuda/{dtype,fast}.cpp.

Test-suite reconciliation (cargo test --release --features cuda on GB10):

- src/models/sanitize.rs: fix NVFP4 block-scale parsing to be load-dtype-agnostic (astype to F32 before the raw-byte parse). The CUDA loader widens F16 safetensors to F32 at load (from_bytes_f16, CUDA branch since 2026-03), so the previous F16 byte parse read zeros on CUDA; this is a pre-existing CUDA bug exposed by running the suite here, not a pin regression, and it also corrects real gemma4-nvfp4 scale handling on CUDA. f16_to_f32 is now cfg(test) (kept for its format unit tests).
- src/models/gemma4_mtp_target_tests.rs: the batched-vs-B=1 bitwise-equality assertion now uses a 2e-3 fp32 tolerance on CUDA (bitwise retained elsewhere). Since MLX #3704 (rope without copy) RoPE preserves input layouts instead of canonicalizing with a copy, so downstream GEMM/SDPA kernels can see different strides for the batched row than for the B=1 replay and pick different reduction orders (observed max_abs 1.6e-4 on fp32 hidden of magnitude ~8). The argmax-equality assertion is unchanged, preserving the functional MTP verify guarantee.
- docs/installation.md: document running the CUDA test suite with --test-threads=1. The JIT'd kernels are now compiled and module-loaded on first use, and those first-use paths are not safe against concurrent test threads: the default parallel run aborts with cudaStreamEndCapture capture errors (module load racing another thread's capture) or, with MLX_USE_CUDA_GRAPHS=0, with cuLaunchKernelEx invalid-argument (kernel-configure race). Inference binaries are unaffected.
- src/main.rs: add the GLM VLM, Kimi VLM, and PaddleOCR VLM families to FAMILY_ORDER (pre-existing gap on main from the recent VLM ports; family_order_is_exhaustive fails without it and blocks the suite).
- src/bin/mlx_server.rs: the legacy-server-mode resolver test fixture now writes a stub model.safetensors next to config.json; the offline completeness gate from PR #604 classifies a config-only directory as an interrupted download and re-fetches from the network, which 404s on the synthetic repo id (pre-existing on main since #604).

Tegra note: upstream #3701 gates managed memory on concurrentManagedAccess only; GB10 reports concurrentManagedAccess=1, so managed memory stays enabled on GB10 under both pins and #3701 is a no-op on this platform (details in docs/benchmark_results/mlx-pin-upgrade-2026-07-03.md).
@inureyes inureyes added type:chore Maintenance tasks (build, CI, etc.) priority:medium Medium priority area:core mlxcel-core: MLX FFI, primitives, KV cache, layers status:review Under review labels Jul 2, 2026
Adds the focused GB10 re-baseline CSV (benchmarks/cuda_gb10_2026-07-03.csv, 19 models covering all Phase-3 outlier families plus dense/MoE/SSM/VLM representatives and a pure-mamba control) and fills the outlier-verdict table in docs/benchmark_results/mlx-pin-upgrade-2026-07-03.md.

Verdicts: MoE prefill collapse (#629) is unchanged (our matmul.cpp CUTLASS GatherMM overlay bypasses upstream's now-JIT'd gather_gemm, so #3706 does not reach the path); the hybrid-SSM decode gap (#631) is unchanged (pure-mamba control healthy, isolating the gap to the hybrid path); the gemma-4-31b nvfp4 outlier (#630) is root-caused (the checkpoint is NVIDIA ModelOpt-packed and now correctly rejected; the genuine MLX-native nvfp4 dequant bug was fixed here in sanitize.rs; mxfp4 sibling healthy) and re-scoped to end-to-end validation plus a guard test. No Phase-3 issue is fully fixed by the pin bump, so Phase 3 stands at three issues. Dense decode is uniformly +1 to +3% with several prefill gains and no cross-family regression.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core mlxcel-core: MLX FFI, primitives, KV cache, layers priority:medium Medium priority status:done Completed type:chore Maintenance tasks (build, CI, etc.)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore(mlx): upgrade pinned MLX commit, rebase CUDA overlays, GB10 re-baseline and outlier triage

1 participant