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
391 lines
10 KiB
Python
391 lines
10 KiB
Python
# Copyright (c) 2025-2026 Advanced Micro Devices, Inc.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""Semantic AST types for AMD GPU instruction semantics.
|
|
|
|
Represents instruction semantics as an expression tree.
|
|
Every XML ``<op>`` element becomes a :class:`SemaNode`. The AST is a tree
|
|
(not SSA/graph), and nodes are immutable for safe functional construction
|
|
during enrichment.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import dataclasses
|
|
from dataclasses import dataclass, field
|
|
from enum import Enum
|
|
from typing import ClassVar, Generator
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class SemaType:
|
|
"""Type annotation from the XML ``<t base="..." size="..."/>`` elements."""
|
|
|
|
base: str
|
|
size: int = 0
|
|
|
|
U1: ClassVar[SemaType]
|
|
U32: ClassVar[SemaType]
|
|
U64: ClassVar[SemaType]
|
|
I32: ClassVar[SemaType]
|
|
I64: ClassVar[SemaType]
|
|
F16: ClassVar[SemaType]
|
|
F32: ClassVar[SemaType]
|
|
F64: ClassVar[SemaType]
|
|
B32: ClassVar[SemaType]
|
|
B64: ClassVar[SemaType]
|
|
BF16: ClassVar[SemaType]
|
|
FP8: ClassVar[SemaType]
|
|
BF8: ClassVar[SemaType]
|
|
FP6: ClassVar[SemaType]
|
|
FP4: ClassVar[SemaType]
|
|
|
|
@property
|
|
def cpp_type(self) -> str:
|
|
"""Map to C++ simulation type."""
|
|
if self.base == 'F':
|
|
if self.size == 16:
|
|
return 'uint16_t'
|
|
if self.size == 32:
|
|
return 'float'
|
|
if self.size == 64:
|
|
return 'double'
|
|
if self.base in ('U', 'B'):
|
|
if self.size <= 8:
|
|
return 'uint8_t'
|
|
if self.size <= 16:
|
|
return 'uint16_t'
|
|
if self.size <= 32:
|
|
return 'uint32_t'
|
|
return 'uint64_t'
|
|
if self.base == 'I':
|
|
if self.size <= 8:
|
|
return 'int8_t'
|
|
if self.size <= 16:
|
|
return 'int16_t'
|
|
if self.size <= 32:
|
|
return 'int32_t'
|
|
return 'int64_t'
|
|
if self.base == 'BF':
|
|
return 'uint8_t' if self.size <= 8 else 'uint16_t'
|
|
if self.base == 'FP':
|
|
if self.size <= 4:
|
|
return 'uint8_t'
|
|
if self.size <= 8:
|
|
return 'uint8_t'
|
|
return 'uint16_t'
|
|
return 'uint32_t'
|
|
|
|
|
|
SemaType.U1 = SemaType('U', 1)
|
|
SemaType.U32 = SemaType('U', 32)
|
|
SemaType.U64 = SemaType('U', 64)
|
|
SemaType.I32 = SemaType('I', 32)
|
|
SemaType.I64 = SemaType('I', 64)
|
|
SemaType.F16 = SemaType('F', 16)
|
|
SemaType.F32 = SemaType('F', 32)
|
|
SemaType.F64 = SemaType('F', 64)
|
|
SemaType.B32 = SemaType('B', 32)
|
|
SemaType.BF16 = SemaType('BF', 16)
|
|
SemaType.FP8 = SemaType('FP', 8)
|
|
SemaType.BF8 = SemaType('BF', 8)
|
|
SemaType.FP6 = SemaType('FP', 6)
|
|
SemaType.FP4 = SemaType('FP', 4)
|
|
SemaType.B64 = SemaType('B', 64)
|
|
|
|
|
|
class SemaNodeKind(Enum):
|
|
"""Discriminant for SemaNode — one per XML op type."""
|
|
|
|
# Arithmetic
|
|
ADD = '+'
|
|
SUB = '-'
|
|
MUL = '*'
|
|
DIV = '/'
|
|
MOD = '%'
|
|
POW = '**'
|
|
ADD_ASSIGN = '+='
|
|
SUB_ASSIGN = '-='
|
|
|
|
# Comparison
|
|
EQ = '=='
|
|
NE = '!='
|
|
LT = '<'
|
|
GT = '>'
|
|
LE = '<='
|
|
GE = '>='
|
|
UNORD_NE = '<>'
|
|
|
|
# Bitwise
|
|
AND = '&'
|
|
OR = '|'
|
|
XOR = '^'
|
|
SHL = '<<'
|
|
SHR = '>>'
|
|
BITNEG = '.bitneg'
|
|
BITCAT = '.bitcat'
|
|
|
|
# Logical
|
|
LAND = '&&'
|
|
LOR = '||'
|
|
BOOLNEG = '.boolneg'
|
|
|
|
# FP intrinsics
|
|
FMA = '.fma'
|
|
SQRT = '.sqrt'
|
|
SIN = '.sin'
|
|
COS = '.cos'
|
|
LOG2 = '.log2'
|
|
LDEXP = '.ldexp'
|
|
FLOOR = '.floor'
|
|
TRUNC = '.trunc'
|
|
FRACT = '.fract'
|
|
FPOW = '.pow'
|
|
|
|
# Sign/abs
|
|
ABS = '.abs'
|
|
UMINUS = '.uminus'
|
|
UPLUS = '.uplus'
|
|
SIGN = '.sign'
|
|
SIGNEXT = '.signext'
|
|
SIGNEXT_FROM_BIT = '.signext_from_bit'
|
|
|
|
# Type conversion
|
|
CAST = '.cast'
|
|
|
|
# Memory / register
|
|
INSTOPERAND = '.instoperand'
|
|
ARRAYDEREF = '.arrayderef'
|
|
ARRAYSLICE = '.arrayslice'
|
|
ARRAYSLICESIZE = '.arrayslicesize'
|
|
FIELDDEREF = '.fieldderef'
|
|
CONS_ARRAY = '.cons_array'
|
|
|
|
# Function call
|
|
CALL = '.call'
|
|
LAMBDA = '.lambda'
|
|
|
|
# Control flow
|
|
SEQ = ':seq'
|
|
IF = ':if'
|
|
FOR = ':for'
|
|
WHILE = ':while'
|
|
BREAK = ':break'
|
|
CONTINUE = ':continue'
|
|
RETURN = ':return'
|
|
|
|
# Declaration / metadata
|
|
DECLARE = ':declare'
|
|
COMMENT = ':comment'
|
|
EVAL = ':eval'
|
|
PRAGMA = ':pragma'
|
|
|
|
# Assignment
|
|
ASSIGN = '='
|
|
|
|
# Ternary
|
|
TERNARY = '?:'
|
|
|
|
# Forward-compatibility
|
|
SWITCH = ':switch'
|
|
CASE = ':case'
|
|
DEFAULT = ':default'
|
|
|
|
# Aggregation
|
|
SUM = '.sum'
|
|
WITHIN = '.within'
|
|
|
|
# FP decomposition
|
|
EXPONENT = '.exponent'
|
|
MANTISSA = '.mantissa'
|
|
|
|
# Synthetic leaf nodes (not XML op types — parser-synthesized)
|
|
LIT = '_lit'
|
|
ID = '_id'
|
|
|
|
|
|
_STATEMENT_KINDS: frozenset[SemaNodeKind] = frozenset(
|
|
{
|
|
SemaNodeKind.SEQ,
|
|
SemaNodeKind.ASSIGN,
|
|
SemaNodeKind.IF,
|
|
SemaNodeKind.FOR,
|
|
SemaNodeKind.WHILE,
|
|
SemaNodeKind.BREAK,
|
|
SemaNodeKind.CONTINUE,
|
|
SemaNodeKind.COMMENT,
|
|
SemaNodeKind.DECLARE,
|
|
SemaNodeKind.PRAGMA,
|
|
SemaNodeKind.RETURN,
|
|
SemaNodeKind.ADD_ASSIGN,
|
|
SemaNodeKind.SUB_ASSIGN,
|
|
}
|
|
)
|
|
|
|
_BINARY_KINDS: frozenset[SemaNodeKind] = frozenset(
|
|
{
|
|
SemaNodeKind.ADD,
|
|
SemaNodeKind.SUB,
|
|
SemaNodeKind.MUL,
|
|
SemaNodeKind.DIV,
|
|
SemaNodeKind.MOD,
|
|
SemaNodeKind.AND,
|
|
SemaNodeKind.OR,
|
|
SemaNodeKind.XOR,
|
|
SemaNodeKind.SHL,
|
|
SemaNodeKind.SHR,
|
|
SemaNodeKind.LAND,
|
|
SemaNodeKind.LOR,
|
|
SemaNodeKind.EQ,
|
|
SemaNodeKind.NE,
|
|
SemaNodeKind.LT,
|
|
SemaNodeKind.GT,
|
|
SemaNodeKind.LE,
|
|
SemaNodeKind.GE,
|
|
SemaNodeKind.UNORD_NE,
|
|
SemaNodeKind.POW,
|
|
SemaNodeKind.FPOW,
|
|
SemaNodeKind.LDEXP,
|
|
}
|
|
)
|
|
|
|
_UNARY_KINDS: frozenset[SemaNodeKind] = frozenset(
|
|
{
|
|
SemaNodeKind.BITNEG,
|
|
SemaNodeKind.BOOLNEG,
|
|
SemaNodeKind.ABS,
|
|
SemaNodeKind.UMINUS,
|
|
SemaNodeKind.UPLUS,
|
|
SemaNodeKind.SIGN,
|
|
SemaNodeKind.SIGNEXT,
|
|
SemaNodeKind.CAST,
|
|
SemaNodeKind.EVAL,
|
|
SemaNodeKind.FLOOR,
|
|
SemaNodeKind.TRUNC,
|
|
SemaNodeKind.COS,
|
|
SemaNodeKind.SIN,
|
|
SemaNodeKind.SQRT,
|
|
SemaNodeKind.LOG2,
|
|
SemaNodeKind.FRACT,
|
|
SemaNodeKind.EXPONENT,
|
|
SemaNodeKind.MANTISSA,
|
|
}
|
|
)
|
|
|
|
_TERNARY_KINDS: frozenset[SemaNodeKind] = frozenset(
|
|
{
|
|
SemaNodeKind.TERNARY,
|
|
SemaNodeKind.ARRAYSLICE,
|
|
SemaNodeKind.ARRAYSLICESIZE,
|
|
SemaNodeKind.FMA,
|
|
}
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class SemaNode:
|
|
"""A single node in the semantic AST tree.
|
|
|
|
Each XML ``<op type="...">`` element becomes one SemaNode. Children are
|
|
the operands/subexpressions. Leaf nodes (literals, identifiers) use
|
|
LIT and ID kinds respectively.
|
|
|
|
Immutable (frozen) with tuple children. Enrichment creates new trees
|
|
via ``dataclasses.replace()``, never mutates existing nodes.
|
|
"""
|
|
|
|
kind: SemaNodeKind
|
|
ty: SemaType | None = None
|
|
children: tuple[SemaNode, ...] = ()
|
|
lit_value: str | None = None
|
|
id_name: str | None = None
|
|
cast_target: SemaType | None = None
|
|
call_name: str | None = None
|
|
call_lambda: SemaType | None = None
|
|
unknown_op: str | None = None
|
|
|
|
def is_leaf(self) -> bool:
|
|
return self.kind in (SemaNodeKind.LIT, SemaNodeKind.ID)
|
|
|
|
def walk(self) -> Generator[SemaNode, None, None]:
|
|
"""Yield all nodes in pre-order traversal."""
|
|
yield self
|
|
for child in self.children:
|
|
yield from child.walk()
|
|
|
|
|
|
class ExecModel(Enum):
|
|
"""Execution model for an instruction, from the ``:pragma`` annotation."""
|
|
|
|
SCALAR = 'scalar'
|
|
VECTOR = 'vector'
|
|
BRANCH = 'branch'
|
|
UNKNOWN = 'unknown'
|
|
|
|
|
|
@dataclass
|
|
class SemaBlock:
|
|
"""Semantics for one instruction — the top-level AST unit."""
|
|
|
|
instruction_name: str
|
|
pragma: ExecModel
|
|
body: SemaNode
|
|
enriched: bool = False
|
|
|
|
@property
|
|
def is_empty(self) -> bool:
|
|
"""True if this is a stub (empty semantics)."""
|
|
return self.body.kind == SemaNodeKind.SEQ and len(self.body.children) == 0
|
|
|
|
|
|
def validate_types(node: SemaNode) -> None:
|
|
"""Assert that statement nodes carry no result type."""
|
|
for n in node.walk():
|
|
if n.kind in _STATEMENT_KINDS:
|
|
assert (
|
|
n.ty is None
|
|
), f"Statement node {n.kind} must have ty=None, got ty={n.ty}"
|
|
|
|
|
|
def validate_well_formed(node: SemaNode) -> None:
|
|
"""Assert structural well-formedness of the AST."""
|
|
for n in node.walk():
|
|
nc = len(n.children)
|
|
if n.kind in (
|
|
SemaNodeKind.ASSIGN,
|
|
SemaNodeKind.ADD_ASSIGN,
|
|
SemaNodeKind.SUB_ASSIGN,
|
|
SemaNodeKind.ARRAYDEREF,
|
|
):
|
|
assert nc == 2, f"{n.kind} must have 2 children, got {nc}"
|
|
elif n.kind == SemaNodeKind.IF:
|
|
assert nc >= 2, f"IF must have >= 2 children, got {nc}"
|
|
elif n.kind == SemaNodeKind.FOR:
|
|
assert (
|
|
nc == 4
|
|
), f"FOR must have 4 children (init, cond, step, body), got {nc}"
|
|
elif n.kind == SemaNodeKind.WHILE:
|
|
assert nc == 2, f"WHILE must have 2 children (cond, body), got {nc}"
|
|
elif n.kind == SemaNodeKind.BITCAT:
|
|
assert nc >= 2, f"BITCAT must have >= 2 children, got {nc}"
|
|
elif n.kind == SemaNodeKind.SIGNEXT_FROM_BIT:
|
|
assert nc in (1, 2), f"SIGNEXT_FROM_BIT must have 1 or 2 children, got {nc}"
|
|
elif n.kind == SemaNodeKind.WITHIN:
|
|
assert nc in (2, 3), f"WITHIN must have 2 or 3 children, got {nc}"
|
|
elif n.kind in _TERNARY_KINDS:
|
|
assert nc == 3, f"{n.kind} must have 3 children, got {nc}"
|
|
elif n.kind in _BINARY_KINDS:
|
|
assert nc == 2, f"{n.kind} must have 2 children, got {nc}"
|
|
elif n.kind == SemaNodeKind.RETURN:
|
|
assert nc == 1, f"RETURN must have 1 child (return value), got {nc}"
|
|
elif n.kind in _UNARY_KINDS:
|
|
assert nc == 1, f"{n.kind} must have 1 child, got {nc}"
|
|
elif n.kind == SemaNodeKind.LIT:
|
|
assert nc == 0, f"LIT must have 0 children, got {nc}"
|
|
assert n.lit_value is not None, "LIT must have lit_value set"
|
|
elif n.kind == SemaNodeKind.ID:
|
|
assert nc == 0, f"ID must have 0 children, got {nc}"
|
|
assert n.id_name is not None, "ID must have id_name set"
|
|
elif n.kind == SemaNodeKind.CALL:
|
|
assert nc >= 1, f"CALL must have >= 1 child (callee id), got {nc}"
|