CUDA programs assume an NVIDIA machine. CuMetal makes a useful subset of them run on Apple Silicon.
It is a compiler, a runtime, and a set of compatibility libraries. CUDA source or PTX goes in. Metal runs on the GPU. There is no NVIDIA hardware in the loop.
This is experimental software. The covered paths execute real kernels and check real answers. Unsupported paths are expected to fail explicitly. That is a better failure mode than silently computing nonsense.
CuMetal requires macOS 14 or newer on Apple Silicon. Install the source-first compiler and runtime from the official CuMetal tap:
brew install lulzx/tap/cumetal
cumetalc vectorAdd.cu -o vectorAdd
./vectorAddVerify the complete local toolchain with:
cumetal doctorHomebrew installs CMake and LLVM as dependencies. Apple's Metal compiler still
comes from Xcode; if xcrun --find metal fails, install Xcode and its Metal
Toolchain component.
The formula deliberately installs the source-first compiler/runtime without
the optional libcuda.dylib binary shim.
You need:
- macOS 14 or newer
- an Apple M-series GPU
- CMake and the Xcode command-line tools
- Apple's Metal toolchain (
xcrun metalandxcrun metallib)
Build it:
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j"$(sysctl -n hw.ncpu)"
bash scripts/ci_report.sh build --exclude-regex '^bench_'bash demos/apollo/run.shApollo progresses from vector addition through a path tracer and requires Apple-GPU provenance at every stage. The 3D Gaussian Splatting, SPH, diffusion, and single-sample workflows are in the demos guide.
Install it without changing shell startup files:
bash install/install.sh build /opt/cumetal
/opt/cumetal/bin/cumetal doctorTo also add CuMetal to your shell's PATH, opt in explicitly:
bash install/install.sh build /opt/cumetal --shell-configRemove the installation with /opt/cumetal/uninstall.sh. The uninstaller uses
the recorded CMake install manifest, so every installed header, tool, shim, and
library is covered.
Source-built programs need no launcher:
cumetalc samples/vectorAdd/vectorAdd.cu -o vectorAdd
./vectorAddcumetal run is a convenience for launching a process with this installation's
runtime library path scoped to that child:
cumetal run ./cuda-applicationIt does not make unsupported binaries portable. Prebuilt CUDA applications
still require a compatible PTX payload and an installation configured with
CUMETAL_ENABLE_BINARY_SHIM=ON; SASS-only applications remain unsupported.
CuMetal is source-first.
CUDA C++ ---> Clang / NVVM ---> typed CuMetal IR --+
|
PTX --------> legacy or typed lowering -------------+--> MSL
|
v
Apple Metal toolchain
|
v
.metallib
|
v
libcumetal -> Metal GPU
There are three entry paths:
- Source recompilation.
cumetalccompiles a.cufile into a runnable executable or a.metallib. This is the primary path. - PTX compatibility. CuMetal parses PTX, builds CFG/SSA, and lowers it toward the same Metal backend. This path currently covers more project-scale CUDA than the typed source path.
- Binary compatibility. An opt-in
libcuda.dylibalias accepts programs already linked against the CUDA Driver API and handles supported fatbinary registration. It is useful, narrower than CUDA, and not the architecture.
The SIMD width is 32. This is fixed. CUDA warp semantics are lowered onto Metal SIMD-group operations; CuMetal does not pretend the machine has a different warp size because that would make every hard problem harder.
Metal calls stay behind the Objective-C++ boundary in
runtime/metal_backend/. CUDA-facing headers are clean-room. No private Apple
API is used.
cumetalc can emit every useful stage:
cumetalc kernel.cu --emit=cumetal-ir -o kernel.cumetal
cumetalc kernel.cu --emit=msl -o kernel.metal
cumetalc kernel.cu --emit=metallib -o kernel.metallib
cumetalc kernel.cu --emit=exe -o kernelImportant switches:
| Switch | Meaning |
|---|---|
--backend=cumetal-ir|legacy |
Select the typed shared-IR backend or the compatibility backend. There is no silent fallback. |
--cuda-device |
Ask a CUDA-capable Clang to produce PTX before CuMetal lowering. |
--entry NAME |
Compile one kernel and its reachable device-call closure. |
--ptx-strict |
Reject unsupported PTX instead of tolerating it. |
--fp64=native|emulate|warn |
Choose the FP64 policy. Default: emulate. |
--save-temps |
Keep link intermediates. |
The default backend follows the input because measured coverage says it should:
| Input corpus | legacy |
cumetal-ir |
|---|---|---|
direct .cu |
0/19 | 10/19 |
.cu --cuda-device / PTX |
17/19 | 6/19 |
Direct .cu therefore defaults to cumetal-ir; PTX and --cuda-device
default to legacy. This is engineering, not ideology. When the measurements
change, the default should change.
The complete compiler boundary is in docs/compiler-architecture.md. Unsupported instructions, calls, pointer conversions, CFGs, and ABI forms are tracked in docs/known-gaps.md.
libcumetal.dylib implements the CUDA Runtime and Driver API over Metal. It
tracks allocations, resolves CUDA pointers to Metal buffers, preserves the
per-thread error model, and maps streams and events onto command queues and
shared-event ordering.
The same library also exports compatibility surfaces for:
- cuBLAS and cublasLt
- cuRAND
- cuFFT
- cuSPARSE
- cuSOLVER Dense
- cuDNN
- CUDA Graphs
- NVML
- NCCL single-rank operations
- a small CPU-backed Thrust surface over unified memory
- async allocation, texture, and surface object lifecycle APIs
These names do not imply full NVIDIA parity. Some operations use MetalPerformanceShaders, some use Accelerate, some exploit unified memory, and some are deliberately partial. Read docs/status.md before building on one.
Large cudaMalloc allocations use MTLHeap suballocation at 4 MiB and above.
Override this for diagnosis:
CUMETAL_MTLHEAP_ALLOC=1 ./program # always
CUMETAL_MTLHEAP_ALLOC=0 ./program # neverBy default, Release builds keep source registration enabled and the
libcuda.dylib alias disabled. Enable the alias explicitly:
cmake -B build-shim \
-DCMAKE_BUILD_TYPE=Release \
-DCUMETAL_ENABLE_BINARY_SHIM=ON
cmake --build build-shimCUMETAL_ENABLE_CUDA_REGISTRATION=ON is the host registration ABI emitted by
Clang for recompiled .cu programs. It is part of the source path.
CUMETAL_ENABLE_BINARY_SHIM=ON only adds the drop-in libcuda.dylib name.
Do not confuse them.
The shim recognizes CMTL envelopes, raw PTX, and basic FatBinary/FatBinary2/FatBinary3 PTX wrappers. It does not execute SASS and does not understand every NVCC fatbinary variant.
Registered PTX is compiled on first use and cached under:
$CUMETAL_CACHE_DIR/registration-jit/
The key includes the input, kernel, lowering policy, schema versions,
toolchain-dependent inputs, and the libcumetal Mach-O UUID. Set
CUMETAL_DEBUG_REGISTRATION=1 to see format detection, compilation, cache hits,
ABI inference, and registration.
The legal boundary is documented in docs/legal-notice.md.
Vector add, SAXPY, reduction, matrix operations, atomics, shared memory, warp operations, streams, events, and selected CUDA library calls have numerical GPU tests. The suite includes negative cases because accepting a program is not the same as implementing it correctly.
The recorded native-Metal comparison, real-program gates, their scope, and third-party projects using CuMetal are in verified results. Exact commands, models, tolerances, and provenance requirements remain in the Apple-GPU execution record.
Durable platform/legal boundaries:
- No SASS execution or decompilation; binary compatibility requires embedded PTX.
- No multi-GPU or peer-to-peer execution on the single-GPU Apple Silicon target.
- No OpenGL, Vulkan, or DirectX interop.
- Metal has no single-dispatch cross-threadgroup barrier. Multi-block cooperative launch/grid sync is rejected; single-block cooperative launch is supported.
- Current public Metal compilation rejects native AIR
double. FP64 register emulation provides about a 44-bit mantissa, and unsupported binary64 memory/conversion boundaries fail compilation.
Engineering gaps, not fundamental impossibilities:
- Dynamic parallelism needs a CPU trampoline and compatible scheduling/error semantics.
- Texture and surface object lifecycle exists; general device-side sampling needs a Metal texture binding ABI.
- CUDA graphs cover tested dependency-ordered kernel/linear-memcpy/memset/host-node capture and replay, cloning, and compatible executable updates, but memory nodes and other advanced behavior are incomplete.
- Device
printfuses a bounded buffer and limits format strings to 256 bytes. - CUDA, library-shim, PhysX, llama.cpp, and PTX coverage is incomplete.
This summary is intentionally short. The authoritative classification of platform boundaries and implementable engineering gaps is in docs/known-gaps.md.
For routine source-first validation:
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
bash scripts/ci_report.sh build --exclude-regex '^bench_'Report passes, skips, and failures separately. A registered test is not a passing test. A skip is not evidence of compatibility. A correct answer without GPU provenance may be a CPU fallback. The test policy exists because all three mistakes have happened before.
Binary-shim validation, focused test selections, CUDA sample setup, CI state, and the runner contract are documented in the testing guide.
| Tool | Job |
|---|---|
cumetalc |
Compile .cu, PTX, or NVVM IR to inspectable stages, .metallib, or an executable |
cumetal |
Check an installation with doctor or launch a child process with run |
air_inspect |
Inspect kernels, bitcode offsets, and metadata in a .metallib |
air_validate |
Validate .metallib structure and optionally check it with xcrun |
cumetal-air-emitter |
AIR research and regression container generation |
cumetal-ptx2llvm |
Legacy PTX-to-LLVM inspection |
ptx_diff |
Compare PTX-related outputs |
cumetal_bench |
Compare covered CuMetal kernels with native Metal |
- Design specification — canonical architecture and semantics
- Current status — what is implemented
- Known gaps — what is partial, wrong, or absent
- Build guide — toolchains and validation
- Demos — runnable showcases and their evidence gates
- Verified results — benchmarks, real programs, and downstream usage
- Testing guide — gates and conformance workflows
- Compiler architecture — lowering paths and migration boundaries
- Apple-GPU execution record — evidence and provenance
- AIR ABI notes — metallib research and limitations
- Correctness audit — failures found by testing the tests
If the README and the spec disagree, the spec wins.