[CUDA] Make the CUDA include roots configurable - #444
Open
GoodOlClint wants to merge 1 commit into
Open
GoodOlClint wants to merge 1 commit into
GoodOlClint wants to merge 1 commit into
Conversation
Two include roots are hardcoded or missing in the SwiftPM CUDA build, and
neither can be pointed elsewhere.
CCCL is hardcoded to the copy inside the CUDA toolkit, for both the `-I` and the
`MLX_CCCL_DIR` define that NVRTC uses at runtime. MLX pins its own CCCL version,
and when the toolkit ships a different one the mismatch surfaces as overload
ambiguity in device code rather than anything naming a version:
mlx/backend/cuda/device/binary_ops.cuh(50): error: more than one instance of
overloaded function "cuda::std::fmod" matches the argument list
Observed with CUDA 13.0 (CCCL 3.0.1) against MLX's pinned CCCL 3.1.3.
cudnn-frontend and CUTLASS are not provided at all. The CMake build fetches both
via FetchContent, but SwiftPM has no equivalent, so the build fails on headers
that are simply absent:
mlx/backend/cuda/cudnn_utils.h:9:10: fatal error: 'cudnn_frontend.h' file not found
mlx/backend/cuda/quantized/qmm/qmv.cu:9:10: fatal error: 'cute/numeric/numeric_types.hpp' file not found
Adds `MLX_CCCL_DIR` to override the CCCL root (defaulting to the toolkit copy, so
existing behaviour is unchanged) and `MLX_CUDA_INCLUDE_PATHS`, a colon-separated
list of additional roots. Both are applied to the C++ sources via Package.swift
and to `.cu` compilation via the CudaBuild plugin, so device and host code
compile against the same headers.
This keeps the roots external rather than vendoring cudnn-frontend and CUTLASS as
submodules; that remains an option if their provisioning should be automatic
rather than configured.
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, thanks!
Member
|
Conflict needs resolving, also #443. |
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"): let the CUDA include roots be pointed somewhere other than the CUDA toolkit. Three headers the CUDA sources need are either hardcoded to the wrong copy or not provided at all, and none of them can currently be redirected.
CCCL is hardcoded to the toolkit copy
Package.swiftpins both the-Iand theMLX_CCCL_DIRdefine (which NVRTC uses to JIT at runtime) to/usr/local/cuda/include/cccl. MLX pins its own CCCL version via FetchContent, so when the toolkit ships a different one, device code fails on ambiguity rather than on anything naming a version:Observed with CUDA 13.0 (CCCL 3.0.1) against MLX's pinned CCCL 3.1.3. Because the define feeds the runtime JIT path too, a skewed CCCL is not only a compile-time problem.
cudnn-frontend and CUTLASS are not provided at all
The CMake build fetches both via FetchContent; SwiftPM has no equivalent, so the build fails on headers that are simply absent:
cudnn_utils.cppis compiled andcudnnis linked, so this is a gap rather than a deliberate exclusion.The change
MLX_CCCL_DIRoverrides the CCCL root, defaulting to the toolkit copy — so an unconfigured build is byte-identical to today.MLX_CUDA_INCLUDE_PATHSis a colon-separated list of additional roots (cudnn-frontend, CUTLASS).Both are applied on both sides: to the C++ sources via
Package.swift, and to.cucompilation via the CudaBuild plugin, so host and device code compile against the same headers. The plugin adds only roots that were explicitly configured — nvcc already finds its own toolkit CCCL without an explicit-I, so an unconfigured build sees no new flags there either.This keeps the roots external and configured rather than vendoring cudnn-frontend and CUTLASS as submodules. Vendoring would make provisioning automatic and is a reasonable alternative if you'd rather these not be the consumer's problem — but it is a much larger change, and the environment knobs are useful regardless (the CCCL skew above needs an override even once the other two are vendored). Happy to go that way instead.
Verification
On macOS (Xcode 26.6, Swift 6.3.3) — inert, as the settings apply only on the CUDA branch of the manifest:
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_CCCL_DIRpointed at MLX's pinned CCCL 3.1.3 andMLX_CUDA_INCLUDE_PATHSsupplying the cudnn-frontend and CUTLASS roots — replacing an ad-hoc pile of-Xcxxflags entirely..cufiles recompiled, and the per-output configuration stamps recorded an include list containing CCCL 3.1.3 plus both extra roots — i.e. the configured roots are what nvcc actually used.Note CI does not cover the SwiftPM CUDA lane —
linux_build_cmake_cudabuilds via CMake, where FetchContent already supplies these headers — so none of this is exercised 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 changesPackage.swift; happy to add a README section on configuring a CUDA build if you'd like them surfaced there.