@@ -24,62 +24,119 @@ if (GGML_METAL_NDEBUG)
2424endif ()
2525
2626set (METALLIB_COMMON "${CMAKE_CURRENT_SOURCE_DIR } /../ggml-common.h" )
27+ set (METALLIB_KERNELS_COMMON "${CMAKE_CURRENT_SOURCE_DIR } /kernels/common.h" )
28+ set (METALLIB_KERNELS_DEQUANTIZE "${CMAKE_CURRENT_SOURCE_DIR } /kernels/dequantize.h" )
29+ set (METALLIB_KERNELS_QUANTIZE "${CMAKE_CURRENT_SOURCE_DIR } /kernels/quantize.h" )
30+
31+ set (METALLIB_KERNEL_SOURCES
32+ kernels/fa.metal
33+ kernels/mul_mv.metal
34+ kernels/mul_mm.metal
35+ kernels/quantize.metal
36+ kernels/softmax.metal
37+ kernels/norm.metal
38+ kernels/unary.metal
39+ kernels/binbcast.metal
40+ kernels/reduce.metal
41+ kernels/tri.metal
42+ kernels/ssm.metal
43+ kernels/wkv.metal
44+ kernels/gated_delta_net.metal
45+ kernels/solve_tri.metal
46+ kernels/rope.metal
47+ kernels/conv.metal
48+ kernels/upscale.metal
49+ kernels/argsort.metal
50+ kernels/pool.metal
51+ kernels/misc.metal
52+ )
53+
2754if (GGML_METAL_EMBED_LIBRARY)
2855 enable_language (ASM )
2956
3057 add_compile_definitions (GGML_METAL_EMBED_LIBRARY )
3158
32- set (METALLIB_SOURCE "${CMAKE_CURRENT_SOURCE_DIR } /ggml-metal.metal" )
33- set (METALLIB_IMPL "${CMAKE_CURRENT_SOURCE_DIR } /ggml-metal-impl.h" )
59+ set (METALLIB_IMPL "${CMAKE_CURRENT_SOURCE_DIR } /ggml-metal-impl.h" )
3460
3561 file (MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR } /autogenerated" )
3662
37- # merge ggml-common.h and ggml-metal.metal into a single file
38- set (METALLIB_EMBED_ASM "${CMAKE_CURRENT_BINARY_DIR } /autogenerated/ggml-metal-embed.s" )
39- set (METALLIB_SOURCE_EMBED "${CMAKE_CURRENT_BINARY_DIR } /autogenerated/ggml-metal-embed.metal" )
40- set (METALLIB_SOURCE_EMBED_TMP "${CMAKE_CURRENT_BINARY_DIR } /autogenerated/ggml-metal-embed.metal.tmp" )
63+ set (METALLIB_EMBED_ASM_FILES "" )
64+ foreach (src ${METALLIB_KERNEL_SOURCES} )
65+ get_filename_component (kind ${src} NAME_WE )
66+ # symbol names must be valid C identifiers ('-' is not allowed)
67+ string (REPLACE "-" "_" kind_sym ${kind} )
68+
69+ set (SRC "${CMAKE_CURRENT_SOURCE_DIR } /kernels/${kind} .metal" )
70+ set (EMBED "${CMAKE_CURRENT_BINARY_DIR } /autogenerated/ggml-metal-embed-${kind} .metal" )
71+ set (ASM "${CMAKE_CURRENT_BINARY_DIR } /autogenerated/ggml-metal-embed-${kind} .s" )
72+
73+ # only prepend headers that this source actually includes
74+ set (HEADERS_FOR_SRC ${METALLIB_KERNELS_COMMON} )
75+ file (STRINGS ${SRC} _has_dequantize REGEX "#include \" dequantize\\ .h\" " )
76+ file (STRINGS ${SRC} _has_quantize REGEX "#include \" quantize\\ .h\" " )
77+ if (_has_dequantize)
78+ list (APPEND HEADERS_FOR_SRC ${METALLIB_KERNELS_DEQUANTIZE} )
79+ endif ()
80+ if (_has_quantize)
81+ list (APPEND HEADERS_FOR_SRC ${METALLIB_KERNELS_QUANTIZE} )
82+ endif ()
83+
84+ add_custom_command (
85+ OUTPUT "${ASM} "
86+ # Step 1: concatenate shared headers + this kernel source
87+ COMMAND cat ${HEADERS_FOR_SRC} ${SRC} > "${EMBED} .tmp1"
88+ # Step 2: remove internal #include and #pragma once
89+ 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"
90+ # Step 3: inline ggml-common.h (replacing __embed_ggml-common.h__ sentinel)
91+ COMMAND sed -e "/__embed_ggml-common.h__/r ${METALLIB_COMMON} " -e "/__embed_ggml-common.h__/d" < "${EMBED} .tmp2" > "${EMBED} .tmp3"
92+ # Step 4: inline ggml-metal-impl.h
93+ COMMAND sed -e "/\# include \" ggml-metal-impl.h\" /r ${METALLIB_IMPL} " -e "/\# include \" ggml-metal-impl.h\" /d" < "${EMBED} .tmp3" > "${EMBED} "
94+ # Step 5: emit an asm chunk with kind-specific start/end symbols
95+ # note: '-' is illegal in C symbols, so we use kind_sym; the macOS
96+ # section name is limited to 16 chars so we keep it shared
97+ # across kinds (__ggml_metallib) and only vary the global symbols.
98+ COMMAND echo ".section __DATA,__ggml_metallib" > "${ASM} "
99+ COMMAND echo ".globl _ggml_metallib_${kind_sym} _start" >> "${ASM} "
100+ COMMAND echo "_ggml_metallib_${kind_sym} _start:" >> "${ASM} "
101+ COMMAND echo .incbin "\" ${EMBED} \" " >> "${ASM} "
102+ COMMAND echo ".globl _ggml_metallib_${kind_sym} _end" >> "${ASM} "
103+ COMMAND echo "_ggml_metallib_${kind_sym} _end:" >> "${ASM} "
104+ DEPENDS ../ggml-common.h ggml-metal-impl.h
105+ kernels/common.h kernels/dequantize.h kernels/quantize.h
106+ kernels/${kind}.metal
107+ COMMENT "Generate embedded Metal library for ${kind} "
108+ VERBATIM
109+ )
41110
42- add_custom_command (
43- OUTPUT "${METALLIB_EMBED_ASM} "
44- COMMAND echo "Embedding Metal library"
45- COMMAND sed -e "/__embed_ggml-common.h__/r ${METALLIB_COMMON} " -e "/__embed_ggml-common.h__/d" < "${METALLIB_SOURCE} " > "${METALLIB_SOURCE_EMBED_TMP} "
46- COMMAND sed -e "/\# include \" ggml-metal-impl.h\" /r ${METALLIB_IMPL} " -e "/\# include \" ggml-metal-impl.h\" /d" < "${METALLIB_SOURCE_EMBED_TMP} " > "${METALLIB_SOURCE_EMBED} "
47- COMMAND echo ".section __DATA,__ggml_metallib" > "${METALLIB_EMBED_ASM} "
48- COMMAND echo ".globl _ggml_metallib_start" >> "${METALLIB_EMBED_ASM} "
49- COMMAND echo "_ggml_metallib_start:" >> "${METALLIB_EMBED_ASM} "
50- COMMAND echo .incbin "\" ${METALLIB_SOURCE_EMBED} \" " >> "${METALLIB_EMBED_ASM} "
51- COMMAND echo ".globl _ggml_metallib_end" >> "${METALLIB_EMBED_ASM} "
52- COMMAND echo "_ggml_metallib_end:" >> "${METALLIB_EMBED_ASM} "
53- DEPENDS ../ggml-common.h ggml-metal.metal ggml-metal-impl.h
54- COMMENT "Generate assembly for embedded Metal library"
55- VERBATIM
56- )
111+ list (APPEND METALLIB_EMBED_ASM_FILES "${ASM} " )
112+ endforeach ()
57113
58- target_sources (ggml-metal PRIVATE " ${METALLIB_EMBED_ASM} " )
114+ target_sources (ggml-metal PRIVATE ${METALLIB_EMBED_ASM_FILES} )
59115else ()
60- # copy metal files to bin directory
116+ # copy header files to bin directory
61117 configure_file (../ggml-common.h ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /ggml-common.h COPYONLY )
62- configure_file (ggml-metal.metal ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /ggml-metal.metal COPYONLY )
63118 configure_file (ggml-metal-impl.h ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /ggml-metal-impl.h COPYONLY )
64119
120+ file (MAKE_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /kernels" )
121+ configure_file (kernels/common.h ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /kernels/common.h COPYONLY )
122+ configure_file (kernels/dequantize.h ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /kernels/dequantize.h COPYONLY )
123+ configure_file (kernels/quantize.h ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /kernels/quantize.h COPYONLY )
124+
125+ foreach (src ${METALLIB_KERNEL_SOURCES} )
126+ configure_file (${src} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /${src} COPYONLY )
127+ endforeach ()
128+
65129 if (GGML_METAL_SHADER_DEBUG)
66- # custom command to do the following:
67- # xcrun -sdk macosx metal -fno-fast-math -c ggml-metal.metal -o ggml-metal.air
68- # xcrun -sdk macosx metallib ggml-metal.air -o default.metallib
69- #
70- # note: this is the only way I found to disable fast-math in Metal. it's ugly, but at least it works
71- # disabling fast math is needed in order to pass tests/test-backend-ops
130+ # note: disabling fast math is needed in order to pass tests/test-backend-ops
72131 # note: adding -fno-inline fixes the tests when using MTL_SHADER_VALIDATION=1
73132 # note: unfortunately, we have to call it default.metallib instead of ggml.metallib
74133 # ref: https://github.com/ggml-org/whisper.cpp/issues/1720
75134 # note: adding -g causes segmentation fault during compile
76- #set(XC_FLAGS -fno-fast-math -fno-inline -g)
77135 set (XC_FLAGS -fno-fast-math -fno-inline)
78136 else ()
79137 set (XC_FLAGS -O3)
80138 endif ()
81139
82- # Append macOS metal versioning flags
83140 if (GGML_METAL_MACOSX_VERSION_MIN)
84141 message (STATUS "Adding -mmacosx-version-min=${GGML_METAL_MACOSX_VERSION_MIN} flag to metal compilation" )
85142 list (APPEND XC_FLAGS -mmacosx-version-min=${GGML_METAL_MACOSX_VERSION_MIN} )
@@ -90,35 +147,46 @@ else()
90147 list (APPEND XC_FLAGS -std=${GGML_METAL_STD} )
91148 endif ()
92149
150+ # Compile each kernel source to .air, then link into default.metallib
151+ set (AIR_FILES "" )
152+ foreach (src ${METALLIB_KERNEL_SOURCES} )
153+ get_filename_component (name ${src} NAME_WE )
154+ set (AIR "${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /${name} .air" )
155+ list (APPEND AIR_FILES ${AIR} )
156+ add_custom_command (
157+ OUTPUT ${AIR}
158+ COMMAND xcrun -sdk macosx metal ${XC_FLAGS} -I ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } -c ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /${src} -o ${AIR}
159+ DEPENDS ${src} kernels/common.h kernels/dequantize.h kernels/quantize.h ${METALLIB_COMMON} ggml-metal-impl.h
160+ COMMENT "Compiling ${src} "
161+ VERBATIM
162+ )
163+ endforeach ()
164+
93165 add_custom_command (
94166 OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /default.metallib
95- COMMAND xcrun -sdk macosx metal ${XC_FLAGS} -c ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /ggml-metal.metal -o - |
96- xcrun -sdk macosx metallib - -o ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /default.metallib
167+ COMMAND xcrun -sdk macosx metallib ${AIR_FILES} -o ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /default.metallib
97168 COMMAND rm -f ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /ggml-common.h
98- COMMAND rm -f ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /ggml-metal.metal
99- DEPENDS ggml-metal.metal ${METALLIB_COMMON}
100- COMMENT "Compiling Metal kernels"
101- )
169+ COMMAND rm -f ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /ggml-metal-impl.h
170+ COMMAND rm -rf ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /kernels
171+ DEPENDS ${AIR_FILES}
172+ COMMENT "Linking Metal kernels into default.metallib"
173+ )
102174
103- # FIXME: only add to the ggml-metal target?
104175 add_custom_target (
105176 ggml-metal-lib ALL
106177 DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /default.metallib
107- )
178+ )
108179endif () # GGML_METAL_EMBED_LIBRARY
109180
110181if (NOT GGML_METAL_EMBED_LIBRARY)
111182 install (
112- FILES src/ggml-metal/ggml-metal.metal
113- PERMISSIONS
114- OWNER_READ
115- OWNER_WRITE
116- GROUP_READ
117- WORLD_READ
118- DESTINATION ${CMAKE_INSTALL_BINDIR} )
119-
120- install (
121- FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /default.metallib
122- DESTINATION ${CMAKE_INSTALL_BINDIR}
123- )
183+ DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR } /kernels/
184+ DESTINATION ${CMAKE_INSTALL_BINDIR} /kernels
185+ FILES_MATCHING PATTERN "*.metal" PATTERN "*.h"
186+ )
187+
188+ install (
189+ FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY } /default.metallib
190+ DESTINATION ${CMAKE_INSTALL_BINDIR}
191+ )
124192endif ()
0 commit comments