Pipeclean byo_llvm, add docs and support requested features. (#14164)

* Reworks LLVM/LLD/Clang/MLIR to use CMake initial-cache files to
configure distribution support
(https://llvm.org/docs/BuildingADistribution.html) for a more precise
build that better matches how the regular distributions are done. This
was suggested by a downstream user who helped work out the incantation.
* Implements libLLVM.so shared library redirection for LLVM
dependencies, when the backing LLVM is configured in such a way.
* Changes byo_llvm.sh to a shared-library based LLVM build.
* Fixes so that IREE can build against the installed MLIR, not just the
MLIR build tree.
* Fixes the iree-tblgen target to be correct in shared-library and
installed builds.
diff --git a/build_tools/cmake/build_and_test_byo_llvm.sh b/build_tools/cmake/build_and_test_byo_llvm.sh
index a2120ac..2b96c16 100755
--- a/build_tools/cmake/build_and_test_byo_llvm.sh
+++ b/build_tools/cmake/build_and_test_byo_llvm.sh
@@ -23,10 +23,10 @@
 # but that exists for good reasons as some users need control more over the
 # building and packaging than over the source repository, and that's good to
 # have test coverage for, and of course that's more convenient for us to test.
-build_tools/scripts/byo_llvm.sh build_llvm
+build_tools/llvm/byo_llvm.sh build_llvm
 
-build_tools/scripts/byo_llvm.sh build_mlir
-build_tools/scripts/byo_llvm.sh build_iree
+build_tools/llvm/byo_llvm.sh build_mlir
+build_tools/llvm/byo_llvm.sh build_iree
 
 echo "*********************** TESTING IREE **********************************"
 iree_build_dir="${IREE_BYOLLVM_BUILD_DIR}/iree"
diff --git a/build_tools/cmake/iree_cc_binary.cmake b/build_tools/cmake/iree_cc_binary.cmake
index 882b13b..ff46706 100644
--- a/build_tools/cmake/iree_cc_binary.cmake
+++ b/build_tools/cmake/iree_cc_binary.cmake
@@ -129,6 +129,7 @@
 
   # Replace dependencies passed by ::name with iree::package::name
   list(TRANSFORM _RULE_DEPS REPLACE "^::" "${_PACKAGE_NS}::")
+  iree_filter_cc_deps(_RULE_DEPS)
 
   # Implicit deps.
   if(IREE_IMPLICIT_DEFS_CC_DEPS)
diff --git a/build_tools/cmake/iree_cc_library.cmake b/build_tools/cmake/iree_cc_library.cmake
index bf2d7b8..868839a 100644
--- a/build_tools/cmake/iree_cc_library.cmake
+++ b/build_tools/cmake/iree_cc_library.cmake
@@ -6,6 +6,35 @@
 
 include(CMakeParseArguments)
 
+# iree_filter_cc_deps(DEPS_VAR)
+#
+# Filters a list of CC dependencies, making alterations as needed.
+# At present, this is used to redirect LLVM libraries to the libLLVM.so
+# dynamic library, when LLVM has been configured to link against it.
+# This is necessary to preserve the one-definition rule in the build graph
+# in a consistent way as to how AddLLVM.cmake does it.
+# Note that this only really works if libLLVM.so was configured to contain
+# "all" components. If this ever becomes unworkable, we may need to port the
+# component naming logic and selectively choose when to divert.
+function(iree_filter_cc_deps DEPS_VAR)
+  set(_deps ${${DEPS_VAR}})
+  set(_new_deps)
+  set(_modified FALSE)
+  foreach(_dep ${_deps})
+    # If linking against the LLVM dylib, then divert any LLVM prefixed
+    # targets there.
+    if(LLVM_LINK_LLVM_DYLIB AND _dep MATCHES "^LLVM")
+      list(APPEND _new_deps "LLVM")
+      set(_modified TRUE)
+    else()
+      list(APPEND _new_deps "${_dep}")
+    endif()
+  endforeach()
+  if(_modified)
+    set(${DEPS_VAR} "${_new_deps}" PARENT_SCOPE)
+  endif()
+endfunction()
+
 # iree_cc_library()
 #
 # CMake function to imitate Bazel's cc_library rule.
@@ -88,6 +117,7 @@
 
   # Replace dependencies passed by ::name with iree::package::name
   list(TRANSFORM _RULE_DEPS REPLACE "^::" "${_PACKAGE_NS}::")
+  iree_filter_cc_deps(_RULE_DEPS)
 
   # Check if this is a header-only library.
   # Note that as of February 2019, many popular OS's (for example, Ubuntu
diff --git a/build_tools/cmake/iree_llvm.cmake b/build_tools/cmake/iree_llvm.cmake
index 1731800..1068940 100644
--- a/build_tools/cmake/iree_llvm.cmake
+++ b/build_tools/cmake/iree_llvm.cmake
@@ -30,6 +30,10 @@
   # 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")
+
   add_subdirectory("third_party/llvm-project/llvm" "llvm-project" EXCLUDE_FROM_ALL)
 
   # Reset CMAKE_BUILD_TYPE to its previous setting.
@@ -87,6 +91,10 @@
   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.
@@ -96,11 +104,13 @@
     set(LLVM_EXTERNAL_LIT "${IREE_SOURCE_DIR}/third_party/llvm-project/llvm/utils/lit/lit.py")
   endif()
 
-  set(IREE_LLVM_LINK_BINARY "${LLVM_INSTALL_DIR}/llvm/bin/llvm-link${CMAKE_EXECUTABLE_SUFFIX}")
-  set(IREE_LLD_BINARY "${LLVM_INSTALL_DIR}/llvm/bin/lld${CMAKE_EXECUTABLE_SUFFIX}")
-  set(IREE_CLANG_BINARY "${LLVM_INSTALL_DIR}/llvm/bin/clang${CMAKE_EXECUTABLE_SUFFIX}")
-  string(REGEX REPLACE "[^0-9].*" "" _LLVM_VERSION_MAJOR "${LLVM_VERSION}")
-  set(IREE_CLANG_BUILTIN_HEADERS_PATH "${LLVM_INSTALL_DIR}/llvm/lib/clang/${_LLVM_VERSION_MAJOR}/include/")
+  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_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()
 endmacro()
 
 # iree_llvm_set_bundled_cmake_options()
diff --git a/build_tools/llvm/README.md b/build_tools/llvm/README.md
new file mode 100644
index 0000000..935a9d9
--- /dev/null
+++ b/build_tools/llvm/README.md
@@ -0,0 +1,54 @@
+# Alternate LLVM dependency management
+
+The default build instructions for IREE do an in-tree build of LLVM and all
+LLVM sub-projects in a single cmake session. While this has the fewest moving
+parts and allows edit-compile-run across the entire project, it is not the
+only way to proceed.
+
+This directory contains helpers and scripts to build-your-own LLVM and have
+IREE use that. LLVM is a complicated dependency to take, and not all
+possible installation modalities are supported. However, for those that are,
+we will attempt to document here and test in the CI.
+
+## Baseline byo_llvm.sh script
+
+The baseline `byo_llvm.sh` script builds the stack of:
+
+* LLVM (with libLLVM.so)
+* Clang (bundled with the LLVM build)
+* LLD (bundled with the LLVM build)
+* MLIR (as a standalone installation that depends on LLVM)
+* IREE (depending on all of the above)
+
+This split is likely the most advanced configuration possible and represents
+a common use case for hardware enablement:
+
+* Often there is an LLVM installation for a part with proprietary backend, etc.
+* IREE is tightly coupled to MLIR, which can drift from the installed LLVM
+  so long as there are not LLVM API breaks.
+* libLLVM.so is built and linked against.
+
+Note that when built in this configuration, the resulting *installed* IREE
+can only be used with `LD_LIBRARY_PATH` set appropriately (or if built against
+a system LLVM). Per usual CMake policy, binaries in the build tree will
+always be hard-coded to the path on the build machine and do not need this.
+
+Always make sure that there is not the same version of LLVM's shared library
+on your library path in a way that will cause it to take precedence.
+
+### Usage:
+
+Note that full CMake command lines are logged so that you can create your
+own scripting as needed.
+
+```
+./build_tools/llvm/byo_llvm.sh build_llvm
+./build_tools/llvm/byo_llvm.sh build_mlir
+./build_tools/llvm/byo_llvm.sh build_iree
+```
+
+Tests can be run with:
+
+```
+./build_tools/llvm/byo_llvm.sh test_iree
+```
diff --git a/build_tools/scripts/byo_llvm.sh b/build_tools/llvm/byo_llvm.sh
similarity index 86%
rename from build_tools/scripts/byo_llvm.sh
rename to build_tools/llvm/byo_llvm.sh
index f5ebdff..4bba7f2 100755
--- a/build_tools/scripts/byo_llvm.sh
+++ b/build_tools/llvm/byo_llvm.sh
@@ -85,13 +85,11 @@
   main_build_dir="${IREE_BYOLLVM_BUILD_DIR}/llvm"
   main_install_dir="${IREE_BYOLLVM_INSTALL_DIR}/llvm"
   targets_to_build="${LLVM_TARGETS_TO_BUILD:-X86}"
-  enable_projects="${LLVM_ENABLE_TARGETS:-clang;lld}"
 
-  cmake_options="-DLLVM_ENABLE_PROJECTS='${enable_projects}' -DLLVM_TARGETS_TO_BUILD='${targets_to_build}'"
-  cmake_options="${cmake_options} -DLLVM_BUILD_EXAMPLES=OFF"
-  cmake_options="${cmake_options} -DLLVM_INSTALL_UTILS=ON"
+  cmake_options="-DLLVM_TARGETS_TO_BUILD='${targets_to_build}'"
   cmake_options="${cmake_options} -DCMAKE_BUILD_TYPE=Release"
-  cmake_options="${cmake_options} -DLLVM_ENABLE_ASSERTIONS=ON"
+  cmake_options="${cmake_options} -C $TD/llvm_config.cmake"
+  cmake_options="${cmake_options} -DCMAKE_INSTALL_PREFIX=${main_install_dir}"
   cmake_options="${cmake_options} $(print_toolchain_config)"
   if $has_lld; then
     cmake_options="${cmake_options} -DLLVM_ENABLE_LLD=ON"
@@ -102,8 +100,9 @@
   echo "CMake Options: ${cmake_options}"
   cmake -GNinja -S "${LLVM_SOURCE_DIR}/llvm" -B "${main_build_dir}" \
     $cmake_options
-  cmake --build "${main_build_dir}"
-  cmake -DCMAKE_INSTALL_PREFIX="${main_install_dir}" -P "${main_build_dir}/cmake_install.cmake"
+  cmake --build "${main_build_dir}" \
+    --target install-toolchain-distribution \
+    --target install-development-distribution
 }
 
 do_build_mlir() {
@@ -114,10 +113,9 @@
 
   cmake_options="-DLLVM_DIR='${main_install_dir}/lib/cmake/llvm'"
   cmake_options="${cmake_options} -DPython3_EXECUTABLE='$(which $python3_command)'"
-  cmake_options="${cmake_options} -DLLVM_INSTALL_TOOLCHAIN_ONLY=OFF"
-  cmake_options="${cmake_options} -DLLVM_BUILD_TOOLS=ON"
-  cmake_options="${cmake_options} -DLLVM_ENABLE_ASSERTIONS=ON"
   cmake_options="${cmake_options} -DMLIR_ENABLE_BINDINGS_PYTHON=ON"
+  cmake_options="${cmake_options} -DCMAKE_INSTALL_PREFIX=${mlir_install_dir}"
+  cmake_options="${cmake_options} -C $TD/mlir_config.cmake"
   cmake_options="${cmake_options} $(print_toolchain_config)"
   if $has_lld; then
     cmake_options="${cmake_options} -DLLVM_ENABLE_LLD=ON"
@@ -128,18 +126,18 @@
   echo "CMake Options: ${cmake_options}"
   cmake -GNinja -S "${LLVM_SOURCE_DIR}/mlir" -B "${mlir_build_dir}" \
     $cmake_options
-  cmake --build "${mlir_build_dir}"
-  cmake -DCMAKE_INSTALL_PREFIX="${mlir_install_dir}" -P "${mlir_build_dir}/cmake_install.cmake"
+  # TODO: We should be able to just do install-mlirdevelopment-distribution to
+  # tightly control distribution, but this presently leaves out the Python
+  # sources needed to build downstream Python bindings. Once this is fixed,
+  # we should use the more fine grained install target.
+  cmake --build "${mlir_build_dir}" \
+    --target install
 }
 
 print_iree_config() {
   llvm_cmake_dir="${IREE_BYOLLVM_INSTALL_DIR}/llvm/lib/cmake/llvm"
   lld_cmake_dir="${IREE_BYOLLVM_INSTALL_DIR}/llvm/lib/cmake/lld"
   clang_cmake_dir="${IREE_BYOLLVM_INSTALL_DIR}/llvm/lib/cmake/clang"
-  # TODO: There seem to be utility exports missing from installed MLIR,
-  # so using the build tree for now. This isn't great but needs fixing
-  # upstream.
-  #mlir_cmake_dir="${IREE_BYOLLVM_INSTALL_DIR}/mlir/lib/cmake/mlir"
   mlir_cmake_dir="${IREE_BYOLLVM_BUILD_DIR}/mlir/lib/cmake/mlir"
 
   if ! [ -d "$llvm_cmake_dir" ]; then
@@ -159,7 +157,7 @@
     return 1
   fi
 
-  echo "-DLLVM_DIR='$llvm_cmake_dir' -DLLD_DIR='$lld_cmake_dir' -DMLIR_DIR='$mlir_cmake_dir' -DIREE_BUILD_BUNDLED_LLVM=OFF -DLLVM_INSTALL_DIR=${IREE_BYOLLVM_INSTALL_DIR}"
+  echo "-DLLVM_DIR='$llvm_cmake_dir' -DLLD_DIR='$lld_cmake_dir' -DMLIR_DIR='$mlir_cmake_dir' -DClang_DIR='$clang_cmake_dir' -DIREE_BUILD_BUNDLED_LLVM=OFF"
 }
 
 do_build_iree() {
@@ -181,7 +179,6 @@
   cmake_options="${cmake_options} -DIREE_HAL_DRIVER_LOCAL_SYNC=ON"
   cmake_options="${cmake_options} -DIREE_HAL_DRIVER_LOCAL_TASK=ON"
   cmake_options="${cmake_options} -DCMAKE_BUILD_TYPE=Release"
-  cmake_options="${cmake_options} -DIREE_ENABLE_ASSERTIONS=ON"
   cmake_options="${cmake_options} $(print_toolchain_config)"
   if $has_lld; then
     cmake_options="${cmake_options} -DIREE_ENABLE_LLD=ON"
diff --git a/build_tools/llvm/llvm_config.cmake b/build_tools/llvm/llvm_config.cmake
new file mode 100644
index 0000000..1af9025
--- /dev/null
+++ b/build_tools/llvm/llvm_config.cmake
@@ -0,0 +1,128 @@
+### Components built ###
+
+set(LLVM_ENABLE_PROJECTS "clang;clang-tools-extra;lld" CACHE STRING "")
+
+### Target architectures ###
+
+# Compiler target architectures
+set(LLVM_TARGETS_TO_BUILD "X86" CACHE STRING "")
+
+set(LLVM_ENABLE_RUNTIMES "compiler-rt" CACHE STRING "")
+
+# CMake builtin variables and modules are not available for this cache file
+# Gather directly build/host information
+execute_process(COMMAND "uname" "-m" OUTPUT_VARIABLE _UNAME_M)
+string(STRIP ${_UNAME_M} BUILD_MACHINE_ARCH)
+
+### Default settings for the toolchain ###
+
+# Use the LLVM components
+set(CLANG_DEFAULT_OBJCOPY llvm-objcopy CACHE STRING "")
+set(CLANG_DEFAULT_LINKER lld CACHE STRING "")
+
+set(CLANG_ENABLE_STATIC_ANALYZER ON CACHE BOOL "")
+set(LLVM_ENABLE_LIBCXX OFF CACHE BOOL "")
+set(LLVM_ENABLE_ZLIB ON CACHE BOOL "")
+
+### Disk size optimizations ###
+
+# All the tools will use libllvm shared library
+set(LLVM_BUILD_LLVM_DYLIB ON CACHE BOOL "")
+set(LLVM_LINK_LLVM_DYLIB ON CACHE BOOL "")
+
+# When exceptions are disabled, unwind tables are large and useless
+set(LLVM_ENABLE_UNWIND_TABLES OFF CACHE BOOL "")
+
+# Mildly useful misc stuff (which might also be hard to cross-compile)
+set(CLANG_ENABLE_ARCMT OFF CACHE BOOL "")
+set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "")
+set(LLVM_ENABLE_TERMINFO OFF CACHE BOOL "")
+set(LLVM_ENABLE_Z3_SOLVER OFF CACHE BOOL "")
+set(LLVM_INCLUDE_DOCS OFF CACHE BOOL "")
+set(LLVM_INCLUDE_GO_TESTS OFF CACHE BOOL "")
+
+# Do not store debug information by default.
+set(CMAKE_BUILD_TYPE Release CACHE STRING "")
+
+# Use the distributions below for the installation
+set(LLVM_INSTALL_TOOLCHAIN_ONLY OFF CACHE BOOL "")
+
+### Distributions ###
+
+set(LLVM_DISTRIBUTIONS
+      Toolchain
+      Development
+    CACHE STRING "")
+
+set(LLVM_TOOLCHAIN_TOOLS
+  llvm-addr2line
+  llvm-ar
+  llvm-cxxfilt
+  llvm-dis
+  llvm-dwarfdump
+  llvm-lib
+  llvm-link
+  llvm-mc
+  llvm-nm
+  llvm-objcopy
+  llvm-objdump
+  llvm-rc
+  llvm-ranlib
+  llvm-readelf
+  llvm-readobj
+  llvm-size
+  llvm-strip
+  llvm-symbolizer
+  llvm-xray
+  CACHE STRING "")
+
+set(LLVM_BUILD_UTILS ON CACHE BOOL "")
+set(LLVM_INSTALL_UTILS ON CACHE BOOL "")
+set(LLVM_TOOLCHAIN_UTILITIES
+    FileCheck
+    count
+    not
+    CACHE STRING "")
+
+set(LLVM_RUNTIME_DISTRIBUTION_COMPONENTS
+    CACHE STRING "")
+
+set(LLVM_Toolchain_DISTRIBUTION_COMPONENTS
+      builtins
+      runtimes
+      clang
+      clang-cpp
+      clang-format
+      clang-offload-bundler
+      clang-resource-headers
+      clang-tidy
+      clangd
+      libclang
+      lld
+      LLVM
+      LTO
+      ${LLVM_TOOLCHAIN_TOOLS}
+      ${LLVM_TOOLCHAIN_UTILITIES}
+    CACHE STRING "")
+
+set(LLVM_Development_DISTRIBUTION_COMPONENTS
+      # LLVM
+      cmake-exports
+      development-cmake-exports
+      toolchain-cmake-exports
+      llc
+      llvm-config
+      llvm-headers
+      llvm-libraries
+      opt
+      Remarks
+      # Clang
+      clang-cmake-exports
+      clang-development-cmake-exports
+      clang-toolchain-cmake-exports
+      clang-headers
+      clang-libraries
+      # LLD
+      lld-cmake-exports
+      lld-toolchain-cmake-exports
+    CACHE STRING "")
diff --git a/build_tools/llvm/mlir_config.cmake b/build_tools/llvm/mlir_config.cmake
new file mode 100644
index 0000000..2214d9d
--- /dev/null
+++ b/build_tools/llvm/mlir_config.cmake
@@ -0,0 +1,58 @@
+# export MLIR_BUILD_DIR=$HOME/tmp/mlirbuild
+# export MLIR_INSTALL_DIR=$HOME/tmp/mlirroot
+# cmake -G Ninja \
+#     -B "$MLIR_BUILD_DIR" -S third_party/llvm-project/mlir \
+#     -DLLVM_DIR="${LLVM_INSTALL_DIR}/lib/cmake/llvm" \
+#     -C build_tools/llvm/mlir_config.cmake \
+#     -DCMAKE_BUILD_TYPE="Release" \
+#     -DPython3_EXECUTABLE='$(which $python3_command)' \
+#     -DMLIR_ENABLE_BINDINGS_PYTHON=ON \
+#     -DCMAKE_INSTALL_PREFIX="$MLIR_INSTALL_DIR" \
+#     -DCMAKE_C_COMPILER_LAUNCHER=ccache \
+#     -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
+#     -DCMAKE_C_COMPILER=clang \
+#     -DCMAKE_CXX_COMPILER=clang++ \
+#     -DCMAKE_LINKER=lld
+# TODO: For some reason Python sources aren't selectable as part of a
+# distribution and targets don't show up properly.
+# ninja -C $MLIR_BUILD_DIR install-mlirdevelopment-distribution
+# The full install target seems to get everything...
+# ninja -C $MLIR_BUILD_DIR install
+
+if(NOT EXISTS ${LLVM_DIR})
+  message(FATAL_ERROR "LLVM_DIR (${LLVM_DIR}) does not exist")
+endif()
+
+# When exceptions are disabled, unwind tables are large and useless
+set(LLVM_ENABLE_UNWIND_TABLES OFF CACHE BOOL "")
+
+# Do not store debug information by default.
+set(CMAKE_BUILD_TYPE Release CACHE STRING "")
+
+# Use the distributions below for the installation
+set(LLVM_INSTALL_TOOLCHAIN_ONLY OFF CACHE BOOL "")
+
+# Build tools and utils.
+set(LLVM_BUILD_TOOLS ON CACHE BOOL "")
+set(LLVM_BUILD_UTILS ON CACHE BOOL "")
+
+### Distributions ###
+
+set(LLVM_DISTRIBUTIONS
+    MlirDevelopment
+    CACHE STRING "")
+
+set(LLVM_MLIR_TOOLS
+      mlir-opt
+      mlir-reduce
+      mlir-tblgen
+      mlir-translate
+    CACHE STRING "")
+
+set(LLVM_MlirDevelopment_DISTRIBUTION_COMPONENTS
+      mlir-cmake-exports
+      mlir-headers
+      mlir-libraries
+      MLIRPythonModules
+      ${LLVM_MLIR_TOOLS}
+    CACHE STRING "")
diff --git a/compiler/src/iree/compiler/Dialect/HAL/Target/LLVMCPU/CMakeLists.txt b/compiler/src/iree/compiler/Dialect/HAL/Target/LLVMCPU/CMakeLists.txt
index ac6450b..1bdf907 100644
--- a/compiler/src/iree/compiler/Dialect/HAL/Target/LLVMCPU/CMakeLists.txt
+++ b/compiler/src/iree/compiler/Dialect/HAL/Target/LLVMCPU/CMakeLists.txt
@@ -145,12 +145,19 @@
 
 function(_add_optional_llvm_target_deps target)
   # Key off of the CodeGen target and then include the rest.
+  # Note that for LLVM DYLIB builds, we just key off of the presence of the
+  # static CodeGen target and iree_filter_cc_deps will replace it with the
+  # dylib accordingly (it is just a check to see if we have compiled in
+  # support for the target).
   if(TARGET "LLVM${target}CodeGen")
-    target_link_libraries(IREELLVMCPUTargetDeps INTERFACE
+    set(_target_deps
       "LLVM${target}AsmParser"
       "LLVM${target}CodeGen"
       "LLVM${target}Desc"
-      "LLVM${target}Info")
+      "LLVM${target}Info"
+    )
+    iree_filter_cc_deps(_target_deps)
+    target_link_libraries(IREELLVMCPUTargetDeps INTERFACE ${_target_deps})
   endif()
 endfunction()
 
diff --git a/llvm-external-projects/iree-dialects/test/lit.cfg.py b/llvm-external-projects/iree-dialects/test/lit.cfg.py
index c0cd5c2..ef6ead1 100644
--- a/llvm-external-projects/iree-dialects/test/lit.cfg.py
+++ b/llvm-external-projects/iree-dialects/test/lit.cfg.py
@@ -70,14 +70,7 @@
 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"
-        ),
-    ),
+    ToolSubst("iree-dialects-opt"),
 ]
 
 llvm_config.add_tool_substitutions(tools, tool_dirs)
diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel
index 231a66a..34ace60 100644
--- a/tools/BUILD.bazel
+++ b/tools/BUILD.bazel
@@ -219,13 +219,12 @@
 iree_compiler_cc_binary(
     name = "iree-tblgen",
     srcs = [
+        "iree-tblgen.cpp",
         "//compiler/src/iree/compiler/Dialect/VM/Tools:GenSrcs",
-        "@llvm-project//mlir:tools/mlir-tblgen/mlir-tblgen.cpp",
     ],
     tags = ["hostonly"],
     deps = [
         "//build_tools:default_linkopts",
-        "//compiler/src/iree/compiler/Utils",
         "@llvm-project//llvm:Support",
         "@llvm-project//llvm:TableGen",
         "@llvm-project//mlir:MlirTableGenMain",
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index e37e639..8d440fd 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -229,20 +229,21 @@
     )
   endif()
 
+  # Tablegen binaries are special snowflakes among special snowflakes.
+  # They must be statically linked against internal LLVM libraries, and they
+  # therefore must not depend on anything outside of the upstream tablegen
+  # libraries. These targets are specially set up to link in the correct
+  # way. This is a necessary diversion from how Bazel deals with it (which
+  # must deep-link to everything to satisfy its checks).
   iree_cc_binary(
     NAME
       iree-tblgen
     SRCS
-      "${IREE_ROOT_DIR}/third_party/llvm-project/mlir/tools/mlir-tblgen/mlir-tblgen.cpp"
+      "iree-tblgen.cpp"
       "${IREE_SOURCE_DIR}/compiler/src/iree/compiler/Dialect/VM/Tools/VMOpEncoderGen.cpp"
       "${IREE_SOURCE_DIR}/compiler/src/iree/compiler/Dialect/VM/Tools/VMOpTableGen.cpp"
     DEPS
-      LLVMSupport
-      LLVMTableGen
-      MLIRSupport
-      MLIRTableGen
       MLIRTblgenLib
-      iree::compiler::Utils
     HOSTONLY
   )
 
diff --git a/tools/iree-tblgen.cpp b/tools/iree-tblgen.cpp
new file mode 100644
index 0000000..cccf912
--- /dev/null
+++ b/tools/iree-tblgen.cpp
@@ -0,0 +1,23 @@
+// 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
+
+// Adapted from mlir-tblgen.cpp. Simply delegates through to MlirTblgenMain.
+
+#include "llvm/TableGen/Record.h"
+#include "mlir/TableGen/GenInfo.h"
+#include "mlir/Tools/mlir-tblgen/MlirTblgenMain.h"
+
+using namespace llvm;
+using namespace mlir;
+
+// Generator that prints records.
+GenRegistration printRecords("print-records", "Print all records to stdout",
+                             [](const RecordKeeper &records, raw_ostream &os) {
+                               os << records;
+                               return false;
+                             });
+
+int main(int argc, char **argv) { return MlirTblgenMain(argc, argv); }