Files
DS4Server/docs/MOJO_SPIKE_84.md
2026-08-31 15:25:04 +02:00

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 .app cannot 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:

  1. 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.
  2. The locked environment still builds through uv and works offline after dependency installation.
  3. otool -L and nm -u show only system libraries and explicitly approved, packaged runtime components.
  4. A clean-machine ad-hoc signed app launch proves install-name, rpath, code-sign, and notice handling.
  5. 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.