mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-09 22:13:43 +08:00
22a8575199
Keep official openpilot ancestry and dependency licenses. Store resources directly in Git, remove LFS setup requirements, and leave the downloadable big model outside the source tree.
680 lines
29 KiB
Python
680 lines
29 KiB
Python
"""Tests for GLOBAL instructions - global memory operations.
|
|
|
|
Includes: global_load_*, global_store_*, global_atomic_*, global_load_d16_*
|
|
"""
|
|
import unittest
|
|
from test.amd.hw.helpers import *
|
|
|
|
class TestGlobalAtomic(unittest.TestCase):
|
|
"""Tests for GLOBAL atomic instructions."""
|
|
|
|
def _make_test(self, setup_instrs, atomic_instr, check_fn, test_offset=2000):
|
|
"""Helper to create atomic test instructions."""
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], s[2]),
|
|
v_mov_b32_e32(v[1], s[3]),
|
|
] + setup_instrs + [atomic_instr, s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], 0),
|
|
v_mov_b32_e32(v[1], 0),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
check_fn(st)
|
|
|
|
def test_global_atomic_add_u32(self):
|
|
"""GLOBAL_ATOMIC_ADD_U32 adds to memory and returns old value."""
|
|
TEST_OFFSET = 2000
|
|
setup = [
|
|
s_mov_b32(s[0], 100),
|
|
v_mov_b32_e32(v[2], s[0]),
|
|
global_store_b32(addr=v[0:1], data=v[2], saddr=SrcEnum.NULL, offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[0], 50),
|
|
v_mov_b32_e32(v[3], s[0]),
|
|
]
|
|
atomic = GLOBAL(GLOBALOp.GLOBAL_ATOMIC_ADD_U32, addr=v[0:1], data=v[3], vdst=v[4], saddr=SrcEnum.NULL, offset=TEST_OFFSET, glc=1)
|
|
def check(st):
|
|
self.assertEqual(st.vgpr[0][4], 100)
|
|
self._make_test(setup, atomic, check, TEST_OFFSET)
|
|
|
|
def test_global_atomic_add_u64(self):
|
|
"""GLOBAL_ATOMIC_ADD_U64 adds 64-bit value and returns old value."""
|
|
TEST_OFFSET = 2000
|
|
setup = [
|
|
s_mov_b32(s[0], 0xFFFFFFFF),
|
|
v_mov_b32_e32(v[2], s[0]),
|
|
s_mov_b32(s[0], 0x00000000),
|
|
v_mov_b32_e32(v[3], s[0]),
|
|
global_store_b64(addr=v[0:1], data=v[2:3], saddr=SrcEnum.NULL, offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[0], 0x00000001),
|
|
v_mov_b32_e32(v[4], s[0]),
|
|
s_mov_b32(s[0], 0x00000000),
|
|
v_mov_b32_e32(v[5], s[0]),
|
|
]
|
|
atomic = GLOBAL(GLOBALOp.GLOBAL_ATOMIC_ADD_U64, addr=v[0:1], data=v[4:5], vdst=v[6:7], saddr=SrcEnum.NULL, offset=TEST_OFFSET, glc=1)
|
|
def check(st):
|
|
self.assertEqual(st.vgpr[0][6], 0xFFFFFFFF)
|
|
self.assertEqual(st.vgpr[0][7], 0x00000000)
|
|
self._make_test(setup, atomic, check, TEST_OFFSET)
|
|
|
|
|
|
class TestGlobalLoad(unittest.TestCase):
|
|
"""Tests for GLOBAL load instructions."""
|
|
|
|
def test_global_load_b96(self):
|
|
"""GLOBAL_LOAD_B96 loads 96-bit value correctly."""
|
|
TEST_OFFSET = 2000
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], s[2]),
|
|
v_mov_b32_e32(v[1], s[3]),
|
|
s_mov_b32(s[0], 0xAAAAAAAA),
|
|
v_mov_b32_e32(v[2], s[0]),
|
|
s_mov_b32(s[0], 0xBBBBBBBB),
|
|
v_mov_b32_e32(v[3], s[0]),
|
|
s_mov_b32(s[0], 0xCCCCCCCC),
|
|
v_mov_b32_e32(v[4], s[0]),
|
|
global_store_b96(addr=v[0:1], data=v[2:4], saddr=SrcEnum.NULL, offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B96, addr=v[0:1], vdst=v[5:7], saddr=SrcEnum.NULL, offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], 0),
|
|
v_mov_b32_e32(v[1], 0),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
self.assertEqual(st.vgpr[0][5], 0xAAAAAAAA)
|
|
self.assertEqual(st.vgpr[0][6], 0xBBBBBBBB)
|
|
self.assertEqual(st.vgpr[0][7], 0xCCCCCCCC)
|
|
|
|
def test_global_load_b128(self):
|
|
"""GLOBAL_LOAD_B128 loads 128-bit value correctly."""
|
|
TEST_OFFSET = 2000
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], s[2]),
|
|
v_mov_b32_e32(v[1], s[3]),
|
|
s_mov_b32(s[0], 0xDEADBEEF),
|
|
v_mov_b32_e32(v[2], s[0]),
|
|
s_mov_b32(s[0], 0xCAFEBABE),
|
|
v_mov_b32_e32(v[3], s[0]),
|
|
s_mov_b32(s[0], 0x12345678),
|
|
v_mov_b32_e32(v[4], s[0]),
|
|
s_mov_b32(s[0], 0x9ABCDEF0),
|
|
v_mov_b32_e32(v[5], s[0]),
|
|
global_store_b128(addr=v[0:1], data=v[2:5], saddr=SrcEnum.NULL, offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B128, addr=v[0:1], vdst=v[6:9], saddr=SrcEnum.NULL, offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], 0),
|
|
v_mov_b32_e32(v[1], 0),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
self.assertEqual(st.vgpr[0][6], 0xDEADBEEF)
|
|
self.assertEqual(st.vgpr[0][7], 0xCAFEBABE)
|
|
self.assertEqual(st.vgpr[0][8], 0x12345678)
|
|
self.assertEqual(st.vgpr[0][9], 0x9ABCDEF0)
|
|
|
|
|
|
class TestGlobalStore(unittest.TestCase):
|
|
"""Tests for GLOBAL store instructions."""
|
|
|
|
def test_global_store_b8_basic(self):
|
|
"""GLOBAL_STORE_B8 stores a single byte from VDATA[7:0]."""
|
|
TEST_OFFSET = 256
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
# First store 0xDEADBEEF to memory
|
|
s_mov_b32(s[4], 0xDEADBEEF),
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
v_mov_b32_e32(v[0], 0),
|
|
global_store_b32(addr=v[0], data=v[2], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
# Now store single byte 0x42 to same address (should only change byte 0)
|
|
v_mov_b32_e32(v[2], 0x42),
|
|
global_store_b8(addr=v[0], data=v[2], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
# Read back and check
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B32, addr=v[0], vdst=v[3], data=v[3], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[3]),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
# Only byte 0 should change from 0xEF to 0x42
|
|
self.assertEqual(st.vgpr[0][0], 0xDEADBE42, "Only byte 0 should be modified")
|
|
|
|
def test_global_store_b8_byte1(self):
|
|
"""GLOBAL_STORE_B8 at offset+1 stores to byte 1."""
|
|
TEST_OFFSET = 256
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0xDEADBEEF),
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
v_mov_b32_e32(v[0], 0),
|
|
global_store_b32(addr=v[0], data=v[2], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[2], 0x42),
|
|
global_store_b8(addr=v[0], data=v[2], saddr=s[2:3], offset=TEST_OFFSET+1),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B32, addr=v[0], vdst=v[3], data=v[3], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[3]),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
self.assertEqual(st.vgpr[0][0], 0xDEAD42EF, "Only byte 1 should be modified")
|
|
|
|
def test_global_store_b16_basic(self):
|
|
"""GLOBAL_STORE_B16 stores a 16-bit value from VDATA[15:0]."""
|
|
TEST_OFFSET = 256
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0xDEADBEEF),
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
v_mov_b32_e32(v[0], 0),
|
|
global_store_b32(addr=v[0], data=v[2], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0xCAFE),
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
global_store_b16(addr=v[0], data=v[2], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B32, addr=v[0], vdst=v[3], data=v[3], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[3]),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
self.assertEqual(st.vgpr[0][0], 0xDEADCAFE, "Only lower 16 bits should be modified")
|
|
|
|
def test_global_store_b16_high_half(self):
|
|
"""GLOBAL_STORE_B16 at offset+2 stores to high 16 bits."""
|
|
TEST_OFFSET = 256
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0xDEADBEEF),
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
v_mov_b32_e32(v[0], 0),
|
|
global_store_b32(addr=v[0], data=v[2], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0xCAFE),
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
global_store_b16(addr=v[0], data=v[2], saddr=s[2:3], offset=TEST_OFFSET+2),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B32, addr=v[0], vdst=v[3], data=v[3], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[3]),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
self.assertEqual(st.vgpr[0][0], 0xCAFEBEEF, "Only upper 16 bits should be modified")
|
|
|
|
def test_global_store_b16_byte_offset_1(self):
|
|
"""GLOBAL_STORE_B16 at byte offset 1 stores bytes 1-2 within the same word."""
|
|
TEST_OFFSET = 256
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0xDDCCBBAA),
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
v_mov_b32_e32(v[0], 0),
|
|
global_store_b32(addr=v[0], data=v[2], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
# Store 0xBEEF at byte offset 1 (bytes 1-2)
|
|
s_mov_b32(s[4], 0xBEEF),
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
global_store_b16(addr=v[0], data=v[2], saddr=s[2:3], offset=TEST_OFFSET+1),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B32, addr=v[0], vdst=v[3], data=v[3], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[3]),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
# Bytes 1-2 should be 0xBEEF (0xEF at byte 1, 0xBE at byte 2)
|
|
# Original: 0xDDCCBBAA -> bytes [AA, BB, CC, DD]
|
|
# After: 0xDDBEEFAA -> bytes [AA, EF, BE, DD]
|
|
self.assertEqual(st.vgpr[0][0], 0xDDBEEFAA, "Bytes 1-2 should be 0xBEEF")
|
|
|
|
def test_global_store_b16_cross_word_boundary(self):
|
|
"""GLOBAL_STORE_B16 at byte offset 3 crosses word boundary (byte 3 of word N, byte 0 of word N+1)."""
|
|
TEST_OFFSET = 256
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
# Initialize two consecutive words
|
|
s_mov_b32(s[4], 0xDDCCBBAA),
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
v_mov_b32_e32(v[0], 0),
|
|
global_store_b32(addr=v[0], data=v[2], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_mov_b32(s[4], 0x44332211),
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
global_store_b32(addr=v[0], data=v[2], saddr=s[2:3], offset=TEST_OFFSET+4),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
# Store 0xBEEF at byte offset 3 (crosses word boundary)
|
|
# Low byte (0xEF) goes to byte 3 of first word
|
|
# High byte (0xBE) goes to byte 0 of second word
|
|
s_mov_b32(s[4], 0xBEEF),
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
global_store_b16(addr=v[0], data=v[2], saddr=s[2:3], offset=TEST_OFFSET+3),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
# Load back both words
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B32, addr=v[0], vdst=v[3], data=v[3], saddr=s[2:3], offset=TEST_OFFSET),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B32, addr=v[0], vdst=v[4], data=v[4], saddr=s[2:3], offset=TEST_OFFSET+4),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[3]),
|
|
v_mov_b32_e32(v[1], v[4]),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
# First word: 0xDDCCBBAA -> 0xEFCCBBAA (byte 3 becomes 0xEF)
|
|
# Second word: 0x44332211 -> 0x443322BE (byte 0 becomes 0xBE)
|
|
self.assertEqual(st.vgpr[0][0], 0xEFCCBBAA, "Byte 3 of first word should be 0xEF")
|
|
self.assertEqual(st.vgpr[0][1], 0x443322BE, "Byte 0 of second word should be 0xBE")
|
|
|
|
def test_global_store_b64_basic(self):
|
|
"""GLOBAL_STORE_B64 stores 8 bytes from v[n:n+1] to memory."""
|
|
TEST_OFFSET = 256
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0xDEADBEEF),
|
|
s_mov_b32(s[5], 0xCAFEBABE),
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
v_mov_b32_e32(v[3], s[5]),
|
|
v_mov_b32_e32(v[0], 0),
|
|
global_store_b64(addr=v[0], data=v[2:3], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B64, addr=v[0], vdst=v[4:5], data=v[4:5], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[4]),
|
|
v_mov_b32_e32(v[1], v[5]),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
self.assertEqual(st.vgpr[0][0], 0xDEADBEEF)
|
|
self.assertEqual(st.vgpr[0][1], 0xCAFEBABE)
|
|
|
|
|
|
class TestD16HiLoads(unittest.TestCase):
|
|
"""Tests for D16_HI load instructions that load into high 16 bits."""
|
|
|
|
def test_global_load_d16_hi_b16_preserves_low_bits(self):
|
|
"""GLOBAL_LOAD_D16_HI_B16 must preserve low 16 bits of destination."""
|
|
TEST_OFFSET = 256
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], s[2]),
|
|
v_mov_b32_e32(v[1], s[3]),
|
|
s_mov_b32(s[4], 0xCAFE),
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
global_store_b16(addr=v[0:1], data=v[2], saddr=SrcEnum.NULL, offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0x0000BEEF),
|
|
v_mov_b32_e32(v[3], s[4]),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_D16_HI_B16, addr=v[0:1], vdst=v[3], data=v[3], saddr=SrcEnum.NULL, offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[3]),
|
|
v_mov_b32_e32(v[1], 0),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
result = st.vgpr[0][0]
|
|
self.assertEqual(result, 0xCAFEBEEF, f"Expected 0xCAFEBEEF, got 0x{result:08x}")
|
|
|
|
def test_global_load_d16_hi_b16_data_differs_from_vdst(self):
|
|
"""GLOBAL_LOAD_D16_HI_B16 where data field differs from vdst."""
|
|
TEST_OFFSET = 256
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0xCAFE),
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
v_mov_b32_e32(v[3], 0),
|
|
global_store_b16(addr=v[3], data=v[2], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0x0000DEAD),
|
|
v_mov_b32_e32(v[0], s[4]), # data field - should NOT affect result
|
|
v_mov_b32_e32(v[1], 0), # vdst - low bits should be preserved
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_D16_HI_B16, addr=v[1], vdst=v[1], data=v[0], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[1]),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
result = st.vgpr[0][0]
|
|
self.assertEqual(result, 0xCAFE0000, f"Expected 0xCAFE0000, got 0x{result:08x}")
|
|
|
|
def test_global_load_d16_hi_u8_data_differs_from_vdst(self):
|
|
"""GLOBAL_LOAD_D16_HI_U8 where data field differs from vdst."""
|
|
TEST_OFFSET = 256
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0xAB),
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
v_mov_b32_e32(v[3], 0),
|
|
global_store_b8(addr=v[3], data=v[2], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0x0000DEAD),
|
|
v_mov_b32_e32(v[4], s[4]), # data field
|
|
s_mov_b32(s[4], 0x0000BEEF),
|
|
v_mov_b32_e32(v[5], s[4]), # vdst
|
|
v_mov_b32_e32(v[3], 0),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_D16_HI_U8, addr=v[3], vdst=v[5], data=v[4], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[5]),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
result = st.vgpr[0][0]
|
|
self.assertEqual(result, 0x00ABBEEF, f"Expected 0x00ABBEEF, got 0x{result:08x}")
|
|
|
|
def test_global_load_d16_hi_b16_same_addr_and_dst_zero_addr(self):
|
|
"""GLOBAL_LOAD_D16_HI_B16 with same register for addr and vdst, addr value=0."""
|
|
TEST_OFFSET = 256
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0xCAFE),
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
v_mov_b32_e32(v[3], 0),
|
|
global_store_b16(addr=v[3], data=v[2], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[1], 0),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_D16_HI_B16, addr=v[1], vdst=v[1], data=v[1], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[1]),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
result = st.vgpr[0][0]
|
|
self.assertEqual(result, 0xCAFE0000, f"Expected 0xCAFE0000, got 0x{result:08x}")
|
|
|
|
def test_global_load_d16_hi_b16_tril_exact_pattern(self):
|
|
"""Exact pattern from tril() failure: data=v0 differs from vdst=v1."""
|
|
TEST_OFFSET = 256
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0x01010101),
|
|
v_mov_b32_e32(v[10], s[4]),
|
|
v_mov_b32_e32(v[3], 0),
|
|
global_store_b32(addr=v[3], data=v[10], saddr=s[2:3], offset=TEST_OFFSET),
|
|
global_store_b32(addr=v[3], data=v[10], saddr=s[2:3], offset=TEST_OFFSET+4),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
# Set v[0] to 0x0101 (simulating prior u16 load result)
|
|
s_mov_b32(s[4], 0x0101),
|
|
v_mov_b32_e32(v[0], s[4]),
|
|
# Set v[1] to 0
|
|
v_mov_b32_e32(v[1], 0),
|
|
# Load using v[1] as addr AND vdst, but v[0] as data
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_D16_HI_B16, addr=v[1], vdst=v[1], data=v[0], saddr=s[2:3], offset=TEST_OFFSET+6),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[1]),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
result = st.vgpr[0][0]
|
|
# Expected: hi=0x0101 (loaded), lo=0x0000 (from v1) -> 0x01010000
|
|
self.assertEqual(result, 0x01010000, f"Expected 0x01010000, got 0x{result:08x}")
|
|
|
|
def test_global_load_d16_hi_i8_data_differs_from_vdst(self):
|
|
"""GLOBAL_LOAD_D16_HI_I8 where data field differs from vdst."""
|
|
TEST_OFFSET = 256
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0x80), # negative signed byte = -128
|
|
v_mov_b32_e32(v[2], s[4]),
|
|
v_mov_b32_e32(v[3], 0),
|
|
global_store_b8(addr=v[3], data=v[2], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0x0000DEAD),
|
|
v_mov_b32_e32(v[4], s[4]), # data field
|
|
s_mov_b32(s[4], 0x0000BEEF),
|
|
v_mov_b32_e32(v[5], s[4]), # vdst
|
|
v_mov_b32_e32(v[3], 0),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_D16_HI_I8, addr=v[3], vdst=v[5], data=v[4], saddr=s[2:3], offset=TEST_OFFSET),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[5]),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
result = st.vgpr[0][0]
|
|
# 0x80 sign-extended = 0xFF80, lo=0xBEEF -> 0xFF80BEEF
|
|
self.assertEqual(result, 0xFF80BEEF, f"Expected 0xFF80BEEF, got 0x{result:08x}")
|
|
|
|
def test_global_store_b64_tril_pattern(self):
|
|
"""Test the exact pattern from tril() kernel that was failing."""
|
|
TEST_OFFSET = 256
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
s_mov_b32(s[4], 0x01010101),
|
|
v_mov_b32_e32(v[10], s[4]),
|
|
v_mov_b32_e32(v[11], s[4]),
|
|
s_mov_b32(s[4], 0x01),
|
|
v_mov_b32_e32(v[12], s[4]),
|
|
v_mov_b32_e32(v[0], 0),
|
|
global_store_b64(addr=v[0], data=v[10:11], saddr=s[2:3], offset=TEST_OFFSET),
|
|
global_store_b8(addr=v[0], data=v[12], saddr=s[2:3], offset=TEST_OFFSET+8),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
|
|
v_mov_b32_e32(v[2], 0),
|
|
v_mov_b32_e32(v[1], 0),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_U16, addr=v[2], vdst=v[0], data=v[0], saddr=s[2:3], offset=TEST_OFFSET+3),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_D16_HI_B16, addr=v[1], vdst=v[1], data=v[1], saddr=s[2:3], offset=TEST_OFFSET+6),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_U8, addr=v[2], vdst=v[3], data=v[3], saddr=s[2:3], offset=TEST_OFFSET),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_U8, addr=v[2], vdst=v[4], data=v[4], saddr=s[2:3], offset=TEST_OFFSET+8),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
|
|
v_and_b32_e32(v[5], 0xffff, v[0]),
|
|
v_lshlrev_b32_e32(v[0], 24, v[0]),
|
|
v_lshrrev_b32_e32(v[5], 8, v[5]),
|
|
v_or_b32_e32(v[0], v[3], v[0]),
|
|
v_or_b32_e32(v[1], v[5], v[1]),
|
|
|
|
global_store_b64(addr=v[2], data=v[0:1], saddr=s[2:3], offset=TEST_OFFSET+16),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B64, addr=v[2], vdst=v[6:7], data=v[6:7], saddr=s[2:3], offset=TEST_OFFSET+16),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[6]),
|
|
v_mov_b32_e32(v[1], v[7]),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
|
|
v0 = st.vgpr[0][0]
|
|
v1 = st.vgpr[0][1]
|
|
self.assertEqual(v0, 0x01000001, f"v0: expected 0x01000001, got 0x{v0:08x}")
|
|
self.assertEqual(v1, 0x01010001, f"v1: expected 0x01010001, got 0x{v1:08x}")
|
|
|
|
byte5 = (v1 >> 8) & 0xff
|
|
self.assertEqual(byte5, 0x00, f"byte5: expected 0x00, got 0x{byte5:02x}")
|
|
|
|
|
|
class TestGlobalOffset(unittest.TestCase):
|
|
"""Tests for GLOBAL instructions with different offsets.
|
|
|
|
These tests verify that instruction deduplication correctly handles different offset values.
|
|
If offset is made dynamic incorrectly, instructions with different offsets may load/store wrong data.
|
|
"""
|
|
|
|
def test_global_load_different_offsets(self):
|
|
"""Load from two different offsets and verify correct values."""
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], s[2]),
|
|
v_mov_b32_e32(v[1], s[3]),
|
|
# Store 0xAAAAAAAA at offset 100
|
|
s_mov_b32(s[0], 0xAAAAAAAA),
|
|
v_mov_b32_e32(v[2], s[0]),
|
|
global_store_b32(addr=v[0:1], data=v[2], saddr=SrcEnum.NULL, offset=100),
|
|
# Store 0xBBBBBBBB at offset 200
|
|
s_mov_b32(s[0], 0xBBBBBBBB),
|
|
v_mov_b32_e32(v[2], s[0]),
|
|
global_store_b32(addr=v[0:1], data=v[2], saddr=SrcEnum.NULL, offset=200),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
# Load from offset 100 -> should get 0xAAAAAAAA
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B32, addr=v[0:1], vdst=v[3], saddr=SrcEnum.NULL, offset=100),
|
|
# Load from offset 200 -> should get 0xBBBBBBBB
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B32, addr=v[0:1], vdst=v[4], saddr=SrcEnum.NULL, offset=200),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[3]),
|
|
v_mov_b32_e32(v[1], v[4]),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
self.assertEqual(st.vgpr[0][0], 0xAAAAAAAA, f"offset 100: expected 0xAAAAAAAA, got 0x{st.vgpr[0][0]:08x}")
|
|
self.assertEqual(st.vgpr[0][1], 0xBBBBBBBB, f"offset 200: expected 0xBBBBBBBB, got 0x{st.vgpr[0][1]:08x}")
|
|
|
|
def test_global_store_different_offsets(self):
|
|
"""Store to two different offsets and verify correct values."""
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], s[2]),
|
|
v_mov_b32_e32(v[1], s[3]),
|
|
# Store 0x11111111 at offset 300
|
|
s_mov_b32(s[0], 0x11111111),
|
|
v_mov_b32_e32(v[2], s[0]),
|
|
global_store_b32(addr=v[0:1], data=v[2], saddr=SrcEnum.NULL, offset=300),
|
|
# Store 0x22222222 at offset 400
|
|
s_mov_b32(s[0], 0x22222222),
|
|
v_mov_b32_e32(v[3], s[0]),
|
|
global_store_b32(addr=v[0:1], data=v[3], saddr=SrcEnum.NULL, offset=400),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
# Load back to verify
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B32, addr=v[0:1], vdst=v[4], saddr=SrcEnum.NULL, offset=300),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B32, addr=v[0:1], vdst=v[5], saddr=SrcEnum.NULL, offset=400),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[4]),
|
|
v_mov_b32_e32(v[1], v[5]),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
self.assertEqual(st.vgpr[0][0], 0x11111111, f"offset 300: expected 0x11111111, got 0x{st.vgpr[0][0]:08x}")
|
|
self.assertEqual(st.vgpr[0][1], 0x22222222, f"offset 400: expected 0x22222222, got 0x{st.vgpr[0][1]:08x}")
|
|
|
|
def test_global_negative_offset_no_saddr(self):
|
|
"""Test negative offset without saddr (VGPR pair for address).
|
|
Store 0xAAAA at offset 100, 0xBBBB at offset 200.
|
|
Load with offset -100 from vaddr pointing to base+200 -> should get 0xAAAA (at 100).
|
|
Load with offset -100 from vaddr pointing to base+300 -> should get 0xBBBB (at 200)."""
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], s[2]),
|
|
v_mov_b32_e32(v[1], s[3]),
|
|
# Store 0xAAAAAAAA at offset 100, 0xBBBBBBBB at offset 200
|
|
s_mov_b32(s[0], 0xAAAAAAAA),
|
|
v_mov_b32_e32(v[2], s[0]),
|
|
global_store_b32(addr=v[0:1], data=v[2], saddr=SrcEnum.NULL, offset=100),
|
|
s_mov_b32(s[0], 0xBBBBBBBB),
|
|
v_mov_b32_e32(v[2], s[0]),
|
|
global_store_b32(addr=v[0:1], data=v[2], saddr=SrcEnum.NULL, offset=200),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
# vaddr = base+200, load with offset -100 -> should get value at 100
|
|
s_add_u32(s[4], s[2], 200),
|
|
s_addc_u32(s[5], s[3], 0),
|
|
v_mov_b32_e32(v[4], s[4]),
|
|
v_mov_b32_e32(v[5], s[5]),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B32, addr=v[4:5], vdst=v[6], saddr=SrcEnum.NULL, offset=-100),
|
|
# vaddr = base+300, load with offset -100 -> should get value at 200
|
|
s_add_u32(s[4], s[2], 300),
|
|
s_addc_u32(s[5], s[3], 0),
|
|
v_mov_b32_e32(v[4], s[4]),
|
|
v_mov_b32_e32(v[5], s[5]),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B32, addr=v[4:5], vdst=v[7], saddr=SrcEnum.NULL, offset=-100),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[6]),
|
|
v_mov_b32_e32(v[1], v[7]),
|
|
v_mov_b32_e32(v[4], 0),
|
|
v_mov_b32_e32(v[5], 0),
|
|
v_mov_b32_e32(v[6], 0),
|
|
v_mov_b32_e32(v[7], 0),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
s_mov_b32(s[4], 0),
|
|
s_mov_b32(s[5], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
self.assertEqual(st.vgpr[0][0], 0xAAAAAAAA, f"offset 200-100=100: expected 0xAAAAAAAA, got 0x{st.vgpr[0][0]:08x}")
|
|
self.assertEqual(st.vgpr[0][1], 0xBBBBBBBB, f"offset 300-100=200: expected 0xBBBBBBBB, got 0x{st.vgpr[0][1]:08x}")
|
|
|
|
def test_global_negative_offset_with_saddr(self):
|
|
"""Test negative offset with saddr (SGPR pair for base address).
|
|
Store 0xAAAA at offset 100, 0xBBBB at offset 200.
|
|
Load with offset -100 from saddr pointing to base+200 -> should get 0xAAAA (at 100).
|
|
Load with offset -100 from saddr pointing to base+300 -> should get 0xBBBB (at 200)."""
|
|
instructions = [
|
|
s_load_b64(s[2:3], s[80:81], 0, soffset=SrcEnum.NULL),
|
|
s_waitcnt_lgkmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], 0),
|
|
# Store 0xAAAAAAAA at offset 100, 0xBBBBBBBB at offset 200
|
|
s_mov_b32(s[0], 0xAAAAAAAA),
|
|
v_mov_b32_e32(v[2], s[0]),
|
|
global_store_b32(addr=v[0], data=v[2], saddr=s[2:3], offset=100),
|
|
s_mov_b32(s[0], 0xBBBBBBBB),
|
|
v_mov_b32_e32(v[2], s[0]),
|
|
global_store_b32(addr=v[0], data=v[2], saddr=s[2:3], offset=200),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
# saddr = base+200, load with offset -100 -> should get value at 100
|
|
s_add_u32(s[4], s[2], 200),
|
|
s_addc_u32(s[5], s[3], 0),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B32, addr=v[0], vdst=v[6], saddr=s[4:5], offset=-100),
|
|
# saddr = base+300, load with offset -100 -> should get value at 200
|
|
s_add_u32(s[4], s[2], 300),
|
|
s_addc_u32(s[5], s[3], 0),
|
|
GLOBAL(GLOBALOp.GLOBAL_LOAD_B32, addr=v[0], vdst=v[7], saddr=s[4:5], offset=-100),
|
|
s_waitcnt_vmcnt(sdst=NULL, simm16=0),
|
|
v_mov_b32_e32(v[0], v[6]),
|
|
v_mov_b32_e32(v[1], v[7]),
|
|
v_mov_b32_e32(v[6], 0),
|
|
v_mov_b32_e32(v[7], 0),
|
|
s_mov_b32(s[2], 0),
|
|
s_mov_b32(s[3], 0),
|
|
s_mov_b32(s[4], 0),
|
|
s_mov_b32(s[5], 0),
|
|
]
|
|
st = run_program(instructions, n_lanes=1)
|
|
self.assertEqual(st.vgpr[0][0], 0xAAAAAAAA, f"offset 200-100=100: expected 0xAAAAAAAA, got 0x{st.vgpr[0][0]:08x}")
|
|
self.assertEqual(st.vgpr[0][1], 0xBBBBBBBB, f"offset 300-100=200: expected 0xBBBBBBBB, got 0x{st.vgpr[0][1]:08x}")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|