diff --git a/CMakeLists.txt b/CMakeLists.txt index 69796c26b13b..c79e6d7609fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,34 @@ project( HOMEPAGE_URL "https://halide-lang.org" ) -enable_testing() +## +# Enable testing. When Halide_ENABLE_MEMCHECK is set (see the `valgrind` preset), +# pull in the full CTest module so that `ctest -T memcheck` reruns the whole +# registered test suite under valgrind in the build tree. Otherwise stick to the +# lighter enable_testing(). We don't probe for a valgrind binary ourselves: +# CTest's memcheck driver already locates (or complains about) MEMORYCHECK_COMMAND +# on its own, and configuring still succeeds if it's missing -- `ctest -T memcheck` +# simply fails at test time in that case. +## + +option(Halide_ENABLE_MEMCHECK + "Configure the build tree so 'ctest -T memcheck' runs tests under valgrind" OFF +) +if (Halide_ENABLE_MEMCHECK) + # Deliberately no --leak-check=full: Halide's JIT triggers one-time LLVM + # ORC allocations that valgrind reports as "possibly lost", which are noise + # rather than Halide bugs. These options catch the errors that matter + # (invalid reads/writes, use of uninitialized values). + set(MEMORYCHECK_COMMAND_OPTIONS "--error-exitcode=1 --track-origins=yes" + CACHE STRING "Options passed to valgrind by 'ctest -T memcheck'" + ) + set(MEMORYCHECK_SUPPRESSIONS_FILE "${CMAKE_CURRENT_SOURCE_DIR}/test/valgrind.supp" + CACHE FILEPATH "Valgrind suppressions file for 'ctest -T memcheck'" + ) + include(CTest) +else () + enable_testing() +endif () ## # Disable find_package(Halide) inside the build diff --git a/CMakePresets.json b/CMakePresets.json index 6692d18b300c..f453d2f64cf6 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -268,9 +268,57 @@ "WITH_TEST_FUZZ": "YES", "BUILD_SHARED_LIBS": "NO" } + }, + { + "name": "valgrind", + "inherits": "debug", + "displayName": "Valgrind (ctest -T memcheck)", + "description": "Debug build wired up so 'ctest -T memcheck' reruns the test suite under valgrind", + "cacheVariables": { + "Halide_ENABLE_MEMCHECK": "ON" + } + }, + { + "name": "sde", + "hidden": true, + "inherits": "release", + "description": "Base preset for running the test suite under Intel SDE emulation" + }, + { + "name": "avx512-cannonlake", + "inherits": "sde", + "displayName": "AVX-512 (Intel SDE, Cannon Lake)", + "description": "Run every registered test under 'sde -cnl --' to emulate a Cannon Lake AVX-512 CPU", + "cacheVariables": { + "CMAKE_CROSSCOMPILING_EMULATOR": "sde;-cnl;--" + } + }, + { + "name": "avx512-knights-landing", + "inherits": "sde", + "displayName": "AVX-512 (Intel SDE, Knights Landing)", + "description": "Run every registered test under 'sde -knl --' to emulate a Knights Landing AVX-512 CPU", + "cacheVariables": { + "CMAKE_CROSSCOMPILING_EMULATOR": "sde;-knl;--" + } } ], "buildPresets": [ + { + "name": "valgrind", + "configurePreset": "valgrind", + "displayName": "Valgrind (ctest -T memcheck)" + }, + { + "name": "avx512-cannonlake", + "configurePreset": "avx512-cannonlake", + "displayName": "AVX-512 (Intel SDE, Cannon Lake)" + }, + { + "name": "avx512-knights-landing", + "configurePreset": "avx512-knights-landing", + "displayName": "AVX-512 (Intel SDE, Knights Landing)" + }, { "name": "macOS-coverage", "configurePreset": "macOS-coverage", @@ -282,6 +330,36 @@ } ], "testPresets": [ + { + "name": "valgrind", + "configurePreset": "valgrind", + "displayName": "Valgrind (ctest -T memcheck)", + "description": "Selects the tests to run under valgrind; CTest presets can't select the '-T memcheck' action on their own, so pass it explicitly: 'ctest --preset valgrind -T memcheck'", + "filter": { + "exclude": { + "label": "no_memcheck" + } + }, + "output": { + "outputOnFailure": true + } + }, + { + "name": "avx512-cannonlake", + "configurePreset": "avx512-cannonlake", + "displayName": "AVX-512 (Intel SDE, Cannon Lake)", + "output": { + "outputOnFailure": true + } + }, + { + "name": "avx512-knights-landing", + "configurePreset": "avx512-knights-landing", + "displayName": "AVX-512 (Intel SDE, Knights Landing)", + "output": { + "outputOnFailure": true + } + }, { "name": "macOS-coverage", "configurePreset": "macOS-coverage", diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 6beaba0ba3e9..b75ba3246334 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -41,7 +41,7 @@ if (Halide_TARGET MATCHES "wasm") endif () # keep-sorted start case=no ignore_prefixes=# -# add_app(auto_viz) # TODO(#5374): missing CMake build +add_app(auto_viz) add_app(bgu) add_app(bilateral_grid) add_app(blur) @@ -59,9 +59,9 @@ add_app(harris) # add_app(HelloAndroidCamera2) # TODO(#5374): missing CMake build add_app(HelloBaremetal) # add_app(HelloiOS) # don't build HelloiOS here because it isn't universal. -# add_app(HelloPyTorch) # TODO(#5374): missing CMake build +add_app(HelloPyTorch) add_app(hexagon_benchmarks) -# add_app(hexagon_dma) # TODO(#5374): missing CMake build +add_app(hexagon_dma) add_app(hist) add_app(iir_blur) add_app(interpolate) @@ -71,11 +71,9 @@ add_app(linear_blur) add_app(local_laplacian) add_app(max_filter) add_app(nl_means) -# add_app(nn_ops) # TODO(#5374): missing CMake build add_app(onnx) add_app(resize) -# add_app(resnet_50) # TODO(#5374): missing CMake build -# add_app(simd_op_check) # TODO(#5374): missing CMake build +add_app(resnet_50) add_app(stencil_chain) add_app(unsharp) add_app(wavelet) diff --git a/apps/HelloPyTorch/CMakeLists.txt b/apps/HelloPyTorch/CMakeLists.txt new file mode 100644 index 000000000000..2b68cc1126af --- /dev/null +++ b/apps/HelloPyTorch/CMakeLists.txt @@ -0,0 +1,257 @@ +cmake_minimum_required(VERSION 3.28) +project(HelloPyTorch) + +enable_testing() + +# Set up language settings +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED YES) +set(CMAKE_CXX_EXTENSIONS NO) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# Find Halide +find_package(Halide REQUIRED) + +# This app wraps Halide-generated ops in a PyTorch C++ extension, so it needs +# Python and PyTorch. Skip cleanly if either is missing, the same way apps/onnx +# gates on its optional dependencies. +find_package(Python 3 COMPONENTS Interpreter Development.Module) +if (NOT Python_FOUND) + message(WARNING "HelloPyTorch: could NOT find Python 3; skipping") + return() +endif () + +# CUDA ops can only be exercised on a machine with a CUDA-enabled PyTorch and a +# GPU, which CPU-only CI does not have. Keep them opt-in. +option(HELLO_PYTORCH_CUDA "Build the CUDA variants of the HelloPyTorch ops" OFF) +if (HELLO_PYTORCH_CUDA) + set(torch_cuda_arg True) +else () + set(torch_cuda_arg False) +endif () + +# Pull the extension's build settings straight from torch, exactly as +# torch.utils.cpp_extension (and hence setup.py) does. We deliberately avoid +# find_package(Torch): its Caffe2 config force-enables the CUDA language and +# demands a standalone CUDA toolkit matching the wheel's version, even though +# this wrapper has no CUDA sources of its own -- which makes it fail to configure +# against a CUDA-enabled wheel unless an exactly-matching toolkit is installed. +execute_process( + COMMAND + "${Python_EXECUTABLE}" + -c "import torch.utils.cpp_extension as C; print(';'.join(C.include_paths(${torch_cuda_arg})))" + OUTPUT_VARIABLE Torch_INCLUDE_DIRS + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE Torch_probe_result + ERROR_VARIABLE Torch_probe_error +) +if (NOT Torch_probe_result EQUAL 0) + message(WARNING "HelloPyTorch: PyTorch is not usable; skipping\n${Torch_probe_error}") + return() +endif () +execute_process( + COMMAND + "${Python_EXECUTABLE}" + -c "import torch.utils.cpp_extension as C; print(';'.join(C.library_paths(${torch_cuda_arg})))" + OUTPUT_VARIABLE Torch_LIBRARY_DIRS + OUTPUT_STRIP_TRAILING_WHITESPACE +) +execute_process( + COMMAND + "${Python_EXECUTABLE}" -c "import torch; print(1 if torch._C._GLIBCXX_USE_CXX11_ABI else 0)" + OUTPUT_VARIABLE Torch_CXX11_ABI + OUTPUT_STRIP_TRAILING_WHITESPACE +) +message(STATUS "HelloPyTorch: using Torch headers from ${Torch_INCLUDE_DIRS}") + +# Resolve the torch shared libraries the extension links against (the set +# CppExtension/CUDAExtension link by default). +set(torch_lib_names c10 torch torch_cpu torch_python) +if (HELLO_PYTORCH_CUDA) + list(APPEND torch_lib_names torch_cuda c10_cuda) + find_package(CUDAToolkit REQUIRED) +endif () +set(torch_libs "") +foreach (name IN LISTS torch_lib_names) + find_library(Torch_${name}_LIBRARY NAMES ${name} PATHS ${Torch_LIBRARY_DIRS} REQUIRED) + list(APPEND torch_libs "${Torch_${name}_LIBRARY}") +endforeach () + +## +# Halide generator and ops +## + +add_halide_generator(add.generator SOURCES src/add_generator.cpp) + +# Type-parameter sets, mirroring the Makefile. +set(add_types_f32 input_a.type=float32 input_b.type=float32 output.type=float32) +set(add_types_f64 input_a.type=float64 input_b.type=float64 output.type=float64) +set(add_grad_types_f32 + input_a.type=float32 input_b.type=float32 d_input_a.type=float32 d_input_b.type=float32 + d_output.type=float32 +) +set(add_grad_types_f64 + input_a.type=float64 input_b.type=float64 d_input_a.type=float64 d_input_b.type=float64 + d_output.type=float64 +) + +# List of ops exposed by the extension, and the PyTorch wrapper headers they +# generate; both are populated by add_pytorch_op() as libraries are added. +set(op_libs "") +set(op_wrapper_headers "") + +# add_halide_library()'s PYTORCH_WRAPPER argument names a variable to receive +# the generated wrapper header's path, not a filename -- see add_halide_library's +# docs. This wraps it so callers don't have to manage that variable (or +# op_libs/op_wrapper_headers) by hand. +function(add_pytorch_op TARGET) + add_halide_library(${TARGET} ${ARGN} PYTORCH_WRAPPER "${TARGET}_pytorch_wrapper") + list(APPEND op_libs "${TARGET}") + list(APPEND op_wrapper_headers "${${TARGET}_pytorch_wrapper}") + set(op_libs "${op_libs}" PARENT_SCOPE) + set(op_wrapper_headers "${op_wrapper_headers}" PARENT_SCOPE) +endfunction() + +# CPU ops ------------------------------------------------------------------- + +# Forward op (add) and hand-written gradient (add_grad). +add_pytorch_op(add_float32 FROM add.generator GENERATOR add PARAMS ${add_types_f32}) +add_pytorch_op(add_float64 FROM add.generator GENERATOR add PARAMS ${add_types_f64}) +add_pytorch_op(add_grad_float32 FROM add.generator GENERATOR add_grad PARAMS ${add_grad_types_f32}) +add_pytorch_op(add_grad_float64 FROM add.generator GENERATOR add_grad PARAMS ${add_grad_types_f64}) + +# Halide-autodifferentiated gradient (add with -d 1 + Li2018 autoscheduler). +add_pytorch_op( + add_halidegrad_float32 + FROM add.generator + GENERATOR add + GRADIENT_DESCENT + AUTOSCHEDULER Halide::Li2018 + PARAMS ${add_types_f32} +) +add_pytorch_op( + add_halidegrad_float64 + FROM add.generator + GENERATOR add + GRADIENT_DESCENT + AUTOSCHEDULER Halide::Li2018 + PARAMS ${add_types_f64} +) + +# CUDA ops ------------------------------------------------------------------ + +if (HELLO_PYTORCH_CUDA) + # The PyTorch CUDA wrapper requires the user_context feature so Halide's and + # PyTorch's GPU memory managers can talk to each other. + set(cuda_features cuda cuda_capability_61 user_context) + + add_pytorch_op( + add_cuda_float32 + FROM add.generator + GENERATOR add + FEATURES ${cuda_features} + PARAMS ${add_types_f32} + ) + add_pytorch_op( + add_cuda_float64 + FROM add.generator + GENERATOR add + FEATURES ${cuda_features} + PARAMS ${add_types_f64} + ) + add_pytorch_op( + add_grad_cuda_float32 + FROM add.generator + GENERATOR add_grad + FEATURES ${cuda_features} + PARAMS ${add_grad_types_f32} + ) + add_pytorch_op( + add_grad_cuda_float64 + FROM add.generator + GENERATOR add_grad + FEATURES ${cuda_features} + PARAMS ${add_grad_types_f64} + ) + add_pytorch_op( + add_halidegrad_cuda_float32 + FROM add.generator + GENERATOR add + GRADIENT_DESCENT + AUTOSCHEDULER Halide::Li2018 + FEATURES ${cuda_features} + PARAMS ${add_types_f32} + ) + add_pytorch_op( + add_halidegrad_cuda_float64 + FROM add.generator + GENERATOR add + GRADIENT_DESCENT + AUTOSCHEDULER Halide::Li2018 + FEATURES ${cuda_features} + PARAMS ${add_types_f64} + ) +endif () + +## +# Synthesize the pybind wrapper (equivalent to setup.py's generate_pybind_wrapper) +## + +set(wrapper "#include \"torch/extension.h\"\n") +if (HELLO_PYTORCH_CUDA) + string(APPEND wrapper "#include \"HalidePyTorchCudaHelpers.h\"\n") +endif () +string(APPEND wrapper "#include \"HalidePyTorchHelpers.h\"\n") +foreach (header IN LISTS op_wrapper_headers) + cmake_path(GET header FILENAME header_filename) + string(APPEND wrapper "#include \"${header_filename}\"\n") +endforeach () +string(APPEND wrapper "\nPYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {\n") +foreach (op IN LISTS op_libs) + string(APPEND wrapper + " m.def(\"${op}\", &${op}_th_, \"PyTorch wrapper of the Halide pipeline ${op}\");\n" + ) +endforeach () +string(APPEND wrapper "}\n") + +set(wrapper_cpp "${CMAKE_CURRENT_BINARY_DIR}/pybind_wrapper.cpp") +file(GENERATE OUTPUT "${wrapper_cpp}" CONTENT "${wrapper}") + +## +# The Python extension module +## + +Python_add_library(halide_ops MODULE WITH_SOABI "${wrapper_cpp}") +set_target_properties(halide_ops PROPERTIES OUTPUT_NAME halide_ops) +target_compile_definitions( + halide_ops + PRIVATE TORCH_EXTENSION_NAME=halide_ops _GLIBCXX_USE_CXX11_ABI=${Torch_CXX11_ABI} +) +target_include_directories(halide_ops PRIVATE ${Torch_INCLUDE_DIRS}) +target_link_libraries(halide_ops PRIVATE ${op_libs} ${torch_libs}) +# Let the freshly built extension find the torch shared libraries at load time. +set_target_properties(halide_ops PROPERTIES BUILD_RPATH "${Torch_LIBRARY_DIRS}") +if (HELLO_PYTORCH_CUDA) + target_link_libraries(halide_ops PRIVATE CUDA::cuda_driver) +endif () + +## +# Test: run test.py against the freshly built extension. +## +# `python -m unittest` puts the working directory on sys.path, so running from +# the source dir makes `modules` and `test` importable; the built extension is +# picked up via PYTHONPATH. +add_test( + NAME hello_pytorch + COMMAND ${Python_EXECUTABLE} -m unittest test + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) +set_tests_properties( + hello_pytorch + PROPERTIES + LABELS HelloPyTorch + ENVIRONMENT "PYTHONPATH=$" + PASS_REGULAR_EXPRESSION "OK" + SKIP_REGULAR_EXPRESSION "\\[SKIP\\]" + TIMEOUT 300 +) diff --git a/apps/auto_viz/CMakeLists.txt b/apps/auto_viz/CMakeLists.txt new file mode 100644 index 000000000000..3b8b7030a5f1 --- /dev/null +++ b/apps/auto_viz/CMakeLists.txt @@ -0,0 +1,70 @@ +cmake_minimum_required(VERSION 3.28) +project(auto_viz) + +enable_testing() + +# Set up language settings +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED YES) +set(CMAKE_CXX_EXTENSIONS NO) + +# Find Halide +find_package(Halide REQUIRED) + +# Generator +add_halide_generator(auto_viz_demo.generator SOURCES auto_viz_demo_generator.cpp) + +# Filters: one library per (schedule, direction) pair, all built with tracing +# enabled so the pipelines can be visualized with HalideTraceViz. +set(variants "") +foreach (schedule IN ITEMS naive lessnaive complex) + add_halide_library( + auto_viz_demo_${schedule}_up + FROM auto_viz_demo.generator + GENERATOR auto_viz_demo + FEATURES trace_all + PARAMS schedule_type=${schedule} upsample=true + ) + add_halide_library( + auto_viz_demo_${schedule}_down + FROM auto_viz_demo.generator + GENERATOR auto_viz_demo + FEATURES trace_all + PARAMS schedule_type=${schedule} upsample=false + ) + list(APPEND variants auto_viz_demo_${schedule}_up auto_viz_demo_${schedule}_down) +endforeach () + +# Main executable +add_executable(auto_viz_demo auto_viz_demo.cpp) +target_link_libraries(auto_viz_demo PRIVATE Halide::ImageIO ${variants}) + +# Test that the app actually works! + +if (WIN32) + set(_auto_viz_trace_sink "NUL") +else () + set(_auto_viz_trace_sink "/dev/null") +endif () + +set(IMAGE ${CMAKE_CURRENT_LIST_DIR}/../images/rgb_small.png) +if (EXISTS ${IMAGE}) + configure_file(${IMAGE} rgb_small.png COPYONLY) + foreach (schedule IN ITEMS naive lessnaive complex) + add_test( + NAME auto_viz_demo_${schedule}_up + COMMAND auto_viz_demo rgb_small.png out_${schedule}_up.png -s ${schedule} -f 4.0 + ) + add_test( + NAME auto_viz_demo_${schedule}_down + COMMAND auto_viz_demo rgb_small.png out_${schedule}_down.png -s ${schedule} -f 0.25 + ) + set_tests_properties( + auto_viz_demo_${schedule}_up auto_viz_demo_${schedule}_down + PROPERTIES + LABELS auto_viz + ENVIRONMENT "HL_TRACE_FILE=${_auto_viz_trace_sink}" + SKIP_REGULAR_EXPRESSION "\\[SKIP\\]" + ) + endforeach () +endif () diff --git a/apps/auto_viz/auto_viz_demo.cpp b/apps/auto_viz/auto_viz_demo.cpp index 3129a8ed8667..180c0e13aaad 100644 --- a/apps/auto_viz/auto_viz_demo.cpp +++ b/apps/auto_viz/auto_viz_demo.cpp @@ -61,7 +61,7 @@ int main(int argc, char **argv) { &auto_viz_demo_complex_up}, {&auto_viz_demo_naive_down, &auto_viz_demo_lessnaive_down, - &auto_viz_demo_complex_up}, + &auto_viz_demo_complex_down}, }; int schedule_idx = 0; diff --git a/apps/auto_viz/auto_viz_demo_generator.cpp b/apps/auto_viz/auto_viz_demo_generator.cpp index 81082294ac90..fc670f2a8ac8 100644 --- a/apps/auto_viz/auto_viz_demo_generator.cpp +++ b/apps/auto_viz/auto_viz_demo_generator.cpp @@ -65,7 +65,7 @@ class AutoVizDemo : public Halide::Generator { Expr beginx = cast(ceil(sourcex - kernel_radius)); Expr beginy = cast(ceil(sourcey - kernel_radius)); - RDom r(0, kernel_taps); + RDom r(0, cast(kernel_taps)); auto kernel = [](Expr x) -> Expr { Expr xx = abs(x); diff --git a/apps/bilateral_grid/CMakeLists.txt b/apps/bilateral_grid/CMakeLists.txt index 4450afa19f02..5a1d3980a020 100644 --- a/apps/bilateral_grid/CMakeLists.txt +++ b/apps/bilateral_grid/CMakeLists.txt @@ -22,6 +22,7 @@ add_halide_generator( add_halide_library( bilateral_grid FROM bilateral_grid.generator + REGISTRATION bilateral_grid_registration STMT bilateral_grid_STMT SCHEDULE bilateral_grid_SCHEDULE ) @@ -63,3 +64,13 @@ if (EXISTS ${IMAGE}) SKIP_REGULAR_EXPRESSION "\\[SKIP\\]" ) endif () + +# RunGen-based benchmark. Run every app benchmark at once with +# `ctest -L benchmark_apps`. +add_executable(bilateral_grid_rungen ${bilateral_grid_registration}) +target_link_libraries(bilateral_grid_rungen PRIVATE Halide::RunGenMain bilateral_grid) +add_test( + NAME bilateral_grid_benchmark + COMMAND bilateral_grid_rungen --benchmarks=all --estimate_all --parsable_output +) +set_tests_properties(bilateral_grid_benchmark PROPERTIES LABELS "bilateral_grid;benchmark_apps") diff --git a/apps/camera_pipe/CMakeLists.txt b/apps/camera_pipe/CMakeLists.txt index 6e441b5302bf..c44c16121d94 100644 --- a/apps/camera_pipe/CMakeLists.txt +++ b/apps/camera_pipe/CMakeLists.txt @@ -19,7 +19,7 @@ add_halide_generator( ) # Filters -add_halide_library(camera_pipe FROM camera_pipe.generator) +add_halide_library(camera_pipe FROM camera_pipe.generator REGISTRATION camera_pipe_registration) add_halide_library( camera_pipe_auto_schedule FROM camera_pipe.generator @@ -53,3 +53,13 @@ if (EXISTS ${IMAGE}) SKIP_REGULAR_EXPRESSION "\\[SKIP\\]" ) endif () + +# RunGen-based benchmark. Run every app benchmark at once with +# `ctest -L benchmark_apps`. +add_executable(camera_pipe_rungen ${camera_pipe_registration}) +target_link_libraries(camera_pipe_rungen PRIVATE Halide::RunGenMain camera_pipe) +add_test( + NAME camera_pipe_benchmark + COMMAND camera_pipe_rungen --benchmarks=all --estimate_all --parsable_output +) +set_tests_properties(camera_pipe_benchmark PROPERTIES LABELS "camera_pipe;benchmark_apps") diff --git a/apps/hexagon_dma/CMakeLists.txt b/apps/hexagon_dma/CMakeLists.txt new file mode 100644 index 000000000000..2fd1c6145a09 --- /dev/null +++ b/apps/hexagon_dma/CMakeLists.txt @@ -0,0 +1,161 @@ +cmake_minimum_required(VERSION 3.28) +project(hexagon_dma) + +enable_testing() + +# Set up language settings +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED YES) +set(CMAKE_CXX_EXTENSIONS NO) + +# Find Halide +find_package(Halide REQUIRED) + +# The pipelines use the hexagon_dma scheduling directives, which require the +# hexagon_dma target feature. They are exercised on the host by linking against +# mock_dma_implementation.cpp, which stands in for the real Hexagon DMA driver. + +# These pipelines don't touch any GPU/device APIs, so always build them for a +# plain "host-hexagon_dma" target rather than inheriting the ambient +# Halide_TARGET. CI configures Halide_TARGET to things like host-cuda or +# host-vulkan-... to exercise GPU apps, and combining hexagon_dma with an +# unrelated device API breaks target auto-detection (e.g. querying the host's +# CUDA compute capability JIT-compiles the whole combined runtime, which then +# fails to link against hexagon_dma's driver-only symbols). + +# Build the full RO+RW schedule matrix when requested (mirrors the Makefile's +# SCHEDULE_ALL). By default we build only the RW async/split/split_async +# schedules, which is what `make all` produces. +option(HEXAGON_DMA_SCHEDULE_ALL "Build the full RO+RW schedule matrix" OFF) + +set(rw_schedules async split split_async) +set(ro_schedules "") +if (HEXAGON_DMA_SCHEDULE_ALL) + list(PREPEND rw_schedules basic fold) + set(ro_schedules basic fold async split split_async) +endif () + +# The pipeline function names use "basic", but the generator's schedule +# GeneratorParam spells that value "none". +function(_dma_sched_value out label) + if (label STREQUAL "basic") + set(${out} none PARENT_SCOPE) + else () + set(${out} ${label} PARENT_SCOPE) + endif () +endfunction() + +## +# YUV pipelines (nv12 = 8-bit, p010 = 16-bit) +## + +add_halide_generator(pipeline_yuv_linear_basic.generator SOURCES pipeline_yuv_linear_basic.cpp) + +set(nv12_types input_y.type=uint8 input_uv.type=uint8 output_y.type=uint8 output_uv.type=uint8) +set(p010_types input_y.type=uint16 input_uv.type=uint16 output_y.type=uint16 output_uv.type=uint16) + +set(yuv_libs "") +foreach (type_name IN ITEMS nv12 p010) + foreach (label IN LISTS rw_schedules) + _dma_sched_value(sched ${label}) + add_halide_library( + pipeline_${type_name}_linear_rw_${label} + FROM pipeline_yuv_linear_basic.generator + GENERATOR pipeline_yuv_linear_basic + TARGETS host + FEATURES hexagon_dma + PARAMS schedule=${sched} use_dma_for_output=true ${${type_name}_types} + ) + list(APPEND yuv_libs pipeline_${type_name}_linear_rw_${label}) + endforeach () + foreach (label IN LISTS ro_schedules) + _dma_sched_value(sched ${label}) + add_halide_library( + pipeline_${type_name}_linear_ro_${label} + FROM pipeline_yuv_linear_basic.generator + GENERATOR pipeline_yuv_linear_basic + TARGETS host + FEATURES hexagon_dma + PARAMS schedule=${sched} use_dma_for_output=false ${${type_name}_types} + ) + list(APPEND yuv_libs pipeline_${type_name}_linear_ro_${label}) + endforeach () +endforeach () + +## +# RAW interleaved pipeline (fixed uint8) +## + +add_halide_generator( + pipeline_raw_linear_interleaved_basic.generator + SOURCES pipeline_raw_linear_interleaved_basic.cpp +) + +set(raw_libs "") +foreach (label IN LISTS rw_schedules) + _dma_sched_value(sched ${label}) + add_halide_library( + pipeline_raw_linear_interleaved_rw_${label} + FROM pipeline_raw_linear_interleaved_basic.generator + GENERATOR pipeline_raw_linear_interleaved_basic + TARGETS host + FEATURES hexagon_dma + PARAMS schedule=${sched} use_dma_for_output=true + ) + list(APPEND raw_libs pipeline_raw_linear_interleaved_rw_${label}) +endforeach () +foreach (label IN LISTS ro_schedules) + _dma_sched_value(sched ${label}) + add_halide_library( + pipeline_raw_linear_interleaved_ro_${label} + FROM pipeline_raw_linear_interleaved_basic.generator + GENERATOR pipeline_raw_linear_interleaved_basic + TARGETS host + FEATURES hexagon_dma + PARAMS schedule=${sched} use_dma_for_output=false + ) + list(APPEND raw_libs pipeline_raw_linear_interleaved_ro_${label}) +endforeach () + +## +# Processing executables (run on the host against the mock DMA driver) +## + +# mock_dma_implementation.cpp stands in for the real Hexagon DMA driver by +# providing weak definitions of its entry points, using the GCC/Clang-only +# WEAK == __attribute__((weak)) from src/runtime/hexagon_dma_pool.h and +# src/runtime/mini_hexagon_dma.h. MSVC has no equivalent for these headers' +# use of __attribute__, so just skip the mock-driver-backed executables and +# tests there; the pipelines themselves still build fine. +if (NOT MSVC) + add_executable( + process_yuv_linear_basic process_yuv_linear_basic.cpp mock_dma_implementation.cpp + ) + target_link_libraries(process_yuv_linear_basic PRIVATE Halide::Tools ${yuv_libs}) + + add_executable( + process_raw_linear_interleaved_basic + process_raw_linear_interleaved_basic.cpp + mock_dma_implementation.cpp + ) + target_link_libraries(process_raw_linear_interleaved_basic PRIVATE Halide::Tools ${raw_libs}) + + if (HEXAGON_DMA_SCHEDULE_ALL) + target_compile_definitions(process_yuv_linear_basic PRIVATE SCHEDULE_ALL) + target_compile_definitions(process_raw_linear_interleaved_basic PRIVATE SCHEDULE_ALL) + endif () + + # Test that the apps actually work! + add_test(NAME process_yuv_linear_basic COMMAND process_yuv_linear_basic 256 128 async rw nv12) + add_test( + NAME process_raw_linear_interleaved_basic + COMMAND process_raw_linear_interleaved_basic 256 128 async rw + ) + set_tests_properties( + process_yuv_linear_basic process_raw_linear_interleaved_basic + PROPERTIES + LABELS hexagon_dma + PASS_REGULAR_EXPRESSION "Success!" + SKIP_REGULAR_EXPRESSION "\\[SKIP\\]" + ) +endif () diff --git a/apps/hexagon_dma/pipeline_raw_linear_interleaved_basic.cpp b/apps/hexagon_dma/pipeline_raw_linear_interleaved_basic.cpp index ffc1b97d019c..e5c410fdbf9c 100644 --- a/apps/hexagon_dma/pipeline_raw_linear_interleaved_basic.cpp +++ b/apps/hexagon_dma/pipeline_raw_linear_interleaved_basic.cpp @@ -42,7 +42,7 @@ class DmaPipeline : public Generator { // Do some common scheduling here. if (use_dma_for_output) { - output.copy_to_device(); + output.copy_to_device(DeviceAPI::HexagonDma); } output diff --git a/apps/hexagon_dma/pipeline_yuv_linear_basic.cpp b/apps/hexagon_dma/pipeline_yuv_linear_basic.cpp index 00a1ef349ddc..e53030a5cd06 100644 --- a/apps/hexagon_dma/pipeline_yuv_linear_basic.cpp +++ b/apps/hexagon_dma/pipeline_yuv_linear_basic.cpp @@ -54,8 +54,8 @@ class DmaPipeline : public Generator { // Do some common scheduling here. if (use_dma_for_output) { - output_y.copy_to_device(); - output_uv.copy_to_device(); + output_y.copy_to_device(DeviceAPI::HexagonDma); + output_uv.copy_to_device(DeviceAPI::HexagonDma); } output_y diff --git a/apps/hexagon_dma/process_yuv_linear_basic.cpp b/apps/hexagon_dma/process_yuv_linear_basic.cpp index fb9cc38ad94c..800dfd08a7f3 100644 --- a/apps/hexagon_dma/process_yuv_linear_basic.cpp +++ b/apps/hexagon_dma/process_yuv_linear_basic.cpp @@ -119,8 +119,8 @@ inline int process_pipeline(T const &type, const int width, const int height, // Setup Halide input buffer with the test buffer Halide::Runtime::Buffer input_validation(data_in, width, height, 2); Halide::Runtime::Buffer input(nullptr, width, (3 * height) / 2); - Halide::Runtime::Buffer input_y = input.cropped(1, 0, height); // Luma plane only - Halide::Runtime::Buffer input_uv = input.cropped(1, height, height / 2); // Chroma plane only, with reduced height + Halide::Runtime::Buffer input_y = input.cropped(1, 0, height); // Luma plane only + Halide::Runtime::Buffer input_uv = input.cropped(1, height, height / 2); // Chroma plane only, with reduced height // describe the UV interleaving for 4:2:0 format input_uv.embed(2, 0); @@ -131,8 +131,8 @@ inline int process_pipeline(T const &type, const int width, const int height, // Setup Halide output buffer Halide::Runtime::Buffer output(width, (3 * height) / 2); - Halide::Runtime::Buffer output_y = output.cropped(1, 0, height); // Luma plane only - Halide::Runtime::Buffer output_uv = output.cropped(1, height, (height / 2)); // Chroma plane only, with reduced height + Halide::Runtime::Buffer output_y = output.cropped(1, 0, height); // Luma plane only + Halide::Runtime::Buffer output_uv = output.cropped(1, height, (height / 2)); // Chroma plane only, with reduced height // describe the UV interleaving for 4:2:0 format output_uv.embed(2, 0); diff --git a/apps/lens_blur/CMakeLists.txt b/apps/lens_blur/CMakeLists.txt index 1cb9098de80a..8219f17c2124 100644 --- a/apps/lens_blur/CMakeLists.txt +++ b/apps/lens_blur/CMakeLists.txt @@ -15,7 +15,7 @@ find_package(Halide REQUIRED) add_halide_generator(lens_blur.generator SOURCES lens_blur_generator.cpp) # Filters -add_halide_library(lens_blur FROM lens_blur.generator) +add_halide_library(lens_blur FROM lens_blur.generator REGISTRATION lens_blur_registration) add_halide_library( lens_blur_auto_schedule FROM lens_blur.generator @@ -59,3 +59,13 @@ if (EXISTS ${IMAGE}) ) endif () endif () + +# RunGen-based benchmark. Run every app benchmark at once with +# `ctest -L benchmark_apps`. +add_executable(lens_blur_rungen ${lens_blur_registration}) +target_link_libraries(lens_blur_rungen PRIVATE Halide::RunGenMain lens_blur) +add_test( + NAME lens_blur_benchmark + COMMAND lens_blur_rungen --benchmarks=all --estimate_all --parsable_output +) +set_tests_properties(lens_blur_benchmark PROPERTIES LABELS "lens_blur;benchmark_apps") diff --git a/apps/local_laplacian/CMakeLists.txt b/apps/local_laplacian/CMakeLists.txt index d4caaf5673de..bb0dcaf31b36 100644 --- a/apps/local_laplacian/CMakeLists.txt +++ b/apps/local_laplacian/CMakeLists.txt @@ -19,7 +19,11 @@ add_halide_generator( ) # Filters -add_halide_library(local_laplacian FROM local_laplacian.generator) +add_halide_library( + local_laplacian + FROM local_laplacian.generator + REGISTRATION local_laplacian_registration +) add_halide_library( local_laplacian_auto_schedule FROM local_laplacian.generator @@ -55,3 +59,13 @@ if (EXISTS ${IMAGE}) SKIP_REGULAR_EXPRESSION "\\[SKIP\\]" ) endif () + +# RunGen-based benchmark. Run every app benchmark at once with +# `ctest -L benchmark_apps`. +add_executable(local_laplacian_rungen ${local_laplacian_registration}) +target_link_libraries(local_laplacian_rungen PRIVATE Halide::RunGenMain local_laplacian) +add_test( + NAME local_laplacian_benchmark + COMMAND local_laplacian_rungen --benchmarks=all --estimate_all --parsable_output +) +set_tests_properties(local_laplacian_benchmark PROPERTIES LABELS "local_laplacian;benchmark_apps") diff --git a/apps/nl_means/CMakeLists.txt b/apps/nl_means/CMakeLists.txt index f71f180fa0de..59a0d338aa7a 100644 --- a/apps/nl_means/CMakeLists.txt +++ b/apps/nl_means/CMakeLists.txt @@ -15,7 +15,7 @@ find_package(Halide REQUIRED) add_halide_generator(nl_means.generator SOURCES nl_means_generator.cpp) # Filters -add_halide_library(nl_means FROM nl_means.generator) +add_halide_library(nl_means FROM nl_means.generator REGISTRATION nl_means_registration) add_halide_library( nl_means_auto_schedule FROM nl_means.generator @@ -40,3 +40,13 @@ if (EXISTS ${IMAGE}) SKIP_REGULAR_EXPRESSION "\\[SKIP\\]" ) endif () + +# RunGen-based benchmark. Run every app benchmark at once with +# `ctest -L benchmark_apps`. +add_executable(nl_means_rungen ${nl_means_registration}) +target_link_libraries(nl_means_rungen PRIVATE Halide::RunGenMain nl_means) +add_test( + NAME nl_means_benchmark + COMMAND nl_means_rungen --benchmarks=all --estimate_all --parsable_output +) +set_tests_properties(nl_means_benchmark PROPERTIES LABELS "nl_means;benchmark_apps") diff --git a/apps/resnet_50/CMakeLists.txt b/apps/resnet_50/CMakeLists.txt new file mode 100644 index 000000000000..3c4fcaca5a96 --- /dev/null +++ b/apps/resnet_50/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.28) +project(resnet_50) + +enable_testing() + +# Set up language settings +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED YES) +set(CMAKE_CXX_EXTENSIONS NO) + +# Find Halide +find_package(Halide REQUIRED) + +# Generator +add_halide_generator(resnet50.generator SOURCES Resnet50Generator.cpp) + +# Filter +add_halide_library(resnet50 FROM resnet50.generator) + +# Main executable +add_executable(resnet_50_process process.cpp) +target_link_libraries(resnet_50_process PRIVATE Halide::ImageIO Halide::Tools resnet50) + +# NOTE: Like the Makefile, we only test that resnet_50 builds. Actually running +# process requires the PyTorch/torchvision weights produced by load_weights.py, +# which we don't want to pull in as a build/test dependency here. diff --git a/apps/simd_op_check/Makefile b/apps/simd_op_check/Makefile deleted file mode 100644 index 6e2f51725fd2..000000000000 --- a/apps/simd_op_check/Makefile +++ /dev/null @@ -1,45 +0,0 @@ -include ../support/Makefile.inc - -CXX-hexagon-32-noos-hvx_128 ?= $(HL_HEXAGON_TOOLS)/bin/hexagon-clang++ - -CXXFLAGS-hexagon-32-noos-hvx_128 ?= -mhvx -mhvx-length=128B -G0 - -LDFLAGS-hexagon-32-noos-hvx_128 ?= -L../../src/runtime/hexagon_remote/bin/v60/ -lsim_qurt - -all: \ - $(BIN)/driver-host \ - $(BIN)/driver-arm-64-android \ - $(BIN)/driver-arm-32-android \ - $(BIN)/driver-hexagon-32-noos-hvx_128 \ - -hvx_128: $(BIN)/driver-hexagon-32-noos-hvx_128 - -arm_32: $(BIN)/driver-arm-32-android -arm_64: $(BIN)/driver-arm-64-android - -host: $(BIN)/driver-host - -$(BIN)/hexagon-32-noos-%/filters.h: - @mkdir -p $(@D) - make -C ../../ bin/correctness_simd_op_check_hvx - cd $(BIN)/hexagon-32-noos-$* && HL_TARGET=hexagon-32-noos-$* LD_LIBRARY_PATH=../../../../bin:$$LD_LIBRARY_PATH ../../../../bin/correctness_simd_op_check_hvx - cat $(BIN)/hexagon-32-noos-$*/test_*.h > $(BIN)/hexagon-32-noos-$*/filter_headers.h - echo "filter filters[] = {" > $(BIN)/hexagon-32-noos-$*/filters.h - cd $(BIN)/hexagon-32-noos-$*; for f in test_*.h; do n=$${f/.h/}; echo '{"'$${n}'", &'$${n}'},'; done >> filters.h - echo '{NULL, NULL}};' >> $(BIN)/hexagon-32-noos-$*/filters.h - -$(BIN)/%/filters.h: - @mkdir -p $(@D) - make -C ../../ bin/correctness_simd_op_check - cd $(BIN)/$* && HL_TARGET=$* LD_LIBRARY_PATH=../../../../bin:$$LD_LIBRARY_PATH ../../../../bin/correctness_simd_op_check - cat $(BIN)/$*/test_*.h > $(BIN)/$*/filter_headers.h - echo "filter filters[] = {" > $(BIN)/$*/filters.h - cd $(BIN)/$*; for f in test_*.h; do n=$${f/.h/}; echo '{"'$${n}'", &'$${n}'},'; done >> filters.h - echo '{NULL, NULL}};' >> $(BIN)/$*/filters.h - -$(BIN)/driver-%: driver.cpp $(BIN)/%/filters.h - @mkdir -p $(@D) - $(CXX-$*) $(CXXFLAGS-$*) -I ../../include $(OPTIMIZE) -I $(BIN)/$* driver.cpp $(BIN)/$*/test_*.o $(BIN)/$*/simd_op_check_runtime.o -o $@ $(LDFLAGS-$) - -clean: - rm -rf $(BIN) diff --git a/apps/simd_op_check/driver.cpp b/apps/simd_op_check/driver.cpp deleted file mode 100644 index fc29c0488c4d..000000000000 --- a/apps/simd_op_check/driver.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "filter_headers.h" -#include -#include -#include - -#ifndef __APPLE__ -extern "C" void *memalign(size_t alignment, size_t size); -#endif - -struct filter { - const char *name; - int (*fn)(halide_buffer_t *, // float32 - halide_buffer_t *, // float64 - halide_buffer_t *, // float16 - halide_buffer_t *, // bfloat16 - halide_buffer_t *, // int8 - halide_buffer_t *, // uint8 - halide_buffer_t *, // int16 - halide_buffer_t *, // uint16 - halide_buffer_t *, // int32 - halide_buffer_t *, // uint32 - halide_buffer_t *, // int64 - halide_buffer_t *, // uint64 - halide_buffer_t *); // output -}; - -template -T rand_value() { - return (T)(rand() * 0.125) - 100; -} - -// Even on android, we want errors to stdout -extern "C" void halide_print(void *, const char *msg) { - printf("%s\n", msg); -} - -template -halide_buffer_t make_buffer(int w, int h, halide_type_t halide_type) { - T *mem = NULL; -#ifdef __APPLE__ - // memalign() isn't present on OSX, but posix_memalign is - int result = posix_memalign((void **)&mem, 128, w * h * sizeof(T)); - if (result != 0 || mem == NULL) { - exit(1); - } -#else - mem = (T *)memalign(128, w * h * sizeof(T)); - if (mem == NULL) { - exit(1); - } -#endif - - halide_buffer_t buf = {0}; - buf.dim = (halide_dimension_t *)malloc(sizeof(halide_dimension_t) * 2); - buf.host = (uint8_t *)mem; - buf.dim[0].extent = w; - buf.dim[1].extent = h; - buf.type = halide_type; - buf.dim[0].stride = 1; - buf.dim[1].stride = w; - buf.dim[0].min = -128; - buf.dim[1].min = 0; - - for (int i = 0; i < w * h; i++) { - mem[i] = rand_value(); - } - - return buf; -} - -#include "filters.h" - -int main(int argc, char **argv) { - const int W = 1024, H = 128; - bool error = false; - // Make some input buffers - halide_buffer_t bufs[] = { - make_buffer(W, H, halide_type_of()), - make_buffer(W, H, halide_type_of()), - make_buffer(W, H, halide_type_t(halide_type_float, 16)), - make_buffer(W, H, halide_type_t(halide_type_bfloat, 16)), - make_buffer(W, H, halide_type_of()), - make_buffer(W, H, halide_type_of()), - make_buffer(W, H, halide_type_of()), - make_buffer(W, H, halide_type_of()), - make_buffer(W, H, halide_type_of()), - make_buffer(W, H, halide_type_of()), - make_buffer(W, H, halide_type_of()), - make_buffer(W, H, halide_type_of())}; - - halide_buffer_t out = make_buffer(1, 1, halide_type_of()); - - double *out_value = (double *)(out.host); - - for (int i = 0; filters[i].fn; i++) { - filter f = filters[i]; - printf("Testing %s\n", f.name); - f.fn(bufs + 0, - bufs + 1, - bufs + 2, - bufs + 3, - bufs + 4, - bufs + 5, - bufs + 6, - bufs + 7, - bufs + 8, - bufs + 9, - bufs + 10, - bufs + 11, - &out); - if (*out_value) { - printf("Error: %f\n", *out_value); - error = true; - } - } - - for (int i = 0; i < sizeof(bufs) / sizeof(halide_buffer_t); i++) { - free(bufs[i].dim); - free(bufs[i].host); - } - free(out.dim); - free(out.host); - - if (!error) { - printf("Success!\n"); - return 0; - } else { - printf("Error occurred\n"); - return -1; - } -} diff --git a/apps/stencil_chain/CMakeLists.txt b/apps/stencil_chain/CMakeLists.txt index 5f18b5f95a3f..938a19bd20da 100644 --- a/apps/stencil_chain/CMakeLists.txt +++ b/apps/stencil_chain/CMakeLists.txt @@ -15,7 +15,11 @@ find_package(Halide REQUIRED) add_halide_generator(stencil_chain.generator SOURCES stencil_chain_generator.cpp) # Filters -add_halide_library(stencil_chain FROM stencil_chain.generator) +add_halide_library( + stencil_chain + FROM stencil_chain.generator + REGISTRATION stencil_chain_registration +) add_halide_library( stencil_chain_auto_schedule FROM stencil_chain.generator @@ -46,3 +50,13 @@ if (EXISTS ${IMAGE}) SKIP_REGULAR_EXPRESSION "\\[SKIP\\]" ) endif () + +# RunGen-based benchmark. Run every app benchmark at once with +# `ctest -L benchmark_apps`. +add_executable(stencil_chain_rungen ${stencil_chain_registration}) +target_link_libraries(stencil_chain_rungen PRIVATE Halide::RunGenMain stencil_chain) +add_test( + NAME stencil_chain_benchmark + COMMAND stencil_chain_rungen --benchmarks=all --estimate_all --parsable_output +) +set_tests_properties(stencil_chain_benchmark PROPERTIES LABELS "stencil_chain;benchmark_apps") diff --git a/doc/BuildingHalideWithCMake.md b/doc/BuildingHalideWithCMake.md index 1f27697de431..4bf6a788406b 100644 --- a/doc/BuildingHalideWithCMake.md +++ b/doc/BuildingHalideWithCMake.md @@ -397,6 +397,41 @@ To use these, you must build LLVM with additional options: -DLLVM_ENABLE_RUNTIMES="compiler-rt;libcxx;libcxxabi;libunwind" ``` +### Valgrind and Intel SDE presets + +These presets rerun the ordinary test suite under an external tool, using +CTest's native mechanisms. + +The `valgrind` preset reruns every registered test under [valgrind] via CTest's +built-in [MemCheck][ctest_memcheck] action. It configures a Debug build with +`Halide_ENABLE_MEMCHECK=ON`, which pulls in the [CTest module][ctest_module] so +that `ctest -T memcheck` works directly in the build tree (no CDash/dashboard +submission required): + +```shell +$ cmake --preset valgrind +$ cmake --build --preset valgrind +$ ctest --preset valgrind -T memcheck +``` + +The `valgrind` test preset excludes the `no_memcheck` label. Valgrind (and its +options and suppressions file) is configured via the standard +`MEMORYCHECK_COMMAND`, `MEMORYCHECK_COMMAND_OPTIONS`, and +`MEMORYCHECK_SUPPRESSIONS_FILE` (`test/valgrind.supp`) cache variables. + +The `avx512-cannonlake` and `avx512-knights-landing` presets rerun every test +under [Intel SDE][intel_sde] to emulate an AVX-512-capable CPU on a host that +lacks one. They set +[`CMAKE_CROSSCOMPILING_EMULATOR`][cmake_crosscompiling_emulator] to +`sde -cnl --` and `sde -knl --` respectively, so every `add_test`-registered +executable runs under the emulator automatically. `sde` must be on your `PATH`: + +```shell +$ cmake --preset avx512-cannonlake +$ cmake --build --preset avx512-cannonlake +$ ctest --preset avx512-cannonlake +``` + ## Build options Halide reads and understands several options that can configure the build. The @@ -431,12 +466,13 @@ The following options are _advanced_ and should not be required in typical workflows. Generally, these are used by Halide's own CI infrastructure, or as escape hatches for third-party packagers. -| Option | Default | Description | -| --------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | -| `Halide_CCACHE_BUILD` | `OFF` | Use ccache with Halide-recommended settings to accelerate rebuilds. | -| `Halide_CCACHE_PARAMS` | `CCACHE_CPP2=yes CCACHE_HASHDIR=yes CCACHE_SLOPPINESS=pch_defines` | Options to pass to `ccache` when using `Halide_CCACHE_BUILD`. | -| `Halide_VERSION_OVERRIDE` | `${Halide_VERSION}` | Override the VERSION for libHalide. | -| `Halide_SOVERSION_OVERRIDE` | `${Halide_VERSION_MAJOR}` | Override the SOVERSION for libHalide. Expects a positive integer (i.e. not a version). | +| Option | Default | Description | +| --------------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- | +| `Halide_CCACHE_BUILD` | `OFF` | Use ccache with Halide-recommended settings to accelerate rebuilds. | +| `Halide_ENABLE_MEMCHECK` | `OFF` | Pull in the CTest module so `ctest -T memcheck` reruns the tests under valgrind. See the `valgrind` preset. | +| `Halide_CCACHE_PARAMS` | `CCACHE_CPP2=yes CCACHE_HASHDIR=yes CCACHE_SLOPPINESS=pch_defines` | Options to pass to `ccache` when using `Halide_CCACHE_BUILD`. | +| `Halide_VERSION_OVERRIDE` | `${Halide_VERSION}` | Override the VERSION for libHalide. | +| `Halide_SOVERSION_OVERRIDE` | `${Halide_VERSION_MAJOR}` | Override the SOVERSION for libHalide. Expects a positive integer (i.e. not a version). | The following options control whether to build certain test subsets. They only apply when `WITH_TESTS=ON`: @@ -578,9 +614,12 @@ On this test system (an M3 MacBook Pro), the build is three times faster, with a [cmake-install]: https://cmake.org/cmake/help/latest/manual/cmake.1.html#install-a-project [cmake-user-interaction]: https://cmake.org/cmake/help/latest/guide/user-interaction/index.html#setting-build-variables [cmake_build_type]: https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html +[cmake_crosscompiling_emulator]: https://cmake.org/cmake/help/latest/variable/CMAKE_CROSSCOMPILING_EMULATOR.html [cmake_presets]: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html [codestylecmake.md]: ./CodeStyleCMake.md [compiling in different directories]: https://ccache.dev/manual/4.11.3.html#_compiling_in_different_directories +[ctest_memcheck]: https://cmake.org/cmake/help/latest/manual/ctest.1.html#dashboard-client-steps +[ctest_module]: https://cmake.org/cmake/help/latest/module/CTest.html [doxygen]: https://www.doxygen.nl/index.html [doxygen-download]: https://www.doxygen.nl/download.html [eigen3cmake]: https://eigen.tuxfamily.org/dox/TopicCMakeGuide.html @@ -595,6 +634,7 @@ On this test system (an M3 MacBook Pro), the build is three times faster, with a [flatbuffers]: https://github.com/google/flatbuffers [halidecmakepackage.md]: ./HalideCMakePackage.md [homebrew]: https://brew.sh +[intel_sde]: https://www.intel.com/content/www/us/en/developer/articles/tool/software-development-emulator.html [lld]: https://lld.llvm.org/ [llvm]: https://github.com/llvm/llvm-project [msvc-cmd]: https://learn.microsoft.com/en-us/cpp/build/building-on-the-command-line @@ -608,6 +648,7 @@ On this test system (an M3 MacBook Pro), the build is three times faster, with a [python]: https://www.python.org/downloads/ [snap store]: https://snapcraft.io/cmake [v8]: https://v8.dev +[valgrind]: https://valgrind.org/ [vcpkg]: https://github.com/Microsoft/vcpkg [vcpkg-overlay]: https://learn.microsoft.com/en-us/vcpkg/concepts/overlay-ports [vcvarsall]: https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line#developer_command_file_locations diff --git a/src/runtime/hexagon_remote/CMakeLists.txt b/src/runtime/hexagon_remote/CMakeLists.txt index a1019be06b00..9b215bde410b 100644 --- a/src/runtime/hexagon_remote/CMakeLists.txt +++ b/src/runtime/hexagon_remote/CMakeLists.txt @@ -1,4 +1,5 @@ include(ExternalProject) +include(GNUInstallDirs) find_package(HexagonSDK REQUIRED) @@ -40,11 +41,20 @@ if (_hexagon_build_type) list(APPEND common_cache_args "-DCMAKE_BUILD_TYPE:STRING=${_hexagon_build_type}") endif () +# The ExternalProject install steps run during *this* project's build, writing +# into INSTALL_DIR (see -DCMAKE_INSTALL_PREFIX= above); the outer +# `cmake --install` does not descend into sub-projects. Point every sub-build at +# one shared staging tree so their qurt/android artifacts merge into a single +# bin/ layout, then relay that tree into the real install prefix with the +# install(DIRECTORY) rule near the end of this file. +set(HEXAGON_REMOTE_STAGE "${CMAKE_CURRENT_BINARY_DIR}/stage") + ExternalProject_Add( hexagon_remote-qurt SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/qurt" CMAKE_CACHE_ARGS "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${HEXAGON_TOOLCHAIN}" ${common_cache_args} PREFIX hexagon + INSTALL_DIR "${HEXAGON_REMOTE_STAGE}" DEPENDS halide_hexagon_remote_idl CONFIGURE_HANDLED_BY_BUILD ON ) @@ -61,6 +71,7 @@ foreach (abi bits IN ZIP_LISTS arm_abis arm_bits) "-DANDROID_PLATFORM:STRING=21" ${common_cache_args} PREFIX arm-${bits}-android + INSTALL_DIR "${HEXAGON_REMOTE_STAGE}" DEPENDS halide_hexagon_remote_idl CONFIGURE_HANDLED_BY_BUILD ON ) @@ -71,6 +82,38 @@ target_compile_features(halide_hexagon_host PRIVATE cxx_std_17) target_include_directories(halide_hexagon_host PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..) target_link_libraries(halide_hexagon_host PRIVATE HexagonSDK::wrapper) +# The simulator-host variant is a normal target of this project, so its +# install rule is honored directly by `cmake --install`. It mirrors the +# committed src/runtime/hexagon_remote/bin/host layout. +install( + TARGETS halide_hexagon_host + DESTINATION "${CMAKE_INSTALL_BINDIR}/host" + COMPONENT Halide_Hexagon +) + +# The qaic-generated IDL header/skel/stub complete the committed +# src/runtime/hexagon_remote/bin/src layout. +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/halide_hexagon_remote.h" + "${CMAKE_CURRENT_BINARY_DIR}/halide_hexagon_remote_skel.c" + "${CMAKE_CURRENT_BINARY_DIR}/halide_hexagon_remote_stub.c" + DESTINATION "${CMAKE_INSTALL_BINDIR}/src" + COMPONENT Halide_Hexagon +) + +# Relay the staged qurt/android artifacts (produced by the ExternalProject +# install steps during the build) into the real install tree. `cmake --install` +# does not descend into ExternalProjects, so without this only the host variant +# above would be installed. USE_SOURCE_PERMISSIONS keeps the executable bit on +# hexagon_sim_remote. +install( + DIRECTORY "${HEXAGON_REMOTE_STAGE}/bin/" + TYPE BIN + COMPONENT Halide_Hexagon + USE_SOURCE_PERMISSIONS +) + add_custom_target(hexagon_remote) add_dependencies( hexagon_remote diff --git a/src/runtime/hexagon_remote/android/CMakeLists.txt b/src/runtime/hexagon_remote/android/CMakeLists.txt index 6d4f3b4f7d20..90eacccc9ca3 100644 --- a/src/runtime/hexagon_remote/android/CMakeLists.txt +++ b/src/runtime/hexagon_remote/android/CMakeLists.txt @@ -29,4 +29,11 @@ target_include_directories( ) target_link_libraries(halide_hexagon_host PRIVATE fastrpc::cdsprpc log) -install(TARGETS halide_hexagon_host DESTINATION bin) +# Mirror the committed src/runtime/hexagon_remote/bin/arm--android layout. +# The bit width comes from the Android NDK toolchain's pointer size. +math(EXPR _android_bits "8 * ${CMAKE_SIZEOF_VOID_P}") +install( + TARGETS halide_hexagon_host + DESTINATION bin/arm-${_android_bits}-android + COMPONENT Halide_Hexagon +) diff --git a/src/runtime/hexagon_remote/qurt/CMakeLists.txt b/src/runtime/hexagon_remote/qurt/CMakeLists.txt index 84bbe4c80a49..8693a32b4eb6 100644 --- a/src/runtime/hexagon_remote/qurt/CMakeLists.txt +++ b/src/runtime/hexagon_remote/qurt/CMakeLists.txt @@ -31,4 +31,17 @@ add_library( target_include_directories(halide_hexagon_remote_skel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../..) target_include_directories(halide_hexagon_remote_skel SYSTEM PRIVATE ${HALIDE_HEXAGON_REMOTE_IDL}) -install(TARGETS sim_qurt hexagon_sim_remote halide_hexagon_remote_skel DESTINATION bin) +# HEXAGON_ARCH (e.g. v65/v66/v68) is set by the Hexagon SDK toolchain file. +# Install into a per-arch subdirectory so the layout mirrors the committed +# src/runtime/hexagon_remote/bin/ tree. +if (NOT HEXAGON_ARCH) + message( + FATAL_ERROR "HEXAGON_ARCH was not set by the Hexagon toolchain; " + "cannot determine the install subdirectory." + ) +endif () +install( + TARGETS sim_qurt hexagon_sim_remote halide_hexagon_remote_skel + DESTINATION bin/${HEXAGON_ARCH} + COMPONENT Halide_Hexagon +) diff --git a/test/correctness/CMakeLists.txt b/test/correctness/CMakeLists.txt index 3a05d2e9bece..141c14ff1788 100644 --- a/test/correctness/CMakeLists.txt +++ b/test/correctness/CMakeLists.txt @@ -448,6 +448,11 @@ tests( # keep-sorted end ) +# tracing_stack does an intentional out-of-bounds read, so exempt it from +# valgrind. Runs under `ctest -T memcheck` should exclude the label with +# `-LE no_memcheck` (the `valgrind` test preset already does this). +set_property(TEST correctness_tracing_stack APPEND PROPERTY LABELS no_memcheck) + # Make sure the test that needs Halide::ImageIO has it target_link_libraries(correctness_image_io PRIVATE Halide::ImageIO) diff --git a/test/valgrind.supp b/test/valgrind.supp new file mode 100644 index 000000000000..b9170f5537f3 --- /dev/null +++ b/test/valgrind.supp @@ -0,0 +1,9 @@ +# Valgrind suppressions for `ctest -T memcheck` (see Halide_ENABLE_MEMCHECK and +# the `valgrind` CMake preset), which reruns every correctness test under +# valgrind. +# +# This file starts empty. The default memcheck options do not enable full leak +# checking, so the one-time LLVM ORC-JIT allocations that Halide's JIT triggers +# are not reported. Add entries here if you widen the options (e.g. add +# `--leak-check=full`) and need to silence errors originating outside Halide's +# own code (LLVM, the C++ runtime, or a GPU/driver stack).