7.7 KiB
Mojo 1.0 Metal integration spike (#84)
Decision
STOP the product integration at the license/runtime gate. DS4Server can pin
and invoke the Mojo 1.0 compiler through uv, but Mojo 1.0's open standard
library does not provide the host-side DeviceContext needed to load and run a
Metal kernel. That API is delivered by MAX and links to separately shipped
AsyncRT and KGEN runtime libraries whose redistribution status is not explicit.
This is a mandatory stop under issue #84, not a performance conclusion. The existing Objective-C Metal backend remains the production path. DS4Server now targets Apple silicon and macOS 15 or newer, matching Mojo 1.0's supported host floor. No MAX wheel, runtime library, virtual environment, or Modular source checkout is bundled.
Run the committed proof from the repository root:
tools/mojo-spike/check.sh
The command syncs the locked uv environment, checks the exact compiler build,
emits a C-ABI object and dynamic library, records their closure under
target/mojo-spike, and verifies that the compiler-only environment cannot
import the GPU host API. Once the lock has been synced, the same proof runs
without network access as UV_OFFLINE=1 tools/mojo-spike/check.sh.
Pinned environment
| Component | Pin / observation |
|---|---|
| Mojo compiler | mojo-compiler==1.0.0 |
| Compiler identity | Mojo 1.0.0 (ed45d567) |
| Dependency manager | uv.lock revision 3; hashes recorded for every wheel |
| Mojo source tag | mojo/v1.0.0, commit b4497b7ce9ba96331c72c637ad41b44bab374f33 |
| MAX probe | max==26.5.0, isolated outside the project environment |
| Host | Apple M5 Max, arm64, macOS 26.6.2 (25G83) |
| Xcode / Metal | Xcode 26.6 (17F113), Metal toolchain 17F109 |
Only mojo-compiler is a project build dependency. The broader mojo package
and MAX are intentionally absent because they add tools and runtimes that the
committed proof does not need or may not redistribute.
Build and ABI findings
@export produces a callable C symbol and mojo build --emit object produces a
Mach-O object that can be linked by the platform linker. This is the viable ABI
boundary if the GPU runtime gate is resolved: opaque handles, fixed-width C
scalars, caller-owned buffers, integer status codes, and an explicit last-error
function. Rust unwinding and Mojo exceptions must never cross it.
mojo build --emit shared-lib is not self-contained. Even the trivial
ds4_mojo_probe export has this non-system dependency:
@rpath/libKGENCompilerRTShared.dylib
The object form avoids that dependency for the trivial CPU export, but it does
not provide a way to construct a Metal device context or launch a GPU kernel.
Importing DeviceContext from the open std.gpu.host package fails because it
is not exported there. Importing the working API from max.gpu.host fails in
the compiler-only environment because MAX is not installed.
Compiling the Apache-licensed max.gpu.host Mojo sources directly did not make
the program self-contained. The link still required these external symbols:
_AsyncRT_DeviceContext_create
_AsyncRT_DeviceContext_deviceName
_AsyncRT_DeviceContext_release
_AsyncRT_DeviceContext_strfree
An isolated max==26.5.0 probe could enumerate the Apple M5 Max, proving that
the API is technically viable when the proprietary runtime is present. Its
executable closure included:
@rpath/libKGENCompilerRTShared.dylib
@rpath/libAsyncRTMojoBindings.dylib
/usr/lib/libSystem.B.dylib
That probe is deliberately not part of the repository dependency graph.
License and redistribution inventory
| Component | Source / package evidence | Product decision |
|---|---|---|
| Mojo language, compiler sources, and standard library | Apache-2.0 with LLVM exceptions in the modular/modular repository |
Approved for build use, subject to normal notices |
mojo-compiler and mojo-compiler-mojo-libs wheels |
Pinned by hash; wheel metadata identifies the MAX Platform Software License | Build-only; do not redistribute the wheels |
| MAX Mojo source modules | Repository source is Apache-2.0 with LLVM exceptions, while Modular separately states MAX usage and distribution remain under the Community License | Source license alone does not approve the runtime closure |
max, max-core, and max-mojo-libs wheels |
Wheel metadata: LicenseRef-MAX-Platform-Software-License |
Not a product dependency; do not bundle |
libAsyncRTMojoBindings.dylib and related AsyncRT libraries |
Supplied only by the MAX wheel in this probe; no component-specific redistribution grant found | Blocked |
libKGENCompilerRTShared.dylib |
Required by compiler-emitted shared libraries and the MAX GPU executable; no component-specific redistribution grant found | Blocked |
| Apple Metal frameworks and system libraries | Supplied by macOS | Approved system dependency |
| Existing DS4Server Objective-C Metal backend | Existing project code and Apple system frameworks | Remains the production implementation |
The governing sources are the Mojo repository license, the Modular repository notice separating MAX licensing, and the MAX and Mojo Community License. The Community License permits redistribution only for components Modular expressly identifies as redistributable and subject to its conditions. No authoritative component list was found that names the two required dylibs. Silence is not permission, so packaging is blocked.
Platform and packaging result
Mojo 1.0 officially requires Apple silicon, macOS 15 or newer, Xcode or Command Line Tools 16 or newer, and the Metal toolchain. DS4Server now declares that same architecture and OS floor in its build environment, package metadata, and README. The native backend remains necessary because the license/runtime gate, not the platform floor, blocks the Mojo backend.
The required package proof cannot pass today:
- a clean machine would need non-system MAX runtime dylibs;
- their exact redistributable status and required notices are unresolved;
- an ad-hoc signed
.appcannot be accepted while those dylibs are absent or copied without permission; - CUDA viability cannot be inferred from this Metal probe and remains a separate Linux/NVIDIA build, runtime, and license audit.
Performance result
No parity number is reported. The approved compiler-only environment cannot launch a Metal operation, so there is no license-safe Mojo candidate to compare with native Metal. Timing a MAX-backed or CPU-only substitute would answer a different question and create a misleading baseline. The parity harness and raw GPU/wall-clock measurements begin only after an approved host/runtime path can build, link, and package.
Conditions to resume
All of the following are required before issue #85 or later implementation may replace native code:
- Modular publishes an authoritative redistribution grant naming every required runtime library, or Mojo exposes a host-side Metal launch path whose complete closure is under an approved open-source/system license.
- The locked environment still builds through
uvand works offline after dependency installation. otool -Landnm -ushow only system libraries and explicitly approved, packaged runtime components.- A clean-machine ad-hoc signed app launch proves install-name, rpath, code-sign, and notice handling.
- The parity harness records identical workloads, correctness, GPU execution time, wall time, and repeated samples against the native Metal oracle.
Until then, later milestone issues are design and inventory work only; they may
not introduce MAX as a Cargo, uv, build-script, or application dependency.