Fix JIT build with old macOS SDK - #3853
Conversation
|
Thanks for the fix, I will find a pre-26 system as soon as I can and test it. Can you share what the jit pre-amble on the machine you tried in the meantime ? |
Thanks @jagrit06! I'm also on macOS 26 locally, so the preamble just includes the NAX/MPP code as before for me. But I did apply this exact patch in ExecuTorch because our test-mlx-backend-operators job runs on Xcode 15.4 / MacOSX14.5 SDK:
Full log: https://ossci-raw-job-status.s3.amazonaws.com/log/pytorch/executorch/89566514106 |
zcbenz
left a comment
There was a problem hiding this comment.
The correct way to fix this is to only make_jit_source the nax headers when the SDK requirement is met.
The NAX GEMM and attention headers 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. Gate the NAX make_jit_source() calls behind the same MLX_METAL_VERSION/MACOS_SDK_VERSION/CMAKE_OSX_DEPLOYMENT_TARGET check used by the metallib path, and define MLX_METAL_NO_NAX when the requirement is unmet. On those SDKs NAX is already runtime-gated via is_nax_available(), so the get_*_nax_kernel entry points in jit_kernels.cpp are unreachable; guarded empty preamble getters keep that translation unit linking. Newer SDKs still build NAX.
efdc2ec to
f043322
Compare
Thanks @zcbenz, I have updated the approach as suggested! |
Summary
The NAX kernel headers unconditionally include
<MetalPerformancePrimitives/MetalPerformancePrimitives.h>, which only ships in the macOS 26 / Xcode 26 SDK. WithMLX_METAL_JIT=ONon an older SDK, the build fails because the JIT preamble generator preprocesses these headers and the include cannot be resolved.Details
make_jit_source→make_compiled_preamble.shrunsxcrun -sdk macosx metal -x metal -E -Hover each NAX header to enumerate/inline its dependencies. On an SDK withoutMetalPerformancePrimitives, that step fatals: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 NAXmake_jit_sourceentries unconditionally, so a JIT build on a pre-26 SDK always tries to preprocessnax.hand fails.make_compiled_preamble.shalready 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(...):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=ONbuild with Xcode 16.4 / macOS 15.5 SDK: fails before, succeeds after.