Repository navigation
Expand file tree
/
Copy pathCMakeLists.txt
More file actions
392 lines (358 loc) · 17.4 KB
/
Copy pathCMakeLists.txt
File metadata and controls
392 lines (358 loc) · 17.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
cmake_minimum_required(VERSION 3.21...4.3)
project(vla_simd VERSION 0.1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
include(GNUInstallDirs)
# Every object ends up in one of the vla_simd_* shared libraries, so build
# the tree PIC and reuse the static libs instead of recompiling the HAL per .so.
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# Only the vla_* entry points are API; hiding the rest stops two engines in one
# process from interposing each other's C++ symbols.
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Release)
endif()
# Appended, not assigned: assigning CMAKE_CXX_FLAGS_RELEASE drops CMake's own
# `-O3 -DNDEBUG`, which left asserts live in a release build.
string(APPEND CMAKE_CXX_FLAGS_RELEASE " -funroll-loops")
string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -funroll-loops")
# ---------------------------------------------------------------------------
# vla_flags: the ISA the HAL is compiled for. An INTERFACE target, not
# CMAKE_CXX_FLAGS_RELEASE, because src/hal/arch.h picks its backend from the
# macros these enable and Debug/RelWithDebInfo would otherwise fall through to
# the scalar kernels.
#
# x86-64 -> AVX2/FMA. Apple arm64 -> -mcpu. Other aarch64 -> baseline
# ARMv8 NEON. Anything else -> scalar fallback.
#
# Non-Apple aarch64 stays on the baseline by choice. Pi 5 (Cortex-A76, ACT, 4
# threads): armv8-a+simd 1031 ms, +dotprod+fp16 1043, -mtune=cortex-a76 1116,
# -mcpu=native 1120. The ISA features are ~free; the 8% is GCC's A76 cost model
# pessimizing the hand-written NEON kernels. Kernels wanting a newer extension
# (SDOT, AVX-VNNI) take it per-function via __attribute__((target(...))) behind
# a runtime check. -DVLA_ARM_NATIVE=ON opts in, -DVLA_ARM_FLAGS overrides.
# ---------------------------------------------------------------------------
add_library(vla_flags INTERFACE)
target_compile_features(vla_flags INTERFACE cxx_std_17)
target_include_directories(vla_flags INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>)
option(VLA_SCALAR "Build the portable scalar backend" OFF)
if(VLA_SCALAR)
target_compile_definitions(vla_flags INTERFACE TCPU_FORCE_SCALAR=1)
elseif(NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
message(WARNING "vla_simd: no ISA flags for ${CMAKE_CXX_COMPILER_ID}; "
"the HAL will fall back to the scalar backend")
elseif(APPLE AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)")
# Apple is the one target that takes +dotprod from -mcpu rather than from the
# pragma in src/hal/neon/gemm_i8.cpp, and native detects the real M-series
# part. The virtualized CI runners resolve native to a CPU without dotprod, so
# CI alone passes -DVLA_APPLE_MCPU=apple-m1.
set(VLA_APPLE_MCPU "native" CACHE STRING "-mcpu for Apple arm64 (CI overrides with apple-m1)")
target_compile_options(vla_flags INTERFACE -mcpu=${VLA_APPLE_MCPU})
message(STATUS "vla_simd arm64: -mcpu=${VLA_APPLE_MCPU} (apple)")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64|armv8")
# Off by default: 8% slower than the baseline on a Cortex-A76 (see above).
# The knob is here so another part can A/B it.
option(VLA_ARM_NATIVE "Build aarch64 with -mcpu=native (build host only)" OFF)
set(VLA_ARM_FLAGS "" CACHE STRING "override the aarch64 -march/-mcpu flags entirely")
# Rows of the mr x 16 fp32 GEMM register tile, chosen per core by the register
# tile selection in docs/benchmark (4: Cortex-A76).
set(VLA_NEON_MR "4" CACHE STRING "NEON fp32 GEMM register tile rows (1-6)")
target_compile_definitions(vla_flags INTERFACE TCPU_NEON_MR=${VLA_NEON_MR})
message(STATUS "vla_simd NEON register tile: ${VLA_NEON_MR}x16")
if(VLA_ARM_FLAGS)
separate_arguments(VLA_ARM_FLAG_LIST NATIVE_COMMAND "${VLA_ARM_FLAGS}")
target_compile_options(vla_flags INTERFACE ${VLA_ARM_FLAG_LIST})
message(STATUS "vla_simd aarch64: ${VLA_ARM_FLAGS} (override)")
elseif(VLA_ARM_NATIVE)
target_compile_options(vla_flags INTERFACE -mcpu=native)
message(STATUS "vla_simd aarch64: -mcpu=native")
else()
target_compile_options(vla_flags INTERFACE -march=armv8-a+simd)
message(STATUS "vla_simd aarch64: baseline armv8-a+simd")
endif()
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64|amd64|i[3-6]86")
# AVX-VNNI is deliberately not global: it would let the compiler emit vpdpbusd
# into any int8 loop and SIGILL pre-Alder-Lake / pre-Zen-4.
# src/hal/avx2/gemm_i8.cpp opts in per-function behind a cpuid check.
target_compile_options(vla_flags INTERFACE -mavx2 -mfma -mf16c)
message(STATUS "vla_simd HAL: x86-avx2")
else()
message(STATUS "vla_simd HAL: scalar (${CMAKE_SYSTEM_PROCESSOR})")
endif()
# GCC defaults to fast, Clang to on. Pin both to what the parity numbers were
# measured against.
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
target_compile_options(vla_flags INTERFACE -ffp-contract=fast)
endif()
# ---------------------------------------------------------------------------
# Sanitizers (dev only). -DVLA_SANITIZE=address,undefined
# ---------------------------------------------------------------------------
set(VLA_SANITIZE "" CACHE STRING "sanitizers to build with, e.g. address,undefined")
if(VLA_SANITIZE)
target_compile_options(vla_flags INTERFACE
-fsanitize=${VLA_SANITIZE} -fno-omit-frame-pointer -fno-sanitize-recover=all)
target_link_options(vla_flags INTERFACE -fsanitize=${VLA_SANITIZE})
message(STATUS "vla_simd sanitizers: ${VLA_SANITIZE}")
endif()
# Apple clang ships without OpenMP; point FindOpenMP at Homebrew's libomp.
if(APPLE AND NOT DEFINED OpenMP_CXX_FLAGS)
execute_process(COMMAND brew --prefix libomp OUTPUT_VARIABLE LIBOMP_PREFIX
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
if(LIBOMP_PREFIX AND EXISTS "${LIBOMP_PREFIX}/lib/libomp.dylib")
set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I${LIBOMP_PREFIX}/include")
set(OpenMP_CXX_LIB_NAMES omp)
set(OpenMP_omp_LIBRARY "${LIBOMP_PREFIX}/lib/libomp.dylib")
endif()
endif()
find_package(OpenMP)
if(OpenMP_CXX_FOUND)
target_link_libraries(vla_flags INTERFACE OpenMP::OpenMP_CXX)
else()
message(WARNING "vla_simd: OpenMP not found - the engine will run single-threaded "
"and every OMP_NUM_THREADS recipe in the docs is inert")
endif()
# MinGW (llvm-mingw): ctypes resolves a DLL's dependencies from the DLL's own
# directory, not PATH, so the toolchain runtimes are copied next to the libraries.
set(VLA_RUNTIME_DLLS "")
if(MINGW)
get_filename_component(VLA_MINGW_ROOT "${CMAKE_CXX_COMPILER}" DIRECTORY)
get_filename_component(VLA_MINGW_ROOT "${VLA_MINGW_ROOT}" DIRECTORY)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64")
set(VLA_MINGW_TRIPLE aarch64-w64-mingw32)
else()
set(VLA_MINGW_TRIPLE x86_64-w64-mingw32)
endif()
foreach(dll libomp.dll libc++.dll libunwind.dll)
find_file(VLA_DLL_${dll} ${dll} HINTS "${VLA_MINGW_ROOT}/${VLA_MINGW_TRIPLE}/bin" "${VLA_MINGW_ROOT}/bin"
NO_DEFAULT_PATH)
if(VLA_DLL_${dll})
list(APPEND VLA_RUNTIME_DLLS "${VLA_DLL_${dll}}")
endif()
endforeach()
message(STATUS "vla_simd runtime DLLs: ${VLA_RUNTIME_DLLS}")
endif()
if(APPLE AND NOT VLA_SCALAR)
# Accelerate sgemm (AMX) is the default Apple GEMM backend; TCPU_ACCEL=0 to
# A/B it. NEW_LAPACK because the legacy CBLAS entry points are deprecated
# since macOS 13.3.
target_compile_definitions(vla_flags INTERFACE TCPU_ACCELERATE ACCELERATE_NEW_LAPACK=1)
target_link_libraries(vla_flags INTERFACE "-framework Accelerate")
endif()
# ---------------------------------------------------------------------------
# HAL + shared layer (model-agnostic): reused by every VLA model
# ---------------------------------------------------------------------------
# vla.cpp GGUF reader: mounts a .gguf as the converted-directory files the
# loaders read (src/io/files.h), so every loader below links it through tcpu_ops.
add_library(tcpu_io STATIC
src/io/files.cpp
src/io/gguf.cpp
src/io/gguf_models.cpp
src/io/gguf_smolvla.cpp
src/io/gguf_turbovla.cpp
src/io/gguf_octo.cpp
src/io/gguf_vla_simd.cpp)
target_link_libraries(tcpu_io PUBLIC vla_flags)
add_library(tcpu_ops STATIC
src/hal/common/env.cpp
src/hal/common/blas.cpp
src/hal/common/gemm.cpp
src/hal/common/gemm_i8.cpp
src/hal/common/attn.cpp
src/hal/avx2/gemm.cpp
src/hal/avx2/gemm_i8.cpp
src/hal/avx2/attn.cpp
src/hal/apple/gemm.cpp
src/hal/apple/attn.cpp
src/hal/neon/gemm.cpp
src/hal/neon/gemm_i8.cpp
src/hal/neon/attn.cpp
src/hal/scalar/gemm.cpp
src/hal/scalar/attn.cpp
src/hal/common/eltwise.cpp
src/hal/common/norms.cpp
src/hal/common/conv.cpp
# nn modules over the HAL backends
src/nn/linear.cpp
src/nn/attention.cpp
src/nn/encoder.cpp
src/nn/t5_encoder.cpp
src/nn/conv.cpp
src/nn/diffusion.cpp
src/nn/linear_bf16.cpp) # load-time bf16 init, linked after the hot objects (see file header)
target_link_libraries(tcpu_ops PUBLIC vla_flags tcpu_io)
add_library(tcpu_preprocess STATIC src/preprocess/image.cpp)
target_link_libraries(tcpu_preprocess PUBLIC vla_flags)
add_library(tcpu_tokenizer STATIC src/tokenizer/tokenizer.cpp src/tokenizer/t5_tokenizer.cpp
src/tokenizer/bert_tokenizer.cpp) # byte-level BPE (Llama-3 / SmolLM2) + sentencepiece unigram (T5) + BERT WordPiece
target_link_libraries(tcpu_tokenizer PUBLIC vla_flags tcpu_io)
# ---------------------------------------------------------------------------
# Models: one library per VLA architecture under src/models/<name>/
# ---------------------------------------------------------------------------
add_library(tcpu_octo STATIC
src/models/octo/small_stem.cpp
src/models/octo/octo_transformer.cpp
src/models/octo/diffusion_head.cpp
src/models/octo/octo_model.cpp)
target_link_libraries(tcpu_octo PUBLIC tcpu_ops tcpu_tokenizer)
add_library(tcpu_act STATIC
src/models/act/resnet_backbone.cpp
src/models/act/act_transformer.cpp
src/models/act/act_model.cpp)
target_link_libraries(tcpu_act PUBLIC tcpu_ops)
add_library(tcpu_smolvla STATIC
src/models/smolvla/smollm2_lm.cpp
src/models/smolvla/siglip_vision.cpp
src/models/smolvla/action_expert.cpp
src/models/smolvla/smolvla_model.cpp)
target_link_libraries(tcpu_smolvla PUBLIC tcpu_ops tcpu_tokenizer tcpu_preprocess)
add_library(tcpu_turbovla STATIC
src/models/turbovla/dinov3_vision.cpp
src/models/turbovla/bert_text.cpp
src/models/turbovla/fusion.cpp
src/models/turbovla/action_head.cpp
src/models/turbovla/turbovla_model.cpp)
target_link_libraries(tcpu_turbovla PUBLIC tcpu_ops tcpu_tokenizer)
add_library(tcpu_impact STATIC
src/models/impact/t5_text.cpp
src/models/impact/impact_model.cpp)
target_link_libraries(tcpu_impact PUBLIC tcpu_act tcpu_ops tcpu_tokenizer)
add_library(tcpu_diffusion STATIC
src/models/diffusion/rgb_encoder.cpp
src/models/diffusion/unet1d.cpp
src/models/diffusion/scheduler.cpp
src/models/diffusion/diffusion_model.cpp)
# The ResNet-18 image backbone IS ACT's, linked rather than copied - the
# reference builds both from the same torchvision resnet18 truncated at layer4.
target_link_libraries(tcpu_diffusion PUBLIC tcpu_act tcpu_ops)
# ---------------------------------------------------------------------------
# Shared libs with the C ABI (include/vla_simd.h) for ctypes/FFI callers. Each
# is its capi translation unit over the static libs, so there is no source list
# to keep in sync. No --whole-archive: every symbol is reachable from the
# exported entry points.
# ---------------------------------------------------------------------------
function(vla_add_shared name capi_src)
add_library(${name} SHARED ${capi_src} src/capi_version.cpp)
target_link_libraries(${name} PRIVATE ${ARGN})
target_include_directories(${name} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
add_library(vla::${name} ALIAS ${name})
if(NOT SKBUILD)
set_target_properties(${name} PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION 1)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(${name} PRIVATE -fno-semantic-interposition)
endif()
if(VLA_RUNTIME_DLLS)
add_custom_command(TARGET ${name} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${VLA_RUNTIME_DLLS} $<TARGET_FILE_DIR:${name}>)
endif()
endfunction()
vla_add_shared(vla_simd_octo src/models/octo/octo_capi.cpp tcpu_octo)
vla_add_shared(vla_simd_act src/models/act/act_capi.cpp tcpu_act)
vla_add_shared(vla_simd_smolvla src/models/smolvla/smolvla_capi.cpp tcpu_smolvla)
vla_add_shared(vla_simd_turbovla src/models/turbovla/turbovla_capi.cpp tcpu_turbovla)
vla_add_shared(vla_simd_impact src/models/impact/impact_capi.cpp tcpu_impact)
vla_add_shared(vla_simd_diffusion src/models/diffusion/diffusion_capi.cpp tcpu_diffusion)
if(NOT SKBUILD AND NOT WIN32)
# vla-simd-gguf info|extract: inspect a vla.cpp GGUF, or write it out as the
# converted directory it loads as. POSIX only: extraction relies on openat and
# O_NOFOLLOW to refuse symlinked destinations.
add_executable(vla-simd-gguf src/io/gguf_tool.cpp)
target_link_libraries(vla-simd-gguf PRIVATE tcpu_io)
endif()
if(PROJECT_IS_TOP_LEVEL AND NOT SKBUILD)
enable_testing()
add_executable(test_ops tests/test_ops.cpp)
target_link_libraries(test_ops PRIVATE tcpu_ops)
add_test(NAME ops COMMAND test_ops)
set_tests_properties(ops PROPERTIES ENVIRONMENT OMP_NUM_THREADS=4)
add_executable(test_gguf tests/test_gguf.cpp)
target_link_libraries(test_gguf PRIVATE tcpu_io)
add_test(NAME gguf COMMAND test_gguf)
add_executable(test_math tests/test_math.cpp)
target_link_libraries(test_math PRIVATE tcpu_diffusion tcpu_smolvla tcpu_octo tcpu_turbovla tcpu_preprocess tcpu_tokenizer)
add_test(NAME math COMMAND test_math)
set_tests_properties(math PROPERTIES ENVIRONMENT OMP_NUM_THREADS=4)
set_tests_properties(ops gguf math PROPERTIES TIMEOUT 120)
# packed GEMM GFLOP/s on the policies' layer shapes, for register-tile selection
add_executable(bench_tile tools/bench_tile.cpp)
target_link_libraries(bench_tile PRIVATE tcpu_ops)
endif()
# ---------------------------------------------------------------------------
# Warnings, first-party targets only.
# ---------------------------------------------------------------------------
set(VLA_FIRST_PARTY
tcpu_io tcpu_ops tcpu_preprocess tcpu_tokenizer tcpu_octo tcpu_act tcpu_smolvla tcpu_turbovla
tcpu_impact tcpu_diffusion
vla_simd_octo vla_simd_act vla_simd_smolvla vla_simd_turbovla vla_simd_impact
vla_simd_diffusion)
if(TARGET test_ops)
list(APPEND VLA_FIRST_PARTY test_ops test_gguf test_math bench_tile)
endif()
if(TARGET vla-simd-gguf)
list(APPEND VLA_FIRST_PARTY vla-simd-gguf)
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
foreach(tgt IN LISTS VLA_FIRST_PARTY)
target_compile_options(${tgt} PRIVATE -Wall -Wextra -Wshadow)
endforeach()
endif()
# ---------------------------------------------------------------------------
# Link-time optimization. Off: measured neutral, and it slows debug and
# sanitizer links.
# ---------------------------------------------------------------------------
option(VLA_LTO "Link-time optimization in Release/RelWithDebInfo" OFF)
if(VLA_LTO)
include(CheckIPOSupported)
check_ipo_supported(RESULT VLA_IPO_OK OUTPUT VLA_IPO_WHY)
endif()
if(VLA_LTO AND VLA_IPO_OK AND NOT VLA_SANITIZE)
set_property(TARGET ${VLA_FIRST_PARTY}
PROPERTY INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
set_property(TARGET ${VLA_FIRST_PARTY}
PROPERTY INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON)
elseif(VLA_LTO)
message(STATUS "vla_simd: LTO unavailable (${VLA_IPO_WHY})")
endif()
if(SKBUILD)
install(TARGETS vla_simd_octo vla_simd_act vla_simd_smolvla vla_simd_turbovla
vla_simd_impact vla_simd_diffusion
LIBRARY DESTINATION vla_simd
RUNTIME DESTINATION vla_simd)
if(VLA_RUNTIME_DLLS)
install(FILES ${VLA_RUNTIME_DLLS} DESTINATION vla_simd)
endif()
return()
endif()
# ---------------------------------------------------------------------------
# Install / export for find_package(vla_simd). Only the shared libs and the C
# header are API.
# ---------------------------------------------------------------------------
include(CMakePackageConfigHelpers)
install(TARGETS vla_simd_octo vla_simd_act vla_simd_smolvla vla_simd_turbovla
vla_simd_impact vla_simd_diffusion
EXPORT vla_simdTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES include/vla_simd.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
if(VLA_RUNTIME_DLLS)
install(FILES ${VLA_RUNTIME_DLLS} DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
install(EXPORT vla_simdTargets NAMESPACE vla::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/vla_simd)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/vla_simdConfig.cmake.in"
"@PACKAGE_INIT@\ninclude(\"\${CMAKE_CURRENT_LIST_DIR}/vla_simdTargets.cmake\")\n")
configure_package_config_file(
"${CMAKE_CURRENT_BINARY_DIR}/vla_simdConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/vla_simdConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/vla_simd)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/vla_simdConfigVersion.cmake"
COMPATIBILITY SameMajorVersion)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/vla_simdConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/vla_simdConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/vla_simd)