mirror of
https://github.com/tinygrad/tinygrad.git
synced 2026-06-24 02:14:17 +00:00
cleanup
This commit is contained in:
parent
9f98001652
commit
67d54782db
1 changed files with 28 additions and 58 deletions
86
test/external/external_test_gpu_crash.py
vendored
86
test/external/external_test_gpu_crash.py
vendored
|
|
@ -4,28 +4,20 @@
|
|||
These tests intentionally cause GPU faults to verify error handling.
|
||||
Run with: AMD=1 python -m pytest test/external/external_test_gpu_crash.py -v
|
||||
"""
|
||||
import unittest, os, re
|
||||
import unittest, re
|
||||
from tinygrad.device import Device
|
||||
from extra.assembly.amd.autogen.rdna3.ins import * # noqa: F403
|
||||
from extra.assembly.amd.dsl import s, v, Inst, NULL
|
||||
|
||||
def assemble_kernel(insts:list[Inst], name:str="test") -> str:
|
||||
def assemble(code:str, name:str="test") -> str:
|
||||
kd = {"next_free_vgpr": 8, "next_free_sgpr": 8, "wavefront_size32": 1, "user_sgpr_kernarg_segment_ptr": 1, "kernarg_size": 8}
|
||||
disasm = "\n".join(inst.disasm() for inst in insts)
|
||||
hsasrc = f".text\n.globl {name}\n.p2align 8\n.type {name},@function\n{name}:\n{disasm}\n"
|
||||
return hsasrc + f".rodata\n.p2align 6\n.amdhsa_kernel {name}\n" + "\n".join(f".amdhsa_{k} {v}" for k, v in kd.items()) + "\n.end_amdhsa_kernel"
|
||||
return f".text\n.globl {name}\n.p2align 8\n.type {name},@function\n{name}:\n{code}\n.rodata\n.p2align 6\n.amdhsa_kernel {name}\n" + \
|
||||
"\n".join(f".amdhsa_{k} {v}" for k,v in kd.items()) + "\n.end_amdhsa_kernel"
|
||||
|
||||
def assemble_raw_bytes(raw_bytes:bytes, name:str="test") -> str:
|
||||
"""Assemble raw bytes into a kernel."""
|
||||
kd = {"next_free_vgpr": 8, "next_free_sgpr": 8, "wavefront_size32": 1, "user_sgpr_kernarg_segment_ptr": 1, "kernarg_size": 8}
|
||||
byte_str = ",".join(f"0x{b:02x}" for b in raw_bytes)
|
||||
hsasrc = f".text\n.globl {name}\n.p2align 8\n.type {name},@function\n{name}:\n.byte {byte_str}\n"
|
||||
return hsasrc + f".rodata\n.p2align 6\n.amdhsa_kernel {name}\n" + "\n".join(f".amdhsa_{k} {v}" for k, v in kd.items()) + "\n.end_amdhsa_kernel"
|
||||
|
||||
@unittest.skipIf(os.environ.get("AMD") != "1" or os.environ.get("MOCKGPU") == "1", "AMD with AM driver required")
|
||||
@unittest.skipIf(Device.DEFAULT != "AMD", "AMD required")
|
||||
class TestGPUCrash(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
from tinygrad.device import Device
|
||||
from tinygrad.runtime.support.compiler_amd import HIPCompiler
|
||||
cls.dev = Device["AMD"]
|
||||
cls.compiler = HIPCompiler(cls.dev.arch)
|
||||
|
|
@ -39,21 +31,13 @@ class TestGPUCrash(unittest.TestCase):
|
|||
except Exception:
|
||||
self.fail("Device not working before test")
|
||||
|
||||
def _run_kernel(self, insts: list[Inst]):
|
||||
def _run(self, code: str):
|
||||
from tinygrad.runtime.ops_amd import AMDProgram
|
||||
asm_src = assemble_kernel(insts)
|
||||
lib = self.compiler.compile(asm_src)
|
||||
prg = AMDProgram(self.dev, "test", lib)
|
||||
buf = self.dev.allocator.alloc(64)
|
||||
prg(buf, global_size=(1,1,1), local_size=(1,1,1), wait=True)
|
||||
prg = AMDProgram(self.dev, "test", self.compiler.compile(assemble(code)))
|
||||
prg(self.dev.allocator.alloc(64), global_size=(1,1,1), local_size=(1,1,1), wait=True)
|
||||
|
||||
def _run_raw(self, raw_bytes: bytes):
|
||||
from tinygrad.runtime.ops_amd import AMDProgram
|
||||
asm_src = assemble_raw_bytes(raw_bytes)
|
||||
lib = self.compiler.compile(asm_src)
|
||||
prg = AMDProgram(self.dev, "test", lib)
|
||||
buf = self.dev.allocator.alloc(64)
|
||||
prg(buf, global_size=(1,1,1), local_size=(1,1,1), wait=True)
|
||||
def _run_insts(self, insts: list[Inst]): self._run("\n".join(i.disasm() for i in insts))
|
||||
def _run_bytes(self, raw: bytes): self._run(".byte " + ",".join(f"0x{b:02x}" for b in raw))
|
||||
|
||||
def _assert_gpu_fault(self, func):
|
||||
"""Assert that func raises a RuntimeError indicating a GPU fault (not a setup error)."""
|
||||
|
|
@ -72,24 +56,19 @@ class TestIllegalInstructions(TestGPUCrash):
|
|||
|
||||
def test_all_ones_encoding(self):
|
||||
"""All-ones encoding (0xFFFFFFFF) is undefined."""
|
||||
raw_bytes = bytes([0xff, 0xff, 0xff, 0xff]) + s_endpgm().to_bytes()
|
||||
self._assert_gpu_fault(lambda: self._run_raw(raw_bytes))
|
||||
self._assert_gpu_fault(lambda: self._run_bytes(bytes([0xff, 0xff, 0xff, 0xff]) + s_endpgm().to_bytes()))
|
||||
|
||||
def test_all_zeros_encoding(self):
|
||||
"""All-zeros encoding (0x00000000) is undefined."""
|
||||
raw_bytes = bytes([0x00, 0x00, 0x00, 0x00]) + s_endpgm().to_bytes()
|
||||
self._assert_gpu_fault(lambda: self._run_raw(raw_bytes))
|
||||
self._assert_gpu_fault(lambda: self._run_bytes(bytes([0x00, 0x00, 0x00, 0x00]) + s_endpgm().to_bytes()))
|
||||
|
||||
def test_random_garbage_bytes(self):
|
||||
"""Random garbage bytes that don't decode to valid instructions."""
|
||||
raw_bytes = bytes([0xDE, 0xAD, 0xBE, 0xEF]) + s_endpgm().to_bytes()
|
||||
self._assert_gpu_fault(lambda: self._run_raw(raw_bytes))
|
||||
self._assert_gpu_fault(lambda: self._run_bytes(bytes([0xDE, 0xAD, 0xBE, 0xEF]) + s_endpgm().to_bytes()))
|
||||
|
||||
def test_truncated_instruction(self):
|
||||
"""Only half of a 64-bit instruction."""
|
||||
partial = s_load_b64(s[0:1], s[2:3], 0, soffset=NULL).to_bytes()[:4]
|
||||
raw_bytes = partial + s_endpgm().to_bytes()
|
||||
self._assert_gpu_fault(lambda: self._run_raw(raw_bytes))
|
||||
self._assert_gpu_fault(lambda: self._run_bytes(s_load_b64(s[0:1], s[2:3], 0, soffset=NULL).to_bytes()[:4] + s_endpgm().to_bytes()))
|
||||
|
||||
|
||||
class TestOutOfBoundsMemoryAccess(TestGPUCrash):
|
||||
|
|
@ -99,37 +78,37 @@ class TestOutOfBoundsMemoryAccess(TestGPUCrash):
|
|||
"""Global load from NULL pointer."""
|
||||
insts = [v_mov_b32_e32(v[0], 0), v_mov_b32_e32(v[1], 0),
|
||||
global_load_b32(v[2], addr=v[0:1], saddr=NULL, offset=0), s_waitcnt(0), s_endpgm()]
|
||||
self._assert_gpu_fault(lambda: self._run_kernel(insts))
|
||||
self._assert_gpu_fault(lambda: self._run_insts(insts))
|
||||
|
||||
def test_global_store_null_ptr(self):
|
||||
"""Global store to NULL pointer."""
|
||||
insts = [v_mov_b32_e32(v[0], 0), v_mov_b32_e32(v[1], 0), v_mov_b32_e32(v[2], 0xDEADBEEF),
|
||||
global_store_b32(addr=v[0:1], data=v[2], saddr=NULL, offset=0), s_waitcnt(0), s_endpgm()]
|
||||
self._assert_gpu_fault(lambda: self._run_kernel(insts))
|
||||
self._assert_gpu_fault(lambda: self._run_insts(insts))
|
||||
|
||||
def test_global_load_unmapped_high_address(self):
|
||||
"""Global load from high unmapped address (0xDEAD00000000)."""
|
||||
insts = [v_mov_b32_e32(v[0], 0x00000000), v_mov_b32_e32(v[1], 0xDEAD),
|
||||
global_load_b32(v[2], addr=v[0:1], saddr=NULL, offset=0), s_waitcnt(0), s_endpgm()]
|
||||
self._assert_gpu_fault(lambda: self._run_kernel(insts))
|
||||
self._assert_gpu_fault(lambda: self._run_insts(insts))
|
||||
|
||||
def test_global_store_unmapped_high_address(self):
|
||||
"""Global store to high unmapped address."""
|
||||
insts = [v_mov_b32_e32(v[0], 0x00000000), v_mov_b32_e32(v[1], 0xDEAD), v_mov_b32_e32(v[2], 0x12345678),
|
||||
global_store_b32(addr=v[0:1], data=v[2], saddr=NULL, offset=0), s_waitcnt(0), s_endpgm()]
|
||||
self._assert_gpu_fault(lambda: self._run_kernel(insts))
|
||||
self._assert_gpu_fault(lambda: self._run_insts(insts))
|
||||
|
||||
def test_global_load_b128_misaligned(self):
|
||||
"""128-bit load from misaligned address."""
|
||||
insts = [v_mov_b32_e32(v[0], 0xBEEF0001), v_mov_b32_e32(v[1], 0xDEAD),
|
||||
global_load_b128(v[2:5], addr=v[0:1], saddr=NULL, offset=0), s_waitcnt(0), s_endpgm()]
|
||||
self._assert_gpu_fault(lambda: self._run_kernel(insts))
|
||||
self._assert_gpu_fault(lambda: self._run_insts(insts))
|
||||
|
||||
def test_global_atomic_unmapped(self):
|
||||
"""Atomic operation on unmapped memory."""
|
||||
insts = [v_mov_b32_e32(v[0], 0xBEEF0000), v_mov_b32_e32(v[1], 0xDEAD), v_mov_b32_e32(v[2], 1),
|
||||
global_atomic_add_u32(addr=v[0:1], data=v[2], saddr=NULL, offset=0), s_waitcnt(0), s_endpgm()]
|
||||
self._assert_gpu_fault(lambda: self._run_kernel(insts))
|
||||
self._assert_gpu_fault(lambda: self._run_insts(insts))
|
||||
|
||||
|
||||
class TestSMEMFaults(TestGPUCrash):
|
||||
|
|
@ -139,19 +118,19 @@ class TestSMEMFaults(TestGPUCrash):
|
|||
"""SMEM load from NULL base."""
|
||||
insts = [s_mov_b32(s[2], 0), s_mov_b32(s[3], 0),
|
||||
s_load_b32(s[4], s[2:3], 0, soffset=NULL), s_waitcnt(0), s_endpgm()]
|
||||
self._assert_gpu_fault(lambda: self._run_kernel(insts))
|
||||
self._assert_gpu_fault(lambda: self._run_insts(insts))
|
||||
|
||||
def test_smem_load_unmapped(self):
|
||||
"""SMEM load from unmapped address."""
|
||||
insts = [s_mov_b32(s[2], 0xBEEF0000), s_mov_b32(s[3], 0xDEAD),
|
||||
s_load_b32(s[4], s[2:3], 0, soffset=NULL), s_waitcnt(0), s_endpgm()]
|
||||
self._assert_gpu_fault(lambda: self._run_kernel(insts))
|
||||
self._assert_gpu_fault(lambda: self._run_insts(insts))
|
||||
|
||||
def test_smem_load_b256_misaligned(self):
|
||||
"""256-bit SMEM load from misaligned address."""
|
||||
insts = [s_mov_b32(s[2], 0xBEEF0004), s_mov_b32(s[3], 0xDEAD),
|
||||
s_load_b256(s[4:11], s[2:3], 0, soffset=NULL), s_waitcnt(0), s_endpgm()]
|
||||
self._assert_gpu_fault(lambda: self._run_kernel(insts))
|
||||
self._assert_gpu_fault(lambda: self._run_insts(insts))
|
||||
|
||||
|
||||
class TestFlatMemoryFaults(TestGPUCrash):
|
||||
|
|
@ -161,19 +140,19 @@ class TestFlatMemoryFaults(TestGPUCrash):
|
|||
"""FLAT load from NULL address."""
|
||||
insts = [v_mov_b32_e32(v[0], 0), v_mov_b32_e32(v[1], 0),
|
||||
flat_load_b32(v[2], addr=v[0:1], saddr=NULL, offset=0), s_waitcnt(0), s_endpgm()]
|
||||
self._assert_gpu_fault(lambda: self._run_kernel(insts))
|
||||
self._assert_gpu_fault(lambda: self._run_insts(insts))
|
||||
|
||||
def test_flat_store_null(self):
|
||||
"""FLAT store to NULL address."""
|
||||
insts = [v_mov_b32_e32(v[0], 0), v_mov_b32_e32(v[1], 0), v_mov_b32_e32(v[2], 0xDEADBEEF),
|
||||
flat_store_b32(addr=v[0:1], data=v[2], saddr=NULL, offset=0), s_waitcnt(0), s_endpgm()]
|
||||
self._assert_gpu_fault(lambda: self._run_kernel(insts))
|
||||
self._assert_gpu_fault(lambda: self._run_insts(insts))
|
||||
|
||||
def test_flat_atomic_null(self):
|
||||
"""FLAT atomic on NULL address."""
|
||||
insts = [v_mov_b32_e32(v[0], 0), v_mov_b32_e32(v[1], 0), v_mov_b32_e32(v[2], 1),
|
||||
flat_atomic_add_u32(addr=v[0:1], data=v[2], saddr=NULL, offset=0), s_waitcnt(0), s_endpgm()]
|
||||
self._assert_gpu_fault(lambda: self._run_kernel(insts))
|
||||
self._assert_gpu_fault(lambda: self._run_insts(insts))
|
||||
|
||||
|
||||
class TestScratchMemoryFaults(TestGPUCrash):
|
||||
|
|
@ -183,16 +162,7 @@ class TestScratchMemoryFaults(TestGPUCrash):
|
|||
"""Scratch load with huge offset beyond allocated scratch."""
|
||||
insts = [v_mov_b32_e32(v[0], 0),
|
||||
scratch_load_b32(v[1], addr=v[0], saddr=NULL, offset=0x1FFF), s_waitcnt(0), s_endpgm()]
|
||||
self._assert_gpu_fault(lambda: self._run_kernel(insts))
|
||||
|
||||
|
||||
class TestControlFlowFaults(TestGPUCrash):
|
||||
"""Tests for control flow faults."""
|
||||
|
||||
def test_missing_endpgm(self):
|
||||
"""Kernel that runs off the end without s_endpgm."""
|
||||
raw_bytes = s_nop(0).to_bytes() + s_nop(0).to_bytes()
|
||||
self._assert_gpu_fault(lambda: self._run_raw(raw_bytes))
|
||||
self._assert_gpu_fault(lambda: self._run_insts(insts))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue