| # Copyright 2023 The IREE Authors | |
| # | |
| # Licensed under the Apache License v2.0 with LLVM Exceptions. | |
| # See https://llvm.org/LICENSE.txt for license information. | |
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| # Support for configuring LLVM/MLIR and dependent sub-projects. | |
| # There are three top-level entry-points: | |
| # iree_llvm_configure_bundled() : Configures the bundled LLVM and | |
| # sub-projects from third_party. | |
| # iree_llvm_configure_installed() : Configures from installed LLVM and | |
| # sub-projects. | |
| # iree_add_llvm_external_project(...) : Adds an external LLVM project in | |
| # a similar fashion to LLVM_EXTERNAL_PROJECTS define (but in a way that | |
| # does not require a source build of LLVM). | |
| macro(iree_llvm_configure_bundled) | |
| message(STATUS "Adding bundled LLVM source dependency") | |
| iree_llvm_set_bundled_cmake_options() | |
| # Enable MLIR Python bindings if IREE Python bindings enabled. | |
| if(IREE_BUILD_PYTHON_BINDINGS) | |
| set(MLIR_ENABLE_BINDINGS_PYTHON ON) | |
| set(MHLO_ENABLE_BINDINGS_PYTHON ON) | |
| endif() | |
| # Disable LLVM's warnings. | |
| set(LLVM_ENABLE_WARNINGS OFF) | |
| # Stash cmake build type in case LLVM messes with it. | |
| set(_CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}") | |
| # Setup LLVM lib and bin directories. | |
| set(LLVM_LIBRARY_OUTPUT_INTDIR "${CMAKE_CURRENT_BINARY_DIR}/llvm-project/lib") | |
| set(LLVM_RUNTIME_OUTPUT_INTDIR "${CMAKE_CURRENT_BINARY_DIR}/llvm-project/bin") | |
| message(STATUS "Configuring third_party/llvm-project") | |
| list(APPEND CMAKE_MESSAGE_INDENT " ") | |
| set(_BUNDLED_LLVM_CMAKE_SOURCE_SUBDIR "third_party/llvm-project/llvm") | |
| add_subdirectory("${_BUNDLED_LLVM_CMAKE_SOURCE_SUBDIR}" "llvm-project" EXCLUDE_FROM_ALL) | |
| get_directory_property(LLVM_VERSION_MAJOR DIRECTORY "${_BUNDLED_LLVM_CMAKE_SOURCE_SUBDIR}" LLVM_VERSION_MAJOR) | |
| if (NOT LLVM_VERSION_MAJOR) | |
| message(SEND_ERROR "Failed to read LLVM_VERSION_MAJOR property on LLVM directory. Should have been set since https://github.com/llvm/llvm-project/pull/83346.") | |
| endif() | |
| list(POP_BACK CMAKE_MESSAGE_INDENT) | |
| # Reset CMAKE_BUILD_TYPE to its previous setting. | |
| set(CMAKE_BUILD_TYPE "${_CMAKE_BUILD_TYPE}" ) | |
| # Set some CMake variables that mirror things exported in the find_package | |
| # world. Source of truth for these is in an installed LLVMConfig.cmake, | |
| # MLIRConfig.cmake, LLDConfig.cmake (etc) and in the various standalone | |
| # build segments of each project's top-level CMakeLists. | |
| set(LLVM_CMAKE_DIR "${IREE_BINARY_DIR}/llvm-project/lib/cmake/llvm") | |
| list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") | |
| # TODO: Fix MLIR upstream so it doesn't spew into the containing project | |
| # binary dir. See mlir/cmake/modules/CMakeLists.txt | |
| # (and other LLVM sub-projects). | |
| set(MLIR_CMAKE_DIR "${CMAKE_BINARY_DIR}/lib/cmake/mlir") | |
| if(NOT EXISTS "${MLIR_CMAKE_DIR}/AddMLIR.cmake") | |
| message(SEND_ERROR "Could not find AddMLIR.cmake in ${MLIR_CMAKE_DIR}: LLVM sub-projects may have changed their layout. See the mlir_cmake_builddir variable in mlir/cmake/modules/CMakeLists.txt") | |
| endif() | |
| list(APPEND CMAKE_MODULE_PATH "${MLIR_CMAKE_DIR}") | |
| set(LLVM_INCLUDE_DIRS | |
| ${IREE_SOURCE_DIR}/${_BUNDLED_LLVM_CMAKE_SOURCE_SUBDIR}/include | |
| ${IREE_BINARY_DIR}/llvm-project/include | |
| ) | |
| set(MLIR_INCLUDE_DIRS | |
| ${IREE_SOURCE_DIR}/third_party/llvm-project/mlir/include | |
| ${IREE_BINARY_DIR}/llvm-project/tools/mlir/include | |
| ) | |
| set(LLD_INCLUDE_DIRS | |
| ${IREE_SOURCE_DIR}/third_party/llvm-project/lld/include | |
| ${IREE_BINARY_DIR}/llvm-project/tools/lld/include | |
| ) | |
| set(LLVM_BINARY_DIR "${IREE_BINARY_DIR}/llvm-project") | |
| set(LLVM_TOOLS_BINARY_DIR "${LLVM_BINARY_DIR}/bin") | |
| set(LLVM_EXTERNAL_LIT "${IREE_SOURCE_DIR}/${_BUNDLED_LLVM_CMAKE_SOURCE_SUBDIR}/utils/lit/lit.py") | |
| set(IREE_LLVM_LINK_BINARY "$<TARGET_FILE:${IREE_LLVM_LINK_TARGET}>") | |
| set(IREE_LLD_BINARY "$<TARGET_FILE:${IREE_LLD_TARGET}>") | |
| set(IREE_CLANG_BINARY "$<TARGET_FILE:${IREE_CLANG_TARGET}>") | |
| set(IREE_LLVM_OBJCOPY_BINARY "$<TARGET_FILE:llvm-objcopy>") | |
| set(IREE_CLANG_BUILTIN_HEADERS_PATH "${LLVM_BINARY_DIR}/lib/clang/${LLVM_VERSION_MAJOR}/include/") | |
| if(IREE_ENABLE_RUNTIME_COVERAGE) | |
| set(IREE_LLVM_COV_BINARY "$<TARGET_FILE:${IREE_LLVM_COV_TARGET}>") | |
| set(IREE_LLVM_PROFDATA_BINARY "$<TARGET_FILE:${IREE_LLVM_PROFDATA_TARGET}>") | |
| endif() | |
| endmacro() | |
| macro(iree_llvm_configure_installed) | |
| message(STATUS "Using installed LLVM components") | |
| find_package(LLVM REQUIRED) | |
| list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") | |
| include_directories(${LLVM_INCLUDE_DIRS}) | |
| find_package(MLIR REQUIRED) | |
| list(APPEND CMAKE_MODULE_PATH "${MLIR_CMAKE_DIR}") | |
| include_directories(${MLIR_INCLUDE_DIRS}) | |
| find_package(LLD REQUIRED) | |
| list(APPEND CMAKE_MODULE_PATH "${LLD_CMAKE_DIR}") | |
| include_directories(${LLD_INCLUDE_DIRS}) | |
| find_package(Clang REQUIRED) | |
| list(APPEND CMAKE_MODULE_PATH "${CLANG_CMAKE_DIR}") | |
| include_directories(${CLANG_INCLUDE_DIRS}) | |
| # Lit never gets installed with LLVM. So we have to reach into our copy | |
| # of the monorepo to get it. I'm sorry. If this doesn't work for you, | |
| # feel free to -DLLVM_EXTERNAL_LIT to provide your own. | |
| # Note that LLVM style lit test helpers use LLVM_EXTERNAL_LIT, if provided, | |
| # so this is consistent between the projects. | |
| if(NOT LLVM_EXTERNAL_LIT) | |
| set(LLVM_EXTERNAL_LIT "${IREE_SOURCE_DIR}/third_party/llvm-project/llvm/utils/lit/lit.py") | |
| endif() | |
| set(IREE_LLVM_LINK_BINARY "$<TARGET_FILE:llvm-link>") | |
| set(IREE_LLD_BINARY "$<TARGET_FILE:lld>") | |
| set(IREE_CLANG_BINARY "$<TARGET_FILE:clang>") | |
| set(IREE_LLVM_OBJCOPY_BINARY "$<TARGET_FILE:llvm-objcopy>") | |
| set(IREE_CLANG_BUILTIN_HEADERS_PATH "${LLVM_LIBRARY_DIR}/clang/${LLVM_VERSION_MAJOR}/include") | |
| if(NOT EXISTS "${IREE_CLANG_BUILTIN_HEADERS_PATH}") | |
| message(WARNING "Could not find installed clang-resource-headers (tried ${IREE_CLANG_BUILTIN_HEADERS_PATH})") | |
| endif() | |
| if(IREE_ENABLE_RUNTIME_COVERAGE) | |
| set(IREE_LLVM_COV_BINARY "$<TARGET_FILE:llvm-cov>") | |
| set(IREE_LLVM_PROFDATA_BINARY "$<TARGET_FILE:llvm-profdata>") | |
| endif() | |
| endmacro() | |
| # iree_llvm_set_bundled_cmake_options() | |
| # Hard-code various LLVM CMake options needed for an in-tree bundled build. | |
| macro(iree_llvm_set_bundled_cmake_options) | |
| # When enabling an IREE CPU backend, automatically enable these targets. | |
| set(IREE_DEFAULT_CPU_LLVM_TARGETS "X86;ARM;AArch64;RISCV" | |
| CACHE STRING "Initialization value for default LLVM CPU targets.") | |
| # Catch an incorrect usage pattern that has been used in the past. | |
| if("host" IN_LIST IREE_DEFAULT_CPU_LLVM_TARGETS) | |
| message(SEND_ERROR "IREE_DEFAULT_CPU_LLVM_TARGETS may not contain 'host'.") | |
| endif() | |
| # These defaults are moderately important to us, but the user *can* | |
| # override them (enabling some of these brings in deps that will conflict, | |
| # so ymmv). | |
| set(LLVM_INCLUDE_EXAMPLES OFF CACHE BOOL "") | |
| set(LLVM_INCLUDE_TESTS OFF CACHE BOOL "") | |
| set(LLVM_INCLUDE_BENCHMARKS OFF CACHE BOOL "") | |
| set(LLVM_APPEND_VC_REV OFF CACHE BOOL "") | |
| set(LLVM_ENABLE_IDE ON CACHE BOOL "") | |
| set(LLVM_ENABLE_BINDINGS OFF CACHE BOOL "") | |
| # Force LLVM to avoid dependencies, which we don't ever really want in our | |
| # limited builds. | |
| set(LLVM_ENABLE_LIBEDIT OFF CACHE BOOL "Default disable") | |
| set(LLVM_ENABLE_LIBXML2 OFF CACHE BOOL "Default disable") | |
| set(LLVM_ENABLE_TERMINFO OFF CACHE BOOL "Default disable") | |
| set(LLVM_ENABLE_ZLIB OFF CACHE BOOL "Default disable") | |
| set(LLVM_ENABLE_ZSTD OFF CACHE BOOL "Default disable") | |
| set(LLVM_FORCE_ENABLE_STATS ON CACHE BOOL "Default enable") | |
| # LLVM defaults to building all targets. We always enable targets that we need | |
| # as we need them, so default to none. The user can override this as needed, | |
| # which is fine. | |
| set(LLVM_TARGETS_TO_BUILD "" CACHE STRING "") | |
| # We enable LLVM projects as needed. The user can override this. | |
| set(LLVM_ENABLE_PROJECTS "" CACHE STRING "") | |
| set(LLVM_EXTERNAL_PROJECTS "" CACHE STRING "") | |
| # Default Python bindings to off (for all sub-projects). | |
| set(MLIR_ENABLE_BINDINGS_PYTHON OFF CACHE BOOL "") | |
| set(MHLO_ENABLE_BINDINGS_PYTHON OFF CACHE BOOL "") | |
| # Disable MLIR attempting to configure Python dev packages. We take care of | |
| # that in IREE as a super-project. | |
| set(MLIR_DISABLE_CONFIGURE_PYTHON_DEV_PACKAGES ON CACHE BOOL "" FORCE) | |
| # Enable reverse iteration over LLVM unordered maps/sets. | |
| if(IREE_REVERSE_ITERATION) | |
| set(LLVM_ENABLE_REVERSE_ITERATION ON CACHE BOOL "" FORCE) | |
| endif() | |
| # If we are building clang/lld/etc, these will be the targets. | |
| # Otherwise, empty so scripts can detect unavailability. | |
| set(IREE_CLANG_TARGET) | |
| set(IREE_LLD_TARGET) | |
| # Unconditionally enable some other cheap LLVM tooling. | |
| set(IREE_LLVM_LINK_TARGET llvm-link) | |
| set(IREE_FILECHECK_TARGET FileCheck) | |
| set(IREE_NOT_TARGET not) | |
| # Unconditionally enable mlir. | |
| list(APPEND LLVM_ENABLE_PROJECTS mlir) | |
| # Coverage tools. | |
| set(IREE_LLVM_COV_TARGET) | |
| set(IREE_LLVM_PROFDATA_TARGET) | |
| if(IREE_ENABLE_RUNTIME_COVERAGE) | |
| set(IREE_LLVM_COV_TARGET llvm-cov) | |
| set(IREE_LLVM_PROFDATA_TARGET llvm-profdata) | |
| endif() | |
| # Configure LLVM based on enabled IREE target backends. | |
| message(STATUS "IREE compiler target backends:") | |
| if(IREE_TARGET_BACKEND_CUDA) | |
| message(STATUS " - cuda") | |
| list(APPEND LLVM_TARGETS_TO_BUILD NVPTX) | |
| set(IREE_CLANG_TARGET clang) | |
| endif() | |
| if(IREE_TARGET_BACKEND_LLVM_CPU) | |
| message(STATUS " - llvm-cpu") | |
| list(APPEND LLVM_TARGETS_TO_BUILD "${IREE_DEFAULT_CPU_LLVM_TARGETS}") | |
| # Misconfiguration of LLVM sometimes results in empty LLVM_TARGETS_TO_BUILD. | |
| # This has led to hard-to-diagnose issues, so better catch that here. | |
| # Note that the placement of that check matters: | |
| # - Here we are inside the if IREE_TARGET_BACKEND_LLVM_CPU, so we know that | |
| # we should have at least one LLVM CPU target enabled. | |
| # - Here we are before some other if branches below for other backends that | |
| # append more targets to the list, making it it unconditionally nonempty. | |
| if (NOT LLVM_TARGETS_TO_BUILD) | |
| message(SEND_ERROR "LLVM_TARGETS_TO_BUILD should not be empty.") | |
| endif() | |
| set(IREE_CLANG_TARGET clang) | |
| set(IREE_LLD_TARGET lld) | |
| endif() | |
| if(IREE_TARGET_BACKEND_LLVM_CPU_WASM) | |
| message(STATUS " - llvm-cpu (wasm)") | |
| list(APPEND LLVM_TARGETS_TO_BUILD WebAssembly) | |
| set(IREE_CLANG_TARGET clang) | |
| set(IREE_LLD_TARGET lld) | |
| endif() | |
| if(IREE_TARGET_BACKEND_METAL_SPIRV) | |
| message(STATUS " - metal-spirv") | |
| endif() | |
| if(IREE_TARGET_BACKEND_ROCM OR IREE_HAL_DRIVER_AMDGPU) | |
| list(APPEND LLVM_TARGETS_TO_BUILD AMDGPU SPIRV) | |
| set(IREE_CLANG_TARGET clang) | |
| endif() | |
| if(IREE_TARGET_BACKEND_VULKAN_SPIRV) | |
| message(STATUS " - vulkan-spirv") | |
| endif() | |
| if(IREE_TARGET_BACKEND_VMVX) | |
| message(STATUS " - vmvx") | |
| endif() | |
| if(IREE_TARGET_BACKEND_WEBGPU_SPIRV) | |
| message(STATUS " - webgpu-spirv") | |
| endif() | |
| if(IREE_CLANG_TARGET) | |
| list(APPEND LLVM_ENABLE_PROJECTS clang) | |
| endif() | |
| if(IREE_LLD_TARGET) | |
| list(APPEND LLVM_ENABLE_PROJECTS lld) | |
| endif() | |
| if(IREE_BUILD_CLANG_TOOLS_EXTRA) | |
| list(APPEND LLVM_ENABLE_PROJECTS clang-tools-extra) | |
| endif() | |
| list(REMOVE_DUPLICATES LLVM_ENABLE_PROJECTS) | |
| list(REMOVE_DUPLICATES LLVM_TARGETS_TO_BUILD) | |
| message(VERBOSE "Building LLVM Targets: ${LLVM_TARGETS_TO_BUILD}") | |
| message(VERBOSE "Building LLVM Projects: ${LLVM_ENABLE_PROJECTS}") | |
| endmacro() | |
| # iree_add_llvm_external_project(name location [SILENCE_DEPRECATIONS]) | |
| # Adds a project as if by appending to the LLVM_EXTERNAL_PROJECTS CMake | |
| # variable. This is done by setting the same top-level variables that the LLVM | |
| # machinery is expected to export and including the sub directory explicitly. | |
| # The project binary dir will be llvm-external-projects/${name} | |
| # Call this after appropriate LLVM/MLIR packages have been loaded. | |
| # | |
| # Options: | |
| # SILENCE_DEPRECATIONS: | |
| # Suppresses all deprecation warnings for this external project. Useful for | |
| # third-party dependencies that use deprecated APIs and are slow to update. | |
| function(iree_llvm_add_external_project name location) | |
| cmake_parse_arguments(_RULE "SILENCE_DEPRECATIONS" "" "" ${ARGN}) | |
| message(STATUS "Adding LLVM external project ${name} -> ${location}") | |
| if(NOT EXISTS "${location}/CMakeLists.txt") | |
| message(FATAL_ERROR "External project location ${location} is not valid") | |
| endif() | |
| # Optionally silence warnings for this external project. | |
| # Modification is in function scope only, so parent scope is unaffected. | |
| if(_RULE_SILENCE_DEPRECATIONS) | |
| if(MSVC) | |
| # MSVC: C4996 is the deprecation warning. | |
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4996") | |
| else() | |
| # GCC/Clang: Use standard deprecation warning flag. | |
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations") | |
| endif() | |
| endif() | |
| # Disable PCH for external projects (llvm/llvm-project@b82c7fc65229). | |
| # External projects live in a separate binary dir from LLVM, so CMake cannot | |
| # resolve the LLVMSupport PCH path for targets that try to reuse it. | |
| # Function scope ensures this doesn't affect LLVM/MLIR's own PCH. | |
| set(CMAKE_DISABLE_PRECOMPILE_HEADERS ON) | |
| add_subdirectory(${location} "llvm-external-projects/${name}" EXCLUDE_FROM_ALL) | |
| endfunction() | |
| # iree_llvm_add_usage_requirements(llvm_library usage_library) | |
| # Adds |usage_library| as a link dependency of |llvm_library| in a way that | |
| # is legal regardless of whether imported or built. | |
| function(iree_llvm_add_usage_requirements llvm_library usage_library) | |
| get_target_property(_imported ${llvm_library} IMPORTED) | |
| if(_imported) | |
| set_property(TARGET ${llvm_library} | |
| APPEND PROPERTY IMPORTED_LINK_INTERFACE_LIBRARIES ${usage_library}) | |
| else() | |
| # We can't easily add an out-of-project link library directly, because | |
| # then it needs to be installed/exported, etc. Big mess. So just splice | |
| # the include directories from the usage_library onto the llvm_library. | |
| get_property(_includes TARGET ${usage_library} PROPERTY INTERFACE_INCLUDE_DIRECTORIES) | |
| set_property(TARGET ${llvm_library} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES | |
| $<BUILD_INTERFACE:${_includes}> | |
| ) | |
| endif() | |
| endfunction() |