blob: e4c0d031bec42da9238025d029ff2639c1461dbc [file]
# Copyright 2026 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
"""Repository rule exposing optional AMDGPU builtin device compiler tools."""
_STUB_BUILD = """\
# AUTO-GENERATED by amdgpu_device_toolchain_repo.bzl - do not edit.
package(default_visibility = ["//visibility:public"])
filegroup(
name = "unused",
srcs = [],
)
"""
_STUB_PATHS = """\
# AUTO-GENERATED by amdgpu_device_toolchain_repo.bzl - do not edit.
AMDGPU_DEVICE_TOOLCHAIN_AVAILABLE = False
AMDGPU_DEVICE_TOOLCHAIN_ERROR = "AMDGPU device binary source mode requires --repo_env=IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN=rocm, llvm-tools, llvm-project, or auto."
AMDGPU_CLANG_TOOL = ""
AMDGPU_LLVM_LINK_TOOL = ""
AMDGPU_LLD_TOOL = ""
AMDGPU_LLVM_OBJCOPY_TOOL = ""
AMDGPU_CLANG_RESOURCE_HEADERS = ""
AMDGPU_CLANG_RESOURCE_MARKER = ""
AMDGPU_CLANG_RESOURCE_INCLUDE_ARGS = []
"""
_PREBUILT_TOOL_BZL = """\
\"\"\"Rule for exposing pre-built binaries as executable Bazel targets.\"\"\"
def _prebuilt_tool_impl(ctx):
out = ctx.actions.declare_file(ctx.label.name)
ctx.actions.symlink(
output = out,
target_file = ctx.file.src,
is_executable = True,
)
runfiles = ctx.runfiles(files = ctx.files.data + [ctx.file.src])
return [DefaultInfo(
executable = out,
files = depset([out]),
runfiles = runfiles,
)]
prebuilt_tool = rule(
implementation = _prebuilt_tool_impl,
executable = True,
attrs = {
"src": attr.label(
allow_single_file = True,
mandatory = True,
doc = "The pre-built binary file.",
),
"data": attr.label_list(
allow_files = True,
doc = "Additional files the tool needs at runtime.",
),
},
doc = "Wraps a pre-built binary as an executable Bazel target.",
)
"""
_LOCAL_BUILD_TEMPLATE = """\
# AUTO-GENERATED by amdgpu_device_toolchain_repo.bzl - do not edit.
load(":prebuilt_tool.bzl", "prebuilt_tool")
package(default_visibility = ["//visibility:public"])
prebuilt_tool(
name = "clang",
src = "bin/clang",
)
prebuilt_tool(
name = "llvm-link",
src = "bin/llvm-link",
)
prebuilt_tool(
name = "lld",
src = "bin/lld",
)
prebuilt_tool(
name = "llvm-objcopy",
src = "bin/llvm-objcopy",
)
filegroup(
name = "clang_resource_headers",
srcs = glob(["clang_resource_include/**"]),
)
filegroup(
name = "clang_resource_include_marker",
srcs = ["clang_resource_include/stddef.h"],
)
"""
_LOCAL_PATHS_TEMPLATE = """\
# AUTO-GENERATED by amdgpu_device_toolchain_repo.bzl - do not edit.
AMDGPU_DEVICE_TOOLCHAIN_AVAILABLE = True
AMDGPU_DEVICE_TOOLCHAIN_ERROR = ""
AMDGPU_CLANG_TOOL = "@iree_amdgpu_device_toolchain//:clang"
AMDGPU_LLVM_LINK_TOOL = "@iree_amdgpu_device_toolchain//:llvm-link"
AMDGPU_LLD_TOOL = "@iree_amdgpu_device_toolchain//:lld"
AMDGPU_LLVM_OBJCOPY_TOOL = "@iree_amdgpu_device_toolchain//:llvm-objcopy"
AMDGPU_CLANG_RESOURCE_HEADERS = "@iree_amdgpu_device_toolchain//:clang_resource_headers"
AMDGPU_CLANG_RESOURCE_MARKER = "@iree_amdgpu_device_toolchain//:clang_resource_include_marker"
AMDGPU_CLANG_RESOURCE_INCLUDE_ARGS = [
"--clang-resource-include",
"$$(dirname $(location @iree_amdgpu_device_toolchain//:clang_resource_include_marker))",
]
"""
_LLVM_PROJECT_BUILD = """\
# AUTO-GENERATED by amdgpu_device_toolchain_repo.bzl - do not edit.
package(default_visibility = ["//visibility:public"])
alias(
name = "clang",
actual = "@llvm-project//clang:clang",
)
alias(
name = "llvm-link",
actual = "@llvm-project//llvm:llvm-link",
)
alias(
name = "lld",
actual = "@llvm-project//lld:lld",
)
alias(
name = "llvm-objcopy",
actual = "@llvm-project//llvm:llvm-objcopy",
)
"""
_LLVM_PROJECT_PATHS = """\
# AUTO-GENERATED by amdgpu_device_toolchain_repo.bzl - do not edit.
AMDGPU_DEVICE_TOOLCHAIN_AVAILABLE = True
AMDGPU_DEVICE_TOOLCHAIN_ERROR = ""
AMDGPU_CLANG_TOOL = "@iree_amdgpu_device_toolchain//:clang"
AMDGPU_LLVM_LINK_TOOL = "@iree_amdgpu_device_toolchain//:llvm-link"
AMDGPU_LLD_TOOL = "@iree_amdgpu_device_toolchain//:lld"
AMDGPU_LLVM_OBJCOPY_TOOL = "@iree_amdgpu_device_toolchain//:llvm-objcopy"
AMDGPU_CLANG_RESOURCE_HEADERS = "@llvm-project//clang:builtin_headers_gen"
AMDGPU_CLANG_RESOURCE_MARKER = ""
AMDGPU_CLANG_RESOURCE_INCLUDE_ARGS = [
"--clang-resource-include",
"$(BINDIR)/external/+_repo_rules+llvm-project/clang/staging/include/",
]
"""
def _split_env_list(value):
if not value:
return []
normalized = value
for separator in [",", ";", "\n", "\t", " "]:
normalized = normalized.replace(separator, ":")
return [part for part in normalized.split(":") if part]
def _join_path(left, right):
if left.endswith("/"):
return left + right
return left + "/" + right
def _dirname(path):
if "/" not in path:
return "."
return path.rsplit("/", 1)[0]
def _basename(path):
if "/" not in path:
return path
return path.rsplit("/", 1)[1]
def _append_unique(values, new_values):
for value in new_values:
if value not in values:
values.append(value)
def _is_executable(repository_ctx, path):
result = repository_ctx.execute(["test", "-x", path], quiet = True)
return result.return_code == 0
def _find_tool(
repository_ctx,
names,
directories,
explicit_env_vars,
description,
additional_candidates = ()):
candidates = []
for env_var in explicit_env_vars:
value = repository_ctx.getenv(env_var)
if value:
candidates.append(value)
candidates.extend(additional_candidates)
for directory in directories:
for name in names:
candidates.append(_join_path(directory, name))
for candidate in candidates:
if _is_executable(repository_ctx, candidate):
return str(repository_ctx.path(candidate))
fail("Could not find {}. Tried env vars [{}] and candidate directories [{}].".format(
description,
", ".join(explicit_env_vars),
", ".join(directories),
))
def _rocm_tool_dirs(root):
return [
_join_path(root, "llvm/bin"),
_join_path(root, "lib/llvm/bin"),
_join_path(root, "bin"),
]
def _candidate_rocm_roots(repository_ctx):
roots = []
for env_var in [
"IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_ROCM_PATH",
"IREE_ROCM_PATH",
"ROCM_PATH",
"ROCM_ROOT",
"ROCM_HOME",
"HIP_PATH",
]:
for value in _split_env_list(repository_ctx.getenv(env_var)):
_append_unique(roots, [value])
if env_var == "HIP_PATH" and _basename(value) == "hip":
_append_unique(roots, [_dirname(value)])
hipcc = repository_ctx.which("hipcc")
if hipcc:
_append_unique(roots, [_dirname(_dirname(str(hipcc)))])
if repository_ctx.path("/opt/rocm").exists:
_append_unique(roots, ["/opt/rocm"])
return roots
def _candidate_tool_dirs(repository_ctx, mode):
dirs = []
for env_var in [
"IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_LLVM_TOOLS_DIR",
"IREE_HOST_BIN_DIR",
"IREE_HOST_TOOLS",
"IREE_HOST_TOOLS_DIR",
"IREE_BINARY_DIR",
"IREE_LLVM_TOOLS_DIR",
"IREE_LLVM_TOOL_DIR",
"LLVM_TOOLS_BINARY_DIR",
"LLVM_BINARY_DIR",
]:
for value in _split_env_list(repository_ctx.getenv(env_var)):
_append_unique(dirs, [value])
expanded_dirs = []
for directory in dirs:
expanded_dirs.extend([
directory,
_join_path(directory, "bin"),
_join_path(directory, "llvm-project/bin"),
_join_path(directory, "llvm/bin"),
_join_path(directory, "lib/llvm/bin"),
])
dirs = []
_append_unique(dirs, expanded_dirs)
if mode in ["auto", "rocm"]:
for root in _candidate_rocm_roots(repository_ctx):
_append_unique(dirs, _rocm_tool_dirs(root))
return dirs
def _clang_tool_candidates(repository_ctx, clang, names):
candidates = []
clang_dir = _dirname(clang)
for name in names:
result = repository_ctx.execute(
[clang, "--print-prog-name=" + name],
quiet = True,
)
if result.return_code != 0:
continue
printed_name = result.stdout.strip()
if not printed_name:
continue
if printed_name.startswith("/"):
_append_unique(candidates, [printed_name])
else:
_append_unique(candidates, [
_join_path(clang_dir, printed_name),
printed_name,
])
return candidates
def _detect_clang_resource_include(repository_ctx, clang):
explicit = repository_ctx.getenv("IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_CLANG_RESOURCE_INCLUDE")
if not explicit:
explicit = repository_ctx.getenv("IREE_CLANG_BUILTIN_HEADERS_PATH")
if explicit:
path = repository_ctx.path(explicit)
if not path.exists:
fail("Configured clang resource include directory does not exist: {}".format(explicit))
return str(path)
result = repository_ctx.execute([clang, "-print-resource-dir"], quiet = True)
if result.return_code != 0:
fail("Failed to query clang resource directory from {}:\n{}".format(
clang,
result.stderr,
))
include_dir = _join_path(result.stdout.strip(), "include")
path = repository_ctx.path(include_dir)
if not path.exists:
fail("clang resource include directory does not exist: {}".format(include_dir))
return str(path)
def _write_local_toolchain_repo(repository_ctx, tools, clang_resource_include):
repository_ctx.file("prebuilt_tool.bzl", _PREBUILT_TOOL_BZL)
repository_ctx.file("bin/.keep", "")
for name, path in tools.items():
repository_ctx.symlink(repository_ctx.path(path), "bin/" + name)
repository_ctx.symlink(
repository_ctx.path(clang_resource_include),
"clang_resource_include",
)
repository_ctx.file("BUILD.bazel", _LOCAL_BUILD_TEMPLATE)
repository_ctx.file("paths.bzl", _LOCAL_PATHS_TEMPLATE)
def _amdgpu_device_toolchain_repo_impl(repository_ctx):
mode = repository_ctx.getenv("IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN") or "none"
if mode not in ["none", "auto", "rocm", "llvm-tools", "llvm-project"]:
fail("Unknown IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN '{}'. Available: none, auto, rocm, llvm-tools, llvm-project.".format(mode))
if mode == "none":
repository_ctx.file("BUILD.bazel", _STUB_BUILD)
repository_ctx.file("paths.bzl", _STUB_PATHS)
return
if mode == "llvm-project":
repository_ctx.file("BUILD.bazel", _LLVM_PROJECT_BUILD)
repository_ctx.file("paths.bzl", _LLVM_PROJECT_PATHS)
return
tool_dirs = _candidate_tool_dirs(repository_ctx, mode)
if mode == "rocm" and not tool_dirs:
fail("IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN=rocm requires a ROCm/TheRock root such as IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_ROCM_PATH, IREE_ROCM_PATH, ROCM_PATH, ROCM_ROOT, ROCM_HOME, HIP_PATH, hipcc on PATH, or /opt/rocm.")
clang = _find_tool(
repository_ctx,
["clang", "amdclang", "clang-22"],
tool_dirs,
["IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_CLANG_BINARY", "IREE_CLANG_BINARY", "CLANG"],
"clang with AMDGPU support",
)
llvm_link = _find_tool(
repository_ctx,
["llvm-link"],
tool_dirs,
["IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_LLVM_LINK_BINARY", "IREE_LLVM_LINK_BINARY", "LLVM_LINK"],
"llvm-link",
additional_candidates = _clang_tool_candidates(repository_ctx, clang, ["llvm-link"]),
)
lld = _find_tool(
repository_ctx,
["lld", "ld.lld"],
tool_dirs,
["IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_LLD_BINARY", "IREE_LLD_BINARY", "LLD", "LD_LLD"],
"lld",
additional_candidates = _clang_tool_candidates(repository_ctx, clang, ["lld", "ld.lld"]),
)
llvm_objcopy = _find_tool(
repository_ctx,
["llvm-objcopy"],
tool_dirs,
["IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_LLVM_OBJCOPY_BINARY", "IREE_LLVM_OBJCOPY_BINARY", "LLVM_OBJCOPY"],
"llvm-objcopy",
additional_candidates = _clang_tool_candidates(repository_ctx, clang, ["llvm-objcopy"]),
)
clang_resource_include = _detect_clang_resource_include(repository_ctx, clang)
_write_local_toolchain_repo(
repository_ctx,
{
"clang": clang,
"llvm-link": llvm_link,
"lld": lld,
"llvm-objcopy": llvm_objcopy,
},
clang_resource_include,
)
amdgpu_device_toolchain_repo = repository_rule(
implementation = _amdgpu_device_toolchain_repo_impl,
environ = [
"CLANG",
"HIP_PATH",
"IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN",
"IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_CLANG_BINARY",
"IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_CLANG_RESOURCE_INCLUDE",
"IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_LLD_BINARY",
"IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_LLVM_LINK_BINARY",
"IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_LLVM_OBJCOPY_BINARY",
"IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_LLVM_TOOLS_DIR",
"IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_ROCM_PATH",
"IREE_CLANG_BINARY",
"IREE_CLANG_BUILTIN_HEADERS_PATH",
"IREE_HOST_BIN_DIR",
"IREE_HOST_TOOLS",
"IREE_HOST_TOOLS_DIR",
"IREE_BINARY_DIR",
"IREE_LLD_BINARY",
"IREE_LLVM_LINK_BINARY",
"IREE_LLVM_OBJCOPY_BINARY",
"IREE_LLVM_TOOL_DIR",
"IREE_LLVM_TOOLS_DIR",
"IREE_ROCM_PATH",
"LD_LLD",
"LLD",
"LLVM_BINARY_DIR",
"LLVM_LINK",
"LLVM_OBJCOPY",
"LLVM_TOOLS_BINARY_DIR",
"ROCM_HOME",
"ROCM_PATH",
"ROCM_ROOT",
],
doc = "Exposes optional AMDGPU builtin device compiler tools.",
)