chore(mlx): bump pin to e9463bb and rebase CUDA overlays (#625)#642
Merged
Conversation
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).
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.
This was referenced Jul 2, 2026
15 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bumps the pinned MLX commit from
a6ec7123dac814417147e21d4aeed694924ddd4d(2026-06-10) toe9463bbfc1a7cd9e0e6b96aaa3068a316e234a63(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 atdocs/benchmark_results/mlx-pin-upgrade-2026-07-03.md).Pin bump
src/lib/mlx-cpp/CMakeLists.txtGIT_TAG->e9463bb...src/lib/mlxcel-core/build.rsMLX_EXPECTED_COMMIT->e9463bb...(the mismatch check and.mlx-build-commitpurge forced the clean MLX rebuild)MLX_CCCL_DIRis now defined on the newmlx_dirsOBJECT target (upstream moved the macro consumer intodirs.cppto isolate dynamic defines from the compile cache), with a fallback to themlxtarget for older trees. Verified in the generatedmlx_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:
quantized/qmm/qmm.hglobal_scaleparam (#3723).quantized/qmm/qmv.cudevice/cute_dequant.cuhinclude (#3576).primitives.cppquantized/quantized.cppensure_row_contiguousfix forw/scales/biasesinQuantizedMatmul::eval_gpu(3D batched MLA weights, upstream-rejected) onto the new base, which now passesstd::nulloptglobal_scale to qmv. GatherQMM path returns to pristine upstream.jit_module.cpp/proc/self/exedir resolution +MLXCEL_CCCL_DIRoverride + cold-JIT notice onto the new base; adopted upstream'scccl_dir()fallback, extra bundled include path, JIT'd qmm header registration (#3706/#3576), CUTLASS nvrtc flags, andget_jit_module(Device&)signature.MLX_PTX_CACHE_DIRhandling 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__launch_bounds__), and adopts upstream's large-gridindex_restfix (grid.z) andget_launch_args_generalin both same-type and mixed general paths.patches-cuda/ops.cppbf16_mixed_astype()+ its use in add/subtract/multiply/divide/maximum/minimum. Drops stale v0.31.1-era reversions (safe_castoverflow checks,arangezero-step check, use-after-move-proneauto& shapebindings) 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 withcannot open source file "cute/numeric/numeric_types.hpp"). Fixed in this PR, mirroring the existing CCCL solution:jit_module.cppoverlay resolves CUTLASS headers viaMLXCEL_CUTLASS_DIRenv override, the bundled<exe>/../includelayout, or the compiled-inMLX_CUTLASS_DIRbuild-tree fallback.mlx-cpp/CMakeLists.txtcompiles in the_deps/cutlass-src/includefallback for source builds.release.ymlbundlesinclude/cute/+include/cutlass/in both Linux CUDA archive jobs (with completeness checks).docs/installation.mddocuments the new runtime requirement and env var.build.rsnow watches../mlx-cpp/patches-cudawith 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 cudaon 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 viaastypebefore 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 testrun 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 ascudaStreamEndCapture ... previous error during capture(module load racing another thread's stream capture) and, withMLX_USE_CUDA_GRAPHS=0, ascuLaunchKernelEx ... invalid argument(kernel-configure race inget_kernel_and_dims). The suite is stable with--test-threads=1; documented indocs/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 updatingFAMILY_ORDERinsrc/main.rs; the three families are now listed beforeOther 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
cargo build --release --features cudafrom purged_deps(fetched e9463bb, all overlays applied; verified in the configured tree andmlx_dirsflags)cargo test --features cudalocallyCloses #625