Skip to content

fix(metal): build the GPT-OSS MXFP4 prefill tile into the metallib - #24

Merged
Gajesh2007 merged 2 commits into
mainfrom
fix/mxfp4-prefill-tile-aot
Sep 28, 2026
Merged

Gajesh2007 merged 2 commits into
mainfrom
fix/mxfp4-prefill-tile-aot

Conversation

@davidtai

@davidtai davidtai commented Sep 25, 2026 •

Copy link
Copy Markdown

Proposed changes

This PR adds two gather_qmm_rhs kernels for the GPT-OSS MXFP4 prefill tile to mlx/backend/metal/kernels/fp_quantized.metal:

  • mxfp4_gather_qmm_rhs_nt_float_gs_32_b_4_bm_32_bn_32_bk_32_wm_2_wn_2
  • mxfp4_gather_qmm_rhs_nt_bfloat16_t_gs_32_b_4_bm_32_bn_32_bk_32_wm_2_wn_2

MLX_GPTOSS_MXFP4_PREFILL_TILE=m32n32k32 selects the tile bm 32, bn 32, bk 32, wm 2, wn 2 (gptoss_mxfp4_policy.h). quantized.cpp selects this tile only for mxfp4, group size 32, 4 bits, transposed weights, and float32 or bfloat16 inputs. These two kernels cover all of these cases.

Before this PR, only builds that compile jit_kernels.cpp could make this kernel. These builds make the kernel from source at run time and do not read mlx.metallib. A CMake build with the default MLX_METAL_JIT=OFF (CMakeLists.txt:44) compiles nojit_kernels.cpp instead. It loads kernels only from mlx.metallib, so it failed with "Unable to load function". The C++ test gptoss mxfp4 prefill tiles preserve expert boundaries and partial rows (tests/gptoss_mxfp4_tests.cpp) showed the failure in #22 (run https://github.com/Layr-Labs/mlx/actions/runs/36172106158).

The fix goes into the metallib because every CMake build with the default MLX_METAL_JIT=OFF fails without it: the Python wheel, the C++ test binary and any other CMake consumer. d-inference does not fail without this fix. On Apple platforms its SwiftPM build compiles jit_kernels.cpp: libs/mlx-swift/Package.swift in d-inference excludes nojit_kernels.cpp on every platform (line 284) and excludes jit_kernels.cpp only on platforms without Metal (line 25). Its JIT loader builds this kernel from source at run time.

This PR does not change the test. It does not change host code, because quantized.cpp already asks for these kernel names. It adds one sentence about these kernels to the GPT-OSS section of fork.yaml.

Runner probe result

Run: https://github.com/Layr-Labs/mlx/actions/runs/36174742752 (success, 22.0 minutes). This is the Fork Tests job of #22 with the same kernel diff, before this branch moved onto main.

  • Runner label: macos-26 (image macos-26-arm64, version 20260907.0351.1). macOS 26.6.2 on an Apple M1 (Virtual) with 3 cores.
  • Xcode 26.6 (17F113). Swift 6.3.3.
  • Build minutes: 13.5 with an empty compile cache (wheel 6.8, C++ 6.7).
  • Metal C++ tests: 290 of 290 test cases passed. 3865 of 3865 assertions passed.
  • Ring distributed test: 8 processes ran 13 tests each. All passed.
  • Python tests: 875 tests ran in 292 seconds and passed. 52 tests were skipped by skip conditions in the test sources: 45 need PyTorch, which the action does not install; 3 are too slow for CI; 1 uses too much memory for CI; 2 are for CUDA only; 1 needs different head and value dimensions.
  • Example extension: it built, and its test passed on the CPU and the GPU.

Test plan

CONTRIBUTING checklist

Dependencies

The base branch is main. This PR merges before #22. Its evidence is run https://github.com/Layr-Labs/mlx/actions/runs/36172106158 (red without the fix, on main plus the CI files of #22 only) and run https://github.com/Layr-Labs/mlx/actions/runs/36174742752 (green with the identical kernel diff). main has no Fork Tests workflow yet, so the checks of this PR do not build with Metal. The next Fork Tests run of #22, after #22 is updated from main, tests this change on main.

  • ☐ I understand it is strictly prohibited to use AI to write PR description
  • AI usage disclosure: this change and this description were written by Claude (Opus 5.5) under David Tai's direction and review.

🤖 Generated with Claude Code

davidtai and others added 2 commits September 25, 2026 15:44
MLX_GPTOSS_MXFP4_PREFILL_TILE=m32n32k32 selects the gather_qmm_rhs
tile bm 32, bn 32, bk 32, wm 2, wn 2. fp_quantized.metal did not
instantiate that tile. A build with MLX_METAL_JIT=OFF loads kernels
only from mlx.metallib, so it could not load the kernel.

Add the two instantiations that quantized.cpp can select: mxfp4, group
size 32, 4 bits, transposed weights, float and bfloat16.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Say in the GPT-OSS section of fork.yaml that fp_quantized.metal builds
the two m32n32k32 prefill-tile kernels ahead of time, so CMake builds
with the default MLX_METAL_JIT=OFF can load them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@davidtai
davidtai changed the base branch from ci/fork-tests to main September 25, 2026 20:45
@davidtai
davidtai force-pushed the fix/mxfp4-prefill-tile-aot branch from 54910ff to 5621947 Compare September 25, 2026 20:45
@Gajesh2007
Gajesh2007 merged commit f8dbb9a into main Sep 28, 2026
14 checks passed
@Gajesh2007
Gajesh2007 deleted the fix/mxfp4-prefill-tile-aot branch September 28, 2026 21:46
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