[Bazel] Migrate to bzlmod for LLVM compatibility (#22771)

LLVM has fully migrated to bzlmod and now requires bzlmod features. This
change migrates IREE's Bazel build to use bzlmod.

Changes:
- Add MODULE.bazel with dependencies matching LLVM's versions
- Add build_tools/bazel/extensions.bzl module extension for creating
IREE-specific repositories
- Enable bzlmod in iree.bazelrc
- Update flatcc BUILD.overlay to use includes= instead of hardcoded
-Iexternal/... paths (bzlmod changes external paths)
- Update bitcode library paths for bzlmod repository naming
- Update llvm-project submodule to include upstream bzlmod changes
- Add BZLMOD_LLVM.md documentation for downstream consumers

The iree_extension creates llvm-raw only when IREE is the root module,
allowing downstream consumers to provide their own LLVM by creating
their own llvm-raw repository.

---------

Signed-off-by: default <bartel@roofline.ai>
diff --git a/.gitignore b/.gitignore
index 47630fc..98a5c89 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,7 +32,6 @@
 
 # Bazel artifacts
 **/bazel-*
-MODULE.bazel
 MODULE.bazel.lock
 
 # Executables
diff --git a/MODULE.bazel b/MODULE.bazel
new file mode 100644
index 0000000..c45ab5e
--- /dev/null
+++ b/MODULE.bazel
@@ -0,0 +1,88 @@
+# Copyright 2024 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
+
+"""Bazel module configuration for IREE."""
+
+module(
+    name = "iree_core",
+    version = "0.0.1",
+)
+
+# Core Bazel dependencies - versions aligned with LLVM's MODULE.bazel
+bazel_dep(name = "apple_support", version = "1.24.1", repo_name = "build_bazel_apple_support")
+bazel_dep(name = "bazel_skylib", version = "1.8.2")
+bazel_dep(name = "platforms", version = "1.0.0")
+bazel_dep(name = "rules_cc", version = "0.2.11")
+bazel_dep(name = "rules_python", version = "1.6.3")
+
+# Python pip dependencies
+pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
+pip.parse(
+    hub_name = "pip",
+    python_version = "3.11",
+    requirements_lock = "//build_tools/bazel:requirements.txt",
+)
+use_repo(pip, "pip")
+
+bazel_dep(name = "rules_shell", version = "0.6.1")
+
+# Required by LLVM (for clang-fuzzer tools)
+bazel_dep(name = "protobuf", version = "31.1", repo_name = "com_google_protobuf")
+
+# LLVM also needs these
+bazel_dep(name = "rules_android", version = "0.6.6")
+bazel_dep(name = "rules_foreign_cc", version = "0.15.1")
+bazel_dep(name = "zlib-ng", version = "2.0.7", repo_name = "llvm_zlib")
+bazel_dep(name = "zstd", version = "1.5.7", repo_name = "llvm_zstd")
+
+# LLVM project overlay module
+bazel_dep(name = "llvm-project-overlay", version = "main")
+local_path_override(
+    module_name = "llvm-project-overlay",
+    path = "third_party/llvm-project/utils/bazel",
+)
+
+# Use LLVM's extension for third-party dependencies
+llvm_repos_ext = use_extension(
+    "@llvm-project-overlay//:extensions.bzl",
+    "llvm_repos_extension",
+)
+use_repo(
+    llvm_repos_ext,
+    "gmp",
+    "mpc",
+    "mpfr",
+    "nanobind",
+    "pfm",
+    "pybind11",
+    "pyyaml",
+    "robin_map",
+    # Note: vulkan_headers is provided by iree_ext with IREE's custom BUILD.overlay
+    "vulkan_sdk",
+)
+
+# IREE extension creates llvm-raw (when IREE is root) and other IREE-specific repos
+iree_ext = use_extension("//build_tools/bazel:extensions.bzl", "iree_extension")
+use_repo(
+    iree_ext,
+    "com_github_dvidelabs_flatcc",
+    "com_google_benchmark",
+    "com_google_googletest",
+    "hsa_runtime_headers",
+    "iree_cuda",
+    "llvm-raw",
+    "nccl",
+    "spirv_cross",
+    "stablehlo",
+    "tracy_client",
+    "vulkan_headers",
+    "webgpu_headers",
+)
+
+# Configure LLVM using the llvm-raw repo (creates llvm-project)
+llvm_configure = use_repo_rule("@llvm-raw//utils/bazel:configure.bzl", "llvm_configure")
+
+llvm_configure(name = "llvm-project")
diff --git a/WORKSPACE b/WORKSPACE
deleted file mode 100644
index 392016b..0000000
--- a/WORKSPACE
+++ /dev/null
@@ -1,103 +0,0 @@
-# Copyright 2019 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
-
-# Workspace file for the IREE project.
-# buildozer: disable=positional-args
-
-workspace(name = "iree_core")
-
-load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
-load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
-
-###############################################################################
-# Skylib
-http_archive(
-    name = "bazel_skylib",
-    sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506",
-    urls = [
-        "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
-        "https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
-    ],
-)
-
-load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
-
-bazel_skylib_workspace()
-###############################################################################
-
-###############################################################################
-# llvm-project
-
-maybe(
-    http_archive,
-    name = "rules_shell",
-    sha256 = "e6b87c89bd0b27039e3af2c5da01147452f240f75d505f5b6880874f31036307",
-    strip_prefix = "rules_shell-0.6.1",
-    url = "https://github.com/bazelbuild/rules_shell/releases/download/v0.6.1/rules_shell-v0.6.1.tar.gz",
-)
-
-load("@rules_shell//shell:repositories.bzl", "rules_shell_dependencies", "rules_shell_toolchains")
-
-rules_shell_dependencies()
-
-rules_shell_toolchains()
-
-new_local_repository(
-    name = "llvm-raw",
-    build_file_content = "# empty",
-    path = "third_party/llvm-project",
-)
-
-load("@llvm-raw//utils/bazel:configure.bzl", "llvm_configure")
-
-llvm_configure(
-    name = "llvm-project",
-    # Keep this in sync with the targets in iree_llvm.cmake.
-    targets = [
-        "AArch64",
-        "ARM",
-        "RISCV",
-        "X86",
-        "NVPTX",
-        "AMDGPU",
-        "WebAssembly",
-    ],
-)
-
-###############################################################################
-
-###############################################################################
-# All other IREE submodule dependencies
-
-load("//build_tools/bazel:workspace.bzl", "configure_iree_cuda_deps", "configure_iree_submodule_deps")
-
-configure_iree_submodule_deps()
-
-configure_iree_cuda_deps()
-
-###############################################################################
-maybe(
-    http_archive,
-    name = "llvm_zlib",
-    build_file = "@llvm-raw//utils/bazel/third_party_build:zlib-ng.BUILD",
-    sha256 = "e36bb346c00472a1f9ff2a0a4643e590a254be6379da7cddd9daeb9a7f296731",
-    strip_prefix = "zlib-ng-2.0.7",
-    urls = [
-        "https://github.com/zlib-ng/zlib-ng/archive/refs/tags/2.0.7.zip",
-    ],
-)
-
-###############################################################################
-maybe(
-    http_archive,
-    name = "llvm_zstd",
-    build_file = "@llvm-raw//utils/bazel/third_party_build:zstd.BUILD",
-    sha256 = "7c42d56fac126929a6a85dbc73ff1db2411d04f104fae9bdea51305663a83fd0",
-    strip_prefix = "zstd-1.5.2",
-    urls = [
-        "https://github.com/facebook/zstd/releases/download/v1.5.2/zstd-1.5.2.tar.gz",
-    ],
-)
diff --git a/build_tools/bazel/BZLMOD_LLVM.md b/build_tools/bazel/BZLMOD_LLVM.md
new file mode 100644
index 0000000..0782511
--- /dev/null
+++ b/build_tools/bazel/BZLMOD_LLVM.md
@@ -0,0 +1,215 @@
+# Using IREE with a Custom LLVM via Bzlmod
+
+This document explains how projects that depend on IREE can provide their own LLVM
+instead of using IREE's bundled version.
+
+## Terminology
+
+### Bzlmod
+Bazel's module system (introduced in Bazel 6.0, default in Bazel 7.0+). It replaces
+the legacy WORKSPACE file with `MODULE.bazel` for managing external dependencies.
+
+### Root Module
+The top-level project being built. In bzlmod, only the root module's `MODULE.bazel`
+is fully evaluated - dependency modules have limited control over the build graph.
+
+### Module Extension
+A mechanism for creating repositories dynamically in bzlmod. Extensions are defined
+in `.bzl` files and invoked via `use_extension()` in MODULE.bazel.
+
+### `use_extension()`
+Runs a module extension's implementation function, which typically creates repositories.
+Returns an extension proxy that can be passed to `use_repo()`.
+
+### `use_repo()`
+Imports repositories created by a module extension into the current module's visibility
+scope. Without `use_repo()`, repos created by an extension exist but aren't accessible
+to your BUILD files.
+
+```python
+# Extension creates repos internally
+ext = use_extension("@some_module//:extensions.bzl", "some_extension")
+
+# use_repo makes specific repos visible as @repo_a, @repo_b, etc.
+use_repo(ext, "repo_a", "repo_b")
+```
+
+### `use_repo_rule()`
+Imports a repository rule from another module so it can be called directly in
+MODULE.bazel to create a repository.
+
+### `llvm-raw`
+A repository containing the raw LLVM source code. This is the input to the LLVM
+build configuration.
+
+### `llvm-project`
+The configured LLVM repository created by `llvm_configure`. It overlays Bazel BUILD
+files onto the `llvm-raw` source and extracts CMake configuration variables.
+
+### `llvm-project-overlay`
+The bzlmod module name for LLVM's Bazel integration (located at
+`llvm-project/utils/bazel/`). It provides the `llvm_repos_extension` and
+`llvm_configure` rule.
+
+## How It Works
+
+IREE's module extension (`iree_extension`) creates the `llvm-raw` repository
+**only when IREE is the root module**:
+
+```python
+# In build_tools/bazel/extensions.bzl
+def _iree_extension_impl(module_ctx):
+    if any([m.is_root and m.name == "iree_core" for m in module_ctx.modules]):
+        new_local_repository(
+            name = "llvm-raw",
+            build_file_content = "# empty",
+            path = "third_party/llvm-project",
+        )
+    # ... other repos
+```
+
+When your project depends on IREE, IREE is **not** the root module - your project is.
+Therefore, IREE's extension will not create `llvm-raw`, and you must provide it yourself.
+
+## MODULE.bazel Ordering
+
+The order of statements in MODULE.bazel matters:
+
+1. `module()` - must be first
+2. `bazel_dep()` - declare module dependencies
+3. `local_path_override()` - must come after the `bazel_dep()` it overrides
+4. `use_extension()` - must come after the `bazel_dep()` that provides the extension
+5. `use_repo()` - must come after its corresponding `use_extension()`
+6. `use_repo_rule()` + invocation - can reference repos created by earlier extensions
+
+## Example: Using Your Own LLVM
+
+```python
+# my_project/MODULE.bazel
+
+module(
+    name = "my_project",
+    version = "1.0.0",
+)
+
+# Standard bazel dependencies (must match or be compatible with IREE's versions)
+bazel_dep(name = "bazel_skylib", version = "1.8.2")
+bazel_dep(name = "platforms", version = "1.0.0")
+bazel_dep(name = "rules_cc", version = "0.2.11")
+# ... other deps as needed
+
+# Depend on IREE
+bazel_dep(name = "iree_core", version = "0.0.1")
+
+# Override IREE to use your local checkout (optional, for development)
+local_path_override(
+    module_name = "iree_core",
+    path = "third_party/iree",
+)
+
+# Depend on LLVM overlay module
+bazel_dep(name = "llvm-project-overlay", version = "main")
+local_path_override(
+    module_name = "llvm-project-overlay",
+    path = "my/custom/llvm-project/utils/bazel",
+)
+
+# Create your own llvm-raw repository pointing to your LLVM
+new_local_repository = use_repo_rule(
+    "@bazel_tools//tools/build_defs/repo:local.bzl",
+    "new_local_repository",
+)
+new_local_repository(
+    name = "llvm-raw",
+    path = "my/custom/llvm-project",
+    build_file_content = "# empty",
+)
+
+# Use LLVM's extension for third-party deps (gmp, mpfr, etc.)
+llvm_repos_ext = use_extension(
+    "@llvm-project-overlay//:extensions.bzl",
+    "llvm_repos_extension",
+)
+use_repo(
+    llvm_repos_ext,
+    "gmp",
+    "mpc",
+    "mpfr",
+    "nanobind",
+    "pfm",
+    "pybind11",
+    "vulkan_sdk",
+)
+
+# Use IREE's extension (won't create llvm-raw since you're the root module)
+iree_ext = use_extension(
+    "@iree_core//build_tools/bazel:extensions.bzl",
+    "iree_extension",
+)
+use_repo(
+    iree_ext,
+    "com_github_dvidelabs_flatcc",
+    "com_google_benchmark",
+    "com_google_googletest",
+    "stablehlo",
+    # ... other IREE repos you need
+)
+
+# Configure LLVM (creates llvm-project from your llvm-raw)
+llvm_configure = use_repo_rule(
+    "@llvm-raw//utils/bazel:configure.bzl",
+    "llvm_configure",
+)
+llvm_configure(name = "llvm-project")
+```
+
+## Using LLVM from an HTTP Archive
+
+If you want to fetch LLVM from a release tarball instead of a local path:
+
+```python
+# my_project/MODULE.bazel
+
+http_archive = use_repo_rule(
+    "@bazel_tools//tools/build_defs/repo:http.bzl",
+    "http_archive",
+)
+
+LLVM_COMMIT = "abc123..."  # Your desired commit
+LLVM_SHA256 = "..."        # SHA256 of the tarball
+
+http_archive(
+    name = "llvm-raw",
+    build_file_content = "# empty",
+    sha256 = LLVM_SHA256,
+    strip_prefix = "llvm-project-" + LLVM_COMMIT,
+    urls = ["https://github.com/llvm/llvm-project/archive/{}.tar.gz".format(LLVM_COMMIT)],
+)
+```
+
+## Version Compatibility
+
+When providing your own LLVM, ensure compatibility with IREE:
+
+1. **LLVM Version**: IREE targets a specific LLVM commit. Check IREE's
+   `third_party/llvm-project` submodule for the expected version.
+
+2. **Bazel Dependencies**: Your LLVM's `utils/bazel/MODULE.bazel` declares
+   dependency versions. These should be compatible with IREE's dependencies.
+
+3. **API Compatibility**: LLVM APIs change between versions. Your LLVM must
+   be API-compatible with what IREE expects.
+
+## Troubleshooting
+
+### "repository 'llvm-raw' is not defined"
+You haven't created the `llvm-raw` repository. As the root module, you must
+define it yourself (see examples above).
+
+### Build errors in LLVM code
+Your LLVM version may be incompatible with IREE. Check that your LLVM commit
+is close to IREE's expected version.
+
+### Duplicate repository errors
+Multiple modules may be trying to create the same repository. Ensure only
+one source defines each repository name.
diff --git a/build_tools/bazel/extensions.bzl b/build_tools/bazel/extensions.bzl
new file mode 100644
index 0000000..a9ecad1
--- /dev/null
+++ b/build_tools/bazel/extensions.bzl
@@ -0,0 +1,99 @@
+# Copyright 2024 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
+
+"""Bzlmod extension for IREE repository rules."""
+
+load("@bazel_tools//tools/build_defs/repo:local.bzl", "local_repository", "new_local_repository")
+load("//build_tools/bazel:workspace.bzl", "cuda_auto_configure")
+
+def _iree_extension_impl(module_ctx):
+    """Implementation of the IREE module extension."""
+
+    # Create llvm-raw only when IREE is the root module.
+    # This allows downstream consumers to provide their own LLVM.
+    if any([m.is_root and m.name == "iree_core" for m in module_ctx.modules]):
+        new_local_repository(
+            name = "llvm-raw",
+            build_file_content = "# empty",
+            path = "third_party/llvm-project",
+        )
+
+    # Googletest
+    local_repository(
+        name = "com_google_googletest",
+        path = "third_party/googletest",
+    )
+
+    # Flatcc
+    new_local_repository(
+        name = "com_github_dvidelabs_flatcc",
+        build_file = "@iree_core//:build_tools/third_party/flatcc/BUILD.overlay",
+        path = "third_party/flatcc",
+    )
+
+    # Vulkan headers
+    new_local_repository(
+        name = "vulkan_headers",
+        build_file = "@iree_core//:build_tools/third_party/vulkan_headers/BUILD.overlay",
+        path = "third_party/vulkan_headers",
+    )
+
+    # StableHLO
+    local_repository(
+        name = "stablehlo",
+        path = "third_party/stablehlo",
+    )
+
+    # Benchmark
+    local_repository(
+        name = "com_google_benchmark",
+        path = "third_party/benchmark",
+    )
+
+    # SPIRV-Cross
+    new_local_repository(
+        name = "spirv_cross",
+        build_file = "@iree_core//:build_tools/third_party/spirv_cross/BUILD.overlay",
+        path = "third_party/spirv_cross",
+    )
+
+    # Tracy
+    new_local_repository(
+        name = "tracy_client",
+        build_file = "@iree_core//:build_tools/third_party/tracy_client/BUILD.overlay",
+        path = "third_party/tracy",
+    )
+
+    # NCCL
+    new_local_repository(
+        name = "nccl",
+        build_file = "@iree_core//:build_tools/third_party/nccl/BUILD.overlay",
+        path = "third_party/nccl",
+    )
+
+    # HSA runtime headers
+    new_local_repository(
+        name = "hsa_runtime_headers",
+        build_file = "@iree_core//:build_tools/third_party/hsa-runtime-headers/BUILD.overlay",
+        path = "third_party/hsa-runtime-headers",
+    )
+
+    # WebGPU headers
+    new_local_repository(
+        name = "webgpu_headers",
+        build_file = "@iree_core//:build_tools/third_party/webgpu-headers/BUILD.overlay",
+        path = "third_party/webgpu-headers",
+    )
+
+    # CUDA auto-configuration
+    cuda_auto_configure(
+        name = "iree_cuda",
+        iree_repo_alias = "@iree_core",
+    )
+
+iree_extension = module_extension(
+    implementation = _iree_extension_impl,
+)
diff --git a/build_tools/bazel/iree.bazelrc b/build_tools/bazel/iree.bazelrc
index 34b8259..60bfb01 100644
--- a/build_tools/bazel/iree.bazelrc
+++ b/build_tools/bazel/iree.bazelrc
@@ -8,6 +8,10 @@
 # Common flags that apply to all configurations.
 # Use sparingly for things common to all compilers and platforms.
 ###############################################################################
+
+# Enable bzlmod (required for LLVM integration as of Nov 2025)
+common --enable_bzlmod
+
 # Prevent invalid caching if input files are modified during a build.
 build --experimental_guard_against_concurrent_changes
 # Default to optimized builds
diff --git a/build_tools/bazel/iree_amdgpu_binary.bzl b/build_tools/bazel/iree_amdgpu_binary.bzl
index 4018588..8504fb6 100644
--- a/build_tools/bazel/iree_amdgpu_binary.bzl
+++ b/build_tools/bazel/iree_amdgpu_binary.bzl
@@ -37,7 +37,7 @@
     link_tool = "@llvm-project//llvm:llvm-link"
     lld_tool = "@llvm-project//lld:lld"
     builtin_headers_dep = "@llvm-project//clang:builtin_headers_gen"
-    builtin_headers_path = "external/llvm-project/clang/staging/include/"
+    builtin_headers_path = "external/_main~_repo_rules~llvm-project/clang/staging/include/"
 
     base_copts = [
         # C configuration.
diff --git a/build_tools/bazel/iree_bitcode_library.bzl b/build_tools/bazel/iree_bitcode_library.bzl
index a219779..03a3274 100644
--- a/build_tools/bazel/iree_bitcode_library.bzl
+++ b/build_tools/bazel/iree_bitcode_library.bzl
@@ -67,7 +67,7 @@
     clang_tool = "@llvm-project//clang:clang"
     link_tool = "@llvm-project//llvm:llvm-link"
     builtin_headers_dep = "@llvm-project//clang:builtin_headers_gen"
-    builtin_headers_path = "external/llvm-project/clang/staging/include/"
+    builtin_headers_path = "external/_main~_repo_rules~llvm-project/clang/staging/include/"
 
     base_copts = [
         # Target architecture
@@ -189,7 +189,7 @@
     clang_tool = "@llvm-project//clang:clang"
     link_tool = "@llvm-project//llvm:llvm-link"
     builtin_headers_dep = "@llvm-project//clang:builtin_headers_gen"
-    builtin_headers_path = "external/llvm-project/clang/staging/include/"
+    builtin_headers_path = "external/_main~_repo_rules~llvm-project/clang/staging/include/"
 
     base_copts = [
         "-x",
diff --git a/build_tools/bazel/requirements.txt b/build_tools/bazel/requirements.txt
new file mode 100644
index 0000000..24ce15a
--- /dev/null
+++ b/build_tools/bazel/requirements.txt
@@ -0,0 +1 @@
+numpy
diff --git a/build_tools/bazel_to_cmake/bazel_to_cmake_targets.py b/build_tools/bazel_to_cmake/bazel_to_cmake_targets.py
index 1b7a48d..597f7f8 100644
--- a/build_tools/bazel_to_cmake/bazel_to_cmake_targets.py
+++ b/build_tools/bazel_to_cmake/bazel_to_cmake_targets.py
@@ -121,6 +121,9 @@
                 "@spirv_cross//:spirv_cross_lib": ["spirv-cross-msl"],
                 "@hsa_runtime_headers": ["hsa_runtime::headers"],
                 "@webgpu_headers": [],
+                # py_binary targets have no CMake equivalent.
+                # This is the only target bazel needs to execute the lit tests.
+                ":python_with_numpy": [],
             }
         )
 
@@ -202,6 +205,9 @@
             return self._convert_mlir_target(target)
         if target.startswith("@iree_cuda//"):
             return self._convert_iree_cuda_target(target)
+        # pip dependencies don't exist in CMake (system Python is used).
+        if target.startswith("@pip//"):
+            return []
         if target.startswith(f"{iree_core_repo}//"):
             return self._convert_iree_core_target(target)
         if target.startswith("@"):
diff --git a/build_tools/third_party/flatcc/BUILD.overlay b/build_tools/third_party/flatcc/BUILD.overlay
index 9142b81..d810dc6 100644
--- a/build_tools/third_party/flatcc/BUILD.overlay
+++ b/build_tools/third_party/flatcc/BUILD.overlay
@@ -41,16 +41,14 @@
         "include/flatcc/flatcc_json_parser.h",
         "include/flatcc/flatcc_json_printer.h",
     ],
-    copts = [
-        "-Iexternal/com_github_dvidelabs_flatcc/config/",
-        "-Iexternal/com_github_dvidelabs_flatcc/include/",
-    ] + select({
+    copts = select({
         "@bazel_tools//src/conditions:windows": [],
         "//conditions:default": [
             "-Wno-implicit-fallthrough",
         ],
     }),
     includes = [
+        "config/",
         "include/",
     ],
     deps = [
@@ -82,16 +80,14 @@
         "include/flatcc/flatcc_verifier.h",
         "include/flatcc/reflection/flatbuffers_common_reader.h",
     ] + glob(["include/flatcc/portable/**/*.h"]),
-    copts = [
-        "-Iexternal/com_github_dvidelabs_flatcc/config/",
-        "-Iexternal/com_github_dvidelabs_flatcc/include/",
-    ] + select({
+    copts = select({
         "@bazel_tools//src/conditions:windows": [],
         "//conditions:default": [
             "-Wno-implicit-fallthrough",
         ],
     }),
     includes = [
+        "config/",
         "include/",
     ],
     strip_include_prefix = "include",
@@ -117,10 +113,10 @@
     ] + glob([
         "external/**/*.h",
     ]),
-    copts = [
-      "-Iexternal/com_github_dvidelabs_flatcc/config/",
-      "-Iexternal/com_github_dvidelabs_flatcc/external/",
-      "-Iexternal/com_github_dvidelabs_flatcc/include/",
+    includes = [
+      "config/",
+      "external/",
+      "include/",
     ],
 )
 
@@ -133,9 +129,9 @@
         ":compiler",
         ":runtime",
     ],
-    copts = [
-      "-Iexternal/com_github_dvidelabs_flatcc/config/",
-      "-Iexternal/com_github_dvidelabs_flatcc/external/",
-      "-Iexternal/com_github_dvidelabs_flatcc/include/",
+    includes = [
+      "config/",
+      "external/",
+      "include/",
     ],
 )
diff --git a/compiler/plugins/target/LLVMCPU/LLVMTargetOptions.cpp b/compiler/plugins/target/LLVMCPU/LLVMTargetOptions.cpp
index c6826b9..8ead1b2 100644
--- a/compiler/plugins/target/LLVMCPU/LLVMTargetOptions.cpp
+++ b/compiler/plugins/target/LLVMCPU/LLVMTargetOptions.cpp
@@ -444,6 +444,7 @@
 #define LLVM_INITIALIZE_TARGET_SystemZ()
 #define LLVM_INITIALIZE_TARGET_VE()
 #define LLVM_INITIALIZE_TARGET_XCore()
+#define LLVM_INITIALIZE_TARGET_SPIRV()
 
 #define LLVM_TARGET(TargetName) LLVM_INITIALIZE_TARGET_##TargetName()
 #include "llvm/Config/Targets.def"
diff --git a/third_party/llvm-project b/third_party/llvm-project
index ceea449..3564791 160000
--- a/third_party/llvm-project
+++ b/third_party/llvm-project
@@ -1 +1 @@
-Subproject commit ceea449d9a1a024fe10596752aa11eeb575a47ad
+Subproject commit 356479191ca001df47136c89cc9a761c64a6323c
diff --git a/tools/test/BUILD.bazel b/tools/test/BUILD.bazel
index b45399b..62a76be 100644
--- a/tools/test/BUILD.bazel
+++ b/tools/test/BUILD.bazel
@@ -6,6 +6,7 @@
 
 # Smoke tests for the execution of tool binaries.
 
+load("@rules_python//python:defs.bzl", "py_binary")
 load("//build_tools/bazel:enforce_glob.bzl", "enforce_glob")
 load("//build_tools/bazel:iree_lit_test.bzl", "iree_lit_test_suite")
 
@@ -14,6 +15,12 @@
     licenses = ["notice"],  # Apache 2.0
 )
 
+py_binary(
+    name = "python_with_numpy",
+    srcs = ["python_with_numpy.py"],
+    deps = ["@pip//numpy"],
+)
+
 iree_lit_test_suite(
     name = "lit",
     timeout = "moderate",
@@ -81,11 +88,15 @@
         "//compiler/src/iree/compiler/Utils:td_files",
         "@llvm-project//mlir:OpBaseTdFiles",
     ],
+    env = {
+        "PYTHON": "python_with_numpy",
+    },
     tags = [
         "driver=local-task",
         "hostonly",
     ],
     tools = [
+        ":python_with_numpy",
         "//tools:iree-benchmark-executable",
         "//tools:iree-benchmark-module",
         "//tools:iree-compile",
diff --git a/tools/test/iree-tblgen-json.td b/tools/test/iree-tblgen-json.td
index 4102e04..7f7f8bc 100644
--- a/tools/test/iree-tblgen-json.td
+++ b/tools/test/iree-tblgen-json.td
@@ -7,7 +7,7 @@
 // RUN: iree-tblgen --gen-dialect-json %s \
 // RUN:   -I %S/../../compiler/src \
 // RUN:   -I %S/../../third_party/llvm-project/mlir/include \
-// RUN:   -I %S/../../external/llvm-project/mlir/include \
+// RUN:   -I %S/../../external/_main~_repo_rules~llvm-project/mlir/include \
 // RUN:   | FileCheck %s
 
 include "mlir/IR/OpBase.td"
diff --git a/tools/test/python_with_numpy.py b/tools/test/python_with_numpy.py
new file mode 100644
index 0000000..bdec063
--- /dev/null
+++ b/tools/test/python_with_numpy.py
@@ -0,0 +1,21 @@
+# Copyright 2025 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
+
+"""Python interpreter wrapper that has numpy available.
+
+Used by lit tests that need numpy but run under Bazel's hermetic Python.
+This script acts as a Python interpreter: it takes a script path as the
+first argument and executes it.
+"""
+
+import runpy
+import sys
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        script = sys.argv[1]
+        sys.argv = sys.argv[1:]  # Shift argv so script sees correct args
+        runpy.run_path(script, run_name="__main__")