Add an option to disable use of bundled LLVM. (#12256)

* Adds an option `IREE_BUILD_BUNDLED_LLVM` (default ON) which builds
LLVM+tools from the in-repo submodule (`third_party/llvm-project`). When
disabled, CMake must be configured such that find_package for `LLVM`,
`MLIR`, and `LLD` functions and are at commit levels appropriate.
* Re-organizes the LLVM cmake setup to be better contained and
normalized vs in-tree/bundled and installed.
* Switches MLIR sub-projects iree-dialects, mlir-hlo and torch-dialects
to configure from the main IREE build so that they can be used on top of
a locally built or installed LLVM.
* Updates mechanisms for locating lit so that it is consistent between
the two modes.
* Moves the binary directory for `third_party/llvm-project/llvm` ->
`llvm-project`. This means that folks interacting with the bundled LLVM
in the build directory will say things like `ninja llvm-project/all` or
`./llvm-project/bin/mlir-opt`.
* Moves the MLIR sub-projects from under
`llvm-project/tools/{subproject}` ->
`llvm-external-projects/{subproject}` so that it is consistent between
bundled and installed modes. Practically, this means that if invoking
`iree-dialects-opt` from the command line, it is in a different place.
* Fix to a transform dialect ODR issue which was latent and a namespace
conflict with upstream. Not sure what triggered it but put it down to
the vagaries of linking.
* Fix to the VMVX dependent dialects that came up when building IREE
with all backends disabled.

With this, I was able to install my own LLVM with these commands:

```
cd third_party/llvm-project
mkdir build
cd build
cmake -G Ninja ../llvm \
   -DLLVM_ENABLE_PROJECTS="mlir;lld" \
   -DLLVM_BUILD_EXAMPLES=OFF \
   -DLLVM_TARGETS_TO_BUILD="" \
   -DLLVM_INSTALL_UTILS=ON \
   -DCMAKE_BUILD_TYPE=Release \
   -DLLVM_ENABLE_ASSERTIONS=ON \
   -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DLLVM_ENABLE_LLD=ON \
   -DLLVM_CCACHE_BUILD=ON
ninja
cmake -DCMAKE_INSTALL_PREFIX=$HOME/tmp/llvm -P cmake_install.cmake
```

```
cmake -GNinja -B ../iree-build-inst/ -S . \
    -DIREE_TARGET_BACKEND_DEFAULTS=OFF \
    -DIREE_HAL_DRIVER_DEFAULTS=OFF \
    -DIREE_HAL_DRIVER_LOCAL_SYNC=ON \
    -DIREE_HAL_DRIVER_LOCAL_TASK=ON \
    -DIREE_BUILD_BUNDLED_LLVM=OFF \
    -DLLVM_DIR=$HOME/tmp/llvm/lib/cmake/llvm \
    -DMLIR_DIR=$HOME/tmp/llvm/lib/cmake/mlir \
    -DLLD_DIR=$HOME/tmp/llvm/lib/cmake/lld \
    -DCMAKE_BUILD_TYPE=Release
```

* Python builds have not yet been tested in the installed path and need
some ergonomic help.
* Will follow-up with a script to build various configurations of
LLVM/MLIR/LLD so that we can wire it up to CI.

Progress on #12231
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 78107fb..ea1f600 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -60,6 +60,7 @@
 option(IREE_BUILD_SAMPLES "Builds IREE sample projects." ON)
 option(IREE_BUILD_PYTHON_BINDINGS "Builds the IREE python bindings" OFF)
 option(IREE_BUILD_TRACY "Builds tracy server tools." OFF)
+option(IREE_BUILD_BUNDLED_LLVM "Builds the bundled llvm-project (vs using installed)" ON)
 
 option(IREE_BYTECODE_MODULE_FORCE_LLVM_SYSTEM_LINKER "Use the system linker when generating IREE modules in tests/samples/benchmarks (useful for Tracy)." OFF)
 
@@ -431,6 +432,7 @@
 include(iree_c_module)
 include(iree_python)
 include(iree_lit_test)
+include(iree_llvm)
 include(iree_add_all_subdirs)
 include(iree_check_test)
 include(iree_trace_runner_test)
@@ -564,81 +566,36 @@
 
 if(NOT IREE_BUILD_COMPILER)
   message(STATUS "Not adding LLVM/MLIR because the configuration does not require it")
-elseif(TARGET LLVMSupport)
-  message(STATUS "Not adding IREE bundled LLVM because it has already been included")
-  if(NOT TARGET MLIRIR)
-    message(FATAL_ERROR "Detected externally provided LLVM project but could not find MLIR projects (is it enabled/installed?)")
-  endif()
 else()
-  message(STATUS "Adding bundled LLVM source dependency")
-  iree_set_llvm_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)
+  # Get the main LLVM deps.
+  if(IREE_BUILD_BUNDLED_LLVM)
+    iree_llvm_configure_bundled()
+  else()
+    iree_llvm_configure_installed()
   endif()
 
-  # Disable LLVM's warnings.
-  set(LLVM_ENABLE_WARNINGS OFF)
+  # Also add a library that can be depended on to get LLVM includes setup
+  # properly. bazel_to_cmake targets this for some header only pseudo deps.
+  add_library(IREELLVMIncludeSetup INTERFACE)
+  target_include_directories(IREELLVMIncludeSetup INTERFACE
+    ${LLVM_INCLUDE_DIRS}
+    ${MLIR_INCLUDE_DIRS}
+    ${LLD_INCLUDE_DIRS}
+  )
 
-  # Stash cmake build type in case LLVM messes with it.
-  set(_CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}")
+  # Splice the includes setup into base LLVM libraries so that using them
+  # gets everything nice and tidy. It would be super if some day, LLVM
+  # libraries set their right usage requirements for includes. In the meantime
+  # we add usage requirements to libraries at the root of all things LLVM.
+  iree_llvm_add_usage_requirements(LLVMSupport IREELLVMIncludeSetup)
+  iree_llvm_add_usage_requirements(MLIRSupport IREELLVMIncludeSetup)
 
   # Add default external projects.
-  iree_add_llvm_external_project(mlir-iree-dialects MLIR_IREE_DIALECTS ${CMAKE_CURRENT_SOURCE_DIR}/llvm-external-projects/iree-dialects)
-  iree_add_llvm_external_project(mlir-hlo MLIR_HLO ${CMAKE_CURRENT_SOURCE_DIR}/third_party/mlir-hlo)
+  iree_llvm_add_external_project(mlir-iree-dialects ${CMAKE_CURRENT_SOURCE_DIR}/llvm-external-projects/iree-dialects)
+  iree_llvm_add_external_project(mlir-hlo ${CMAKE_CURRENT_SOURCE_DIR}/third_party/mlir-hlo)
   if(IREE_INPUT_TORCH)
-    iree_add_llvm_external_project(torch-mlir-dialects TORCH_MLIR_DIALECTS ${CMAKE_CURRENT_SOURCE_DIR}/third_party/torch-mlir-dialects)
+    iree_llvm_add_external_project(torch-mlir-dialects ${CMAKE_CURRENT_SOURCE_DIR}/third_party/torch-mlir-dialects)
   endif()
-
-  add_subdirectory("third_party/llvm-project/llvm" EXCLUDE_FROM_ALL)
-
-  # Reset CMAKE_BUILD_TYPE to its previous setting.
-  set(CMAKE_BUILD_TYPE "${_CMAKE_BUILD_TYPE}" )
-
-  # Extend module path to allow submodules to use LLVM and MLIR CMake modules.
-  list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_BINARY_DIR}/lib/cmake/mlir")
-  list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_BINARY_DIR}/third_party/llvm-project/llvm/lib/cmake/llvm/")
-
-  # Add the bundled include directories for cmake files looking for them.
-  list(APPEND LLVM_INCLUDE_DIRS
-    ${CMAKE_CURRENT_SOURCE_DIR}/third_party/llvm-project/llvm/include
-    ${CMAKE_CURRENT_BINARY_DIR}/third_party/llvm-project/llvm/include
-  )
-  list(APPEND MLIR_INCLUDE_DIRS
-    ${CMAKE_CURRENT_SOURCE_DIR}/third_party/llvm-project/mlir/include
-    ${CMAKE_CURRENT_BINARY_DIR}/third_party/llvm-project/llvm/tools/mlir/include
-  )
-
-  # TODO: It should be possible to fix upstream targets so as to not require
-  # any of these. Remove as they become unnecessary.
-  function(_hack_llvm_include_paths)
-    set(_COMMON_INCLUDE_DIRS
-      # LLVM
-      $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/third_party/llvm-project/llvm/include>
-      $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/third_party/llvm-project/llvm/include>
-
-      # MLIR
-      $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/third_party/llvm-project/mlir/include>
-      $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/third_party/llvm-project/llvm/tools/mlir/include>
-
-      # LLD
-      $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/third_party/llvm-project/lld/include>
-      $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/third_party/llvm-project/llvm/tools/lld/include>
-
-    )
-    # Avoid globally modifying paths by instead adding the include paths to the
-    # rules that really should have them in the first place.
-    target_include_directories(LLVMSupport PUBLIC ${_COMMON_INCLUDE_DIRS})
-    target_include_directories(MLIRSupport PUBLIC ${_COMMON_INCLUDE_DIRS})
-
-    # Also add a library that can be depended on to get LLVM includes setup
-    # properly. bazel_to_cmake targets this for some header only pseudo deps.
-    add_library(IREELLVMIncludeSetup INTERFACE)
-    target_include_directories(IREELLVMIncludeSetup INTERFACE ${_COMMON_INCLUDE_DIRS})
-  endfunction()
-  _hack_llvm_include_paths()
 endif()
 
 #-------------------------------------------------------------------------------
diff --git a/build_tools/cmake/iree_external_cmake_options.cmake b/build_tools/cmake/iree_external_cmake_options.cmake
index 641da4e..bc969c8 100644
--- a/build_tools/cmake/iree_external_cmake_options.cmake
+++ b/build_tools/cmake/iree_external_cmake_options.cmake
@@ -26,93 +26,6 @@
   set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
 endmacro()
 
-macro(iree_set_llvm_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.")
-
-  # 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 "")
-
-  # 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 "")
-
-  # If we are building LLD, this will be the target. Otherwise, empty.
-  set(IREE_LLD_TARGET)
-
-  # Unconditionally enable mlir.
-  list(APPEND LLVM_ENABLE_PROJECTS mlir)
-
-  # 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)
-  endif()
-  if(IREE_TARGET_BACKEND_LLVM_CPU)
-    message(STATUS "  - llvm-cpu")
-    list(APPEND LLVM_TARGETS_TO_BUILD "${IREE_DEFAULT_CPU_LLVM_TARGETS}")
-    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_LLD_TARGET lld)
-  endif()
-  if(IREE_TARGET_BACKEND_METAL_SPIRV)
-    message(STATUS "  - metal-spirv")
-  endif()
-  if(IREE_TARGET_BACKEND_ROCM)
-    message(STATUS "  - rocm")
-    list(APPEND LLVM_TARGETS_TO_BUILD AMDGPU)
-  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)
-    message(STATUS "  - webgpu")
-  endif()
-
-  if(IREE_LLD_TARGET)
-    list(APPEND LLVM_ENABLE_PROJECTS lld)
-  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()
-
-macro(iree_add_llvm_external_project name identifier location)
-  message(STATUS "Adding LLVM external project ${name} (${identifier}) -> ${location}")
-  if(NOT EXISTS "${location}/CMakeLists.txt")
-    message(FATAL_ERROR "External project location ${location} is not valid")
-  endif()
-  list(APPEND LLVM_EXTERNAL_PROJECTS ${name})
-  list(REMOVE_DUPLICATES LLVM_EXTERNAL_PROJECTS)
-  set(LLVM_EXTERNAL_${identifier}_SOURCE_DIR ${location})
-endmacro()
-
 macro(iree_set_spirv_headers_cmake_options)
   set(SPIRV_HEADERS_SKIP_EXAMPLES ON CACHE BOOL "" FORCE)
   set(SPIRV_HEADERS_SKIP_INSTALL ON CACHE BOOL "" FORCE)
diff --git a/build_tools/cmake/iree_lit_test.cmake b/build_tools/cmake/iree_lit_test.cmake
index 2d83470..1cd6416 100644
--- a/build_tools/cmake/iree_lit_test.cmake
+++ b/build_tools/cmake/iree_lit_test.cmake
@@ -69,7 +69,7 @@
       ${_NAME_PATH}
     COMMAND
       "${Python3_EXECUTABLE}"
-      "${LLVM_SOURCE_DIR}/utils/lit/lit.py"
+      "${LLVM_EXTERNAL_LIT}"
       ${_LIT_PATH_ARGS}
       ${_TEST_FILE_PATH}
   )
diff --git a/build_tools/cmake/iree_llvm.cmake b/build_tools/cmake/iree_llvm.cmake
new file mode 100644
index 0000000..1e7ef73
--- /dev/null
+++ b/build_tools/cmake/iree_llvm.cmake
@@ -0,0 +1,201 @@
+# 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}")
+
+  add_subdirectory("third_party/llvm-project/llvm" "llvm-project" EXCLUDE_FROM_ALL)
+
+  # 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.
+  set(MLIR_CMAKE_DIR "${IREE_BINARY_DIR}/lib/cmake/mlir")
+  list(APPEND CMAKE_MODULE_PATH "${MLIR_CMAKE_DIR}")
+
+  set(LLVM_INCLUDE_DIRS
+    ${IREE_SOURCE_DIR}/third_party/llvm-project/llvm/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 "${LLVM_TOOLS_BINARY_DIR}/llvm-lit")
+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})
+
+  # 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()
+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.")
+
+  # 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 "")
+
+  # 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 "")
+
+  # If we are building LLD, this will be the target. Otherwise, empty.
+  set(IREE_LLD_TARGET)
+
+  # Unconditionally enable mlir.
+  list(APPEND LLVM_ENABLE_PROJECTS mlir)
+
+  # 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)
+  endif()
+  if(IREE_TARGET_BACKEND_LLVM_CPU)
+    message(STATUS "  - llvm-cpu")
+    list(APPEND LLVM_TARGETS_TO_BUILD "${IREE_DEFAULT_CPU_LLVM_TARGETS}")
+    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_LLD_TARGET lld)
+  endif()
+  if(IREE_TARGET_BACKEND_METAL_SPIRV)
+    message(STATUS "  - metal-spirv")
+  endif()
+  if(IREE_TARGET_BACKEND_ROCM)
+    message(STATUS "  - rocm")
+    list(APPEND LLVM_TARGETS_TO_BUILD AMDGPU)
+  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)
+    message(STATUS "  - webgpu")
+  endif()
+
+  if(IREE_LLD_TARGET)
+    list(APPEND LLVM_ENABLE_PROJECTS lld)
+  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)
+# 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.
+function(iree_llvm_add_external_project name location)
+  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()
+  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()
diff --git a/build_tools/third_party/mlir-hlo/CMakeLists.txt b/build_tools/third_party/mlir-hlo/CMakeLists.txt
index a117efa..a68af7e 100644
--- a/build_tools/third_party/mlir-hlo/CMakeLists.txt
+++ b/build_tools/third_party/mlir-hlo/CMakeLists.txt
@@ -8,7 +8,7 @@
   "${IREE_SOURCE_DIR}/third_party/mlir-hlo/"
 )
 set(TF_MLIR_HLO_BINARY_DIR
-  "${IREE_BINARY_DIR}/third_party/llvm-project/llvm/tools/mlir-hlo/"
+  "${IREE_BINARY_DIR}/llvm-external-projects/mlir-hlo"
 )
 
 external_cc_library(
diff --git a/compiler/src/iree/compiler/Codegen/Common/TransformDialectInterpreterPass.cpp b/compiler/src/iree/compiler/Codegen/Common/TransformDialectInterpreterPass.cpp
index d89e6d1..39f8637 100644
--- a/compiler/src/iree/compiler/Codegen/Common/TransformDialectInterpreterPass.cpp
+++ b/compiler/src/iree/compiler/Codegen/Common/TransformDialectInterpreterPass.cpp
@@ -53,7 +53,7 @@
 /// This needs to be its own pass because the registration mechanism and ops
 /// available are different than for other interpreters.
 class TransformDialectInterpreterPass
-    : public transform::TransformInterpreterPassBase<
+    : public transform::iree_dialects::TransformInterpreterPassBase<
           TransformDialectInterpreterPass,
           iree_compiler::TransformDialectInterpreterBase> {
  public:
diff --git a/compiler/src/iree/compiler/Dialect/Flow/Transforms/DispatchWithTransformDialect.cpp b/compiler/src/iree/compiler/Dialect/Flow/Transforms/DispatchWithTransformDialect.cpp
index 9f48d11..93b8d70 100644
--- a/compiler/src/iree/compiler/Dialect/Flow/Transforms/DispatchWithTransformDialect.cpp
+++ b/compiler/src/iree/compiler/Dialect/Flow/Transforms/DispatchWithTransformDialect.cpp
@@ -31,7 +31,7 @@
 /// formation. This needs to be its own pass because the registration mechanism
 /// and ops available are different than for other interpreters.
 struct DispatchWithTransformDialect
-    : public transform::TransformInterpreterPassBase<
+    : public transform::iree_dialects::TransformInterpreterPassBase<
           DispatchWithTransformDialect, DispatchWithTransformDialectBase> {
   void getDependentDialects(DialectRegistry &registry) const override {
     // clang-format off
diff --git a/compiler/src/iree/compiler/Dialect/HAL/Target/LLVM/LinkerTool.cpp b/compiler/src/iree/compiler/Dialect/HAL/Target/LLVM/LinkerTool.cpp
index d84d26d..aebda57 100644
--- a/compiler/src/iree/compiler/Dialect/HAL/Target/LLVM/LinkerTool.cpp
+++ b/compiler/src/iree/compiler/Dialect/HAL/Target/LLVM/LinkerTool.cpp
@@ -205,9 +205,8 @@
   }
 
   // Next search around in the CMake build tree.
-  toolPath = findToolAtPath(
-      normalizedToolNames,
-      mainExecutableDir + "/../third_party/llvm-project/llvm/bin/");
+  toolPath = findToolAtPath(normalizedToolNames,
+                            mainExecutableDir + "/../llvm-project/bin/");
   if (!toolPath.empty()) {
     LLVM_DEBUG(llvm::dbgs()
                << "Found tool in build tree at path " << toolPath << "\n");
diff --git a/compiler/src/iree/compiler/Dialect/HAL/Target/VMVX/BUILD b/compiler/src/iree/compiler/Dialect/HAL/Target/VMVX/BUILD
index fb9e904..0d1effe 100644
--- a/compiler/src/iree/compiler/Dialect/HAL/Target/VMVX/BUILD
+++ b/compiler/src/iree/compiler/Dialect/HAL/Target/VMVX/BUILD
@@ -40,6 +40,7 @@
         "//compiler/src/iree/compiler/Dialect/VMVX/IR:VMVXDialect",
         "//compiler/src/iree/compiler/Dialect/VMVX/Transforms",
         "//compiler/src/iree/compiler/Utils",
+        "//llvm-external-projects/iree-dialects:IREELinalgExtDialect",
         "@llvm-project//llvm:Support",
         "@llvm-project//mlir:IR",
         "@llvm-project//mlir:Pass",
diff --git a/compiler/src/iree/compiler/Dialect/HAL/Target/VMVX/CMakeLists.txt b/compiler/src/iree/compiler/Dialect/HAL/Target/VMVX/CMakeLists.txt
index c480bc2..d60973b 100644
--- a/compiler/src/iree/compiler/Dialect/HAL/Target/VMVX/CMakeLists.txt
+++ b/compiler/src/iree/compiler/Dialect/HAL/Target/VMVX/CMakeLists.txt
@@ -22,6 +22,7 @@
   SRCS
     "VMVXTarget.cpp"
   DEPS
+    IREELinalgExtDialect
     LLVMSupport
     MLIRIR
     MLIRPass
diff --git a/compiler/src/iree/compiler/Dialect/HAL/Target/VMVX/VMVXTarget.cpp b/compiler/src/iree/compiler/Dialect/HAL/Target/VMVX/VMVXTarget.cpp
index d06f2af..031e7d4 100644
--- a/compiler/src/iree/compiler/Dialect/HAL/Target/VMVX/VMVXTarget.cpp
+++ b/compiler/src/iree/compiler/Dialect/HAL/Target/VMVX/VMVXTarget.cpp
@@ -6,6 +6,7 @@
 
 #include "iree/compiler/Dialect/HAL/Target/VMVX/VMVXTarget.h"
 
+#include "iree-dialects/Dialect/LinalgExt/IR/LinalgExtDialect.h"
 #include "iree/compiler/Codegen/Dialect/IREECodegenDialect.h"
 #include "iree/compiler/Codegen/Passes.h"
 #include "iree/compiler/Dialect/Flow/IR/FlowOps.h"
@@ -52,7 +53,8 @@
 
   void getDependentDialects(DialectRegistry &registry) const override {
     registry.insert<IREE::Codegen::IREECodegenDialect, IREE::VM::VMDialect,
-                    IREE::VMVX::VMVXDialect>();
+                    IREE::VMVX::VMVXDialect,
+                    IREE::LinalgExt::IREELinalgExtDialect>();
   }
 
   IREE::HAL::DeviceTargetAttr getDefaultDeviceTarget(
diff --git a/llvm-external-projects/iree-dialects/CMakeLists.txt b/llvm-external-projects/iree-dialects/CMakeLists.txt
index 7773407..2d4d0db 100644
--- a/llvm-external-projects/iree-dialects/CMakeLists.txt
+++ b/llvm-external-projects/iree-dialects/CMakeLists.txt
@@ -11,37 +11,32 @@
 set(IREE_DIALECTS_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
 message(STATUS "Building iree-dialects project at ${IREE_DIALECTS_SOURCE_DIR} (into ${IREE_DIALECTS_BINARY_DIR})")
 
-# TODO: Fix this upstream so that global include directories are not needed.
-set(MLIR_MAIN_SRC_DIR ${LLVM_MAIN_SRC_DIR}/../mlir)
-set(MLIR_INCLUDE_DIR ${LLVM_MAIN_SRC_DIR}/../mlir/include)
-set(MLIR_GENERATED_INCLUDE_DIR ${LLVM_BINARY_DIR}/tools/mlir/include)
-
 # TODO: Needed for tablegen. Remove.
-include_directories(SYSTEM ${MLIR_INCLUDE_DIR})
-include_directories(SYSTEM ${MLIR_GENERATED_INCLUDE_DIR})
+include_directories(SYSTEM ${MLIR_INCLUDE_DIRS})
 include_directories(SYSTEM ${IREE_DIALECTS_SOURCE_DIR}/include)
 
 function(iree_dialects_target_includes target)
   set(_DIRS
-    $<BUILD_INTERFACE:${MLIR_INCLUDE_DIR}>
-    $<BUILD_INTERFACE:${MLIR_GENERATED_INCLUDE_DIR}>
-    $<BUILD_INTERFACE:${IREE_DIALECTS_SOURCE_DIR}/include>
-    $<BUILD_INTERFACE:${IREE_DIALECTS_BINARY_DIR}/include>
+    ${MLIR_INCLUDE_DIRS}
+    ${IREE_DIALECTS_SOURCE_DIR}/include
+    ${IREE_DIALECTS_BINARY_DIR}/include
   )
   # In LLVM parlance, the actual target may just be an interface and may not
   # be responsible for actually compiling anything. The corresponding obj.
   # target, when present, is just used for compilation and does not
   # contribute to the interface properties.
   # TODO: Normalize this upstream.
-  target_include_directories(${target} PUBLIC ${_DIRS})
+  set_property(TARGET ${target} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES
+    $<BUILD_INTERFACE:${_DIRS}>)
   if(TARGET obj.${target})
-    target_include_directories(obj.${target} PRIVATE ${_DIRS})
+    set_property(TARGET ${target} APPEND PROPERTY INCLUDE_DIRECTORIES
+      $<BUILD_INTERFACE:${_DIRS}>)
   endif()
 endfunction()
 
 # Configure CMake and tablegen.
-list(APPEND CMAKE_MODULE_PATH ${MLIR_MAIN_SRC_DIR}/cmake/modules)
-list(APPEND CMAKE_MODULE_PATH ${LLVM_MAIN_SRC_DIR}/cmake)
+list(APPEND CMAKE_MODULE_PATH ${MLIR_CMAKE_DIR})
+list(APPEND CMAKE_MODULE_PATH ${LLVM_CMAKE_DIR})
 
 include(TableGen)
 include(AddLLVM)
diff --git a/llvm-external-projects/iree-dialects/include/iree-dialects/Dialect/LinalgTransform/TransformInterpreterPassBase.h b/llvm-external-projects/iree-dialects/include/iree-dialects/Dialect/LinalgTransform/TransformInterpreterPassBase.h
index ec2365c..ae97eb4 100644
--- a/llvm-external-projects/iree-dialects/include/iree-dialects/Dialect/LinalgTransform/TransformInterpreterPassBase.h
+++ b/llvm-external-projects/iree-dialects/include/iree-dialects/Dialect/LinalgTransform/TransformInterpreterPassBase.h
@@ -21,6 +21,7 @@
 class Region;
 
 namespace transform {
+namespace iree_dialects {
 namespace detail {
 /// Template-free implementation of TransformInterpreterPassBase::initialize.
 LogicalResult
@@ -145,6 +146,7 @@
   std::shared_ptr<OwningOpRef<ModuleOp>> sharedTransformModule = nullptr;
 };
 
+} // namespace iree_dialects
 } // namespace transform
 } // namespace mlir
 #endif // IREE_DIALECTS_LINALG_TRANSFORM_TRANSFORM_INTERPRETER_UTILS_H
diff --git a/llvm-external-projects/iree-dialects/lib/Dialect/LinalgTransform/Passes/TransformInterpreter.cpp b/llvm-external-projects/iree-dialects/lib/Dialect/LinalgTransform/Passes/TransformInterpreter.cpp
index 7d7d7a3..33969d4 100644
--- a/llvm-external-projects/iree-dialects/lib/Dialect/LinalgTransform/Passes/TransformInterpreter.cpp
+++ b/llvm-external-projects/iree-dialects/lib/Dialect/LinalgTransform/Passes/TransformInterpreter.cpp
@@ -43,7 +43,7 @@
 /// This needs to be its own pass because the registration mechanism and ops
 /// available are different than for other interpreters.
 class TransformDialectInterpreter
-    : public transform::TransformInterpreterPassBase<
+    : public transform::iree_dialects::TransformInterpreterPassBase<
           TransformDialectInterpreter, PassWrapperStub> {
 public:
   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TransformDialectInterpreter)
diff --git a/llvm-external-projects/iree-dialects/lib/Dialect/LinalgTransform/Passes/TransformInterpreterPassBase.cpp b/llvm-external-projects/iree-dialects/lib/Dialect/LinalgTransform/Passes/TransformInterpreterPassBase.cpp
index 87d5457..53494a1 100644
--- a/llvm-external-projects/iree-dialects/lib/Dialect/LinalgTransform/Passes/TransformInterpreterPassBase.cpp
+++ b/llvm-external-projects/iree-dialects/lib/Dialect/LinalgTransform/Passes/TransformInterpreterPassBase.cpp
@@ -201,8 +201,9 @@
 
 /// Prints the module rooted at `root` to `os` and appends
 /// `transformContainer` if it is not nested in `root`.
-llvm::raw_ostream &printModuleForRepro(llvm::raw_ostream &os, Operation *root,
-                                       Operation *transformContainer) {
+static llvm::raw_ostream &printModuleForRepro(llvm::raw_ostream &os,
+                                              Operation *root,
+                                              Operation *transformContainer) {
   root->print(os);
   if (!root->isAncestor(transformContainer)) {
     transformContainer->print(os);
@@ -212,10 +213,11 @@
 
 /// Saves the payload and the transform IR into a temporary file and reports
 /// the file name to `os`.
-void saveReproToTempFile(
-    llvm::raw_ostream &os, Operation *target, Operation *transformContainer,
-    StringRef passName, const Pass::Option<std::string> &debugPayloadRootTag,
-    const Pass::Option<std::string> &debugTransformRootTag) {
+static void
+saveReproToTempFile(llvm::raw_ostream &os, Operation *target,
+                    Operation *transformContainer, StringRef passName,
+                    const Pass::Option<std::string> &debugPayloadRootTag,
+                    const Pass::Option<std::string> &debugTransformRootTag) {
   using llvm::sys::fs::TempFile;
   Operation *root = getRootOperation(target);
 
@@ -366,7 +368,8 @@
   });
 }
 
-LogicalResult transform::detail::interpreterBaseRunOnOperationImpl(
+LogicalResult
+transform::iree_dialects::detail::interpreterBaseRunOnOperationImpl(
     Operation *target, StringRef passName,
     const std::shared_ptr<OwningOpRef<ModuleOp>> &sharedTransformModule,
     const Pass::Option<std::string> &transformFileName,
@@ -468,7 +471,7 @@
   return success();
 }
 
-LogicalResult transform::detail::interpreterBaseInitializeImpl(
+LogicalResult transform::iree_dialects::detail::interpreterBaseInitializeImpl(
     MLIRContext *context, StringRef transformFileName,
     std::shared_ptr<OwningOpRef<ModuleOp>> &module) {
   OwningOpRef<ModuleOp> parsed;
diff --git a/llvm-external-projects/iree-dialects/test/lit.cfg.py b/llvm-external-projects/iree-dialects/test/lit.cfg.py
index 802853b..0a3fe29 100644
--- a/llvm-external-projects/iree-dialects/test/lit.cfg.py
+++ b/llvm-external-projects/iree-dialects/test/lit.cfg.py
@@ -60,11 +60,19 @@
 config.standalone_tools_dir = os.path.join(config.iree_dialects_obj_root, 'bin')
 
 # Tweak the PATH to include the tools dir.
-llvm_config.with_environment('PATH', config.llvm_tools_dir, append_path=True)
+llvm_config.with_environment('PATH',
+                             config.llvm_tools_binary_dir,
+                             append_path=True)
 
-tool_dirs = [config.llvm_tools_dir]
+tool_dirs = [config.llvm_tools_binary_dir]
 tools = [
     ToolSubst('%PYTHON', config.python_executable, unresolved='ignore'),
+    # Since we build iree-dialects out of tree, we don't have a common tools
+    # directory, so substitute binaries needed to an explicit path.
+    ToolSubst(
+        'iree-dialects-opt',
+        os.path.join(config.iree_dialects_obj_root,
+                     'tools/iree-dialects-opt/iree-dialects-opt'))
 ]
 
 llvm_config.add_tool_substitutions(tools, tool_dirs)
diff --git a/llvm-external-projects/iree-dialects/test/lit.site.cfg.py.in b/llvm-external-projects/iree-dialects/test/lit.site.cfg.py.in
index 9573f03..3f1cc11 100644
--- a/llvm-external-projects/iree-dialects/test/lit.site.cfg.py.in
+++ b/llvm-external-projects/iree-dialects/test/lit.site.cfg.py.in
@@ -4,10 +4,7 @@
 
 config.enable_bindings_python = @MLIR_ENABLE_BINDINGS_PYTHON@
 config.iree_dialects_obj_root = "@IREE_DIALECTS_BINARY_DIR@"
-config.llvm_src_root = "@LLVM_SOURCE_DIR@"
-config.llvm_obj_root = "@LLVM_BINARY_DIR@"
-config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
-config.llvm_lib_dir = "@LLVM_LIBS_DIR@"
+config.llvm_tools_binary_dir = "@LLVM_TOOLS_BINARY_DIR@"
 config.llvm_shlib_dir = "@SHLIBDIR@"
 config.llvm_shlib_ext = "@SHLIBEXT@"
 config.llvm_exe_ext = "@EXEEXT@"
diff --git a/llvm-external-projects/iree-dialects/tools/iree-dialects-opt/CMakeLists.txt b/llvm-external-projects/iree-dialects/tools/iree-dialects-opt/CMakeLists.txt
index 9b87ed5..65c0bf5 100644
--- a/llvm-external-projects/iree-dialects/tools/iree-dialects-opt/CMakeLists.txt
+++ b/llvm-external-projects/iree-dialects/tools/iree-dialects-opt/CMakeLists.txt
@@ -31,7 +31,7 @@
   MLIRVectorTransformOps
 )
 
-add_llvm_tool(iree-dialects-opt
+add_llvm_executable(iree-dialects-opt
   iree-dialects-opt.cpp
 
   DEPENDS
diff --git a/runtime/src/iree/builtins/device/bin/build.sh b/runtime/src/iree/builtins/device/bin/build.sh
index de15dfd..be9bc93 100755
--- a/runtime/src/iree/builtins/device/bin/build.sh
+++ b/runtime/src/iree/builtins/device/bin/build.sh
@@ -17,7 +17,7 @@
 CLANG_INCLUDE="${CLANG_INCLUDE:-/usr/lib/llvm-13/lib/clang/13.0.0/include/}"
 IREE_SRC_DIR="$(git rev-parse --show-toplevel)"
 IREE_BUILD_DIR="${IREE_BUILD_DIR:-${IREE_SRC_DIR?}/../build}"
-LLVM_AS="${LLVM_AS:-${IREE_BUILD_DIR}/third_party/llvm-project/llvm/bin/llvm-as}"
+LLVM_AS="${LLVM_AS:-${IREE_BUILD_DIR}/llvm-project/bin/llvm-as}"
 
 SCRIPT_DIR="$(realpath `dirname $0`)"
 OUT="${SCRIPT_DIR?}/"
diff --git a/runtime/src/iree/builtins/musl/bin/build.sh b/runtime/src/iree/builtins/musl/bin/build.sh
index 944ec0b..af982d0 100755
--- a/runtime/src/iree/builtins/musl/bin/build.sh
+++ b/runtime/src/iree/builtins/musl/bin/build.sh
@@ -15,9 +15,9 @@
 
 CLANG="${CLANG:-clang}"
 CLANGXX="${CLANGXX:-$(which clang++)}"
-LLVM_AS="${LLVM_AS:-${IREE_BUILD_DIR}/third_party/llvm-project/llvm/bin/llvm-as}"
-LLVM_LINK="${LLVM_LINK:-${IREE_BUILD_DIR}/third_party/llvm-project/llvm/bin/llvm-link}"
-LLVM_OPT="${LLVM_OPT:-${IREE_BUILD_DIR}/third_party/llvm-project/llvm/bin/opt}"
+LLVM_AS="${LLVM_AS:-${IREE_BUILD_DIR}/llvm-project/bin/llvm-as}"
+LLVM_LINK="${LLVM_LINK:-${IREE_BUILD_DIR}/llvm-project/bin/llvm-link}"
+LLVM_OPT="${LLVM_OPT:-${IREE_BUILD_DIR}/llvm-project/bin/opt}"
 
 IREE_SRC_DIR="$(git rev-parse --show-toplevel)"
 IREE_BUILD_DIR="${IREE_BUILD_DIR:-${IREE_SRC_DIR?}/../build}"
diff --git a/third_party/torch-mlir-dialects/CMakeLists.txt b/third_party/torch-mlir-dialects/CMakeLists.txt
index b0a5f47..4fbad5f 100644
--- a/third_party/torch-mlir-dialects/CMakeLists.txt
+++ b/third_party/torch-mlir-dialects/CMakeLists.txt
@@ -11,38 +11,32 @@
 set(TORCH_MLIR_DIALECTS_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
 message(STATUS "Building torch-mlir-dialects project at ${TORCH_MLIR_DIALECTS_SOURCE_DIR} (into ${TORCH_MLIR_DIALECTS_BINARY_DIR})")
 
-# TODO: Fix this upstream so that global include directories are not needed.
-set(MLIR_MAIN_SRC_DIR ${LLVM_MAIN_SRC_DIR}/../mlir)
-set(MLIR_INCLUDE_DIR ${LLVM_MAIN_SRC_DIR}/../mlir/include)
-set(MLIR_GENERATED_INCLUDE_DIR ${LLVM_BINARY_DIR}/tools/mlir/include)
-
 # TODO: Needed for tablegen. Remove.
-include_directories(SYSTEM ${MLIR_INCLUDE_DIR})
-include_directories(SYSTEM ${MLIR_GENERATED_INCLUDE_DIR})
+include_directories(SYSTEM ${MLIR_INCLUDE_DIRS})
 include_directories(SYSTEM ${TORCH_MLIR_DIALECTS_SOURCE_DIR}/include)
 
 function(torch_mlir_dialects_target_includes target)
-  set(_dirs
-    $<BUILD_INTERFACE:${MLIR_INCLUDE_DIR}>
-    $<BUILD_INTERFACE:${MLIR_GENERATED_INCLUDE_DIR}>
-    $<BUILD_INTERFACE:${TORCH_MLIR_DIALECTS_SOURCE_DIR}/include>
-    $<BUILD_INTERFACE:${TORCH_MLIR_DIALECTS_BINARY_DIR}/include>
+  set(_DIRS
+    ${MLIR_INCLUDE_DIRS}
+    ${TORCH_MLIR_DIALECTS_SOURCE_DIR}/include
+    ${TORCH_MLIR_DIALECTS_BINARY_DIR}/include
   )
   # In LLVM parlance, the actual target may just be an interface and may not
   # be responsible for actually compiling anything. The corresponding obj.
   # target, when present, is just used for compilation and does not
   # contribute to the interface properties.
   # TODO: Normalize this upstream.
-  target_include_directories(${target} PUBLIC ${_dirs})
+  set_property(TARGET ${target} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES
+    $<BUILD_INTERFACE:${_DIRS}>)
   if(TARGET obj.${target})
-    target_include_directories(obj.${target} PRIVATE ${_dirs})
+    set_property(TARGET ${target} APPEND PROPERTY INCLUDE_DIRECTORIES
+      $<BUILD_INTERFACE:${_DIRS}>)
   endif()
 endfunction()
 
 # Configure CMake and tablegen.
-list(APPEND CMAKE_MODULE_PATH ${MLIR_MAIN_SRC_DIR}/cmake/modules)
-list(APPEND CMAKE_MODULE_PATH ${LLVM_MAIN_SRC_DIR}/cmake)
-set(MLIR_TABLEGEN_EXE mlir-tblgen)
+list(APPEND CMAKE_MODULE_PATH ${MLIR_CMAKE_DIR})
+list(APPEND CMAKE_MODULE_PATH ${LLVM_CMAKE_DIR})
 
 include(TableGen)
 include(AddLLVM)
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 8640bff..007d9fd 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -279,19 +279,6 @@
   # below, which we use for cross-platform testing.
   set_target_properties(FileCheck PROPERTIES EXCLUDE_FROM_ALL OFF)
   set_target_properties(not PROPERTIES EXCLUDE_FROM_ALL OFF)
-
-  # Bundle the FileCheck and associated binaries from LLVM into our tests/bin
-  # directory so installed FileCheck tests are hermetic.
-  install(
-    TARGETS FileCheck
-    DESTINATION "tests/bin"
-    COMPONENT Tests
-  )
-  install(
-    TARGETS not
-    DESTINATION "tests/bin"
-    COMPONENT Tests
-  )
 elseif(NOT IREE_HOST_BIN_DIR)
   message(STATUS
       "*Not* building or importing IREE's compiler tools.\n   "