| #!/usr/bin/env python3 | |
| # 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 | |
| """Generates shared AMDGPU device binary target map fragments. | |
| The map in this file is the source of truth for the small generated tables used | |
| by Bazel, CMake, and the runtime device-library loader. Keep build logic in | |
| Starlark/CMake; keep target facts here. | |
| """ | |
| import argparse | |
| import difflib | |
| import re | |
| import sys | |
| from pathlib import Path | |
| DEFAULT_TARGET_SELECTIONS = ( | |
| "gfx9-generic", | |
| "gfx90a", | |
| "gfx9-4-generic", | |
| "gfx10-1-generic", | |
| "gfx10-3-generic", | |
| "gfx11-generic", | |
| "gfx12-generic", | |
| ) | |
| # Each exact target must match an HSA ISA architecture suffix. Each code object | |
| # target must be accepted by LLVM clang/lld as an AMDGPU -march value. Generic | |
| # code object coverage follows LLVM generic processor documentation; TheRock | |
| # family membership follows ROCm/TheRock's cmake/therock_amdgpu_targets.cmake. | |
| EXACT_TARGET_CODE_OBJECTS = ( | |
| ("gfx900", "gfx9-generic"), | |
| ("gfx902", "gfx9-generic"), | |
| ("gfx904", "gfx9-generic"), | |
| ("gfx90c", "gfx9-generic"), | |
| ("gfx906", "gfx9-generic"), | |
| ("gfx908", "gfx908"), | |
| ("gfx909", "gfx9-generic"), | |
| ("gfx90a", "gfx90a"), | |
| ("gfx940", "gfx9-4-generic"), | |
| ("gfx941", "gfx9-4-generic"), | |
| ("gfx942", "gfx9-4-generic"), | |
| ("gfx950", "gfx9-4-generic"), | |
| ("gfx1010", "gfx10-1-generic"), | |
| ("gfx1011", "gfx10-1-generic"), | |
| ("gfx1012", "gfx10-1-generic"), | |
| ("gfx1013", "gfx10-1-generic"), | |
| ("gfx1030", "gfx10-3-generic"), | |
| ("gfx1031", "gfx10-3-generic"), | |
| ("gfx1032", "gfx10-3-generic"), | |
| ("gfx1033", "gfx10-3-generic"), | |
| ("gfx1034", "gfx10-3-generic"), | |
| ("gfx1035", "gfx10-3-generic"), | |
| ("gfx1036", "gfx10-3-generic"), | |
| ("gfx1100", "gfx11-generic"), | |
| ("gfx1101", "gfx11-generic"), | |
| ("gfx1102", "gfx11-generic"), | |
| ("gfx1103", "gfx11-generic"), | |
| ("gfx1150", "gfx11-generic"), | |
| ("gfx1151", "gfx11-generic"), | |
| ("gfx1152", "gfx11-generic"), | |
| ("gfx1153", "gfx11-generic"), | |
| ("gfx1170", "gfx11-generic"), | |
| ("gfx1171", "gfx11-generic"), | |
| ("gfx1172", "gfx11-generic"), | |
| ("gfx1200", "gfx12-generic"), | |
| ("gfx1201", "gfx12-generic"), | |
| ("gfx1250", "gfx12-5-generic"), | |
| ("gfx1251", "gfx12-5-generic"), | |
| ) | |
| FEATURE_SRAMECC = "sramecc" | |
| FEATURE_XNACK = "xnack" | |
| # Feature support follows ROCr's ISA registry. A target absent from a feature | |
| # set does not support that feature; supported targets may still select an | |
| # explicit on/off mode at runtime. | |
| TARGET_FEATURE_SUPPORT = { | |
| "gfx900": (FEATURE_XNACK,), | |
| "gfx902": (FEATURE_XNACK,), | |
| "gfx904": (FEATURE_XNACK,), | |
| "gfx906": (FEATURE_SRAMECC, FEATURE_XNACK), | |
| "gfx908": (FEATURE_SRAMECC, FEATURE_XNACK), | |
| "gfx909": (FEATURE_XNACK,), | |
| "gfx90c": (FEATURE_XNACK,), | |
| "gfx90a": (FEATURE_SRAMECC, FEATURE_XNACK), | |
| "gfx940": (FEATURE_SRAMECC, FEATURE_XNACK), | |
| "gfx941": (FEATURE_SRAMECC, FEATURE_XNACK), | |
| "gfx942": (FEATURE_SRAMECC, FEATURE_XNACK), | |
| "gfx950": (FEATURE_SRAMECC, FEATURE_XNACK), | |
| "gfx1010": (FEATURE_XNACK,), | |
| "gfx1011": (FEATURE_XNACK,), | |
| "gfx1012": (FEATURE_XNACK,), | |
| "gfx1013": (FEATURE_XNACK,), | |
| } | |
| ALL_EXACT_TARGETS = object() | |
| TARGET_FAMILIES = ( | |
| ("all", ALL_EXACT_TARGETS), | |
| ( | |
| "dcgpu-all", | |
| ("gfx908", "gfx90a", "gfx940", "gfx941", "gfx942", "gfx950"), | |
| ), | |
| ( | |
| "dgpu-all", | |
| ( | |
| "gfx900", | |
| "gfx902", | |
| "gfx904", | |
| "gfx906", | |
| "gfx909", | |
| "gfx1010", | |
| "gfx1011", | |
| "gfx1012", | |
| "gfx1013", | |
| "gfx1030", | |
| "gfx1031", | |
| "gfx1032", | |
| "gfx1034", | |
| "gfx1100", | |
| "gfx1101", | |
| "gfx1102", | |
| "gfx1200", | |
| "gfx1201", | |
| "gfx1250", | |
| "gfx1251", | |
| ), | |
| ), | |
| ("gfx900-dgpu", ("gfx900",)), | |
| ("gfx906-dgpu", ("gfx906",)), | |
| ("gfx908-dcgpu", ("gfx908",)), | |
| ("gfx90a-dcgpu", ("gfx90a",)), | |
| ("gfx90c-igpu", ("gfx90c",)), | |
| ("gfx94X-all", ("gfx940", "gfx941", "gfx942")), | |
| ("gfx94X-dcgpu", ("gfx940", "gfx941", "gfx942")), | |
| ("gfx950-all", ("gfx950",)), | |
| ("gfx950-dcgpu", ("gfx950",)), | |
| ("gfx101X-all", ("gfx1010", "gfx1011", "gfx1012", "gfx1013")), | |
| ("gfx101X-dgpu", ("gfx1010", "gfx1011", "gfx1012", "gfx1013")), | |
| ( | |
| "gfx103X-all", | |
| ( | |
| "gfx1030", | |
| "gfx1031", | |
| "gfx1032", | |
| "gfx1033", | |
| "gfx1034", | |
| "gfx1035", | |
| "gfx1036", | |
| ), | |
| ), | |
| ("gfx103X-dgpu", ("gfx1030", "gfx1031", "gfx1032", "gfx1034")), | |
| ("gfx103X-igpu", ("gfx1033", "gfx1035", "gfx1036")), | |
| ("gfx110X-all", ("gfx1100", "gfx1101", "gfx1102", "gfx1103")), | |
| ("gfx110X-dgpu", ("gfx1100", "gfx1101", "gfx1102")), | |
| ("gfx110X-igpu", ("gfx1103",)), | |
| ("gfx115X-all", ("gfx1150", "gfx1151", "gfx1152", "gfx1153")), | |
| ("gfx115X-igpu", ("gfx1150", "gfx1151", "gfx1152", "gfx1153")), | |
| ("gfx117X-all", ("gfx1170", "gfx1171", "gfx1172")), | |
| ("gfx120X-all", ("gfx1200", "gfx1201")), | |
| ("gfx125X-all", ("gfx1250", "gfx1251")), | |
| ( | |
| "igpu-all", | |
| ( | |
| "gfx90c", | |
| "gfx1033", | |
| "gfx1035", | |
| "gfx1036", | |
| "gfx1103", | |
| "gfx1150", | |
| "gfx1151", | |
| "gfx1152", | |
| "gfx1153", | |
| ), | |
| ), | |
| ) | |
| def find_repo_root(): | |
| current = Path(__file__).resolve() | |
| while current != current.parent: | |
| if (current / "runtime" / "src" / "iree").exists(): | |
| return current | |
| current = current.parent | |
| print("error: could not find IREE repository root", file=sys.stderr) | |
| sys.exit(1) | |
| def append_unique(values, new_values): | |
| for value in new_values: | |
| if value not in values: | |
| values.append(value) | |
| def exact_targets(): | |
| return [exact_target for exact_target, _ in EXACT_TARGET_CODE_OBJECTS] | |
| def code_object_targets(): | |
| values = [] | |
| for _, code_object_target in EXACT_TARGET_CODE_OBJECTS: | |
| append_unique(values, [code_object_target]) | |
| return values | |
| def family_targets(targets): | |
| if targets is ALL_EXACT_TARGETS: | |
| return exact_targets() | |
| return list(targets) | |
| def target_family_names(): | |
| return [family for family, _ in TARGET_FAMILIES] | |
| def validate_target_map(): | |
| exact = exact_targets() | |
| if len(set(exact)) != len(exact): | |
| raise ValueError("duplicate exact AMDGPU targets in target map") | |
| families = target_family_names() | |
| if len(set(families)) != len(families): | |
| raise ValueError("duplicate AMDGPU target families in target map") | |
| exact_set = set(exact) | |
| feature_targets = set(TARGET_FEATURE_SUPPORT) | |
| unknown_feature_targets = sorted(feature_targets - exact_set) | |
| if unknown_feature_targets: | |
| raise ValueError( | |
| "target feature support references unknown exact targets: {}".format( | |
| ", ".join(unknown_feature_targets) | |
| ) | |
| ) | |
| for family, targets in TARGET_FAMILIES: | |
| unknown_targets = sorted(set(family_targets(targets)) - exact_set) | |
| if unknown_targets: | |
| raise ValueError( | |
| "target family {} references unknown exact targets: {}".format( | |
| family, ", ".join(unknown_targets) | |
| ) | |
| ) | |
| def generated_header(comment_prefix, output_path): | |
| return "\n".join( | |
| [ | |
| "{} Generated by build_tools/scripts/amdgpu_target_map.py.".format( | |
| comment_prefix | |
| ), | |
| "{} Do not edit directly; edit the map in that script and regenerate.".format( | |
| comment_prefix | |
| ), | |
| "{} Output: {}".format(comment_prefix, output_path), | |
| ] | |
| ) | |
| def bzl_list(name, values): | |
| lines = ["{} = [".format(name)] | |
| lines.extend([' "{}",'.format(value) for value in values]) | |
| lines.append("]") | |
| return "\n".join(lines) | |
| def bzl_string_dict(name, values): | |
| lines = ["{} = {{".format(name)] | |
| for key, value in values: | |
| lines.append(' "{}": "{}",'.format(key, value)) | |
| lines.append("}") | |
| return "\n".join(lines) | |
| def bzl_family_dict(name): | |
| lines = ["{} = {{".format(name)] | |
| for family, targets in TARGET_FAMILIES: | |
| values = family_targets(targets) | |
| if targets is ALL_EXACT_TARGETS: | |
| lines.append( | |
| ' "{}": IREE_HAL_AMDGPU_DEVICE_BINARY_EXACT_TARGETS,'.format(family) | |
| ) | |
| elif len(values) == 1: | |
| lines.append(' "{}": ["{}"],'.format(family, values[0])) | |
| else: | |
| lines.append(' "{}": ['.format(family)) | |
| lines.extend([' "{}",'.format(value) for value in values]) | |
| lines.append(" ],") | |
| lines.append("}") | |
| return "\n".join(lines) | |
| def render_bzl(): | |
| output_path = "runtime/src/iree/hal/drivers/amdgpu/device/binaries/target_map.bzl" | |
| return ( | |
| "\n\n".join( | |
| [ | |
| generated_header("#", output_path), | |
| bzl_list( | |
| "IREE_HAL_AMDGPU_DEVICE_BINARY_DEFAULT_TARGETS", | |
| DEFAULT_TARGET_SELECTIONS, | |
| ), | |
| bzl_list( | |
| "IREE_HAL_AMDGPU_DEVICE_BINARY_EXACT_TARGETS", | |
| exact_targets(), | |
| ), | |
| bzl_list( | |
| "IREE_HAL_AMDGPU_DEVICE_BINARY_CODE_OBJECT_TARGETS", | |
| code_object_targets(), | |
| ), | |
| bzl_string_dict( | |
| "IREE_HAL_AMDGPU_DEVICE_BINARY_EXACT_TARGET_CODE_OBJECTS", | |
| EXACT_TARGET_CODE_OBJECTS, | |
| ), | |
| bzl_list( | |
| "IREE_HAL_AMDGPU_DEVICE_BINARY_TARGET_FAMILY_NAMES", | |
| target_family_names(), | |
| ), | |
| bzl_family_dict("IREE_HAL_AMDGPU_DEVICE_BINARY_TARGET_FAMILIES"), | |
| ] | |
| ) | |
| + "\n" | |
| ) | |
| def cmake_list(name, values): | |
| lines = ["set({}".format(name)] | |
| lines.extend([' "{}"'.format(value) for value in values]) | |
| lines.append(")") | |
| return "\n".join(lines) | |
| def cmake_identifier(value): | |
| return re.sub(r"[^A-Za-z0-9_]", "_", value) | |
| def render_cmake(): | |
| output_path = "runtime/src/iree/hal/drivers/amdgpu/device/binaries/target_map.cmake" | |
| lines = [ | |
| generated_header("#", output_path), | |
| "", | |
| cmake_list("_IREE_HAL_AMDGPU_DEVICE_TARGETS", exact_targets()), | |
| "", | |
| cmake_list( | |
| "_IREE_HAL_AMDGPU_DEVICE_CODE_OBJECT_TARGETS", code_object_targets() | |
| ), | |
| "", | |
| ] | |
| for exact_target, code_object_target in EXACT_TARGET_CODE_OBJECTS: | |
| lines.append( | |
| 'set(_IREE_HAL_AMDGPU_DEVICE_TARGET_CODE_OBJECT_{} "{}")'.format( | |
| exact_target, code_object_target | |
| ) | |
| ) | |
| lines.extend( | |
| [ | |
| "", | |
| cmake_list( | |
| "_IREE_HAL_AMDGPU_DEVICE_TARGET_FAMILIES", target_family_names() | |
| ), | |
| "", | |
| ] | |
| ) | |
| for family, targets in TARGET_FAMILIES: | |
| var_name = "_IREE_HAL_AMDGPU_DEVICE_TARGET_FAMILY_{}".format( | |
| cmake_identifier(family) | |
| ) | |
| if targets is ALL_EXACT_TARGETS: | |
| lines.append("set({}".format(var_name)) | |
| lines.append(" ${_IREE_HAL_AMDGPU_DEVICE_TARGETS}") | |
| lines.append(")") | |
| else: | |
| lines.append(cmake_list(var_name, family_targets(targets))) | |
| lines.append("") | |
| return "\n".join(lines) | |
| def render_target_id_inl(): | |
| output_path = "runtime/src/iree/hal/drivers/amdgpu/util/target_id_map.inl" | |
| lines = [ | |
| generated_header("//", output_path), | |
| "//", | |
| "// Included inside iree_hal_amdgpu_target_id_mappings.", | |
| "", | |
| "// clang-format off", | |
| ] | |
| feature_flag_names = { | |
| FEATURE_SRAMECC: "IREE_HAL_AMDGPU_TARGET_FEATURE_SUPPORT_SRAMECC", | |
| FEATURE_XNACK: "IREE_HAL_AMDGPU_TARGET_FEATURE_SUPPORT_XNACK", | |
| } | |
| for exact_target, code_object_target in EXACT_TARGET_CODE_OBJECTS: | |
| features = TARGET_FEATURE_SUPPORT.get(exact_target, ()) | |
| feature_flags = " | ".join(feature_flag_names[feature] for feature in features) | |
| if not feature_flags: | |
| feature_flags = "IREE_HAL_AMDGPU_TARGET_FEATURE_SUPPORT_NONE" | |
| lines.append( | |
| '{{IREE_SVL("{}"), IREE_SVL("{}"), {}}},'.format( | |
| exact_target, code_object_target, feature_flags | |
| ) | |
| ) | |
| lines.append("") | |
| return "\n".join(lines) | |
| def generated_outputs(repo_root): | |
| binary_output_dir = ( | |
| repo_root / "runtime/src/iree/hal/drivers/amdgpu/device/binaries" | |
| ) | |
| util_output_dir = repo_root / "runtime/src/iree/hal/drivers/amdgpu/util" | |
| return { | |
| binary_output_dir / "target_map.bzl": render_bzl(), | |
| binary_output_dir / "target_map.cmake": render_cmake(), | |
| util_output_dir / "target_id_map.inl": render_target_id_inl(), | |
| } | |
| def check_outputs(repo_root, outputs): | |
| failed = False | |
| for path, content in outputs.items(): | |
| if not path.exists(): | |
| print("error: {} does not exist".format(path), file=sys.stderr) | |
| failed = True | |
| continue | |
| existing = path.read_text() | |
| if existing == content: | |
| continue | |
| rel_path = path.relative_to(repo_root) | |
| print("error: {} is out of date".format(rel_path), file=sys.stderr) | |
| diff = difflib.unified_diff( | |
| existing.splitlines(keepends=True), | |
| content.splitlines(keepends=True), | |
| fromfile=str(rel_path), | |
| tofile=str(rel_path) + " (generated)", | |
| ) | |
| sys.stderr.writelines(diff) | |
| failed = True | |
| if failed: | |
| print( | |
| "Run 'python build_tools/scripts/amdgpu_target_map.py' to regenerate.", | |
| file=sys.stderr, | |
| ) | |
| return 1 | |
| print("AMDGPU target map generated files are up to date.") | |
| return 0 | |
| def write_outputs(outputs): | |
| for path, content in outputs.items(): | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| path.write_text(content) | |
| print("Wrote {}".format(path)) | |
| return 0 | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description="Generate AMDGPU device binary target map fragments." | |
| ) | |
| parser.add_argument( | |
| "--check", | |
| action="store_true", | |
| help="Check that generated files are up to date without modifying them.", | |
| ) | |
| args = parser.parse_args() | |
| validate_target_map() | |
| repo_root = find_repo_root() | |
| outputs = generated_outputs(repo_root) | |
| if args.check: | |
| return check_outputs(repo_root, outputs) | |
| return write_outputs(outputs) | |
| if __name__ == "__main__": | |
| sys.exit(main()) |