Files
TheRock/rocm-systems/emulation/rocjitsu/lib/python/amdisa/sema_fingerprint.py
T
derek.holloway 7502bf64a4
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
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

168 lines
4.7 KiB
Python

# Copyright (c) 2025-2026 Advanced Micro Devices, Inc.
# SPDX-License-Identifier: MIT
"""Semantic fingerprinting for cross-ISA equivalence detection.
Computes a stable SHA-256 hash of a :class:`~amdisa.sema_ast.SemaBlock`'s
semantic content. Two instructions with identical fingerprints are
semantically equivalent — this enables algorithmic cross-ISA comparison
for DBT legalization, replacing hand-maintained mnemonic rename maps.
**What is hashed:** tree structure, node kinds, type annotations, cast
targets, call targets, wavefront context IDs (SCC, VCC, EXEC, etc.),
operand class tags (S, D), operand index literals, bit-range bounds.
**What is normalized:** local temporary names (alpha-renameable), general
literal values, comment text.
**IMPORTANT:** Fingerprinting must operate on pre-enrichment ASTs.
Enrichment adds encoding-specific modifier nodes (NEG/ABS/CLAMP/OMOD)
that vary across ISAs even when the core semantics are identical.
"""
from __future__ import annotations
import hashlib
import logging
from amdisa.sema_ast import SemaBlock, SemaNode, SemaNodeKind
_log = logging.getLogger(__name__)
_CONTEXT_IDS: frozenset[str] = frozenset(
{
'SCC',
'VCC',
'EXEC',
'EXEC_LO',
'MEM',
'LDS',
'VGPR',
'SGPR',
'laneId',
'M0',
'PC',
'TRAPSTS',
'VCCZ',
'EXECZ',
'MODE',
'WAVE64',
'TBA',
'TTMP',
'SHADER_CYCLES_HI',
'SHADER_CYCLES_LO',
'WAVE_STATUS',
'ROUND_MODE',
'DENORM',
'OPSEL',
'OPSEL_HI',
'BARRIER_STATE',
'HW_REGISTERS',
}
)
_OPERAND_TAGS: frozenset[str] = frozenset({'S', 'D'})
_LIT_HASH_PARENTS: frozenset[SemaNodeKind] = frozenset(
{
SemaNodeKind.INSTOPERAND,
SemaNodeKind.ARRAYSLICE,
SemaNodeKind.ARRAYSLICESIZE,
}
)
def fingerprint(block: SemaBlock) -> bytes:
"""Compute a stable hash of a SemaBlock's semantic content.
Returns a 32-byte SHA-256 digest.
Raises:
AssertionError: If ``block.enriched`` is True.
"""
assert not block.enriched, (
"fingerprinting must operate on pre-enrichment ASTs; "
"call fingerprint() before sema_enrich.enrich_block()"
)
h = hashlib.sha256()
h.update(b'v1')
h.update(block.pragma.value.encode())
_hash_node(h, block.body, parent_kind=None)
return h.digest()
def _hash_node(
h: hashlib._Hash,
node: SemaNode,
parent_kind: SemaNodeKind | None,
) -> None:
h.update(node.kind.value.encode())
if node.ty:
h.update(f'{node.ty.base}{node.ty.size}'.encode())
if node.cast_target:
h.update(f'cast:{node.cast_target.base}{node.cast_target.size}'.encode())
if node.call_name:
h.update(f'call:{node.call_name}'.encode())
if node.kind == SemaNodeKind.ID and node.id_name:
if node.id_name in _CONTEXT_IDS or node.id_name in _OPERAND_TAGS:
h.update(f'id:{node.id_name}'.encode())
if node.kind == SemaNodeKind.LIT and parent_kind in _LIT_HASH_PARENTS:
h.update(f'lit:{node.lit_value}'.encode())
for child in node.children:
_hash_node(h, child, parent_kind=node.kind)
def are_equivalent(a: SemaBlock, b: SemaBlock) -> bool:
"""True if two SemaBlocks have identical semantic fingerprints."""
return fingerprint(a) == fingerprint(b)
def build_equivalence_map(
src_blocks: dict[str, SemaBlock],
dst_blocks: dict[str, SemaBlock],
) -> dict[str, str | None]:
"""Map each source mnemonic to its semantically equivalent target mnemonic.
Uses O(N+M) hash-table lookup: builds a ``dict[bytes, str]`` of target
fingerprints, then looks up each source fingerprint.
Args:
src_blocks: Source ISA SemaBlocks keyed by mnemonic.
dst_blocks: Target ISA SemaBlocks keyed by mnemonic.
Returns:
Dict mapping source mnemonic to equivalent target mnemonic, or
``None`` if no equivalent exists.
"""
dst_by_fp: dict[bytes, str] = {}
for mnemonic, block in dst_blocks.items():
if block.is_empty:
continue
fp = fingerprint(block)
if fp in dst_by_fp:
_log.debug(
'fingerprint collision: %s and %s have identical semantics; '
'keeping %s',
dst_by_fp[fp],
mnemonic,
dst_by_fp[fp],
)
else:
dst_by_fp[fp] = mnemonic
result: dict[str, str | None] = {}
for mnemonic, block in src_blocks.items():
if block.is_empty:
result[mnemonic] = None
continue
fp = fingerprint(block)
result[mnemonic] = dst_by_fp.get(fp)
return result