[CUDA] Allow selecting nvcc's host compiler, and invalidate on change - #443
Open
GoodOlClint wants to merge 1 commit into
Open
GoodOlClint wants to merge 1 commit into
GoodOlClint wants to merge 1 commit into
Conversation
The CudaBuild plugin hardwires nvcc's host compiler to the Swift toolchain's
own clang++, which cannot be overridden. When that clang is outside the range
the installed CUDA release accepts, nvcc refuses to compile anything:
crt/host_config.h:151:2: error: -- unsupported clang version! clang version
must be less than 21 and greater than 3.2
This is reachable today: Swift 6.3.3 ships clang 21.0.0, while CUDA 13.0
accepts only clang < 21. The plugin passes its resolved path to encuda as
--clangpp, which encuda appends as -ccbin= *after* anything in
NVCC_PREPEND_FLAGS, so that environment variable cannot win either.
Resolve the host compiler from, in priority order: the MLX_CUDA_HOST_COMPILER
environment variable, a new `hostCompiler` key in CudaBuild.json, then the
toolchain's clang++ as before. Existing configurations are unaffected.
It must still be a clang: nvcc preprocesses with the host compiler and SwiftPM
compiles the resulting .cpp, so pointing -ccbin at g++ yields a translation
unit full of GCC-only `_Float32` declarations that clang then rejects.
Also stamp the compilation configuration beside each generated .cpp and treat a
changed signature as out of date. encuda's --incremental check compared only
input/output modification times, which cannot observe a changed host compiler,
std, arch, or include path — so switching host compilers silently reused .cpp
files preprocessed by the previous one. That produced a flood of errors from
glibc headers rather than anything pointing at the real cause.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Jul 30, 2026
davidkoski
approved these changes
Aug 17, 2026
davidkoski
left a comment
Member
There was a problem hiding this comment.
Change looks good to me. Thank you!
Member
|
Conflict needs resolving (vs other PR). It looks like it may collide with #444 as well. |
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.
Proposed changes
A follow-up to #413 ("Allowing SPM to compile on Linux with CUDA"): make nvcc's host compiler selectable, and make
--incrementalnotice when it changes.The problem
Plugins/CudaBuild/plugin.swiftresolves the host compiler ascontext.tool(named: "clang++")— the Swift toolchain's own clang — and passes it to encuda as--clangpp, which encuda turns into-ccbin=on the nvcc command line. There is no way to point it elsewhere:CudaBuild.jsonhas no key for it (CUDA_ARCHis the plugin's only environment knob).NVCC_PREPEND_FLAGScannot win, because-ccbin=arrives later on the command line. OnlyNVCC_APPEND_FLAGSbeats it, which is not a reasonable thing to ask of a consumer.When the toolchain's clang falls outside the range the installed CUDA release accepts, nvcc refuses to compile anything:
This is reachable with current releases: Swift 6.3.3 ships clang 21.0.0, and CUDA 13.0 requires clang < 21. It is specific to that toolkit version — CUDA 13.1 raises the ceiling to < 22 and accepts clang 21 — but 13.0 is what a current CUDA 13 install may well be, and the only escape today is to not use Swift 6.3.
The change
Resolve the host compiler in priority order:
MLX_CUDA_HOST_COMPILERenvironment variablehostCompilerkey inCudaBuild.jsonclang++, exactly as beforeso an unconfigured build is unchanged.
It must still be a clang: nvcc preprocesses with the host compiler and SwiftPM then compiles the resulting
.cpp, so pointing-ccbinatg++produces a translation unit full of GCC-only_Float32declarations that clang rejects. That constraint is noted at the resolver rather than left to be rediscovered.Why the invalidation half is in the same PR
encuda's
--incrementalcheck compares only input/output modification times, which cannot observe a changed compiler. Shipping an overridable host compiler without fixing that would be actively unsafe: switching host compilers silently reuses.cppfiles preprocessed by the previous one, and the failure surfaces as a flood of errors from glibc headers with nothing pointing at the real cause. (That is exactly how this was found.)So the compilation configuration — nvcc path,
-ccbin,-std,CUDA_ARCH, include roots — is stamped in a<output>.encuda-stampbeside each generated.cpp, and a changed signature counts as out of date. It also fixes stale output after an arch or include-path change, which had the same blind spot.Verification
On macOS (Xcode 26.6, Swift 6.3.3) — inert, since the resolver runs only after the plugin's
isCudaEnabled()guard:pre-commit run --all-files— clean.scripts/verify-docs.sh— passes.xcodebuild build-for-testing -scheme mlx-swift-Package -destination 'platform=macOS'— builds;CmlxTestsandMLXTestspass.On a real CUDA device (DGX Spark / GB10, sm_121, CUDA 13.0.88, Swift 6.3.3, aarch64, Ubuntu 24.04):
swift buildcompleted cleanly in 466.89s withMLX_CUDA_HOST_COMPILER=/usr/bin/clang++-18and noNVCC_APPEND_FLAGSand no-Xcxxoverrides — where before this change the build could not be completed on that toolkit/toolchain pair at all..cufiles recompiled and 95.encuda-stampfiles were written, each recordingccbin=/usr/bin/clang++-18— i.e. the override is what nvcc actually used, not a stale artifact being reused.Note CI does not cover the SwiftPM CUDA lane —
linux_build_cmake_cudabuilds via CMake, on cuda-12.9 with swift-6.2.3 — so this permutation (CUDA 13.0 + Swift 6.3) is untested there.Related
Two other follow-ups to #413 are open alongside this one, and all three touch
Plugins/CudaBuild/plugin.swift. They are functionally independent, but whichever merges first will leave the others needing a trivial rebase — happy to reorder or stack them however you prefer:Checklist
Put an
xin the boxes that apply.pre-commit run --all-filesto format my code / installed pre-commit prior to committing changes