mesa/src/nouveau/headers/class_parser.py
Faith Ekstrand 53bc795616 nouveau: Use hyphenated arguments to class_parser.py
This is more consistent with other code generators in Mesa.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/27397>
2024-04-08 20:28:43 +00:00

385 lines
12 KiB
Python

#! /usr/bin/env python3
# script to parse nvidia CL headers and generate inlines to be used in pushbuffer encoding.
# probably needs python3.9
import argparse
import os.path
import sys
from mako.template import Template
METHOD_ARRAY_SIZES = {
'BIND_GROUP_CONSTANT_BUFFER' : 16,
'CALL_MME_DATA' : 256,
'CALL_MME_MACRO' : 256,
'LOAD_CONSTANT_BUFFER' : 16,
'LOAD_INLINE_QMD_DATA' : 64,
'SET_ANTI_ALIAS_SAMPLE_POSITIONS' : 4,
'SET_BLEND' : 8,
'SET_BLEND_PER_TARGET_*' : 8,
'SET_COLOR_TARGET_*' : 8,
'SET_COLOR_COMPRESSION' : 8,
'SET_COLOR_CLEAR_VALUE' : 4,
'SET_CT_WRITE' : 8,
'SET_MME_SHADOW_SCRATCH' : 256,
'SET_PIPELINE_*' : 6,
'SET_SCG_COMPUTE_SCHEDULING_PARAMETERS' : 16,
'SET_SCISSOR_*' : 16,
'SET_SHADER_PERFORMANCE_SNAPSHOT_COUNTER_VALUE*' : 8,
'SET_SHADER_PERFORMANCE_COUNTER_VALUE*' : 8,
'SET_SHADER_PERFORMANCE_COUNTER_EVENT' : 8,
'SET_SHADER_PERFORMANCE_COUNTER_CONTROL_A' : 8,
'SET_SHADER_PERFORMANCE_COUNTER_CONTROL_B' : 8,
'SET_STREAM_OUT_BUFFER_*' : 4,
'SET_STREAM_OUT_CONTROL_*' : 4,
'SET_VIEWPORT_*' : 16,
'SET_VERTEX_ATTRIBUTE_*' : 16,
'SET_VERTEX_STREAM_*' : 16,
}
METHOD_IS_FLOAT = [
'SET_BLEND_CONST_*',
'SET_DEPTH_BIAS',
'SET_SLOPE_SCALE_DEPTH_BIAS',
'SET_DEPTH_BIAS_CLAMP',
'SET_DEPTH_BOUNDS_M*',
'SET_LINE_WIDTH_FLOAT',
'SET_ALIASED_LINE_WIDTH_FLOAT',
'SET_VIEWPORT_SCALE_*',
'SET_VIEWPORT_OFFSET_*',
'SET_VIEWPORT_CLIP_MIN_Z',
'SET_VIEWPORT_CLIP_MAX_Z',
'SET_Z_CLEAR_VALUE',
]
TEMPLATE_H = Template("""\
/* parsed class ${nvcl} */
#include "nvtypes.h"
#include "${clheader}"
#include <assert.h>
#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()};
%endfor
};
static inline void
__${nvcl}_${mthd}(uint32_t *val_out, struct nv_${nvcl.lower()}_${mthd} 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
%>
%if field_width == 32:
val |= st.${field_name.lower()};
%else:
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}
%endfor
%endfor
%if len(mthddict[mthd].field_name_start) > 1:
struct nv_${nvcl.lower()}_${mthd} __data = args; ${bs}
%else:
<% field_name = next(iter(mthddict[mthd].field_name_start)).lower() %>\
struct nv_${nvcl.lower()}_${mthd} __data = { .${field_name} = (args) }; ${bs}
%endif
__${nvcl}_${mthd}(&val, __data); ${bs}
}
%if mthddict[mthd].is_array:
#define VA_${nvcl}_${mthd}(i) V_${nvcl}_${mthd}
%else:
#define VA_${nvcl}_${mthd} V_${nvcl}_${mthd}
%endif
%if mthddict[mthd].is_array:
#define P_${nvcl}_${mthd}(push, idx, args...) do { ${bs}
%else:
#define P_${nvcl}_${mthd}(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}
%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}
%else:
nv_push_val(push, ${nvcl}_${mthd}, nvk_p_ret); ${bs}
%endif
} while(0)
%endfor
const char *P_PARSE_${nvcl}_MTHD(uint16_t idx);
void P_DUMP_${nvcl}_MTHD_DATA(FILE *fp, uint16_t idx, uint32_t data,
const char *prefix);
""")
TEMPLATE_C = Template("""\
#include "${header}"
#include <stdio.h>
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:
<% continue %>
%endif
%if mthddict[mthd].is_array:
%for i in range(mthddict[mthd].array_size):
case ${nvcl}_${mthd}(${i}):
return "${nvcl}_${mthd}(${i})";
%endfor
% else:
case ${nvcl}_${mthd}:
return "${nvcl}_${mthd}";
%endif
%endfor
default:
return "unknown method";
}
}
void
P_DUMP_${nvcl}_MTHD_DATA(FILE *fp, uint16_t idx, uint32_t data,
const char *prefix)
{
uint32_t parsed;
switch (idx) {
%for mthd in mthddict:
%if mthddict[mthd].is_array and mthddict[mthd].array_size == 0:
<% continue %>
%endif
%if mthddict[mthd].is_array:
%for i in range(mthddict[mthd].array_size):
case ${nvcl}_${mthd}(${i}):
%endfor
% else:
case ${nvcl}_${mthd}:
%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
%>
%if field_width == 32:
parsed = data;
%else:
parsed = (data >> ${field_start}) & ((1u << ${field_width}) - 1);
%endif
fprintf(fp, "%s.${field_name} = ", prefix);
%if len(mthddict[mthd].field_defs[field_name]):
switch (parsed) {
%for d in mthddict[mthd].field_defs[field_name]:
case ${nvcl}_${mthd}_${field_name}_${d}:
fprintf(fp, "${d}${bs}n");
break;
%endfor
default:
fprintf(fp, "0x%x${bs}n", parsed);
break;
}
%else:
%if mthddict[mthd].is_float:
fprintf(fp, "%ff (0x%x)${bs}n", uif(parsed), parsed);
%else:
fprintf(fp, "(0x%x)${bs}n", parsed);
%endif
%endif
%endfor
break;
%endfor
default:
fprintf(fp, "%s.VALUE = 0x%x${bs}n", prefix, data);
break;
}
}
""")
def glob_match(glob, name):
if glob.endswith('*'):
return name.startswith(glob[:-1])
else:
assert '*' not in glob
return name == glob
class method(object):
@property
def array_size(self):
for (glob, value) in METHOD_ARRAY_SIZES.items():
if glob_match(glob, self.name):
return value
return 0
@property
def is_float(self):
for glob in METHOD_IS_FLOAT:
if glob_match(glob, self.name):
assert len(self.field_defs) == 1
return True
return False
def parse_header(nvcl, f):
# Simple state machine
# state 0 looking for a new method define
# state 1 looking for new fields in a method
# state 2 looking for enums for a fields in a method
# blank lines reset the state machine to 0
version = None
state = 0
mthddict = {}
curmthd = {}
for line in f:
if line.strip() == "":
state = 0
if (curmthd):
if not len(curmthd.field_name_start):
del mthddict[curmthd.name]
curmthd = {}
continue
if line.startswith("#define"):
list = line.split();
if "_cl_" in list[1]:
continue
if not list[1].startswith(nvcl):
if len(list) > 2 and list[2].startswith("0x"):
assert version is None
version = (list[1], list[2])
continue
if list[1].endswith("TYPEDEF"):
continue
if state == 2:
teststr = nvcl + "_" + curmthd.name + "_" + curfield + "_"
if ":" in list[2]:
state = 1
elif teststr in list[1]:
curmthd.field_defs[curfield][list[1].removeprefix(teststr)] = list[2]
else:
state = 1
if state == 1:
teststr = nvcl + "_" + curmthd.name + "_"
if teststr in list[1]:
if ("0x" in list[2]):
state = 1
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
state = 2
else:
if not len(curmthd.field_name_start):
del mthddict[curmthd.name]
curmthd = {}
state = 0
if state == 0:
if (curmthd):
if not len(curmthd.field_name_start):
del mthddict[curmthd.name]
teststr = nvcl + "_"
is_array = 0
if (':' in list[2]):
continue
name = list[1].removeprefix(teststr)
if name.endswith("(i)"):
is_array = 1
name = name.removesuffix("(i)")
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 = {}
mthddict[x.name] = x
curmthd = x
state = 1
return (version, mthddict)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--out-h', required=True, help='Output C header.')
parser.add_argument('--out-c', required=True, help='Output C file.')
parser.add_argument('--in-h',
help='Input class header file.',
required=True)
args = parser.parse_args()
clheader = os.path.basename(args.in_h)
nvcl = clheader
nvcl = nvcl.removeprefix("cl")
nvcl = nvcl.removesuffix(".h")
nvcl = nvcl.upper()
nvcl = "NV" + nvcl
with open(args.in_h, 'r', encoding='utf-8') as f:
(version, mthddict) = parse_header(nvcl, f)
environment = {
'clheader': clheader,
'header': os.path.basename(args.out_h),
'nvcl': nvcl,
'version': version,
'mthddict': mthddict,
'bs': '\\'
}
try:
with open(args.out_h, 'w', encoding='utf-8') as f:
f.write(TEMPLATE_H.render(**environment))
with open(args.out_c, 'w', encoding='utf-8') as f:
f.write(TEMPLATE_C.render(**environment))
except Exception:
# In the event there's an error, this imports some helpers from mako
# to print a useful stack trace and prints it, then exits with
# status 1, if python is run with debug; otherwise it just raises
# the exception
import sys
from mako import exceptions
print(exceptions.text_error_template().render(), file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()