Files
TheRock/rocm-libraries/projects/hipblaslt/tensilelite/rocisa/test/test_instruction.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

442 lines
17 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 rocisa
from rocisa.container import sgpr, vgpr
from copy import deepcopy
import math
def test_instruction_common():
from rocisa.instruction import SMovB32
inst = SMovB32(dst=sgpr(1), src=1.6)
assert str(inst) == "s_mov_b32 s1, 1.6000000000000001\n"
assert str(inst.dst) == "s1"
# Due to conversion between C++ and Python
assert math.isclose(inst.srcs[0], 1.6, abs_tol=0.000001)
# You cannot use srcs[0] = 1.3 because it may break the C++ memory layout
# so nanobind disabled it
inst.srcs = [1.3]
assert math.isclose(inst.srcs[0], 1.3, abs_tol=0.000001)
inst.setSrc(0, 1.4)
assert math.isclose(inst.srcs[0], 1.4, abs_tol=0.000001)
inst2 = deepcopy(inst)
inst.setSrc(0, 2.0)
assert math.isclose(inst2.srcs[0], 1.4, abs_tol=0.000001)
from rocisa.instruction import InstructionInputVector
iiv = InstructionInputVector()
iiv.append(1.0)
iiv.append("hello")
iiv.append(sgpr(1))
assert str(iiv[2]) == "s1"
from rocisa.code import Module
from rocisa.container import vgpr
from rocisa.instruction import BufferLoadB64
module = Module("Test")
module.add(BufferLoadB64(vgpr(1), vgpr(2), vgpr(3), 3))
assert rocisa.countGlobalRead(module) == 1
def test_instruction_cvt():
from rocisa.instruction import VCvtF16toF32, VCvtF32toF16, VCvtF32toU32, VCvtU32toF32, \
VCvtI32toF32, VCvtF32toI32, VCvtFP8toF32, VCvtBF8toF32, VCvtPkFP8toF32, VCvtPkBF8toF32, \
VCvtPkF32toFP8, VCvtPkF32toBF8, VCvtSRF32toFP8, VCvtSRF32toBF8
# Test VCvtF16toF32
inst = VCvtF16toF32(dst=vgpr(1), src=vgpr(2), comment="test comment")
assert str(inst) == "v_cvt_f32_f16 v1, v2 // test comment\n"
assert str(inst.dst) == "v1"
assert str(inst.srcs[0]) == "v2"
assert inst.comment == "test comment"
# Test VCvtF32toF16
inst = VCvtF32toF16(dst=vgpr(1), src=vgpr(2), comment="test comment")
assert str(inst) == "v_cvt_f16_f32 v1, v2 // test comment\n"
assert str(inst.dst) == "v1"
assert str(inst.srcs[0]) == "v2"
assert inst.comment == "test comment"
# Test VCvtF32toU32
inst = VCvtF32toU32(dst=vgpr(1), src=vgpr(2), comment="test comment")
assert str(inst) == "v_cvt_u32_f32 v1, v2 // test comment\n"
assert str(inst.dst) == "v1"
assert str(inst.srcs[0]) == "v2"
assert inst.comment == "test comment"
# Test VCvtU32toF32
inst = VCvtU32toF32(dst=vgpr(1), src=vgpr(2), comment="test comment")
assert str(inst) == "v_cvt_f32_u32 v1, v2 // test comment\n"
assert str(inst.dst) == "v1"
assert str(inst.srcs[0]) == "v2"
assert inst.comment == "test comment"
# Test VCvtI32toF32
inst = VCvtI32toF32(dst=vgpr(1), src=vgpr(2), comment="test comment")
assert str(inst) == "v_cvt_f32_i32 v1, v2 // test comment\n"
assert str(inst.dst) == "v1"
assert str(inst.srcs[0]) == "v2"
assert inst.comment == "test comment"
# Test VCvtF32toI32
inst = VCvtF32toI32(dst=vgpr(1), src=vgpr(2), comment="test comment")
assert str(inst) == "v_cvt_i32_f32 v1, v2 // test comment\n"
assert str(inst.dst) == "v1"
assert str(inst.srcs[0]) == "v2"
assert inst.comment == "test comment"
# Test VCvtFP8toF32
inst = VCvtFP8toF32(dst=vgpr(1), src=vgpr(2), comment="test comment")
assert str(inst) == "v_cvt_f32_fp8 v1, v2 // test comment\n"
assert str(inst.dst) == "v1"
assert str(inst.srcs[0]) == "v2"
assert inst.comment == "test comment"
# Test VCvtBF8toF32
inst = VCvtBF8toF32(dst=vgpr(1), src=vgpr(2), comment="test comment")
assert str(inst) == "v_cvt_f32_bf8 v1, v2 // test comment\n"
assert str(inst.dst) == "v1"
assert str(inst.srcs[0]) == "v2"
assert inst.comment == "test comment"
# Test VCvtPkFP8toF32
inst = VCvtPkFP8toF32(dst=vgpr(1), src=vgpr(2), comment="test comment")
assert str(inst) == "v_cvt_pk_f32_fp8 v1, v2 // test comment\n"
assert str(inst.dst) == "v1"
assert str(inst.srcs[0]) == "v2"
assert inst.comment == "test comment"
# Test VCvtPkBF8toF32
inst = VCvtPkBF8toF32(dst=vgpr(1), src=vgpr(2), comment="test comment")
assert str(inst) == "v_cvt_pk_f32_bf8 v1, v2 // test comment\n"
assert str(inst.dst) == "v1"
assert str(inst.srcs[0]) == "v2"
assert inst.comment == "test comment"
# Test VCvtPkF32toFP8
inst = VCvtPkF32toFP8(dst=vgpr(1), src0=vgpr(2), src1=vgpr(3), comment="test comment")
assert str(inst) == "v_cvt_pk_fp8_f32 v1, v2, v3 // test comment\n"
assert str(inst.dst) == "v1"
assert str(inst.srcs[0]) == "v2"
assert str(inst.srcs[1]) == "v3"
assert inst.comment == "test comment"
# Test VCvtPkF32toBF8
inst = VCvtPkF32toBF8(dst=vgpr(1), src0=vgpr(2), src1=vgpr(3), comment="test comment")
assert str(inst) == "v_cvt_pk_bf8_f32 v1, v2, v3 // test comment\n"
assert str(inst.dst) == "v1"
assert str(inst.srcs[0]) == "v2"
assert str(inst.srcs[1]) == "v3"
assert inst.comment == "test comment"
# Test VCvtSRF32toFP8
inst = VCvtSRF32toFP8(dst=vgpr(1), src0=vgpr(2), src1=vgpr(3), comment="test comment")
assert str(inst) == "v_cvt_sr_fp8_f32 v1, v2, v3 // test comment\n"
assert str(inst.dst) == "v1"
assert str(inst.srcs[0]) == "v2"
assert str(inst.srcs[1]) == "v3"
assert inst.comment == "test comment"
# Test VCvtSRF32toBF8
inst = VCvtSRF32toBF8(dst=vgpr(1), src0=vgpr(2), src1=vgpr(3), comment="test comment")
assert str(inst) == "v_cvt_sr_bf8_f32 v1, v2, v3 // test comment\n"
assert str(inst.dst) == "v1"
assert str(inst.srcs[0]) == "v2"
assert str(inst.srcs[1]) == "v3"
assert inst.comment == "test comment"
def test_instruction_tdm():
from rocisa.instruction import TensorLoadToLds
from rocisa.container import sgpr
inst = TensorLoadToLds(
group0=sgpr(0, 4),
group1=sgpr(0, 8),
group2=sgpr(0, 4),
group3=sgpr(0, 4),
comment=""
)
assert str(inst) == "tensor_load_to_lds s[0:3], s[0:7], s[0:3], s[0:3]\n"
try:
TensorLoadToLds(
group0=vgpr(0, 4),
group1=sgpr(0, 8),
group2=sgpr(0, 4),
group3=sgpr(0, 4),
comment=""
)
assert False, "Should have raised ValueError"
except ValueError as e:
pass
def test_instruction_tdm_2_sgprs():
from rocisa.instruction import TensorLoadToLds
from rocisa.container import sgpr
inst = TensorLoadToLds(
group0=sgpr(0, 4),
group1=sgpr(0, 8),
group2=None,
group3=None,
comment=""
)
assert str(inst) == "tensor_load_to_lds s[0:3], s[0:7]\n"
try:
TensorLoadToLds(
group0=vgpr(0, 4),
group1=sgpr(0, 8),
group2=None,
group3=None,
comment=""
)
assert False, "Should have raised ValueError"
except ValueError as e:
pass
def test_instruction_swait_xcnt():
from rocisa.instruction import SWaitXCnt
# Default constructor: xcnt=0, no comment -> "s_wait_xcnt 0\n"
inst = SWaitXCnt()
assert str(inst) == "s_wait_xcnt 0\n"
assert inst.comment == ""
# Explicit xcnt within range
inst = SWaitXCnt(xcnt=5)
assert str(inst) == "s_wait_xcnt 5\n"
# min(xcnt, 63) clamping: xcnt=100 should clamp to 63
inst = SWaitXCnt(xcnt=100)
assert str(inst) == "s_wait_xcnt 63\n"
# Boundary: xcnt=63 should also produce 63 (not clamped further)
inst = SWaitXCnt(xcnt=63)
assert str(inst) == "s_wait_xcnt 63\n"
# Comment formatting: use relaxed assertions to sidestep alignment math
inst = SWaitXCnt(xcnt=5, comment="test comment")
assert inst.comment == "test comment"
assert str(inst).startswith("s_wait_xcnt 5")
assert "// test comment" in str(inst)
assert str(inst).endswith("\n")
# deepcopy independence: mutate the original AFTER deepcopy and confirm
# the copy is unaffected (SWaitXCnt does not expose xcnt as a writable
# attribute, so we mutate the inherited `comment` field).
inst = SWaitXCnt(xcnt=7, comment="original")
inst2 = deepcopy(inst)
inst.comment = "mutated"
assert inst2.comment == "original"
assert str(inst2).startswith("s_wait_xcnt 7")
assert "// original" in str(inst2)
assert "// mutated" not in str(inst2)
# Embed in a Module and verify rendered text contains the instruction
from rocisa.code import Module
module = Module("Test")
module.add(SWaitXCnt(xcnt=3))
assert "s_wait_xcnt 3" in str(module)
def test_instruction_global_wb():
from rocisa.instruction import GlobalWb
from rocisa.enum import CacheScope
# Default constructor: SCOPE_DEV, no comment
inst = GlobalWb()
assert str(inst) == "global_wb scope:SCOPE_DEV\n"
assert inst.scope == CacheScope.SCOPE_DEV
assert inst.comment == ""
# SCOPE_NONE: scope modifier omitted
inst = GlobalWb(scope=CacheScope.SCOPE_NONE)
assert str(inst) == "global_wb\n"
assert inst.scope == CacheScope.SCOPE_NONE
# Alternate scopes: enum -> string conversion
inst = GlobalWb(scope=CacheScope.SCOPE_CU)
assert str(inst) == "global_wb scope:SCOPE_CU\n"
inst = GlobalWb(scope=CacheScope.SCOPE_SE)
assert str(inst) == "global_wb scope:SCOPE_SE\n"
inst = GlobalWb(scope=CacheScope.SCOPE_SYS)
assert str(inst) == "global_wb scope:SCOPE_SYS\n"
# Comment formatting (relaxed assertions to avoid alignment math)
inst = GlobalWb(scope=CacheScope.SCOPE_DEV, comment="release fence")
assert inst.comment == "release fence"
assert str(inst).startswith("global_wb scope:SCOPE_DEV")
assert "// release fence" in str(inst)
assert str(inst).endswith("\n")
# deepcopy independence: mutate the original `scope` (and comment) after
# deepcopy and confirm the copy is unaffected.
inst = GlobalWb(scope=CacheScope.SCOPE_DEV, comment="orig")
inst2 = deepcopy(inst)
inst.scope = CacheScope.SCOPE_SYS
inst.comment = "mutated"
assert inst2.scope == CacheScope.SCOPE_DEV
assert inst2.comment == "orig"
assert str(inst2).startswith("global_wb scope:SCOPE_DEV")
assert "// orig" in str(inst2)
assert "// mutated" not in str(inst2)
assert "SCOPE_SYS" not in str(inst2)
# Embed in a Module and verify rendered text contains the instruction
from rocisa.code import Module
module = Module("Test")
module.add(GlobalWb(scope=CacheScope.SCOPE_DEV))
assert "global_wb scope:SCOPE_DEV" in str(module)
def test_instruction_global_inv():
from rocisa.instruction import GlobalInv
from rocisa.enum import CacheScope
# Default constructor: SCOPE_DEV, no comment
inst = GlobalInv()
assert str(inst) == "global_inv scope:SCOPE_DEV\n"
assert inst.scope == CacheScope.SCOPE_DEV
assert inst.comment == ""
# SCOPE_NONE: scope modifier omitted
inst = GlobalInv(scope=CacheScope.SCOPE_NONE)
assert str(inst) == "global_inv\n"
assert inst.scope == CacheScope.SCOPE_NONE
# Alternate scope sanity check
inst = GlobalInv(scope=CacheScope.SCOPE_CU)
assert str(inst) == "global_inv scope:SCOPE_CU\n"
# Comment formatting (relaxed assertions to avoid alignment math)
inst = GlobalInv(scope=CacheScope.SCOPE_DEV, comment="acquire fence")
assert inst.comment == "acquire fence"
assert str(inst).startswith("global_inv scope:SCOPE_DEV")
assert "// acquire fence" in str(inst)
assert str(inst).endswith("\n")
# deepcopy independence: mutate the original `scope` (and comment) after
# deepcopy and confirm the copy is unaffected.
inst = GlobalInv(scope=CacheScope.SCOPE_DEV, comment="orig")
inst2 = deepcopy(inst)
inst.scope = CacheScope.SCOPE_NONE
inst.comment = "mutated"
assert inst2.scope == CacheScope.SCOPE_DEV
assert inst2.comment == "orig"
assert str(inst2).startswith("global_inv scope:SCOPE_DEV")
assert "// orig" in str(inst2)
assert "// mutated" not in str(inst2)
def test_instruction_scalar_float():
# gfx12+ scalar-float / scalar-u64 instruction wrappers. Each src is an
# InstructionInput variant (register | int | double | str); the cases below
# exercise those arms and assert the emitted mnemonic/operands.
from rocisa.instruction import SMulF32, SAddF32, SCvtF32U32, SCvtU32F32, \
VSRcpF32, SMulU64
# --- SMulF32: s_mul_f32 dst, src0, src1 ---
inst = SMulF32(sgpr(0), sgpr(1), sgpr(2))
assert str(inst) == "s_mul_f32 s0, s1, s2\n"
assert str(inst.dst) == "s0"
assert str(inst.srcs[0]) == "s1"
assert str(inst.srcs[1]) == "s2"
assert str(SMulF32(sgpr(0), sgpr(1), 2)) == "s_mul_f32 s0, s1, 2\n"
assert str(SMulF32(sgpr(0), "s1", sgpr(2))) == "s_mul_f32 s0, s1, s2\n"
# --- SAddF32: s_add_f32 dst, src0, src1 ---
inst = SAddF32(sgpr(0), sgpr(1), sgpr(2))
assert str(inst) == "s_add_f32 s0, s1, s2\n"
assert str(inst.dst) == "s0"
assert str(inst.srcs[0]) == "s1"
assert str(inst.srcs[1]) == "s2"
assert str(SAddF32(sgpr(0), sgpr(1), 3)) == "s_add_f32 s0, s1, 3\n"
# integral doubles get a ".0" suffix
assert str(SAddF32(sgpr(0), sgpr(1), 2.0)) == "s_add_f32 s0, s1, 2.0\n"
# --- SCvtF32U32: s_cvt_f32_u32 dst, src ---
inst = SCvtF32U32(sgpr(0), sgpr(1))
assert str(inst) == "s_cvt_f32_u32 s0, s1\n"
assert str(inst.dst) == "s0"
assert str(inst.srcs[0]) == "s1"
assert str(SCvtF32U32(sgpr(0), "s2")) == "s_cvt_f32_u32 s0, s2\n"
# --- SCvtU32F32: s_cvt_u32_f32 dst, src ---
inst = SCvtU32F32(sgpr(0), sgpr(1))
assert str(inst) == "s_cvt_u32_f32 s0, s1\n"
assert str(inst.dst) == "s0"
assert str(inst.srcs[0]) == "s1"
assert str(SCvtU32F32(sgpr(0), 1.5)) == "s_cvt_u32_f32 s0, 1.5\n"
# --- VSRcpF32: v_s_rcp_f32 dst, src (vgpr dst) ---
inst = VSRcpF32(vgpr(0), sgpr(1))
assert str(inst) == "v_s_rcp_f32 v0, s1\n"
assert str(inst.dst) == "v0"
assert str(inst.srcs[0]) == "s1"
# vgpr src must stay clean (no s_set_vgpr_msb) under the default ISA
assert str(VSRcpF32(vgpr(0), vgpr(1))) == "v_s_rcp_f32 v0, v1\n"
assert str(VSRcpF32(vgpr(0), 0.5)) == "v_s_rcp_f32 v0, 0.5\n"
# --- SMulU64: s_mul_u64 dst, src0, src1 (64-bit register pairs) ---
inst = SMulU64(sgpr(0, 2), sgpr(2, 2), sgpr(4, 2))
assert str(inst) == "s_mul_u64 s[0:1], s[2:3], s[4:5]\n"
assert str(inst.dst) == "s[0:1]"
assert str(inst.srcs[0]) == "s[2:3]"
assert str(inst.srcs[1]) == "s[4:5]"
assert str(SMulU64(sgpr(0, 2), sgpr(2, 2), 4)) == "s_mul_u64 s[0:1], s[2:3], 4\n"
# --- Comment formatting (relaxed to sidestep the 50-col alignment pad) ---
inst = SMulF32(sgpr(0), sgpr(1), sgpr(2), comment="scale")
assert str(inst).startswith("s_mul_f32 s0, s1, s2")
assert "// scale" in str(inst)
assert str(inst).endswith("\n")
assert inst.comment == "scale"
# --- deepcopy independence + comment accessor, one per class ---
for a in [
SMulF32(sgpr(0), sgpr(1), sgpr(2), comment="orig"),
SAddF32(sgpr(0), sgpr(1), sgpr(2), comment="orig"),
SCvtF32U32(sgpr(0), sgpr(1), comment="orig"),
SCvtU32F32(sgpr(0), sgpr(1), comment="orig"),
VSRcpF32(vgpr(0), sgpr(1), comment="orig"),
SMulU64(sgpr(0, 2), sgpr(2, 2), sgpr(4, 2), comment="orig"),
]:
assert a.comment == "orig"
b = deepcopy(a)
a.comment = "mutated"
assert b.comment == "orig"
assert "// orig" in str(b)
assert "// mutated" not in str(b)
if __name__ == "__main__":
test_instruction_common()
test_instruction_cvt()
test_instruction_tdm()
test_instruction_tdm_2_sgprs()
test_instruction_swait_xcnt()
test_instruction_global_wb()
test_instruction_global_inv()
test_instruction_scalar_float()