mirror of
https://github.com/tinygrad/tinygrad.git
synced 2026-01-09 15:08:02 -05:00
* autogen: don't fast-fail, produce patch artifact on differences All verification steps now use continue-on-error to run completely. Each job generates a patch artifact containing all differences found. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * add gen from header test * fix tests * fail if diff * add forward decl autogen test * remove confusing/wrong comments * macos unittests set LIBCLANG_PATH --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
257 lines
8.8 KiB
Python
257 lines
8.8 KiB
Python
import ctypes, subprocess, tempfile, unittest
|
|
from tinygrad.helpers import WIN
|
|
from tinygrad.runtime.support.c import Struct
|
|
from tinygrad.runtime.support.autogen import gen
|
|
|
|
class TestAutogen(unittest.TestCase):
|
|
def test_packed_struct_sizeof(self):
|
|
layout = [('a', ctypes.c_char), ('b', ctypes.c_int, 5), ('c', ctypes.c_char)]
|
|
class Y(ctypes.Structure): _fields_, _pack_, _layout_ = layout, 1, 'ms'
|
|
class Z(Struct): pass
|
|
Z._packed_, Z._fields_ = True, layout
|
|
self.assertEqual(ctypes.sizeof(Y), 6)
|
|
self.assertEqual(ctypes.sizeof(Z), 3)
|
|
layout = [('a', ctypes.c_int, 31), ('b', ctypes.c_int, 31), ('c', ctypes.c_int, 1), ('d', ctypes.c_int, 1)]
|
|
class Foo(ctypes.Structure): _fields_, _layout_ = layout, 'gcc-sysv'
|
|
class Bar(ctypes.Structure): _fields_, _pack_, _layout_ = layout, 1, 'ms'
|
|
class Baz(Struct): pass
|
|
Baz._packed_, Baz._fields_ = True, layout
|
|
self.assertEqual(ctypes.sizeof(Foo), 12)
|
|
self.assertEqual(ctypes.sizeof(Bar), 12)
|
|
self.assertEqual(ctypes.sizeof(Baz), 8)
|
|
|
|
@unittest.skipIf(WIN, "doesn't compile on windows")
|
|
def test_packed_struct_interop(self):
|
|
class Baz(Struct): pass
|
|
Baz._packed_ = True
|
|
Baz._fields_ = [('a', ctypes.c_int, 30), ('b', ctypes.c_int, 30), ('c', ctypes.c_int, 2), ('d', ctypes.c_int, 2)]
|
|
src = '''
|
|
struct __attribute__((packed)) baz {
|
|
int a:30;
|
|
int b:30;
|
|
int c:2;
|
|
int d:2;
|
|
};
|
|
|
|
int test(struct baz x) {
|
|
return x.a + x.b + x.c + x.d;
|
|
}
|
|
'''
|
|
args = ('-x', 'c', '-fPIC', '-shared')
|
|
with tempfile.NamedTemporaryFile(suffix=".so") as f:
|
|
subprocess.check_output(('clang',) + args + ('-', '-o', f.name), input=src.encode('utf-8'))
|
|
b = Baz(0xAA000, 0x00BB0, 0, 1)
|
|
test = ctypes.CDLL(f.name).test
|
|
test.argtypes = [Baz]
|
|
self.assertEqual(test(b), b.a + b.b + b.c + b.d)
|
|
|
|
# https://github.com/python/cpython/issues/90914
|
|
@unittest.skipIf(WIN, "doesn't compile on windows")
|
|
def test_bitfield_interop(self):
|
|
class Baz(Struct): pass
|
|
Baz._fields_ = [(chr(ord('a') + i), ctypes.c_bool, 1) for i in range(8)]
|
|
src = '''#include <stdbool.h>
|
|
struct baz {
|
|
bool a:1;
|
|
bool b:1;
|
|
bool c:1;
|
|
bool d:1;
|
|
bool e:1;
|
|
bool f:1;
|
|
bool g:1;
|
|
bool h:1;
|
|
};
|
|
|
|
int test(struct baz x) {
|
|
return x.c;
|
|
}
|
|
'''
|
|
args = ('-x', 'c', '-fPIC', '-shared')
|
|
with tempfile.NamedTemporaryFile(suffix=".so") as f:
|
|
subprocess.check_output(('clang',) + args + ('-', '-o', f.name), input=src.encode('utf-8'))
|
|
test = ctypes.CDLL(f.name).test
|
|
test.argtypes = [Baz]
|
|
for i in range(8): self.assertEqual(test(Baz(*(j==i for j in range(8)))), i==2)
|
|
|
|
@unittest.skipIf(WIN, "doesn't compile on windows")
|
|
def test_packed_structs(self):
|
|
NvU32 = ctypes.c_uint32
|
|
NvU64 = ctypes.c_uint64
|
|
class FWSECLIC_READ_VBIOS_DESC(Struct): pass
|
|
FWSECLIC_READ_VBIOS_DESC._packed_ = True
|
|
FWSECLIC_READ_VBIOS_DESC._fields_ = [
|
|
('version', NvU32),
|
|
('size', NvU32),
|
|
('gfwImageOffset', NvU64),
|
|
('gfwImageSize', NvU32),
|
|
('flags', NvU32),
|
|
]
|
|
class FWSECLIC_FRTS_REGION_DESC(Struct): pass
|
|
FWSECLIC_FRTS_REGION_DESC._packed_ = True
|
|
FWSECLIC_FRTS_REGION_DESC._fields_ = [
|
|
('version', NvU32),
|
|
('size', NvU32),
|
|
('frtsRegionOffset4K', NvU32),
|
|
('frtsRegionSize', NvU32),
|
|
('frtsRegionMediaType', NvU32),
|
|
]
|
|
class FWSECLIC_FRTS_CMD(Struct): pass
|
|
FWSECLIC_FRTS_CMD._packed_ = True
|
|
FWSECLIC_FRTS_CMD._fields_ = [
|
|
('readVbiosDesc', FWSECLIC_READ_VBIOS_DESC),
|
|
('frtsRegionDesc', FWSECLIC_FRTS_REGION_DESC),
|
|
]
|
|
read_vbios_desc = FWSECLIC_READ_VBIOS_DESC(version=0x1, size=ctypes.sizeof(FWSECLIC_READ_VBIOS_DESC), flags=2)
|
|
frst_reg_desc = FWSECLIC_FRTS_REGION_DESC(version=0x1, size=ctypes.sizeof(FWSECLIC_FRTS_REGION_DESC),
|
|
frtsRegionOffset4K=0xdead, frtsRegionSize=0x100, frtsRegionMediaType=2)
|
|
frts_cmd = FWSECLIC_FRTS_CMD(readVbiosDesc=read_vbios_desc, frtsRegionDesc=frst_reg_desc)
|
|
assert int.from_bytes(frts_cmd, 'little') == 0x2000001000000dead0000001400000001000000020000000000000000000000000000001800000001
|
|
assert int.from_bytes(frts_cmd.readVbiosDesc, 'little') == int.from_bytes(read_vbios_desc, 'little')
|
|
assert int.from_bytes(frts_cmd.frtsRegionDesc, 'little') == int.from_bytes(frst_reg_desc, 'little')
|
|
assert frts_cmd.readVbiosDesc.__class__ is FWSECLIC_READ_VBIOS_DESC
|
|
assert frts_cmd.frtsRegionDesc.__class__ is FWSECLIC_FRTS_REGION_DESC
|
|
|
|
def test_packed_fields(self):
|
|
uint8_t = ctypes.c_ubyte
|
|
uint16_t = ctypes.c_ushort
|
|
uint32_t = ctypes.c_uint32
|
|
|
|
class struct_die_info(Struct): pass
|
|
struct_die_info._packed_ = True
|
|
struct_die_info._fields_ = [
|
|
('die_id', uint16_t),
|
|
('die_offset', uint16_t),
|
|
]
|
|
die_info = struct_die_info
|
|
class struct_ip_discovery_header(Struct): pass
|
|
class struct_ip_discovery_header_0(ctypes.Union): pass
|
|
class struct_ip_discovery_header_0_0(Struct): pass
|
|
uint8_t = ctypes.c_ubyte
|
|
struct_ip_discovery_header_0_0._fields_ = [
|
|
('base_addr_64_bit', uint8_t,1),
|
|
('reserved', uint8_t,7),
|
|
('reserved2', uint8_t),
|
|
]
|
|
struct_ip_discovery_header_0._anonymous_ = ['_0']
|
|
struct_ip_discovery_header_0._packed_ = True
|
|
struct_ip_discovery_header_0._fields_ = [
|
|
('padding', (uint16_t * 1)),
|
|
('_0', struct_ip_discovery_header_0_0),
|
|
]
|
|
struct_ip_discovery_header._anonymous_ = ['_0']
|
|
struct_ip_discovery_header._packed_ = True
|
|
struct_ip_discovery_header._fields_ = [
|
|
('signature', uint32_t),
|
|
('version', uint16_t),
|
|
('size', uint16_t),
|
|
('id', uint32_t),
|
|
('num_dies', uint16_t),
|
|
('die_info', (die_info * 16)),
|
|
('_0', struct_ip_discovery_header_0),
|
|
]
|
|
ip_discovery_header = struct_ip_discovery_header
|
|
|
|
hdr = b'IPDS\x04\x00|\x1d\x80\x1a\xffd\x01\x00\x00\x00\x8c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00' # noqa: E501
|
|
ihdr = ip_discovery_header.from_buffer_copy(hdr)
|
|
|
|
assert ctypes.sizeof(ihdr) == 80
|
|
assert ihdr.signature == 0x53445049
|
|
assert ihdr.version == 0x0004
|
|
assert ihdr.num_dies == 1
|
|
assert ihdr.base_addr_64_bit == 1
|
|
|
|
@unittest.skipIf(WIN, "doesn't compile on windows")
|
|
def test_gen_from_header(self):
|
|
header_content = """
|
|
typedef struct {
|
|
int x;
|
|
int y;
|
|
} Point;
|
|
|
|
typedef enum {
|
|
RED = 0,
|
|
GREEN = 1,
|
|
BLUE = 2
|
|
} Color;
|
|
|
|
typedef struct {
|
|
Point origin;
|
|
int width;
|
|
int height;
|
|
Color color;
|
|
} Rectangle;
|
|
|
|
int add_points(Point a, Point b);
|
|
"""
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.h') as f:
|
|
f.write(header_content)
|
|
f.flush()
|
|
|
|
generated_code = gen(name="test_header", dll=None, files=[f.name])
|
|
|
|
namespace = {}
|
|
exec(generated_code, namespace)
|
|
|
|
self.assertIn('Point', namespace)
|
|
self.assertIn('Color', namespace)
|
|
self.assertIn('Rectangle', namespace)
|
|
self.assertIn('RED', namespace)
|
|
self.assertIn('GREEN', namespace)
|
|
self.assertIn('BLUE', namespace)
|
|
|
|
self.assertEqual(namespace['RED'], 0)
|
|
self.assertEqual(namespace['GREEN'], 1)
|
|
self.assertEqual(namespace['BLUE'], 2)
|
|
|
|
Point = namespace['Point']
|
|
p = Point()
|
|
self.assertIsInstance(p, Struct)
|
|
self.assertTrue(hasattr(p, 'x'))
|
|
self.assertTrue(hasattr(p, 'y'))
|
|
|
|
Rectangle = namespace['Rectangle']
|
|
rect = Rectangle()
|
|
self.assertTrue(hasattr(rect, 'origin'))
|
|
self.assertTrue(hasattr(rect, 'width'))
|
|
self.assertTrue(hasattr(rect, 'height'))
|
|
self.assertTrue(hasattr(rect, 'color'))
|
|
|
|
@unittest.skipIf(WIN, "doesn't compile on windows")
|
|
def test_struct_ordering(self):
|
|
header_content = """
|
|
struct A;
|
|
struct C;
|
|
typedef struct A A;
|
|
|
|
struct B {
|
|
struct C *c_ptr;
|
|
};
|
|
|
|
struct C {
|
|
struct A *a_ptr;
|
|
};
|
|
|
|
struct A {
|
|
int x;
|
|
struct B *b_ptr;
|
|
};
|
|
"""
|
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.h') as f:
|
|
f.write(header_content)
|
|
f.flush()
|
|
generated_code = gen(name="test_ordering", dll=None, files=[f.name])
|
|
namespace = {}
|
|
exec(generated_code, namespace)
|
|
self.assertIn('struct_A', namespace)
|
|
self.assertIn('struct_B', namespace)
|
|
self.assertIn('struct_C', namespace)
|
|
A, B, C = namespace['struct_A'], namespace['struct_B'], namespace['struct_C']
|
|
a, b, c = A(), B(), C()
|
|
self.assertTrue(hasattr(a, 'x'))
|
|
self.assertTrue(hasattr(a, 'b_ptr'))
|
|
self.assertTrue(hasattr(b, 'c_ptr'))
|
|
self.assertTrue(hasattr(c, 'a_ptr'))
|
|
|
|
if __name__ == "__main__": unittest.main()
|