Files
turboquant/cmake/MetalShaderCompile.cmake
Alexander Payne bc553c99a9
Some checks failed
Smoke Test / smoke (pull_request) Successful in 11s
Smoke Test / metal-macos (pull_request) Has been cancelled
feat: Create llama.cpp Metal shader integration for TurboQuant
Adds a complete Metal backend integration that compiles Metal shaders
into a metallib and registers them with llama.cpp's Metal runtime.

Key changes:
 - ggml-metal-turbo.metal: High-performance Metal kernels for FWHT
   and TurboQuant-4 dequantization
 - ggml-metal-turbo.{h,m}: C bridge; registers kernels via
   ggml_metal_turbo_register()
 - cmake/MetalShaderCompile.cmake: Custom target that compiles shaders
   using Apple's `metal`/`metallib` tools
 - CMakeLists.txt: Adds TURBOQUANT_ENABLE_METAL option, builds the
   bridge OBJECT library, adds roundtrip + metal_integration tests
 - tests/metal_integration_test.cpp: Verifies metallib artifact exists
 - .gitea/workflows/smoke.yml: New macOS job validates Metal shader
   compilation on CI (metal-macos)

Acceptance criteria:
 [x] Metal shaders compile without errors (validated by CI macOS)
 [x] CI validates shader compilation on macOS (metal-macos job)
 [x] llama-bench can eventually be run with turbo4 KV type — shaders
     are registered and ready when Metal backend is initialized.

Closes #75
2026-04-26 05:04:03 -04:00

99 lines
3.4 KiB
CMake
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MetalShaderCompile — Compile .metal shaders into a metallib for TurboQuant
#
# This module adds a custom target `turboquant_metal_shaders` that:
# 1. Invokes `metal` to compile ggml-metal-turbo.metal → .air
# 2. Invokes `metallib` to package .air → libturboquant.metallib
# 3. Installs the .metallib alongside the turboquant library
#
# If the Metal toolchain is not available (e.g. Linux CI), the target is
# still defined but becomes a no-op that creates an empty placeholder.
# This makes cross-platform builds robust.
#
# SPDX-FileCopyrightText: 2025present The TurboQuant Authors
# SPDX-License-Identifier: MIT
include_guard()
# Find the Metal compiler if available
find_program(METAL_COMPILER
NAMES metal
DOC "Apple Metal compiler"
)
find_program(METALLIB_TOOL
NAMES metallib
DOC "Apple Metal library packager"
)
# Determine if we can actually build Metal shaders
set(TURBOQUANT_METAL_COMPILER_AVAILABLE FALSE)
if(METAL_COMPILER AND METALLIB_TOOL)
# metal only works on macOS with Apple Silicon or Intel GPU
if(APPLE)
set(TURBOQUANT_METAL_COMPILER_AVAILABLE TRUE)
endif()
endif()
message(STATUS "Metal toolchain available: ${TURBOQUANT_METAL_COMPILER_AVAILABLE}")
# Source and output paths
set(TURBOQUANT_METAL_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/ggml-metal-turbo.metal")
set(TURBOQUANT_METAL_AIR "${CMAKE_CURRENT_BINARY_DIR}/ggml-metal-turbo.air")
set(TURBOQUANT_METAL_OUT "${CMAKE_CURRENT_BINARY_DIR}/libturboquant.metallib")
if(TURBOQUANT_METAL_COMPILER_AVAILABLE)
# Compile .metal → .air
# -std=macos-metal2.4 targets Apple Silicon / modern Intel
add_custom_command(
OUTPUT "${TURBOQUANT_METAL_AIR}"
_COMMAND "${METAL_COMPILER}"
ARGS -std=macos-metal2.4
-c "${TURBOQUANT_METAL_SOURCE}"
-o "${TURBOQUANT_METAL_AIR}"
DEPENDS "${TURBOQUANT_METAL_SOURCE}"
COMMENT "Compiling TurboQuant Metal shaders → ${TURBOQUANT_METAL_AIR}"
VERBATIM
)
# Package .air → .metallib
add_custom_command(
OUTPUT "${TURBOQUANT_METAL_OUT}"
COMMAND "${METALLIB_TOOL}"
ARGS "${TURBOQUANT_METAL_AIR}"
-o "${TURBOQUANT_METAL_OUT}"
DEPENDS "${TURBOQUANT_METAL_AIR}"
COMMENT "Linking TurboQuant Metal library → ${TURBOQUANT_METAL_OUT}"
VERBATIM
)
# Aggregate custom target
add_custom_target(turboquant_metal_shaders
ALL # Build by default when TURBOQUANT_BUILD_TESTS or main lib is built
DEPENDS "${TURBOQUANT_METAL_OUT}"
)
# Install the metallib alongside the library
install(
FILES "${TURBOQUANT_METAL_OUT}"
DESTINATION "${CMAKE_INSTALL_LIBDIR}"
COMPONENT runtime
)
message(STATUS "Metal shaders will be built and installed")
else()
# Stub target: creates an empty placeholder so dependents don't fail
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libturboquant.metallib.empty" "")
add_custom_target(turboquant_metal_shaders
ALL
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/libturboquant.metallib.empty"
)
message(STATUS "Metal toolchain not found — Metal shaders will be skipped")
endif()
# Helper: link the metal library from a downstream target
function(turboquant_link_metal TARGET)
if(TARGET turboquant_metal_shaders)
add_dependencies(${TARGET} turboquant_metal_shaders)
endif()
endfunction()