CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
622 lines
21 KiB
Python
622 lines
21 KiB
Python
################################################################################
|
|
#
|
|
# Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved.
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cop-
|
|
# ies of the Software, and to permit persons to whom the Software is furnished
|
|
# to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in all
|
|
# copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM-
|
|
# PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNE-
|
|
# CTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
################################################################################
|
|
|
|
import re
|
|
import collections
|
|
|
|
from pathlib import Path
|
|
from subprocess import run, PIPE
|
|
from typing import List, Optional, Set, Tuple, Union, NamedTuple, Dict
|
|
|
|
from .Types import IsaVersion
|
|
from .Utilities import print1
|
|
|
|
import rocisa
|
|
|
|
# Translate GPU targets to filename prefixes in tensilelite logic files
|
|
architectureMap = {
|
|
"all": "_",
|
|
"gfx000": "none",
|
|
"gfx803": "r9nano",
|
|
"gfx900": "vega10",
|
|
"gfx906": "vega20",
|
|
"gfx906:xnack+": "vega20",
|
|
"gfx906:xnack-": "vega20",
|
|
"gfx908": "arcturus",
|
|
"gfx908:xnack+": "arcturus",
|
|
"gfx908:xnack-": "arcturus",
|
|
"gfx90a": "aldebaran",
|
|
"gfx90a:xnack+": "aldebaran",
|
|
"gfx90a:xnack-": "aldebaran",
|
|
"gfx942": "aquavanjaram",
|
|
"gfx942:xnack+": "aquavanjaram",
|
|
"gfx942:xnack-": "aquavanjaram",
|
|
"gfx950": "gfx950",
|
|
"gfx950:xnack+": "gfx950",
|
|
"gfx950:xnack-": "gfx950",
|
|
"gfx1010": "navi10",
|
|
"gfx1011": "navi12",
|
|
"gfx1012": "navi14",
|
|
"gfx1030": "navi21",
|
|
"gfx1100": "navi31",
|
|
"gfx1101": "navi32",
|
|
"gfx1102": "navi33",
|
|
"gfx1103": "gfx1103",
|
|
"gfx1150": "gfx1150",
|
|
"gfx1151": "gfx1151",
|
|
"gfx1152": "gfx1152",
|
|
"gfx1153": "gfx1153",
|
|
"gfx1200": "gfx1200",
|
|
"gfx1201": "gfx1201",
|
|
"gfx1250": "gfx1250",
|
|
}
|
|
|
|
gfxVariantMap = {
|
|
"gfx906": ["gfx906:xnack+", "gfx906:xnack-"],
|
|
"gfx908": ["gfx908:xnack+", "gfx908:xnack-"],
|
|
"gfx90a": ["gfx90a:xnack+", "gfx90a:xnack-"],
|
|
"gfx942": ["gfx942:xnack+", "gfx942:xnack-"],
|
|
"gfx950": ["gfx950:xnack+", "gfx950:xnack-"],
|
|
}
|
|
|
|
SUPPORTED_ISA = [
|
|
IsaVersion(8, 0, 3),
|
|
IsaVersion(9, 0, 0),
|
|
IsaVersion(9, 0, 6),
|
|
IsaVersion(9, 0, 8),
|
|
IsaVersion(9, 0, 10),
|
|
IsaVersion(9, 4, 2),
|
|
IsaVersion(9, 5, 0),
|
|
IsaVersion(10, 1, 0),
|
|
IsaVersion(10, 1, 1),
|
|
IsaVersion(10, 1, 2),
|
|
IsaVersion(10, 3, 0),
|
|
IsaVersion(11, 0, 0),
|
|
IsaVersion(11, 0, 1),
|
|
IsaVersion(11, 0, 2),
|
|
IsaVersion(11, 0, 3),
|
|
IsaVersion(11, 5, 0),
|
|
IsaVersion(11, 5, 1),
|
|
IsaVersion(11, 5, 2),
|
|
IsaVersion(11, 5, 3),
|
|
IsaVersion(12, 0, 0),
|
|
IsaVersion(12, 0, 1),
|
|
IsaVersion(12, 5, 0),
|
|
]
|
|
|
|
# Source-of-truth chip IDs used to generate build predicates.
|
|
GFX_CHIP_IDS = {
|
|
"gfx942": ["74a0", "74a1", "74a2", "74a3", "74a5", "74a9"],
|
|
"gfx950": ["75a0", "75b0", "75a2", "75b2", "75a3", "75b3", "75a8", "75b8"],
|
|
}
|
|
|
|
# Source-of-truth CU counts used to generate build predicates.
|
|
GFX_CU_COUNTS = {
|
|
"gfx942": ["20", "38", "64", "80", "96", "228", "304"],
|
|
}
|
|
|
|
SUPPORTED_BUILD_CHIP_IDS = {
|
|
f"id={chipId}": gfx for gfx, chipIds in GFX_CHIP_IDS.items() for chipId in chipIds
|
|
}
|
|
|
|
SUPPORTED_BUILD_CU_COUNTS = {
|
|
f"cu={cuCount}": gfx for gfx, cuCounts in GFX_CU_COUNTS.items() for cuCount in cuCounts
|
|
}
|
|
|
|
SUPPORTED_CHIP_ID_FALLBACKS = {
|
|
"id=75b0": ["id=75a0"],
|
|
"id=75a2": ["id=75a0"],
|
|
"id=75b2": ["id=75a0"],
|
|
"id=75a3": ["id=75a0"],
|
|
"id=75b3": ["id=75a0"],
|
|
"id=75a8": ["id=75a0"],
|
|
"id=75b8": ["id=75a0"],
|
|
}
|
|
|
|
# `None` refers to an unspecified CU count.
|
|
SUPPORTED_CU_COUNT_FALLBACKS = {
|
|
"cu=20": None,
|
|
"cu=38": None,
|
|
"cu=64": None,
|
|
"cu=80": None,
|
|
"cu=96": None,
|
|
"cu=228": None,
|
|
"cu=304": None,
|
|
}
|
|
|
|
def supportsChipIdPredicate(gfx: str) -> bool:
|
|
"""
|
|
Returns whether PCI chip ID predicates are currently enabled for a GFX architecture.
|
|
|
|
Extend as needed to support other architectures.
|
|
"""
|
|
return gfx == "gfx950"
|
|
|
|
|
|
def isaToGfx(arch: IsaVersion) -> str:
|
|
"""Converts an ISA version to a gfx architecture name.
|
|
|
|
Args:
|
|
arch: An object representing the major, minor, and step version of the ISA.
|
|
|
|
Returns:
|
|
The name of the GPU architecture (e.g., 'gfx906').
|
|
"""
|
|
# Convert last digit to hex because reasons
|
|
name = str(arch[0]) + str(arch[1]) + ("%x" % arch[2])
|
|
return "gfx" + "".join(map(str, name))
|
|
|
|
|
|
SUPPORTED_GFX = [isaToGfx(isa) for isa in SUPPORTED_ISA]
|
|
|
|
|
|
def gfxToIsa(name: str) -> Optional[IsaVersion]:
|
|
"""Extracts the ISA version from a given gfx architecture name.
|
|
|
|
Args:
|
|
name: The gfx name of the GPU architecture (e.g., 'gfx906').
|
|
|
|
Returns:
|
|
An object representing the major, minor, and step version of the ISA.
|
|
Returns None if the name does not match the expected pattern.
|
|
"""
|
|
match = re.search(r"gfx([0-9a-fA-F]{3,})", name)
|
|
if not match:
|
|
return None
|
|
ipart = match.group(1)
|
|
step = int(ipart[-1], 16)
|
|
|
|
ipart = ipart[:-1]
|
|
minor = int(ipart[-1])
|
|
|
|
ipart = ipart[:-1]
|
|
major = int(ipart)
|
|
return IsaVersion(major, minor, step)
|
|
|
|
|
|
def gfxToSwCodename(gfxName: str) -> Optional[str]:
|
|
"""Retrieves the common name for a given gfx architecture name.
|
|
|
|
Args:
|
|
gfxName: The name of the GPU architecture (e.g., gfx1100).
|
|
|
|
Returns:
|
|
The common name of the GPU architecture (e.g., navi31) if found in ``architectureMap``.
|
|
Returns None if the name is not found.
|
|
"""
|
|
if gfxName in architectureMap:
|
|
return architectureMap[gfxName]
|
|
else:
|
|
for archKey in architectureMap:
|
|
if gfxName in archKey:
|
|
return architectureMap[archKey]
|
|
return None
|
|
|
|
|
|
def gfxToVariants(gfx: str) -> List[str]:
|
|
"""Retrieves the list of variants for a given gfx architecture name.
|
|
|
|
Args:
|
|
gfx: The name of the GPU architecture (e.g., 'gfx906').
|
|
|
|
Returns:
|
|
List of variants for the GPU architecture.
|
|
"""
|
|
return gfxVariantMap.get(gfx, [gfx])
|
|
|
|
|
|
def cliArchsToIsa(cliArchs: str) -> List[IsaVersion]:
|
|
"""Maps the requested gfx architectures to ISA numbers.
|
|
|
|
Args:
|
|
archs: str of ";" or "_" separated gfx architectures (e.g., gfx1100 or gfx90a;gfx1101).
|
|
|
|
Returns:
|
|
List of tuples
|
|
"""
|
|
archs = cliArchs.split(";") if ";" in cliArchs else cliArchs.split("_")
|
|
return SUPPORTED_ISA if "all" in archs else [gfxToIsa(''.join(map(str, arch))) for arch in archs]
|
|
|
|
|
|
def _detectGlobalCurrentISA(detectionTool, deviceId: int):
|
|
"""
|
|
Returns returncode if detection failure
|
|
"""
|
|
# Belt-and-suspenders for the GPU-less --cpu-only switch: when CpuOnly is set,
|
|
# return a spoofed per-arch IsaVersion (derived from gfxToIsa) instead of shelling
|
|
# out to a device-enumeration tool. The arch comes from the CpuOnlyArch plumbing key.
|
|
# This backstops any entry path that reaches detection without passing an arch (the
|
|
# primary path supplies the arch via --gpu-targets and never reaches here). Returning
|
|
# an IsaVersion makes the isinstance(...) guard in detectGlobalCurrentISA pass so the
|
|
# "Failed to detect currect ISA" raise never fires GPU-less.
|
|
# Imported lazily to avoid a circular import (GlobalParameters imports from this module).
|
|
from .GlobalParameters import globalParameters
|
|
if globalParameters.get("CpuOnly"):
|
|
isa = gfxToIsa(globalParameters.get("CpuOnlyArch", "gfx942"))
|
|
if isa is not None:
|
|
print(f"# CpuOnly: spoofing GPU {deviceId} ISA as " + isaToGfx(isa))
|
|
return isa
|
|
process = run([detectionTool], stdout=PIPE)
|
|
archList = []
|
|
for line in process.stdout.decode().split("\n"):
|
|
arch = gfxToIsa(line.strip())
|
|
if arch is not None:
|
|
if arch in SUPPORTED_ISA:
|
|
print(f"# Detected GPU {deviceId} with ISA: " + isaToGfx(arch))
|
|
archList.append(arch)
|
|
if process.returncode:
|
|
print(f"{detectionTool} exited with code {process.returncode}")
|
|
return archList[deviceId] if (len(archList) > 0 and process.returncode == 0) else process.returncode
|
|
|
|
|
|
def detectGlobalCurrentISA(deviceId: int, enumerator: str):
|
|
"""Returns the ISA version for a given device.
|
|
|
|
Given an integer ID for a device, the ISA version tuple
|
|
of the form (X, Y, Z) is computed using first amdgpu-arch.
|
|
If amdgpu-arch fails, rocm_agent_enumerator is used.
|
|
|
|
Args:
|
|
deviceID: an integer indicating the device to inspect.
|
|
|
|
Raises:
|
|
Exception if both tools fail to detect ISA.
|
|
"""
|
|
result = _detectGlobalCurrentISA(enumerator, deviceId)
|
|
if not isinstance(result, IsaVersion):
|
|
raise Exception("Failed to detect currect ISA")
|
|
return result
|
|
|
|
|
|
def detectHostGfxArchs() -> List[str]:
|
|
"""Enumerate the supported GPU architectures physically present on this host.
|
|
|
|
Reuses the same device-enumeration tool selection as the rest of the
|
|
toolchain (``ToolchainDefaults.DEVICE_ENUMERATOR`` -> ``rocm_agent_enumerator``
|
|
or ``amdgpu-arch``) and the canonical ``gfxToIsa``/``isaToGfx`` maps. Each
|
|
enumerated line is normalized through ``gfxToIsa`` (which strips ``:xnack±``
|
|
and other suffixes) and filtered to ``SUPPORTED_ISA``, so CPU agents
|
|
(``gfx000``) and unsupported devices are dropped.
|
|
|
|
Returns:
|
|
A de-duplicated list of canonical gfx names (e.g. ``["gfx950"]``).
|
|
Returns an empty list when no enumerator is available or it fails --
|
|
callers should treat "empty" as "cannot benchmark here".
|
|
"""
|
|
# Lazy import: keep this module free of a load-time dependency on the
|
|
# Toolchain package (which imports Common.Utilities) and avoid any import cycle.
|
|
try:
|
|
from Tensile.Toolchain.Validators import ToolchainDefaults, validateToolchain
|
|
except Exception:
|
|
return []
|
|
|
|
tool = ToolchainDefaults.DEVICE_ENUMERATOR
|
|
try:
|
|
toolPath = validateToolchain(tool)
|
|
except (FileNotFoundError, ValueError):
|
|
return []
|
|
|
|
try:
|
|
process = run([toolPath], stdout=PIPE, stderr=PIPE)
|
|
except OSError:
|
|
return []
|
|
if process.returncode:
|
|
return []
|
|
|
|
archs: List[str] = []
|
|
for line in process.stdout.decode(errors="replace").splitlines():
|
|
isa = gfxToIsa(line.strip())
|
|
if isa is not None and isa in SUPPORTED_ISA:
|
|
gfx = isaToGfx(isa)
|
|
if gfx not in archs:
|
|
archs.append(gfx)
|
|
return archs
|
|
|
|
|
|
def hostHasArch(arch: str) -> bool:
|
|
"""Return True iff ``arch`` matches a supported GPU present on this host.
|
|
|
|
Comparison is done on the normalized ISA version, so ``:xnack±`` / CU
|
|
variants on either side (requested arch or enumerated arch) compare equal.
|
|
"""
|
|
target = gfxToIsa(arch)
|
|
if target is None:
|
|
return False
|
|
return any(gfxToIsa(a) == target for a in detectHostGfxArchs())
|
|
|
|
|
|
class ArchInfo(NamedTuple):
|
|
Name: str
|
|
Gfx: str
|
|
DeviceIds: Optional[Set[str]]
|
|
CUCount: Optional[str] = None
|
|
|
|
|
|
class LogicFileError(Exception):
|
|
def __init__(self, message="Expected line is either not present or is malformed"):
|
|
self.message = message
|
|
super().__init__(self.message)
|
|
|
|
|
|
def _extractArchInfo(file: Union[str, Path], validateDeviceIds: bool = True) -> ArchInfo:
|
|
"""
|
|
Extracts architecture predicate information from a given logic file.
|
|
|
|
The file is expected to have the following format:
|
|
- Line 0: Minimum required version (e.g., "- {MinimumRequiredVersion: 4.33.0}")
|
|
- Line 1: Code name of the architecture (e.g., "- aquavanjaram")
|
|
- Line 2: GFX name of the architecture or a map with variant details (e.g., "- gfx950" or "- {Architecture: gfx950, CUCount: 256}")
|
|
- Line 3: Device IDs (e.g., "- [Device 1234, Device 5678]")
|
|
|
|
Args:
|
|
file: Path to a logic file.
|
|
validateDeviceIds: Whether to validate Device IDs against the supported
|
|
chip-ID tables while parsing.
|
|
Returns:
|
|
ArchInfo: An object containing the extracted architecture predicates.
|
|
Raises:
|
|
LogicFileError: If the file does not match the expected format.
|
|
"""
|
|
|
|
def l0(line: str):
|
|
if not re.match(r"- (?:\{MinimumRequiredVersion|MinimumRequiredVersion:)", line):
|
|
raise LogicFileError(
|
|
f"Expected minimum required version:\n line: {line} file: {file}"
|
|
)
|
|
|
|
def l1(line: str):
|
|
return line[2:].strip()
|
|
|
|
def l2(line: str):
|
|
match1 = re.match(r"- \{Architecture: (\w+), CUCount: (\d+)\}", line)
|
|
match2 = re.match(r"- gfx(\w+)", line)
|
|
if match1:
|
|
architecture, cu_count = match1.groups()
|
|
return architecture, f"cu={cu_count}"
|
|
elif match2:
|
|
return line[2:].strip(), None
|
|
else:
|
|
raise LogicFileError(
|
|
f"Expected architecture and CU count, or only an archiecture: line: {line}"
|
|
)
|
|
|
|
def l3(line: str):
|
|
if re.match(r"- \[Device", line):
|
|
devIds = re.findall(r"Device (\w+)", line)
|
|
# Normalize to lowercase so downstream consumers (predicate
|
|
# tables, fallback maps, chip-ID directory matchers) all agree
|
|
# on the canonical form.
|
|
return set(f"id={id.lower()}" for id in devIds)
|
|
else:
|
|
raise LogicFileError(f"No device IDs found: line: {line}")
|
|
|
|
with open(file, "r") as f:
|
|
l0(f.readline())
|
|
name = l1(f.readline())
|
|
gfx, cu = l2(f.readline())
|
|
deviceIds = l3(f.readline())
|
|
|
|
if validateDeviceIds:
|
|
try:
|
|
for id in deviceIds:
|
|
_verifyPredicate(id, gfx)
|
|
except ValueError as e:
|
|
raise LogicFileError(f"Invalid device ID found while parsing {file}: {e}")
|
|
|
|
return ArchInfo(Name=name, Gfx=gfx, DeviceIds=deviceIds, CUCount=cu)
|
|
|
|
|
|
def _verifyPredicate(predicateSpec: str, gfx: str) -> str:
|
|
"""
|
|
Verifies that a predicate specification is valid.
|
|
|
|
Args:
|
|
predicateSpec: A string representing a predicate specification.
|
|
gfx: GFX architecture to validate device ID against.
|
|
|
|
Returns:
|
|
The validated predicate specification.
|
|
Raises:
|
|
ValueError: If the predicate specification is invalid or if device ID doesn't match GFX architecture.
|
|
"""
|
|
msgPrefix = f"Invalid predicate: {predicateSpec}"
|
|
key, _, val = predicateSpec.partition("=")
|
|
if key == "id":
|
|
if predicateSpec not in SUPPORTED_BUILD_CHIP_IDS:
|
|
raise ValueError(f"{msgPrefix}: device ID not supported")
|
|
if gfx and SUPPORTED_BUILD_CHIP_IDS[predicateSpec] != gfx:
|
|
raise ValueError(f"{msgPrefix}: device ID is not associated with {gfx}")
|
|
elif key == "cu":
|
|
if predicateSpec not in SUPPORTED_BUILD_CU_COUNTS:
|
|
raise ValueError(f"{msgPrefix}: CU count not supported")
|
|
if gfx and SUPPORTED_BUILD_CU_COUNTS[predicateSpec] != gfx:
|
|
raise ValueError(f"{msgPrefix}: CU count is not associated with {gfx}")
|
|
else:
|
|
raise ValueError(f"{msgPrefix}: only device ID and CU count-based predicates are currently supported")
|
|
return predicateSpec
|
|
|
|
|
|
def splitArchsFromPredicates(archSpecs: List[str]) -> Tuple[List[str], Optional[Dict[str, List[str]]]]:
|
|
"""
|
|
Splits a list of architecture specifications into architectures and their predicates.
|
|
|
|
Example inputs:
|
|
["gfx942"] # No predicates
|
|
["gfx942[id=74a0,id=74a1]"] # With device IDs
|
|
["gfx942[cu=80,cu=96]"] # With CU counts
|
|
["gfx942[id=74a0,cu=80]"] # With both
|
|
|
|
Args:
|
|
archSpecs: List of architecture specifications, optionally with predicates in square brackets
|
|
|
|
Returns:
|
|
Tuple of:
|
|
- List of architecture names
|
|
- Dictionary mapping architectures to their predicates (or None if no predicates)
|
|
"""
|
|
# Match predicates in square brackets, e.g., [id=74a0,cu=80]
|
|
pattern = re.compile(r"\[(.*?)\]")
|
|
|
|
architectures = set()
|
|
predicateMap = collections.defaultdict(list)
|
|
|
|
for spec in archSpecs:
|
|
spec = spec.strip()
|
|
arch = spec # Default to full spec if no predicates
|
|
|
|
match = re.search(pattern, spec)
|
|
if match:
|
|
arch = spec[:match.start()].strip()
|
|
predicates = [p.strip().lower() for p in match.group(1).split(",")]
|
|
predicateMap[arch].extend(_verifyPredicate(p, arch) for p in predicates)
|
|
|
|
if arch not in architectureMap:
|
|
raise ValueError(f"Architecture {spec} not supported")
|
|
|
|
architectures.add(arch)
|
|
|
|
return list(architectures), predicateMap or None
|
|
|
|
|
|
def _addVariantMap(
|
|
gfxPredicateMap: Dict[str, Set[Tuple[Path, str]]], spec: str, path: Path, fname: str
|
|
) -> bool:
|
|
"""
|
|
Adds a logic file to a predicate map.
|
|
|
|
Args:
|
|
gfxPredicateMap: Nested dict mapping architectures to their predicate sets
|
|
spec: Predicate specification
|
|
path: Path to the logic file
|
|
fname: Filename of the logic file
|
|
Returns:
|
|
True if the logic file was added to the predicate map, False otherwise
|
|
"""
|
|
if fname not in {x for _, x in gfxPredicateMap[spec]}:
|
|
gfxPredicateMap[spec].add((path, fname))
|
|
return True
|
|
return False
|
|
|
|
|
|
def _populateVariantMap(
|
|
predicateMap: Dict[str, Dict[str, Set[Tuple[Path, str]]]],
|
|
targetLogicFile: Path,
|
|
fallbackKey: str,
|
|
):
|
|
"""
|
|
Populates a predicate map with logic files, handling both exact matches and fallbacks.
|
|
|
|
For each logic file:
|
|
1. First tries to match against specific predicates (device IDs, CU counts)
|
|
2. If matched to any specific predicate, removes from fallbacks
|
|
3. If no specific matches, tries to add to fallbacks based on fallback rules
|
|
|
|
Args:
|
|
predicateMap: Nested dict mapping architectures to their predicate sets
|
|
targetLogicFile: Logic file to process
|
|
fallbackKey: Key used to store fallback matches
|
|
"""
|
|
file = Path(targetLogicFile)
|
|
path, fname = file.parent, file.name
|
|
|
|
archinfo = _extractArchInfo(file)
|
|
if archinfo.Gfx not in predicateMap:
|
|
return
|
|
|
|
gfxPredicateMap = predicateMap[archinfo.Gfx]
|
|
requestedDevIds = {x for x in gfxPredicateMap if x.startswith("id=")}
|
|
requestedCUs = {x for x in gfxPredicateMap if x.startswith("cu=")}
|
|
|
|
fallbackDevIds = {
|
|
fallbackId
|
|
for v in requestedDevIds
|
|
if v in SUPPORTED_CHIP_ID_FALLBACKS
|
|
for fallbackId in SUPPORTED_CHIP_ID_FALLBACKS[v]
|
|
}
|
|
fallbackCUs = {SUPPORTED_CU_COUNT_FALLBACKS[v] for v in requestedCUs if v in SUPPORTED_CU_COUNT_FALLBACKS}
|
|
|
|
isCuFallback = not requestedCUs or archinfo.CUCount in fallbackCUs
|
|
isDevIdFallback = not requestedDevIds or (
|
|
archinfo.DeviceIds and any(fallbackId in archinfo.DeviceIds for fallbackId in fallbackDevIds)
|
|
)
|
|
|
|
if isCuFallback and isDevIdFallback:
|
|
# If the file name is not already in a requested predicate, then add it to the fallback set
|
|
if all(
|
|
fname not in {nm for _, nm in gfxPredicateMap[spec]}
|
|
for spec in gfxPredicateMap
|
|
if spec != fallbackKey
|
|
):
|
|
gfxPredicateMap[fallbackKey].add((path, fname))
|
|
else:
|
|
removeFallbacks = []
|
|
for spec in gfxPredicateMap:
|
|
if spec != fallbackKey: # Don't try to add to fallback set here
|
|
if "id" in spec and archinfo.DeviceIds:
|
|
removeFallbacks.extend(
|
|
_addVariantMap(gfxPredicateMap, spec, path, fname)
|
|
for id in archinfo.DeviceIds
|
|
if id == spec
|
|
)
|
|
if "cu" in spec and archinfo.CUCount:
|
|
removeFallbacks.append(
|
|
_addVariantMap(gfxPredicateMap, spec, path, fname)
|
|
if archinfo.CUCount == spec
|
|
else False
|
|
)
|
|
|
|
if removeFallbacks and any(removeFallbacks):
|
|
gfxPredicateMap[fallbackKey] = {
|
|
x for x in gfxPredicateMap[fallbackKey] if x[1] != fname
|
|
}
|
|
|
|
|
|
def filterLogicFilesByPredicates(
|
|
logicFiles: List[str], variants: Dict[str, Dict[str, Set[Tuple[Path, str]]]]
|
|
) -> List[str]:
|
|
"""
|
|
Filters logic files based on the requested predicates.
|
|
|
|
Args:
|
|
logicFiles: List of logic file paths
|
|
variants: Dictionary mapping architectures to their predicate sets
|
|
|
|
Returns:
|
|
List of logic file paths that match the requested predicates
|
|
"""
|
|
fallbackKey = "fallback"
|
|
# A `spec` here is a variant specification passed via the command line, e.g., "cu=64"
|
|
# This is how the code differentiates variants of the same gfx, as well as "fallback" files
|
|
variantMap = {gfx: {spec: set() for spec in specs} for gfx, specs in variants.items()}
|
|
for file in variantMap.values():
|
|
file[fallbackKey] = set()
|
|
|
|
for logicFile in logicFiles:
|
|
_populateVariantMap(variantMap, Path(logicFile), fallbackKey)
|
|
|
|
return [
|
|
str(p / file)
|
|
for gfxPredicateMap in variantMap.values()
|
|
for files in gfxPredicateMap.values()
|
|
for p, file in files
|
|
]
|