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
378 lines
14 KiB
Python
378 lines
14 KiB
Python
# Copyright (c) 2025-2026 Advanced Micro Devices, Inc.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from functools import cached_property
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from amdisa.isa_profile import IsaProfile
|
|
|
|
|
|
@dataclass
|
|
class MicrocodeField:
|
|
"""A bitfield in an encoding's microcode.
|
|
|
|
Attributes:
|
|
name: Name of the field.
|
|
bit_cnt: Size of the field in bits.
|
|
bit_offset: Offset, in bits, of the field within the encoding.
|
|
"""
|
|
|
|
name: str
|
|
bit_cnt: int
|
|
bit_offset: int
|
|
|
|
|
|
#: Base C++ identifier synthesized for a fieldless operand of each operand
|
|
#: type (fieldless operands have no ``<FieldName>`` in the MR ISA to derive a
|
|
#: name from). Bases are deliberately distinct per type so the name reflects
|
|
#: which operand it is -- in particular ``OPR_EXEC`` (``exec``) and
|
|
#: ``OPR_SDST_EXEC`` (``sdst_exec``) are kept apart. This is a readability
|
|
#: choice, not a correctness one. Any type not listed falls back to the type
|
|
#: name with the ``OPR_`` prefix stripped and lowercased (see
|
|
#: ``synthesize_fieldless_name``).
|
|
_FIELDLESS_NAME_MAP: dict[str, str] = {
|
|
'OPR_VCC': 'vcc',
|
|
'OPR_EXEC': 'exec',
|
|
'OPR_SDST_EXEC': 'sdst_exec',
|
|
'OPR_SSRC_SPECIAL_SCC': 'scc',
|
|
'OPR_PC': 'pc',
|
|
'OPR_SDST_M0': 'm0',
|
|
'OPR_DSMEM': 'dsmem',
|
|
'OPR_GPUMEM': 'gpumem',
|
|
'OPR_FLAT_SCRATCH': 'flat_scratch',
|
|
# Fieldless OPR_VGPR is the image ADDRESS/coordinate operand (vaddr).
|
|
'OPR_VGPR': 'vaddr',
|
|
'OPR_SIMM32': 'simm32',
|
|
}
|
|
|
|
|
|
def synthesize_fieldless_name(operand_type: str) -> str:
|
|
"""Return a stable, valid C++ base identifier for a fieldless operand.
|
|
|
|
Fieldless operands (no ``<FieldName>`` in the MR ISA) cannot derive their
|
|
name from an encoding field, so one is synthesized from the operand type.
|
|
The result is a *base* name; callers must still guarantee uniqueness within
|
|
an instruction (two fieldless operands can share a type). See
|
|
``_FIELDLESS_NAME_MAP`` for the curated bases; anything else strips the
|
|
``OPR_`` prefix and lowercases.
|
|
"""
|
|
if operand_type in _FIELDLESS_NAME_MAP:
|
|
return _FIELDLESS_NAME_MAP[operand_type]
|
|
base = operand_type
|
|
if base.startswith('OPR_'):
|
|
base = base[len('OPR_') :]
|
|
return base.lower()
|
|
|
|
|
|
@dataclass
|
|
class Operand:
|
|
"""An instruction operand.
|
|
|
|
Attributes:
|
|
name: Name of the operand (``<FieldName>`` ? name : synthesized id).
|
|
size: Size of the operand in bits.
|
|
operand_type: ISA-specific operand type.
|
|
is_input: True if the operand is an input.
|
|
is_output: True if the operand is an output.
|
|
is_implicit: True if the operand is implicit (the MR ISA
|
|
``IsImplicit`` attribute)
|
|
is_binary_ucode_required: True if the operand is missing from the
|
|
encoding but implied by the type of the operand.
|
|
order: Order of the operand.
|
|
data_format_name: ISA data format name from the XML operand signature.
|
|
fieldless: True if the operand has no ``<FieldName>`` in the MR ISA.
|
|
"""
|
|
|
|
name: str
|
|
size: int
|
|
operand_type: str
|
|
is_input: bool
|
|
is_output: bool
|
|
is_implicit: bool
|
|
is_binary_ucode_required: bool
|
|
order: int
|
|
data_format_name: str = ''
|
|
fieldless: bool = False
|
|
|
|
|
|
@dataclass
|
|
class OperandNamePattern:
|
|
"""Pattern for resolving operand encoding values to operand name strings.
|
|
|
|
Attributes:
|
|
kind: Pattern type (one of the class constants below).
|
|
prefix: Register prefix for reg_range patterns (e.g., "s", "v").
|
|
min_enum: OpSel enum name for the minimum of a range.
|
|
max_enum: OpSel enum name for the maximum of a range.
|
|
operand_name: Operand name string for named/float_const patterns.
|
|
enum_name: OpSel enum name for single-value patterns.
|
|
"""
|
|
|
|
REG_RANGE = 'reg_range'
|
|
POS_INT = 'pos_int'
|
|
NEG_INT = 'neg_int'
|
|
FLOAT_CONST = 'float_const'
|
|
NAMED = 'named'
|
|
LITERAL = 'literal'
|
|
|
|
kind: str
|
|
prefix: str = ''
|
|
min_enum: str = ''
|
|
max_enum: str = ''
|
|
operand_name: str = ''
|
|
enum_name: str = ''
|
|
|
|
|
|
@dataclass
|
|
class OperandSelector:
|
|
"""Operand encoding values that determine how to select the operand value.
|
|
|
|
Attributes:
|
|
operand_type: Operand's type.
|
|
op_sel_vals: Operand value names and their associated values.
|
|
name_patterns: Patterns for resolving encoding values to operand name
|
|
strings.
|
|
"""
|
|
|
|
operand_type: str
|
|
op_sel_vals: list[tuple[str, str]]
|
|
name_patterns: list[OperandNamePattern] = field(default_factory=list)
|
|
|
|
|
|
class InstBase:
|
|
"""Base class for instruction and encoding objects.
|
|
|
|
Provides encoding name formatting shared by InstEncoding and Instruction.
|
|
|
|
Attributes:
|
|
enc_name: Name of the encoding.
|
|
is_implied_literal_enc: True if this encoding is an implied-literal
|
|
alternate. Used by ``fmt_true_enc_name`` to resolve the display
|
|
name back to the parent encoding format.
|
|
"""
|
|
|
|
def __init__(self, enc_name: str, is_implied_literal_enc: bool = False) -> None:
|
|
self.enc_name = enc_name
|
|
self.is_implied_literal_enc = is_implied_literal_enc
|
|
|
|
@cached_property
|
|
def fmt_enc_name(self) -> str:
|
|
"""Encoding name formatted to C++ PascalCase style."""
|
|
if self.enc_name.split('_')[0] == 'ENC':
|
|
return ''.join(x.capitalize() for x in self.enc_name.split('_')[1:])
|
|
return ''.join(x.capitalize() for x in self.enc_name.split('_'))
|
|
|
|
@cached_property
|
|
def fmt_true_enc_name(self) -> str:
|
|
"""True encoding name, resolving implied-literal alternate encodings.
|
|
|
|
Implied-literal alternates (e.g., ``VOP2_INST_LITERAL``) format as
|
|
their parent encoding name (e.g., ``Vop2``) because the C++ class
|
|
hierarchy inherits from the parent encoding class.
|
|
"""
|
|
if self.is_implied_literal_enc:
|
|
return self.enc_name.split('_')[0].capitalize()
|
|
return self.fmt_enc_name
|
|
|
|
|
|
class InstEncoding(InstBase):
|
|
"""An encoding in the ISA specification.
|
|
|
|
Attributes:
|
|
is_primary_decode: True if decoded from the primary decode table.
|
|
primary_dt_ptrs: Pointers to entries in the primary decode table.
|
|
order: Decoding order for this encoding.
|
|
bit_cnt: Size in bits of the encoding.
|
|
enc_field_bit_cnt: Size in bits of the encoding field.
|
|
op_field_bit_cnt: Size in bits of the opcode field.
|
|
ucode_fields: All microcode fields in this encoding.
|
|
enc_conds: Encoding condition (name, logic) pairs.
|
|
insts: Instructions encoded under this encoding.
|
|
implied_literal_ops: Opcode-to-extension-DWORD-count mapping for
|
|
instructions whose encoding carries an implied literal.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
name: str,
|
|
order: int,
|
|
bit_cnt: int,
|
|
enc_field_bit_cnt: int,
|
|
op_field_bit_cnt: int,
|
|
ucode_fields: list[MicrocodeField],
|
|
enc_conds: list[tuple[str, str]],
|
|
) -> None:
|
|
super().__init__(name)
|
|
self.is_primary_decode = True
|
|
self.primary_dt_ptrs: list[int] | None = None
|
|
self.order = order
|
|
self.bit_cnt = bit_cnt
|
|
self.enc_field_bit_cnt = enc_field_bit_cnt
|
|
self.op_field_bit_cnt = op_field_bit_cnt
|
|
self.opm_field_bit_cnt = 0
|
|
self.ucode_fields = ucode_fields
|
|
self.enc_conds = enc_conds
|
|
self.insts: list[Instruction] = []
|
|
self.implied_literal_ops: dict[str, int] = {}
|
|
|
|
@cached_property
|
|
def has_implied_literal_ops(self) -> bool:
|
|
"""True if this encoding has any implied literal opcodes."""
|
|
return len(self.implied_literal_ops) > 0
|
|
|
|
@cached_property
|
|
def has_variable_implied_literal_size(self) -> bool:
|
|
"""True if an implied literal is not the usual one DWORD."""
|
|
return any(words != 1 for words in self.implied_literal_ops.values())
|
|
|
|
|
|
class Instruction(InstBase):
|
|
"""An AMD GPU ISA instruction.
|
|
|
|
Attributes:
|
|
name: Name of the instruction.
|
|
opcode: Opcode of the instruction.
|
|
operands: The instruction's operands.
|
|
available_encodings: All encoding names listed for this instruction in
|
|
the machine-readable ISA, including alternate DPP, SDWA, and
|
|
literal forms that are not emitted as independent instruction
|
|
classes. ``None`` means encoding provenance is unknown; modifier
|
|
generation rejects that state, so synthetic VOP instructions must
|
|
provide an explicit set.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
name: str,
|
|
enc_name: str,
|
|
opcode: int,
|
|
operands: list[Operand],
|
|
is_implied_literal_enc: bool = False,
|
|
available_encodings: frozenset[str] | None = None,
|
|
) -> None:
|
|
super().__init__(enc_name, is_implied_literal_enc)
|
|
self.name = name
|
|
self.opcode = opcode
|
|
self.operands = operands
|
|
self.available_encodings = available_encodings
|
|
|
|
@cached_property
|
|
def fmt_name(self) -> str:
|
|
"""Instruction name formatted to C++ PascalCase style."""
|
|
return (
|
|
f'{"".join(x.capitalize() for x in self.name.split("_"))}'
|
|
f'{self.fmt_true_enc_name}'
|
|
)
|
|
|
|
@cached_property
|
|
def mnemonic(self) -> str:
|
|
"""Instruction mnemonic (lowercase name)."""
|
|
return self.name.lower()
|
|
|
|
@property
|
|
def implicit_operands(self) -> list[Operand]:
|
|
"""Subset of ``operands`` that are implicit (``is_implicit=True``)."""
|
|
return [op for op in self.operands if op.is_implicit]
|
|
|
|
@property
|
|
def explicit_operands(self) -> list[Operand]:
|
|
"""Subset of ``operands`` that are explicit (``is_implicit=False``)."""
|
|
return [op for op in self.operands if not op.is_implicit]
|
|
|
|
@property
|
|
def src_operands(self) -> list[Operand]:
|
|
"""Subset of ``operands`` that are inputs (``is_input=True``).
|
|
|
|
Includes fieldless inputs, so this is NOT the execute-visible source
|
|
set: the code generator filters those through
|
|
``CodeGenerator._execute_operand_participates``.
|
|
"""
|
|
return [op for op in self.operands if op.is_input]
|
|
|
|
@property
|
|
def dst_operands(self) -> list[Operand]:
|
|
"""Subset of ``operands`` that are outputs (``is_output=True``).
|
|
|
|
Includes fieldless outputs (e.g. the SCC/EXEC side-effect defs), so like
|
|
:attr:`src_operands` this is NOT the execute-visible destination set. No
|
|
in-tree consumer yet; filter via
|
|
``CodeGenerator._execute_operand_participates`` for the execute set.
|
|
"""
|
|
return [op for op in self.operands if op.is_output]
|
|
|
|
def has_implicit_operand(self, operand_type: str) -> bool:
|
|
"""True if any implicit operand has the given ``operand_type``."""
|
|
return any(op.operand_type == operand_type for op in self.implicit_operands)
|
|
|
|
|
|
@dataclass
|
|
class DecodeTableEntry:
|
|
"""An entry in the primary decode table.
|
|
|
|
Attributes:
|
|
enc: Instruction encoding that maps to this entry.
|
|
num_dupe_entries: Number of duplicate entries due to don't-care bits.
|
|
inst_name: Name of the instruction if directly decodable.
|
|
is_primary: True if this entry can directly decode an instruction.
|
|
sub_decode_table: Sub-decode table name (if not primary).
|
|
sub_decode_funcs: Sub-decoder function names (if not primary).
|
|
decode_func: Name of this entry's decode function.
|
|
"""
|
|
|
|
enc: InstEncoding
|
|
num_dupe_entries: int
|
|
inst_name: str | None = None
|
|
is_primary: bool = True
|
|
sub_decode_table: str | None = None
|
|
sub_decode_funcs: list[str] | None = None
|
|
decode_func: str | None = None
|
|
|
|
|
|
class IsaSpec:
|
|
"""Internal representation of a machine-readable ISA spec.
|
|
|
|
Holds the data extracted during parsing. ISA-specific constants and
|
|
encoding rules are delegated to the ``IsaProfile`` (see
|
|
``isa_profile.py``). XML schema constants (element/attribute names)
|
|
live in ``xml_schema.py``.
|
|
|
|
Attributes:
|
|
profile: ISA-specific encoding rules and constants.
|
|
arch_name: Architecture name.
|
|
version: Schema version string.
|
|
encoding_map: Maps encoding names to InstEncoding objects.
|
|
inst_encodings: All encodings parsed from the spec.
|
|
operand_types: All operand type names.
|
|
opnd_selectors: All operand selectors.
|
|
primary_decode_table: The primary decode table (indexed by the top
|
|
``profile.max_enc_bits`` bits of the instruction word).
|
|
alt_encs_with_implied_literal: Alternate encoding names whose
|
|
encoding conditions indicate an implied literal DWORD.
|
|
Populated during parsing using the profile's
|
|
``is_implied_literal_encoding()`` method.
|
|
"""
|
|
|
|
def __init__(self, arch_name: str, version: str, profile: IsaProfile) -> None:
|
|
self.profile = profile
|
|
self.arch_name = arch_name
|
|
self.version = version
|
|
self.encoding_map: dict[str, InstEncoding] = {}
|
|
self.inst_encodings: list[InstEncoding] = []
|
|
self.operand_types: list[str] = []
|
|
self.opnd_selectors: list[OperandSelector] = []
|
|
self.primary_decode_table: list[DecodeTableEntry | None] = [None] * pow(
|
|
2, profile.max_enc_bits
|
|
)
|
|
self.alt_encs_with_implied_literal: set[str] = set()
|
|
# Every fieldless operand type observed while parsing this spec.
|
|
# Consumed by fieldless_policy.validate_fieldless_taxonomy.
|
|
self.fieldless_operand_types: set[str] = set()
|
|
if self.version not in profile.supported_versions:
|
|
raise ValueError(
|
|
f'Unsupported machine-readable ISA spec version: {self.version}'
|
|
)
|