From 5cbf7f5bd7ddc4555070ec6c229a16b1f2d8b499 Mon Sep 17 00:00:00 2001 From: Faith Ekstrand Date: Sat, 22 Mar 2025 23:05:57 -0500 Subject: [PATCH] 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: --- src/nouveau/headers/class_parser.py | 318 ++++++++++++++-------------- 1 file changed, 157 insertions(+), 161 deletions(-) diff --git a/src/nouveau/headers/class_parser.py b/src/nouveau/headers/class_parser.py index 1cd675edb4b..618dc2b239b 100644 --- a/src/nouveau/headers/class_parser.py +++ b/src/nouveau/headers/class_parser.py @@ -75,70 +75,66 @@ TEMPLATE_H = Template("""\ #include #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': '\\' }