SPB Git

spb/forge Public MIT

Forge — LLM training from scratch in pure C++20 + Metal on Apple Silicon.

C++ 61.2% C 23% Python 7.6% TeX 7.2% CMake 1.1%
3.9 KB · 111 lines cmake
Raw Blame History
1# Author: Simon-Pierre Boucher — contact@spboucher.ai2cmake_minimum_required(VERSION 3.24)3project(forge LANGUAGES CXX)45set(CMAKE_CXX_STANDARD 20)6set(CMAKE_CXX_STANDARD_REQUIRED ON)7set(CMAKE_CXX_EXTENSIONS OFF)89if(NOT CMAKE_BUILD_TYPE)10  set(CMAKE_BUILD_TYPE Release)11endif()1213add_compile_options(-Wall -Wextra)1415# ---------------------------------------------------------------------------16# Metal shader library: .metal -> .air -> forge.metallib17# Compiled with -std=metal3.2 (macOS 15+ baseline: simdgroup_matrix, bfloat).18# Debug builds embed sources for the Xcode GPU debugger.19# ---------------------------------------------------------------------------20file(GLOB METAL_SOURCES CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/src/kernels/*.metal)21set(AIR_FILES "")22foreach(shader ${METAL_SOURCES})23  get_filename_component(shader_name ${shader} NAME_WE)24  set(air ${CMAKE_BINARY_DIR}/${shader_name}.air)25  # Metal Performance Primitives (cooperative tensors) need the Metal 426  # language; everything else targets 3.2 so it runs on macOS 15+.27  if(shader_name MATCHES "_mpp$")28    set(metal_std "-std=metal4.0")29  else()30    set(metal_std "-std=metal3.2")31  endif()32  add_custom_command(33    OUTPUT ${air}34    # -frecord-sources is unconditional, not Debug-only: without shader sources35    # embedded in the metallib, the Metal debugger and gpudebug report36    # "no source" and every per-shader/per-line profiling view comes back37    # empty. It costs file size, nothing else.38    COMMAND xcrun -sdk macosx metal ${metal_std} -O239            -gline-tables-only -frecord-sources40            -c ${shader} -o ${air}41    DEPENDS ${shader}42    COMMAND_EXPAND_LISTS43    COMMENT "Compiling Metal shader ${shader_name}.metal"44    VERBATIM)45  list(APPEND AIR_FILES ${air})46endforeach()4748add_custom_command(49  OUTPUT ${CMAKE_BINARY_DIR}/forge.metallib50  # Link with `metal`, not `metallib`: the sources only survive into the51  # library if -frecord-sources is passed at link time too, and `metallib`52  # rejects that flag ("unknown argument").53  COMMAND xcrun -sdk macosx metal -frecord-sources ${AIR_FILES}54          -o ${CMAKE_BINARY_DIR}/forge.metallib55  DEPENDS ${AIR_FILES}56  COMMENT "Linking forge.metallib"57  VERBATIM)58add_custom_target(forge_kernels ALL DEPENDS ${CMAKE_BINARY_DIR}/forge.metallib)5960# ---------------------------------------------------------------------------61# Core library62# ---------------------------------------------------------------------------63add_library(forge_core STATIC64  src/core/metal_impl.cpp65  src/core/tensor.cpp66  src/core/allocator.cpp67  src/core/device.cpp68  src/core/autograd.cpp69  src/core/fmodel.cpp70  src/ops/cpu/cpu_ops.cpp71  src/ops/metal/metal_ops.cpp72  src/ops/ops.cpp73  src/nn/linear.cpp74  src/train/optimizer.cpp75  src/train/dataloader.cpp76  src/train/checkpoint.cpp77  src/train/trainer.cpp78  src/tokenizer/bpe.cpp)7980target_include_directories(forge_core PUBLIC81  ${CMAKE_SOURCE_DIR}/src82  ${CMAKE_SOURCE_DIR}/third_party83  ${CMAKE_SOURCE_DIR}/third_party/metal-cpp)8485target_link_libraries(forge_core PUBLIC86  "-framework Metal" "-framework Foundation" "-framework QuartzCore")8788add_dependencies(forge_core forge_kernels)8990# ---------------------------------------------------------------------------91# CLI92# ---------------------------------------------------------------------------93add_executable(forge src/main.cpp)94target_link_libraries(forge PRIVATE forge_core)9596# ---------------------------------------------------------------------------97# Tests98# ---------------------------------------------------------------------------99enable_testing()100foreach(t test_ops test_gradcheck test_overfit test_tokenizer)101  add_executable(${t} tests/${t}.cpp)102  target_link_libraries(${t} PRIVATE forge_core)103  add_test(NAME ${t} COMMAND ${t} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})104endforeach()105106# Benchmarks: built, not run by ctest.107foreach(b bench_matmul bench_attention bench_precision mppcheck)108  add_executable(${b} tests/${b}.cpp)109  target_link_libraries(${b} PRIVATE forge_core)110endforeach()111