# This file wraps the upstream package config modules for LLVM, Clang, and LLD # to fix pathological issues in their implementations. It creates imported targets # that wrap the key features needed by Halide. set(REASON_FAILURE_MESSAGE "") # Fallback configurations for weirdly built LLVMs set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL MinSizeRel Release RelWithDebInfo "") set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO RelWithDebInfo Release MinSizeRel "") set(CMAKE_MAP_IMPORTED_CONFIG_RELEASE Release MinSizeRel RelWithDebInfo "") set(llvm_paths "") foreach ( template IN ITEMS "/opt/homebrew/opt/llvm@@VERSION@" # Homebrew on macOS "/usr/lib/llvm-@VERSION@" # Debian/Ubuntu packages "/usr/local/lib/llvm-@VERSION@" # Third-party packages "/opt/llvm-@VERSION@" # Third-party packages ) foreach (VERSION RANGE 20 22) # inclusive! string(CONFIGURE "${template}" path @ONLY) list(APPEND llvm_paths "${path}") endforeach () endforeach () list(REVERSE llvm_paths) # search more recent versions first find_package(LLVM PATHS ${llvm_paths}) # Neither LLVM_VERSION nor LLVM_PACKAGE_VERSION work as find_package arguments # in git/development builds as they include a "git" suffix. This applies at # time of writing to versions 18-21, inclusive. if (LLVM_FOUND) set(Halide_LLVM_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") endif () if (NOT DEFINED Halide_LLVM_SHARED_LIBS) # Normally, we don't like making decisions for our users. However, # this avoids an incompatible scenario that is checked below. So # if we didn't do this, the package would fail to be found and # the user would have to either rebuild LLVM or flip this value. if (LLVM_FOUND AND "WebAssembly" IN_LIST LLVM_TARGETS_TO_BUILD AND LLVM_LINK_LLVM_DYLIB) set(Halide_LLVM_SHARED_LIBS YES) else () set(Halide_LLVM_SHARED_LIBS NO) endif () endif () option(Halide_LLVM_SHARED_LIBS "Enable to link to shared libLLVM" "${Halide_LLVM_SHARED_LIBS}") if (LLVM_FOUND) # Package maintainers have some "interesting" ideas as to how they should # lay out the -dev packages, especially when they want to support multiple # parallel versions. These hints take effect at a lower precedence than # Halide_LLVM_ROOT or CMAKE_PREFIX_PATH (which are the standard ways of # setting up the dependency search), but at a higher precedence than the # system-wide fallback locations. find_package( Clang "${Halide_LLVM_VERSION}" EXACT HINTS "${LLVM_INSTALL_PREFIX}" # Same root as the LLVM we found "${LLVM_DIR}/../clang" # LLVM found in $ROOT/lib/cmake/llvm "${LLVM_DIR}/../lib/cmake/clang" # LLVM found in $ROOT/cmake ) foreach (comp IN LISTS LLVM_TARGETS_TO_BUILD) if (comp STREQUAL "WebAssembly") set(Halide_LLVM_${comp}_FOUND 0) find_package( LLD "${Halide_LLVM_VERSION}" EXACT HINTS "${LLVM_INSTALL_PREFIX}" # Homebrew split the LLVM and LLD packages as of version 19, so # having multiple LLVM versions installed leads to the newest # LLD being found without this hint. "${LLVM_INSTALL_PREFIX}/../lld@${LLVM_VERSION_MAJOR}" "${LLVM_DIR}/../lld" "${LLVM_DIR}/../lib/cmake/lld" ) if (NOT LLD_FOUND) string(APPEND REASON_FAILURE_MESSAGE "WebAssembly was not found because liblld is missing. " "Did you `apt install liblld-dev` or `brew install lld`?\n") continue() endif () # LLVM has a mis-feature that allows it to build and export both static and shared libraries at the same # time, while inconsistently linking its own static libraries (for lldWasm and others) to the shared # library. Ignoring this causes Halide to link to both the static AND the shared LLVM libs and it breaks at # runtime. See: https://github.com/halide/Halide/issues/5471 if (LLVM_LINK_LLVM_DYLIB AND NOT Halide_LLVM_SHARED_LIBS) string(APPEND REASON_FAILURE_MESSAGE "WebAssembly was not found because LLD required by was linked to shared LLVM " "(LLVM_LINK_LLVM_DYLIB=${LLVM_LINK_LLVM_DYLIB}) but static LLVM was requested " "(Halide_LLVM_SHARED_LIBS=${Halide_LLVM_SHARED_LIBS}).\n") continue() endif () endif () set(Halide_LLVM_${comp}_FOUND 1) endforeach () set(Halide_LLVM_SHARED_LIBRARY "LLVM") if (Halide_LLVM_SHARED_LIBS AND NOT TARGET "${Halide_LLVM_SHARED_LIBRARY}") string(APPEND Halide_LLVM_SHARED_LIBRARY "-NOTFOUND") string(APPEND REASON_FAILURE_MESSAGE "Halide_LLVM_SHARED_LIBS=${Halide_LLVM_SHARED_LIBS} but the shared LLVM target does not exist.\n") endif () endif () include(FindPackageHandleStandardArgs) find_package_handle_standard_args( Halide_LLVM REQUIRED_VARS LLVM_CONFIG Clang_CONFIG Halide_LLVM_SHARED_LIBRARY VERSION_VAR Halide_LLVM_VERSION REASON_FAILURE_MESSAGE "${REASON_FAILURE_MESSAGE}" HANDLE_COMPONENTS HANDLE_VERSION_RANGE ) function(_Halide_LLVM_link target visibility) llvm_map_components_to_libnames(comps ${ARGN}) target_link_libraries("${target}" "${visibility}" ${comps}) endfunction() if (Halide_LLVM_FOUND) set(Halide_LLVM_COMPONENTS "") foreach (comp IN LISTS Halide_LLVM_FIND_COMPONENTS) if (Halide_LLVM_${comp}_FOUND) list(APPEND Halide_LLVM_COMPONENTS "${comp}") endif () endforeach () if (NOT TARGET Halide_LLVM::Core) add_library(Halide_LLVM::Core INTERFACE IMPORTED) # LLVM_DEFINITIONS is a space-separated list instead of a more typical # CMake semicolon-separated list. For a long time, CMake could handle # this transparently but, since LLVM 17, the flag -D_FILE_OFFSET_BITS=64 # appears on 32-bit Linux. The presence of the `=` here stops CMake # from splitting on spaces, instead corrupting the command line by # folding the other flags into the value of -D_FILE_OFFSET_BITS=64. # For better or worse, since the flag also appears twice, the second # `=` is folded into the value of the first and we get errors of the # form: # # : error: token "=" is not valid in preprocessor expressions # separate_arguments(LLVM_DEFINITIONS NATIVE_COMMAND "${LLVM_DEFINITIONS}") list(REMOVE_ITEM LLVM_DEFINITIONS "-D_GLIBCXX_ASSERTIONS") # work around https://reviews.llvm.org/D142279 list(APPEND LLVM_DEFINITIONS "LLVM_VERSION=${LLVM_VERSION_MAJOR}${LLVM_VERSION_MINOR}") target_compile_definitions(Halide_LLVM::Core INTERFACE ${LLVM_DEFINITIONS}) target_include_directories(Halide_LLVM::Core INTERFACE "${LLVM_INCLUDE_DIRS}") set_property(TARGET Halide_LLVM::Core PROPERTY INTERFACE_CXX_RTTI "${LLVM_ENABLE_RTTI}") set_property(TARGET Halide_LLVM::Core APPEND PROPERTY COMPATIBLE_INTERFACE_BOOL CXX_RTTI) if (Halide_LLVM_SHARED_LIBS) target_link_libraries(Halide_LLVM::Core INTERFACE LLVM ${CMAKE_DL_LIBS}) else () _Halide_LLVM_link(Halide_LLVM::Core INTERFACE orcjit bitwriter linker passes) endif () endif () foreach (comp IN LISTS Halide_LLVM_COMPONENTS) if (NOT TARGET Halide_LLVM::${comp}) add_library(Halide_LLVM::${comp} INTERFACE IMPORTED) target_link_libraries(Halide_LLVM::${comp} INTERFACE Halide_LLVM::Core) if (NOT Halide_LLVM_SHARED_LIBS) _Halide_LLVM_link(Halide_LLVM::${comp} INTERFACE ${comp}) endif () if (comp STREQUAL "WebAssembly") target_include_directories(Halide_LLVM::WebAssembly INTERFACE ${LLD_INCLUDE_DIRS}) target_link_libraries(Halide_LLVM::WebAssembly INTERFACE lldWasm lldCommon) endif () endif () endforeach () endif ()