xref: /aosp_15_r20/external/mesa3d/src/nouveau/headers/struct_parser.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1#! /usr/bin/env python3
2#
3# Copyright © 2024 Collabora Ltd. and Red Hat Inc.
4# SPDX-License-Identifier: MIT
5
6import argparse
7import os.path
8import re
9import sys
10
11from collections import namedtuple
12from mako.template import Template
13
14TEMPLATE_RS = Template("""\
15// Copyright © 2024 Collabora Ltd. and Red Hat Inc.
16// SPDX-License-Identifier: MIT
17
18// This file is generated by struct_parser.py. DO NOT EDIT!
19
20#![allow(non_snake_case)]
21
22use std::ops::Range;
23
24% for s in structs:
25    % for f in s.fields:
26        % if f.stride:
27#[inline]
28pub fn ${s.name}_${f.name}(i: usize) -> Range<usize> {
29    (i * ${f.stride} + ${f.lo})..(i * ${f.stride} + ${f.hi + 1})
30}
31        % else:
32pub const ${s.name}_${f.name}: Range<usize> = ${f.lo}..${f.hi + 1};
33        % endif:
34        % for e in f.enums:
35pub const ${s.name}_${f.name}_${e.name}: u32 = ${e.value};
36        % endfor
37    % endfor
38% endfor
39""")
40
41STRUCTS = [
42    'SPHV3',
43    'SPHV4',
44    'TEXHEADV2',
45    'TEXHEADV3',
46    'TEXHEAD_BL',
47    'TEXHEAD_1D',
48    'TEXHEAD_PITCH',
49    # This one goes last because it's a substring of the others
50    'TEXHEAD',
51    'TEXSAMP',
52    'QMDV00_06',
53    'QMDV01_06',
54    'QMDV01_07',
55    'QMDV02_01',
56    'QMDV02_02',
57    'QMDV02_03',
58    'QMDV02_04',
59    'QMDV03_00',
60]
61
62Enum = namedtuple('Enum', ['name', 'value'])
63
64class Field(object):
65    def __init__(self, name, lo, hi, stride=0):
66        self.name = name
67        self.lo = lo
68        self.hi = hi
69        self.stride = stride
70        self.enums = []
71
72    def add_enum(self, name, value):
73        self.enums.append(Enum(name, value))
74
75class Struct(object):
76    def __init__(self, name):
77        self.name = name
78        self.fields = []
79
80    def add_field(self, name, lo, hi, stride=0):
81        self.fields.append(Field(name, lo, hi, stride))
82
83DRF_RE = re.compile(r'(?P<hi>[0-9]+):(?P<lo>[0-9]+)')
84FIELD_NAME_RE = re.compile(r'_?(?P<dw>[0-9]+)?_?(?P<name>.*)')
85MW_RE = re.compile(r'MW\((?P<hi>[0-9]+):(?P<lo>[0-9]+)\)')
86MW_ARR_RE = re.compile(r'MW\(\((?P<hi>\d+)\+\(i\)\*(?P<stride>\d+)\):\((?P<lo>[0-9]+)\+\(i\)\*(?P=stride)\)\)')
87
88def parse_header(nvcl, file):
89    structs = {}
90    for line in file:
91        line = line.strip().split()
92        if not line:
93            continue
94
95        if line[0] != '#define':
96            continue
97
98        if not line[1].startswith(nvcl):
99            continue
100
101        name = line[1][(len(nvcl)+1):]
102
103        struct = None
104        for s in STRUCTS:
105            if name.startswith(s):
106                if s not in structs:
107                    structs[s] = Struct(s)
108                struct = structs[s]
109                name = name[len(s):]
110                break
111
112        if struct is None:
113            continue
114
115        name_m = FIELD_NAME_RE.match(name)
116        name = name_m.group('name')
117
118        drf = DRF_RE.match(line[2])
119        mw = MW_RE.match(line[2])
120        mw_arr = MW_ARR_RE.match(line[2])
121        if drf:
122            dw = int(name_m.group('dw'))
123            lo = int(drf.group('lo')) + dw * 32
124            hi = int(drf.group('hi')) + dw * 32
125            struct.add_field(name, lo, hi)
126        elif mw:
127            lo = int(mw.group('lo'))
128            hi = int(mw.group('hi'))
129            struct.add_field(name, lo, hi)
130        elif mw_arr:
131            lo = int(mw_arr.group('lo'))
132            hi = int(mw_arr.group('hi'))
133            stride = int(mw_arr.group('stride'))
134            assert name.endswith('(i)')
135            struct.add_field(name.removesuffix('(i)'), lo, hi, stride)
136        else:
137            for f in struct.fields:
138                if name.startswith(f.name + '_'):
139                    name = name[(len(f.name)+1):]
140                    f.add_enum(name, line[2])
141
142    return list(structs.values())
143
144NVCL_RE = re.compile(r'cl(?P<clsver>[0-9a-f]{4}).*')
145
146def main():
147    parser = argparse.ArgumentParser()
148    parser.add_argument('--out-rs', required=True, help='Output Rust file.')
149    parser.add_argument('--in-h',
150                        help='Input class header file.',
151                        required=True)
152    args = parser.parse_args()
153
154    clheader = os.path.basename(args.in_h)
155    nvcl = NVCL_RE.match(clheader).group('clsver')
156    nvcl = nvcl.upper()
157    nvcl = "NV" + nvcl
158
159    with open(args.in_h, 'r', encoding='utf-8') as f:
160        structs = parse_header(nvcl, f)
161
162    try:
163        with open(args.out_rs, 'w', encoding='utf-8') as f:
164            f.write(TEMPLATE_RS.render(structs=structs))
165
166    except Exception:
167        # In the event there's an error, this imports some helpers from mako
168        # to print a useful stack trace and prints it, then exits with
169        # status 1, if python is run with debug; otherwise it just raises
170        # the exception
171        import sys
172        from mako import exceptions
173        print(exceptions.text_error_template().render(), file=sys.stderr)
174        sys.exit(1)
175
176if __name__ == '__main__':
177    main()
178