nouveau/headers: Refactor class_parser

This adds proper Method and Field classes and uses them instead of
the pile of dictionaries we had before.  This is probably faster and
definitely more readable.  I've verified with cl9097.h that this makes
no differenct to the generated C or Rust besides whitespace.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/34136>
This commit is contained in:
Faith Ekstrand
2025-03-22 23:05:57 -05:00
committed by Marge Bot
parent f26cf5633d
commit 5cbf7f5bd7
+157 -161
View File
@@ -75,70 +75,66 @@ TEMPLATE_H = Template("""\
#include <stdio.h>
#include "util/u_math.h"
%for mthd in mthddict:
struct nv_${nvcl.lower()}_${mthd} {
%for field_name in mthddict[mthd].field_name_start:
uint32_t ${field_name.lower()};
%for mthd in methods:
struct nv_${nvcl.lower()}_${mthd.name} {
%for field in mthd.fields:
uint32_t ${field.name.lower()};
%endfor
};
static inline void
__${nvcl}_${mthd}(uint32_t *val_out, struct nv_${nvcl.lower()}_${mthd} st)
__${nvcl}_${mthd.name}(uint32_t *val_out, struct nv_${nvcl.lower()}_${mthd.name} st)
{
uint32_t val = 0;
%for field_name in mthddict[mthd].field_name_start:
<%
field_start = int(mthddict[mthd].field_name_start[field_name])
field_end = int(mthddict[mthd].field_name_end[field_name])
field_width = field_end - field_start + 1
%>
%for field in mthd.fields:
<% field_width = field.end - field.start + 1 %>
%if field_width == 32:
val |= st.${field_name.lower()};
val |= st.${field.name.lower()};
%else:
assert(st.${field_name.lower()} < (1ULL << ${field_width}));
val |= st.${field_name.lower()} << ${field_start};
assert(st.${field.name.lower()} < (1ULL << ${field_width}));
val |= st.${field.name.lower()} << ${field.start};
%endif
%endfor
*val_out = val;
}
#define V_${nvcl}_${mthd}(val, args...) { ${bs}
%for field_name in mthddict[mthd].field_name_start:
%for d in mthddict[mthd].field_defs[field_name]:
UNUSED uint32_t ${field_name}_${d} = ${nvcl}_${mthd}_${field_name}_${d}; ${bs}
#define V_${nvcl}_${mthd.name}(val, args...) { ${bs}
%for field in mthd.fields:
%for d in field.defs:
UNUSED uint32_t ${field.name}_${d} = ${nvcl}_${mthd.name}_${field.name}_${d}; ${bs}
%endfor
%endfor
%if len(mthddict[mthd].field_name_start) > 1:
struct nv_${nvcl.lower()}_${mthd} __data = args; ${bs}
%if len(mthd.fields) > 1:
struct nv_${nvcl.lower()}_${mthd.name} __data = args; ${bs}
%else:
<% field_name = next(iter(mthddict[mthd].field_name_start)).lower() %>\
struct nv_${nvcl.lower()}_${mthd} __data = { .${field_name} = (args) }; ${bs}
<% field_name = mthd.fields[0].name.lower() %>\
struct nv_${nvcl.lower()}_${mthd.name} __data = { .${field_name} = (args) }; ${bs}
%endif
__${nvcl}_${mthd}(&val, __data); ${bs}
__${nvcl}_${mthd.name}(&val, __data); ${bs}
}
%if mthddict[mthd].is_array:
#define VA_${nvcl}_${mthd}(i) V_${nvcl}_${mthd}
%if mthd.is_array:
#define VA_${nvcl}_${mthd.name}(i) V_${nvcl}_${mthd.name}
%else:
#define VA_${nvcl}_${mthd} V_${nvcl}_${mthd}
#define VA_${nvcl}_${mthd.name} V_${nvcl}_${mthd.name}
%endif
%if mthddict[mthd].is_array:
#define P_${nvcl}_${mthd}(push, idx, args...) do { ${bs}
%if mthd.is_array:
#define P_${nvcl}_${mthd.name}(push, idx, args...) do { ${bs}
%else:
#define P_${nvcl}_${mthd}(push, args...) do { ${bs}
#define P_${nvcl}_${mthd.name}(push, args...) do { ${bs}
%endif
%for field_name in mthddict[mthd].field_name_start:
%for d in mthddict[mthd].field_defs[field_name]:
UNUSED uint32_t ${field_name}_${d} = ${nvcl}_${mthd}_${field_name}_${d}; ${bs}
%for field in mthd.fields:
%for d in field.defs:
UNUSED uint32_t ${field.name}_${d} = ${nvcl}_${mthd.name}_${field.name}_${d}; ${bs}
%endfor
%endfor
uint32_t nvk_p_ret; ${bs}
V_${nvcl}_${mthd}(nvk_p_ret, args); ${bs}
%if mthddict[mthd].is_array:
nv_push_val(push, ${nvcl}_${mthd}(idx), nvk_p_ret); ${bs}
V_${nvcl}_${mthd.name}(nvk_p_ret, args); ${bs}
%if mthd.is_array:
nv_push_val(push, ${nvcl}_${mthd.name}(idx), nvk_p_ret); ${bs}
%else:
nv_push_val(push, ${nvcl}_${mthd}, nvk_p_ret); ${bs}
nv_push_val(push, ${nvcl}_${mthd.name}, nvk_p_ret); ${bs}
%endif
} while(0)
@@ -158,18 +154,18 @@ const char*
P_PARSE_${nvcl}_MTHD(uint16_t idx)
{
switch (idx) {
%for mthd in mthddict:
%if mthddict[mthd].is_array and mthddict[mthd].array_size == 0:
%for mthd in methods:
%if mthd.is_array and mthd.array_size == 0:
<% continue %>
%endif
%if mthddict[mthd].is_array:
%for i in range(mthddict[mthd].array_size):
case ${nvcl}_${mthd}(${i}):
return "${nvcl}_${mthd}(${i})";
%if mthd.is_array:
%for i in range(mthd.array_size):
case ${nvcl}_${mthd.name}(${i}):
return "${nvcl}_${mthd.name}(${i})";
%endfor
% else:
case ${nvcl}_${mthd}:
return "${nvcl}_${mthd}";
case ${nvcl}_${mthd.name}:
return "${nvcl}_${mthd.name}";
%endif
%endfor
default:
@@ -183,33 +179,29 @@ P_DUMP_${nvcl}_MTHD_DATA(FILE *fp, uint16_t idx, uint32_t data,
{
uint32_t parsed;
switch (idx) {
%for mthd in mthddict:
%if mthddict[mthd].is_array and mthddict[mthd].array_size == 0:
%for mthd in methods:
%if mthd.is_array and mthd.array_size == 0:
<% continue %>
%endif
%if mthddict[mthd].is_array:
%for i in range(mthddict[mthd].array_size):
case ${nvcl}_${mthd}(${i}):
%if mthd.is_array:
%for i in range(mthd.array_size):
case ${nvcl}_${mthd.name}(${i}):
%endfor
% else:
case ${nvcl}_${mthd}:
case ${nvcl}_${mthd.name}:
%endif
%for field_name in mthddict[mthd].field_name_start:
<%
field_start = int(mthddict[mthd].field_name_start[field_name])
field_end = int(mthddict[mthd].field_name_end[field_name])
field_width = field_end - field_start + 1
%>
%for field in mthd.fields:
<% field_width = field.end - field.start + 1 %>
%if field_width == 32:
parsed = data;
%else:
parsed = (data >> ${field_start}) & ((1u << ${field_width}) - 1);
parsed = (data >> ${field.start}) & ((1u << ${field_width}) - 1);
%endif
fprintf(fp, "%s.${field_name} = ", prefix);
%if len(mthddict[mthd].field_defs[field_name]):
fprintf(fp, "%s.${field.name} = ", prefix);
%if len(field.defs):
switch (parsed) {
%for d in mthddict[mthd].field_defs[field_name]:
case ${nvcl}_${mthd}_${field_name}_${d}:
%for d in field.defs:
case ${nvcl}_${mthd.name}_${field.name}_${d}:
fprintf(fp, "${d}${bs}n");
break;
%endfor
@@ -218,7 +210,7 @@ P_DUMP_${nvcl}_MTHD_DATA(FILE *fp, uint16_t idx, uint32_t data,
break;
}
%else:
%if mthddict[mthd].is_float:
%if mthd.is_float:
fprintf(fp, "%ff (0x%x)${bs}n", uif(parsed), parsed);
%else:
fprintf(fp, "(0x%x)${bs}n", parsed);
@@ -247,41 +239,16 @@ TEMPLATE_RS_MTHD = Template("""\
// parsed class ${nvcl}
## Write out the methods in Rust
%for mthd_name, mthd in mthddict.items():
## Identify the field type.
<%
for field_name, field_value in mthd.field_defs.items():
if field_name == 'V' and len(field_value) > 0:
mthd.field_rs_types[field_name] = to_camel(mthd_name) + 'V'
mthd.field_is_rs_enum[field_name] = True
elif len(field_value) > 0:
assert(field_name != "")
mthd.field_rs_types[field_name] = to_camel(mthd_name) + to_camel(field_name)
mthd.field_is_rs_enum[field_name] = True
elif mthd.is_float:
mthd.field_rs_types[field_name] = "f32"
mthd.field_is_rs_enum[field_name] = False
else:
mthd.field_rs_types[field_name] = "u32"
mthd.field_is_rs_enum[field_name] = False
# TRUE and FALSE are special cases.
if len(field_value) == 2:
for enumerant in field_value:
if enumerant.lower() == 'true' or enumerant.lower() == 'false':
mthd.field_rs_types[field_name] = "bool"
mthd.field_is_rs_enum[field_name] = False
break
%>
%for mthd in methods:
## If there are a range of values for a field, we define an enum.
%for field_name in mthd.field_defs:
%if mthd.field_is_rs_enum[field_name]:
%for field in mthd.fields:
%if field.is_rs_enum:
#[repr(u16)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ${mthd.field_rs_types[field_name]} {
%for field_name, field_value in mthd.field_defs[field_name].items():
${to_camel(rs_field_name(field_name))} = ${field_value.lower()},
pub enum ${field.rs_type(mthd)} {
%for def_name, def_value in field.defs.items():
${to_camel(def_name)} = ${def_value.lower()},
%endfor
}
%endif
@@ -289,9 +256,9 @@ pub enum ${mthd.field_rs_types[field_name]} {
## We also define a struct with the fields for the mthd.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct ${to_camel(mthd_name)} {
%for field_name in mthddict[mthd_name].field_name_start:
pub ${rs_field_name(field_name.lower())}: ${mthd.field_rs_types[field_name]},
pub struct ${to_camel(mthd.name)} {
%for field in mthd.fields:
pub ${field.rs_name}: ${field.rs_type(mthd)},
%endfor
}
@@ -299,12 +266,12 @@ pub struct ${to_camel(mthd_name)} {
## not closed.
% if not mthd.is_array:
## This trait lays out how the conversion to u32 happens
impl Mthd for ${to_camel(mthd_name)} {
impl Mthd for ${to_camel(mthd.name)} {
const ADDR: u16 = ${mthd.addr.replace('(', '').replace(')', '')};
const CLASS: u16 = ${version[1].lower() if version is not None else nvcl.lower().replace("nv", "0x")};
%else:
impl ArrayMthd for ${to_camel(mthd_name)} {
impl ArrayMthd for ${to_camel(mthd.name)} {
const CLASS: u16 = ${version[1].lower() if version is not None else nvcl.lower().replace("nv", "0x")};
fn addr(i: usize) -> u16 {
@@ -316,22 +283,21 @@ impl ArrayMthd for ${to_camel(mthd_name)} {
#[inline]
fn to_bits(self) -> u32 {
let mut val = 0;
%for field_name in mthddict[mthd_name].field_name_start:
<%
field_start = int(mthd.field_name_start[field_name])
field_end = int(mthd.field_name_end[field_name])
field_width = field_end - field_start + 1
field = rs_field_name(field_name.lower()) if mthd.field_rs_types[field_name] == "u32" else f"{rs_field_name(field_name)} as u32"
%>
%for field in mthd.fields:
<% field_width = field.end - field.start + 1 %>
%if field_width == 32:
val |= self.${field};
%else:
%if "as u32" in field:
assert!((self.${field}) < (1 << ${field_width}));
val |= (self.${field}) << ${field_start};
%if field.rs_type(mthd) == "u32":
val |= self.${field.rs_name};
%else:
assert!(self.${field} < (1 << ${field_width}));
val |= self.${field} << ${field_start};
val |= self.${field.rs_name} as u32;
%endif
%else:
%if field.rs_type(mthd) == "u32":
assert!(self.${field.rs_name} < (1 << ${field_width}));
val |= self.${field.rs_name} << ${field.start};
%else:
assert!((self.${field.rs_name} as u32) < (1 << ${field_width}));
val |= (self.${field.rs_name} as u32) << ${field.start};
%endif
%endif
%endfor
@@ -349,19 +315,6 @@ def to_camel(snake_str):
result = ''.join(word.title() for word in snake_str.split('_'))
return result if not result[0].isdigit() else '_' + result
def rs_field_name(name):
name = name.lower()
# Fix up some Rust keywords
if name == 'type':
return 'type_'
elif name == 'override':
return 'override_'
elif name[0].isdigit():
return '_' + name
else:
return name
def glob_match(glob, name):
if glob.endswith('*'):
return name.startswith(glob[:-1])
@@ -369,7 +322,62 @@ def glob_match(glob, name):
assert '*' not in glob
return name == glob
class method(object):
class Field(object):
def __init__(self, name, start, end):
self.name = name
self.start = int(start)
self.end = int(end)
self.defs = {}
@property
def is_bool(self):
if len(self.defs) != 2:
return False
for d in self.defs:
if not d.lower() in ['true', 'false']:
return False
return True
@property
def is_rs_enum(self):
return not self.is_bool and len(self.defs) > 0
def rs_type(self, mthd):
if self.is_bool:
return "bool"
elif self.name == 'V' and len(self.defs) > 0:
return to_camel(mthd.name) + 'V'
elif len(self.defs) > 0:
assert(self.name != "")
return to_camel(mthd.name) + to_camel(self.name)
elif mthd.is_float:
return "f32"
else:
return "u32"
@property
def rs_name(self):
name = self.name.lower()
# Fix up some Rust keywords
if name == 'type':
return 'type_'
elif name == 'override':
return 'override_'
elif name[0].isdigit():
return '_' + name
else:
return name
class Method(object):
def __init__(self, name, addr, is_array=False):
self.name = name
self.addr = addr
self.is_array = is_array
self.fields = []
@property
def array_size(self):
for (glob, value) in METHOD_ARRAY_SIZES.items():
@@ -381,7 +389,7 @@ class method(object):
def is_float(self):
for glob in METHOD_IS_FLOAT:
if glob_match(glob, self.name):
assert len(self.field_defs) == 1
assert len(self.fields) == 1
return True
return False
@@ -394,16 +402,15 @@ def parse_header(nvcl, f):
version = None
state = 0
mthddict = {}
curmthd = {}
for line in f:
methods = {}
curmthd = None
for line in f:
if line.strip() == "":
state = 0
if (curmthd):
if not len(curmthd.field_name_start):
del mthddict[curmthd.name]
curmthd = {}
if curmthd is not None and len(curmthd.fields) == 0:
del methods[curmthd.name]
curmthd = None
continue
if line.startswith("#define"):
@@ -421,11 +428,11 @@ def parse_header(nvcl, f):
continue
if state == 2:
teststr = nvcl + "_" + curmthd.name + "_" + curfield + "_"
teststr = nvcl + "_" + curmthd.name + "_" + curfield.name + "_"
if ":" in list[2]:
state = 1
elif teststr in list[1]:
curmthd.field_defs[curfield][list[1].removeprefix(teststr)] = list[2]
curfield.defs[list[1].removeprefix(teststr)] = list[2]
else:
state = 1
@@ -437,21 +444,20 @@ def parse_header(nvcl, f):
else:
field = list[1].removeprefix(teststr)
bitfield = list[2].split(":")
curmthd.field_name_start[field] = bitfield[1]
curmthd.field_name_end[field] = bitfield[0]
curmthd.field_defs[field] = {}
curfield = field
f = Field(field, bitfield[1], bitfield[0])
curmthd.fields.append(f)
curfield = f
state = 2
else:
if not len(curmthd.field_name_start):
del mthddict[curmthd.name]
curmthd = {}
if len(curmthd.fields) == 0:
del methods[curmthd.name]
curmthd = None
state = 0
if state == 0:
if (curmthd):
if not len(curmthd.field_name_start):
del mthddict[curmthd.name]
if curmthd is not None and len(curmthd.fields) == 0:
del methods[curmthd.name]
teststr = nvcl + "_"
is_array = 0
if (':' in list[2]):
@@ -463,21 +469,12 @@ def parse_header(nvcl, f):
if name.endswith("(j)"):
is_array = 1
name = name.removesuffix("(j)")
x = method()
x.name = name
x.addr = list[2]
x.is_array = is_array
x.field_name_start = {}
x.field_name_end = {}
x.field_defs = {}
x.field_rs_types = {}
x.field_is_rs_enum = {}
mthddict[x.name] = x
curmthd = x
curmthd = Method(name, list[2], is_array)
methods[name] = curmthd
state = 1
return (version, mthddict)
return (version, methods)
def convert_to_rust_constants(filename):
with open(filename, 'r') as file:
@@ -558,14 +555,13 @@ def main():
nvcl = "NV" + nvcl
with open(args.in_h, 'r', encoding='utf-8') as f:
(version, mthddict) = parse_header(nvcl, f)
(version, methods) = parse_header(nvcl, f)
environment = {
'clheader': clheader,
'nvcl': nvcl,
'version': version,
'mthddict': mthddict,
'rs_field_name': rs_field_name,
'methods': list(methods.values()),
'to_camel': to_camel,
'bs': '\\'
}