1find_library(FOUNDATION_LIBRARY Foundation REQUIRED)
  2find_library(METAL_FRAMEWORK    Metal      REQUIRED)
  3find_library(METALKIT_FRAMEWORK MetalKit   REQUIRED)
  4
  5message(STATUS "Metal framework found")
  6
  7ggml_add_backend_library(ggml-metal
  8                         ggml-metal.cpp
  9                         ggml-metal-device.m
 10                         ggml-metal-device.cpp
 11                         ggml-metal-common.cpp
 12                         ggml-metal-context.m
 13                         ggml-metal-ops.cpp
 14                        )
 15
 16target_link_libraries(ggml-metal PRIVATE
 17                      ${FOUNDATION_LIBRARY}
 18                      ${METAL_FRAMEWORK}
 19                      ${METALKIT_FRAMEWORK}
 20                      )
 21
 22if (GGML_METAL_NDEBUG)
 23    add_compile_definitions(GGML_METAL_NDEBUG)
 24endif()
 25
 26set(METALLIB_COMMON "${CMAKE_CURRENT_SOURCE_DIR}/../ggml-common.h")
 27if (GGML_METAL_EMBED_LIBRARY)
 28    enable_language(ASM)
 29
 30    add_compile_definitions(GGML_METAL_EMBED_LIBRARY)
 31
 32    set(METALLIB_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/ggml-metal.metal")
 33    set(METALLIB_IMPL   "${CMAKE_CURRENT_SOURCE_DIR}/ggml-metal-impl.h")
 34
 35    file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/autogenerated")
 36
 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")
 41
 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    )
 57
 58    target_sources(ggml-metal PRIVATE "${METALLIB_EMBED_ASM}")
 59else()
 60    # copy metal files to bin directory
 61    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)
 63    configure_file(ggml-metal-impl.h ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ggml-metal-impl.h COPYONLY)
 64
 65    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
 72        # note: adding -fno-inline fixes the tests when using MTL_SHADER_VALIDATION=1
 73        # note: unfortunately, we have to call it default.metallib instead of ggml.metallib
 74        #       ref: https://github.com/ggml-org/whisper.cpp/issues/1720
 75        # note: adding -g causes segmentation fault during compile
 76        #set(XC_FLAGS -fno-fast-math -fno-inline -g)
 77        set(XC_FLAGS -fno-fast-math -fno-inline)
 78    else()
 79        set(XC_FLAGS -O3)
 80    endif()
 81
 82    # Append macOS metal versioning flags
 83    if (GGML_METAL_MACOSX_VERSION_MIN)
 84        message(STATUS "Adding  -mmacosx-version-min=${GGML_METAL_MACOSX_VERSION_MIN} flag to metal compilation")
 85        list   (APPEND XC_FLAGS -mmacosx-version-min=${GGML_METAL_MACOSX_VERSION_MIN})
 86    endif()
 87
 88    if (GGML_METAL_STD)
 89        message(STATUS "Adding  -std=${GGML_METAL_STD} flag to metal compilation")
 90        list   (APPEND XC_FLAGS -std=${GGML_METAL_STD})
 91    endif()
 92
 93    add_custom_command(
 94        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
 97        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        )
102
103    # FIXME: only add to the ggml-metal target?
104    add_custom_target(
105        ggml-metal-lib ALL
106        DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/default.metallib
107        )
108endif() # GGML_METAL_EMBED_LIBRARY
109
110if (NOT GGML_METAL_EMBED_LIBRARY)
111    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        )
124endif()