mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-09-04 02:37:27 +02:00
241 lines
11 KiB
CMake
241 lines
11 KiB
CMake
find_library(FOUNDATION_LIBRARY Foundation REQUIRED)
|
|
find_library(METAL_FRAMEWORK Metal REQUIRED)
|
|
find_library(METALKIT_FRAMEWORK MetalKit REQUIRED)
|
|
|
|
message(STATUS "Metal framework found")
|
|
|
|
ggml_add_backend_library(ggml-metal
|
|
ggml-metal.cpp
|
|
ggml-metal-device.m
|
|
ggml-metal-device.cpp
|
|
ggml-metal-common.cpp
|
|
ggml-metal-context.m
|
|
ggml-metal-ops.cpp
|
|
ggml-metal-tuning.cpp
|
|
)
|
|
|
|
target_link_libraries(ggml-metal PRIVATE
|
|
${FOUNDATION_LIBRARY}
|
|
${METAL_FRAMEWORK}
|
|
${METALKIT_FRAMEWORK}
|
|
)
|
|
|
|
if (GGML_METAL_NDEBUG)
|
|
add_compile_definitions(GGML_METAL_NDEBUG)
|
|
endif()
|
|
|
|
set(METALLIB_COMMON "${CMAKE_CURRENT_SOURCE_DIR}/../ggml-common.h")
|
|
set(METALLIB_KERNELS_COMMON "${CMAKE_CURRENT_SOURCE_DIR}/kernels/common.h")
|
|
set(METALLIB_KERNELS_DEQUANTIZE "${CMAKE_CURRENT_SOURCE_DIR}/kernels/dequantize.h")
|
|
set(METALLIB_KERNELS_QUANTIZE "${CMAKE_CURRENT_SOURCE_DIR}/kernels/quantize.h")
|
|
|
|
set(METALLIB_KERNEL_SOURCES
|
|
kernels/fa.metal
|
|
kernels/mul_mv.metal
|
|
kernels/mul_mm.metal
|
|
kernels/quantize.metal
|
|
kernels/softmax.metal
|
|
kernels/norm.metal
|
|
kernels/unary.metal
|
|
kernels/binbcast.metal
|
|
kernels/reduce.metal
|
|
kernels/tri.metal
|
|
kernels/ssm.metal
|
|
kernels/wkv.metal
|
|
kernels/gated_delta_net.metal
|
|
kernels/solve_tri.metal
|
|
kernels/rope.metal
|
|
kernels/conv.metal
|
|
kernels/upscale.metal
|
|
kernels/argsort.metal
|
|
kernels/pool.metal
|
|
kernels/misc.metal
|
|
)
|
|
|
|
if (GGML_METAL_EMBED_LIBRARY)
|
|
enable_language(ASM)
|
|
|
|
add_compile_definitions(GGML_METAL_EMBED_LIBRARY)
|
|
|
|
set(METALLIB_IMPL "${CMAKE_CURRENT_SOURCE_DIR}/ggml-metal-impl.h")
|
|
|
|
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/autogenerated")
|
|
|
|
set(METALLIB_EMBED_ASM_FILES "")
|
|
foreach(src ${METALLIB_KERNEL_SOURCES})
|
|
get_filename_component(kind ${src} NAME_WE)
|
|
# symbol names must be valid C identifiers ('-' is not allowed)
|
|
string(REPLACE "-" "_" kind_sym ${kind})
|
|
|
|
set(SRC "${CMAKE_CURRENT_SOURCE_DIR}/kernels/${kind}.metal")
|
|
set(EMBED "${CMAKE_CURRENT_BINARY_DIR}/autogenerated/ggml-metal-embed-${kind}.metal")
|
|
set(ASM "${CMAKE_CURRENT_BINARY_DIR}/autogenerated/ggml-metal-embed-${kind}.s")
|
|
|
|
# only prepend headers that this source actually includes
|
|
set(HEADERS_FOR_SRC ${METALLIB_KERNELS_COMMON})
|
|
file(STRINGS ${SRC} _has_dequantize REGEX "#include \"dequantize\\.h\"")
|
|
file(STRINGS ${SRC} _has_quantize REGEX "#include \"quantize\\.h\"")
|
|
if(_has_dequantize)
|
|
list(APPEND HEADERS_FOR_SRC ${METALLIB_KERNELS_DEQUANTIZE})
|
|
endif()
|
|
if(_has_quantize)
|
|
list(APPEND HEADERS_FOR_SRC ${METALLIB_KERNELS_QUANTIZE})
|
|
endif()
|
|
|
|
add_custom_command(
|
|
OUTPUT "${ASM}"
|
|
# Step 1: concatenate shared headers + this kernel source
|
|
COMMAND cat ${HEADERS_FOR_SRC} ${SRC} > "${EMBED}.tmp1"
|
|
# Step 2: remove internal #include and #pragma once
|
|
COMMAND sed -e "/\#include \"common.h\"/d" -e "/\#include \"dequantize.h\"/d" -e "/\#include \"quantize.h\"/d" -e "/\#pragma once/d" < "${EMBED}.tmp1" > "${EMBED}.tmp2"
|
|
# Step 3: inline ggml-common.h (replacing __embed_ggml-common.h__ sentinel)
|
|
COMMAND sed -e "/__embed_ggml-common.h__/r ${METALLIB_COMMON}" -e "/__embed_ggml-common.h__/d" < "${EMBED}.tmp2" > "${EMBED}.tmp3"
|
|
# Step 4: inline ggml-metal-impl.h
|
|
COMMAND sed -e "/\#include \"ggml-metal-impl.h\"/r ${METALLIB_IMPL}" -e "/\#include \"ggml-metal-impl.h\"/d" < "${EMBED}.tmp3" > "${EMBED}"
|
|
# Step 5: emit an asm chunk with kind-specific start/end symbols
|
|
# note: '-' is illegal in C symbols, so we use kind_sym; the macOS
|
|
# section name is limited to 16 chars so we keep it shared
|
|
# across kinds (__ggml_metallib) and only vary the global symbols.
|
|
COMMAND echo ".section __DATA,__ggml_metallib" > "${ASM}"
|
|
COMMAND echo ".globl _ggml_metallib_${kind_sym}_start" >> "${ASM}"
|
|
COMMAND echo "_ggml_metallib_${kind_sym}_start:" >> "${ASM}"
|
|
COMMAND echo .incbin "\"${EMBED}\"" >> "${ASM}"
|
|
COMMAND echo ".globl _ggml_metallib_${kind_sym}_end" >> "${ASM}"
|
|
COMMAND echo "_ggml_metallib_${kind_sym}_end:" >> "${ASM}"
|
|
DEPENDS ../ggml-common.h ggml-metal-impl.h
|
|
kernels/common.h kernels/dequantize.h kernels/quantize.h
|
|
kernels/${kind}.metal
|
|
COMMENT "Generate embedded Metal library for ${kind}"
|
|
VERBATIM
|
|
)
|
|
|
|
list(APPEND METALLIB_EMBED_ASM_FILES "${ASM}")
|
|
endforeach()
|
|
|
|
target_sources(ggml-metal PRIVATE ${METALLIB_EMBED_ASM_FILES})
|
|
else()
|
|
# copy header files to bin directory
|
|
configure_file(../ggml-common.h ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ggml-common.h COPYONLY)
|
|
configure_file(ggml-metal-impl.h ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ggml-metal-impl.h COPYONLY)
|
|
|
|
file(MAKE_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/kernels")
|
|
configure_file(kernels/common.h ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/kernels/common.h COPYONLY)
|
|
configure_file(kernels/dequantize.h ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/kernels/dequantize.h COPYONLY)
|
|
configure_file(kernels/quantize.h ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/kernels/quantize.h COPYONLY)
|
|
|
|
foreach(src ${METALLIB_KERNEL_SOURCES})
|
|
configure_file(${src} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${src} COPYONLY)
|
|
endforeach()
|
|
|
|
# CMAKE_OSX_SYSROOT is an SDK name or path - xcrun accepts both
|
|
set(METAL_SDK ${CMAKE_OSX_SYSROOT})
|
|
if (NOT METAL_SDK)
|
|
set(METAL_SDK macosx)
|
|
endif()
|
|
|
|
if (CMAKE_OSX_SYSROOT MATCHES "[Ss]imulator")
|
|
set(METAL_TARGET_SIM "-simulator")
|
|
else()
|
|
set(METAL_TARGET_SIM "")
|
|
endif()
|
|
|
|
if (GGML_METAL_SHADER_DEBUG)
|
|
# note: disabling fast math is needed in order to pass tests/test-backend-ops
|
|
# note: adding -fno-inline fixes the tests when using MTL_SHADER_VALIDATION=1
|
|
# note: unfortunately, we have to call it default.metallib instead of ggml.metallib
|
|
# ref: https://github.com/ggml-org/whisper.cpp/issues/1720
|
|
# note: adding -g causes segmentation fault during compile
|
|
set(XC_FLAGS -fno-fast-math -fno-inline)
|
|
else()
|
|
set(XC_FLAGS -O3)
|
|
endif()
|
|
|
|
execute_process(COMMAND xcrun -sdk ${METAL_SDK} --show-sdk-version OUTPUT_VARIABLE METAL_SDK_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
|
|
if (METAL_SDK_VERSION VERSION_GREATER_EQUAL 26.0)
|
|
set(GGML_METAL_HAS_TENSOR_LIB ON)
|
|
else()
|
|
message(STATUS "Metal SDK ${METAL_SDK_VERSION} does not support the tensor API, skipping ggml-tensor.metallib")
|
|
endif()
|
|
|
|
if (GGML_METAL_MACOSX_VERSION_MIN)
|
|
message(STATUS "Adding -mmacosx-version-min=${GGML_METAL_MACOSX_VERSION_MIN} flag to metal compilation")
|
|
list (APPEND XC_FLAGS -mmacosx-version-min=${GGML_METAL_MACOSX_VERSION_MIN})
|
|
elseif (NOT GGML_METAL_TARGET_OS STREQUAL "macos" AND CMAKE_OSX_DEPLOYMENT_TARGET)
|
|
message(STATUS "Adding -mtargetos=${GGML_METAL_TARGET_OS}${CMAKE_OSX_DEPLOYMENT_TARGET}${METAL_TARGET_SIM} flag to metal compilation")
|
|
list (APPEND XC_FLAGS -mtargetos=${GGML_METAL_TARGET_OS}${CMAKE_OSX_DEPLOYMENT_TARGET}${METAL_TARGET_SIM})
|
|
endif()
|
|
|
|
if (GGML_METAL_STD)
|
|
message(STATUS "Adding -std=${GGML_METAL_STD} flag to metal compilation")
|
|
list (APPEND XC_FLAGS -std=${GGML_METAL_STD})
|
|
endif()
|
|
|
|
# Compile each kernel source to .air, then link into default.metallib
|
|
set(AIR_FILES "")
|
|
foreach(src ${METALLIB_KERNEL_SOURCES})
|
|
get_filename_component(name ${src} NAME_WE)
|
|
set(AIR "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}.air")
|
|
list(APPEND AIR_FILES ${AIR})
|
|
add_custom_command(
|
|
OUTPUT ${AIR}
|
|
COMMAND xcrun -sdk ${METAL_SDK} metal ${XC_FLAGS} -I ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} -c ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${src} -o ${AIR}
|
|
DEPENDS ${src} kernels/common.h kernels/dequantize.h kernels/quantize.h ${METALLIB_COMMON} ggml-metal-impl.h
|
|
COMMENT "Compiling ${src}"
|
|
VERBATIM
|
|
)
|
|
endforeach()
|
|
|
|
set(METALLIB_FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/default.metallib)
|
|
|
|
# the tensor API kernels go in a separate metallib, loaded only where supported
|
|
if (GGML_METAL_HAS_TENSOR_LIB)
|
|
set(AIR_MM_TENSOR "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/mul_mm_tensor.air")
|
|
# the tensor API needs OS 26+
|
|
set(XC_FLAGS_TENSOR ${XC_FLAGS} -mtargetos=${GGML_METAL_TARGET_OS}26.0${METAL_TARGET_SIM})
|
|
add_custom_command(
|
|
OUTPUT ${AIR_MM_TENSOR}
|
|
COMMAND xcrun -sdk ${METAL_SDK} metal ${XC_FLAGS_TENSOR} -DGGML_METAL_HAS_TENSOR -I ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} -c ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/kernels/mul_mm.metal -o ${AIR_MM_TENSOR}
|
|
DEPENDS kernels/mul_mm.metal kernels/common.h kernels/dequantize.h ${METALLIB_COMMON} ggml-metal-impl.h
|
|
COMMENT "Compiling kernels/mul_mm.metal (tensor API)"
|
|
VERBATIM
|
|
)
|
|
|
|
add_custom_command(
|
|
OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ggml-tensor.metallib
|
|
COMMAND xcrun -sdk ${METAL_SDK} metallib ${AIR_MM_TENSOR} -o ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ggml-tensor.metallib
|
|
DEPENDS ${AIR_MM_TENSOR}
|
|
COMMENT "Linking tensor API Metal kernels into ggml-tensor.metallib"
|
|
)
|
|
|
|
list(APPEND METALLIB_FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ggml-tensor.metallib)
|
|
endif()
|
|
|
|
add_custom_command(
|
|
OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/default.metallib
|
|
COMMAND xcrun -sdk ${METAL_SDK} metallib ${AIR_FILES} -o ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/default.metallib
|
|
COMMAND rm -f ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ggml-common.h
|
|
COMMAND rm -f ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ggml-metal-impl.h
|
|
COMMAND rm -rf ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/kernels
|
|
DEPENDS ${AIR_FILES} ${AIR_MM_TENSOR}
|
|
COMMENT "Linking Metal kernels into default.metallib"
|
|
)
|
|
|
|
add_custom_target(
|
|
ggml-metal-lib ALL
|
|
DEPENDS ${METALLIB_FILES}
|
|
)
|
|
endif() # GGML_METAL_EMBED_LIBRARY
|
|
|
|
if (NOT GGML_METAL_EMBED_LIBRARY)
|
|
install(
|
|
DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/kernels/
|
|
DESTINATION ${CMAKE_INSTALL_BINDIR}/kernels
|
|
FILES_MATCHING PATTERN "*.metal" PATTERN "*.h"
|
|
)
|
|
|
|
install(
|
|
FILES ${METALLIB_FILES}
|
|
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
)
|
|
endif()
|