Skip to content

Guard NAX MetalPerformancePrimitives include behind __has_include#3853

Open
metascroy wants to merge 1 commit into
ml-explore:mainfrom
metascroy:fix/nax-has-include-guard
Open

Guard NAX MetalPerformancePrimitives include behind __has_include#3853
metascroy wants to merge 1 commit into
ml-explore:mainfrom
metascroy:fix/nax-has-include-guard

Conversation

@metascroy

Copy link
Copy Markdown

Summary

The NAX kernel headers unconditionally include <MetalPerformancePrimitives/MetalPerformancePrimitives.h>, which only ships in the macOS 26 / Xcode 26 SDK. With MLX_METAL_JIT=ON on an older SDK, the build fails because the JIT preamble generator preprocesses these headers and the include cannot be resolved.

Details

make_jit_sourcemake_compiled_preamble.sh runs xcrun -sdk macosx metal -x metal -E -H over each NAX header to enumerate/inline its dependencies. On an SDK without MetalPerformancePrimitives, that step fatals:

nax.h:12:10: fatal error: 'MetalPerformancePrimitives/MetalPerformancePrimitives.h' file not found
Error: Metal compiler header resolution failed for .../steel/gemm/gemm_nax.h

The metallib path already gates NAX on MLX_METAL_VERSION >= 400 && MACOS_SDK_VERSION >= 26.2 && CMAKE_OSX_DEPLOYMENT_TARGET >= 26.2 (kernels/CMakeLists.txt), but the JIT path adds the NAX make_jit_source entries unconditionally, so a JIT build on a pre-26 SDK always tries to preprocess nax.h and fails. make_compiled_preamble.sh already intends to tolerate the framework (grep -v "Xcode" strips it from the inlined preamble); this only breaks when the header is absent from the SDK entirely.

Fix

Wrap the framework include in both NAX headers with #if __has_include(...):

#if __has_include(<MetalPerformancePrimitives/MetalPerformancePrimitives.h>)
#include <MetalPerformancePrimitives/MetalPerformancePrimitives.h>
#endif

On pre-26 SDKs, preprocessing now succeeds and NAX kernels are simply never JIT-compiled (they are runtime-gated by is_nax_available(), which is already false on non-NAX hardware). On the macOS 26 SDK the include is kept and NAX is unchanged.

Testing

  • MLX_METAL_JIT=ON build with Xcode 16.4 / macOS 15.5 SDK: fails before, succeeds after.
  • NAX-capable hardware + macOS 26 SDK: NAX still compiled and used (include present).

The NAX GEMM and attention headers unconditionally include
<MetalPerformancePrimitives/MetalPerformancePrimitives.h>, which only ships in
the macOS 26 / Xcode 26 SDK. With MLX_METAL_JIT=ON on an older SDK, the JIT
preamble generator (make_compiled_preamble.sh) runs `metal -E` over these
headers and fatals on the missing include, breaking the build. The metallib
path already gates NAX on the SDK/Metal version; the JIT path did not.

Wrap the include in `#if __has_include(...)` so preprocessing succeeds on
pre-26 SDKs (NAX kernels are runtime-gated via is_nax_available() and never
compiled there), while newer SDKs still get NAX.
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