<div dir="ltr"><div dir="auto"><br><br>On Mon, Feb 27, 2017 at 5:38 PM, Lionel Landwerlin <<a href="mailto:lionel.g.landwerlin@intel.com" target="_blank">lionel.g.landwerlin@intel.com</a><wbr>> wrote:<br>> Hey Rob,<br>><br>> Your series look pretty good. Just a tiny nit on this patch below.<br>><br>> As we've discussed in the office, I think it would be nice to have part of<br>> this work factored out in src/intel.<br>> But that can be done later on.<br>><br>> Also, I found it a bit disturbing to have the equations in polish notation.<br>> I've given a try at using a more C-like style to it. The result is here :<br>><br>> <a href="https://github.com/djdeath/mesa/commit/ebfaf7aee8efe79dc814bc7bfdcb9d74cec09d3c" target="_blank">https://github.com/djdeath/<wbr>mesa/commit/<wbr>ebfaf7aee8efe79dc814bc7bfdcb9d<wbr>74cec09d3c</a><br>><br>> It's a tiny bit less python (~20 lines) but adds a dependency on<br>> python-parsley.<br>> I also have a script to convert expression from your format to the new one.<br><br>Thanks for prototyping this it interesting to compare. We talked at length offline but putting my thoughts down here too:<br><br>Tbh I'm not sure whether this would be a good direction, considering pros and cons...<br><br>Currently Mesa isn't the only project doing codegen based on these expression, and the codegen in gputop is probably a bit more involved than Mesa's where it also generates MathML for the expressions to have something readable in the UI. We also have an internal 'MDAPI' library which  generate a class/structure based description of these configs. At least considering that we have fairly decent support for rendering equations in gputop it could be good compare implementation details e.g. with regards to avoiding redundant braces considering the precedence of operators if we wanted to go in this direction. (details can be found in gputop/scripts/gputop-oa-codegen.py if curious).<br><div dir="auto"><br>The use of RPN wasn't really a choice by me given that the internal MDAPI XML files that these XML files are generated from themselves have RPN based equations. There are some minor differences to how raw A/B/C counters are read but generally I see some value in minimizing divergence from what VPG maintains internally considering the effort involved in maintaining the conversion script itself and the risk that it becomes hard to compare the two where we're seeing problems with particular counters and want to cross reference what we're doing with what's VPG's driver does on Windows.<br><br>For reference; probably the best place to consider maintaining any code to convert the expression format would be as a patch to gputop/scripts/mdapi-xml-<wbr>convert.py, but it should be considered that these files are shared so I'd have to update these other generators if we made a backwards-incompatible change:<br></div><div dir="auto">- kernel config descriptors</div><div dir="auto">- gputop normalization code (almost identical to Mesa)</div><div dir="auto">- gputop MathML equation descriptions</div><div dir="auto">- MDAPI class based description of counters</div><div dir="auto"><br>To my mind RPN is so simple to 'parse' for codegen that I can barely bring myself to call it parsing - you just tokenize based on spaces and you're done and then you traverse the operators by pushing tokens/operands to a stack, when you hit an operator you pop the operands and push the result. With RPN there's no precedence ambiguity to consider and with the explicitly typed operations like UDIV vs FDIV there's no operand type ambiguity either. I'd certainly be nervous of squashing these both into a single '/' operator and relying on C  type conversion rules. The risk that there are some corner cases where the results won't match the original equations without more explicit type casting seems quite high with the current prototype.<br><br>The primary use for these expressions is for codegen and so I think machine readability and lack of ambiguity is much more important than human readability within the XML files.</div><div dir="auto"><br></div><div dir="auto">RPN seems very well suited here once you're familiar with evaluating RPN as a series of operand pushes and pops for operations.<br></div><div dir="auto"><br></div><div dir="auto">For human readability then I think what gputop does is a pretty decent starting point, being careful with bracketing and supporting mouse overlay descriptions of the different variables, and nice typography and layouting.</div><div dir="auto"><br></div><div dir="auto">It's subjective to say which is more readable to handle in code but personally I find embedding a formal grammar for expressions, introducing the need for operator precedence and type conversion rules to be more of a mental load. The comparison of line count imho doesn't account for the density of the grammar being embedded which needs to understood and maintained across the different codegen scripts. The previous code effectively duplicated the traversal of the RPN expressions since it barely seemed worth factoring out the push and pop of tokens that form the parsing loop. There was also lots of white space between the operator methods.<br><br></div><div dir="auto">For now I think it's nice to see this alternative to compare, but don't think it's the best trade-off to prioritize human readability of the equations in the xml files over over, unambiguous, machine readability. Considering the complexity of some of the equations there's just no way they will ever be practically readable within the xml files and don't really see much alternative to using a typesetting library like mathjax to render the equations e.g. for a UI.<br><br></div><div>Definitely thanks for doing this experiment though!<br><br></div><div>Br,<br></div><div>- Robert<br></div><div dir="auto"><br>><br>> Regardless, this series is :<br>><br>> Reviewed-by: Lionel Landwerlin <<a href="mailto:lionel.g.landwerlin@intel.com" target="_blank">lionel.g.landwerlin@intel.com</a><wbr>><br>><br>> Thanks!<br>><br>> -<br>> Lionel<br>><br>><br>> On 24/02/17 13:58, Robert Bragg wrote:<br>>><br>>> Avoiding lots of error prone boilerplate and easing our ability to add +<br>>> maintain support for multiple OA performance counter queries for each<br>>> generation:<br>>><br>>> This adds a python script to generate code for building up<br>>> performance_queries from the metric sets and counters described in<br>>> brw_oa_hsw.xml as well as functions to normalize each counter based on<br>>> the RPN expressions given.<br>>><br>>> Although the XML file currently only includes a single metric set, the<br>>> code generated assumes there could be many sets.<br>>><br>>> The metrics as described in XML get translated into C structures<br>>> which are registered in a brw->perfquery.oa_metrics_<wbr>table hash table<br>>> keyed by the GUID of the metric set in XML.<br>>><br>>> Signed-off-by: Robert Bragg <<a href="mailto:robert@sixbynine.org" target="_blank">robert@sixbynine.org</a>><br>>> ---<br>>>   src/mesa/drivers/dri/i965/<wbr>Makefile.am      |  15 +-<br>>>   src/mesa/drivers/dri/i965/<wbr>Makefile.sources |   2 +<br>>>   src/mesa/drivers/dri/i965/brw_<wbr>oa.py        | 543<br>>> +++++++++++++++++++++++++++++<br>>>   3 files changed, 559 insertions(+), 1 deletion(-)<br>>>   create mode 100644 src/mesa/drivers/dri/i965/brw_<wbr>oa.py<br>>><br>>> diff --git a/src/mesa/drivers/dri/i965/<wbr>Makefile.am<br>>> b/src/mesa/drivers/dri/i965/<wbr>Makefile.am<br>>> index f87fa67ef8..0130afff5f 100644<br>>> --- a/src/mesa/drivers/dri/i965/<wbr>Makefile.am<br>>> +++ b/src/mesa/drivers/dri/i965/<wbr>Makefile.am<br>>> @@ -93,7 +93,9 @@ BUILT_SOURCES = $(i965_compiler_GENERATED_<wbr>FILES)<br>>>   CLEANFILES = $(BUILT_SOURCES)<br>>>     EXTRA_DIST = \<br>>> -       brw_nir_trig_workarounds.py<br>>> +       brw_nir_trig_workarounds.py \<br>>> +       brw_oa_hsw.xml \<br>>> +       brw_oa.py<br>>>     TEST_LIBS = \<br>>>         <a href="http://libi965_compiler.la" target="_blank">libi965_compiler.la</a> \<br>>> @@ -169,3 +171,14 @@ test_eu_validate_SOURCES = \<br>>>   test_eu_validate_LDADD = \<br>>>         $(top_builddir)/src/gtest/<a href="http://libgtest.la" target="_blank">libg<wbr>test.la</a> \<br>>>         $(TEST_LIBS)<br>>> +<br>>> +BUILT_SOURCES = \<br>>> +       brw_oa_hsw.h \<br>>> +       brw_oa_hsw.c<br>>> +<br>>> +brw_oa_hsw.h brw_oa_hsw.c: brw_oa_hsw.xml brw_oa.py Makefile<br>>> +       $(PYTHON2) $(PYTHON_FLAGS) $(srcdir)/brw_oa.py \<br>>> +          --header=$(builddir)/brw_oa_<wbr>hsw.h \<br>>> +          --code=$(builddir)/brw_oa_hsw.<wbr>c \<br>>> +          --chipset="hsw" \<br>>> +          $(srcdir)/brw_oa_hsw.xml<br>>> diff --git a/src/mesa/drivers/dri/i965/<wbr>Makefile.sources<br>>> b/src/mesa/drivers/dri/i965/<wbr>Makefile.sources<br>>> index 5278e86339..60acd15d41 100644<br>>> --- a/src/mesa/drivers/dri/i965/<wbr>Makefile.sources<br>>> +++ b/src/mesa/drivers/dri/i965/<wbr>Makefile.sources<br>>> @@ -135,6 +135,8 @@ i965_FILES = \<br>>>         brw_nir_uniforms.cpp \<br>>>         brw_object_purgeable.c \<br>>>         brw_pipe_control.c \<br>>> +       brw_oa_hsw.h \<br>>> +       brw_oa_hsw.c \<br>>>         brw_performance_query.h \<br>>>         brw_performance_query.c \<br>>>         brw_program.c \<br>>> diff --git a/src/mesa/drivers/dri/i965/<wbr>brw_oa.py<br>>> b/src/mesa/drivers/dri/i965/<wbr>brw_oa.py<br>>> new file mode 100644<br>>> index 0000000000..2c622531af<br>>> --- /dev/null<br>>> +++ b/src/mesa/drivers/dri/i965/<wbr>brw_oa.py<br>>> @@ -0,0 +1,543 @@<br>>> +#!/usr/bin/env python2<br>>> +#<br>>> +# Copyright (c) 2015 Intel Corporation<br>>> +#<br>>> +# Permission is hereby granted, free of charge, to any person obtaining a<br>>> +# copy of this software and associated documentation files (the<br>>> "Software"),<br>>> +# to deal in the Software without restriction, including without<br>>> limitation<br>>> +# the rights to use, copy, modify, merge, publish, distribute,<br>>> sublicense,<br>>> +# and/or sell copies of the Software, and to permit persons to whom the<br>>> +# Software is furnished to do so, subject to the following conditions:<br>>> +#<br>>> +# The above copyright notice and this permission notice (including the<br>>> next<br>>> +# paragraph) shall be included in all copies or substantial portions of<br>>> the<br>>> +# Software.<br>>> +#<br>>> +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS<br>>> OR<br>>> +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF<br>>> MERCHANTABILITY,<br>>> +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT<br>>> SHALL<br>>> +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR<br>>> OTHER<br>>> +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING<br>>> +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER<br>>> DEALINGS<br>>> +# IN THE SOFTWARE.<br>>> +<br>>> +import xml.etree.ElementTree as ET<br>>> +import argparse<br>>> +import sys<br>>> +<br>>> +def print_err(*args):<br>>> +    sys.stderr.write(' '.join(map(str,args)) + '\n')<br>>> +<br>>> +c_file = None<br>>> +_c_indent = 0<br>>> +<br>>> +def c(*args):<br>>> +    if c_file:<br>>> +        code = ' '.join(map(str,args))<br>>> +        for line in code.splitlines():<br>>> +            text = ''.rjust(_c_indent) + line<br>>> +            c_file.write(text.rstrip() + "\n")<br>>> +<br>>> +# indented, but no trailing newline...<br>>> +def c_line_start(code):<br>>> +    if c_file:<br>>> +        c_file.write(''.rjust(_c_<wbr>indent) + code)<br>>> +def c_raw(code):<br>>> +    if c_file:<br>>> +        c_file.write(code)<br>>> +<br>>> +def c_indent(n):<br>>> +    global _c_indent<br>>> +    _c_indent = _c_indent + n<br>>> +def c_outdent(n):<br>>> +    global _c_indent<br>>> +    _c_indent = _c_indent - n<br>>> +<br>>> +header_file = None<br>>> +_h_indent = 0<br>>> +<br>>> +def h(*args):<br>>> +    if header_file:<br>>> +        code = ' '.join(map(str,args))<br>>> +        for line in code.splitlines():<br>>> +            text = ''.rjust(_h_indent) + line<br>>> +            header_file.write(text.rstrip(<wbr>) + "\n")<br>>> +<br>>> +def h_indent(n):<br>>> +    global _c_indent<br>>> +    _h_indent = _h_indent + n<br>>> +def h_outdent(n):<br>>> +    global _c_indent<br>>> +    _h_indent = _h_indent - n<br>>> +<br>>> +<br>>> +def emit_fadd(tmp_id, args):<br>>> +    c("double tmp" + str(tmp_id) +" = " + args[1] + " + " + args[0] +<br>>> ";")<br>>> +    return tmp_id + 1<br>>> +<br>>> +# Be careful to check for divide by zero...<br>>> +def emit_fdiv(tmp_id, args):<br>>> +    c("double tmp" + str(tmp_id) +" = " + args[1] + ";")<br>>> +    c("double tmp" + str(tmp_id + 1) +" = " + args[0] + ";")<br>>> +    c("double tmp" + str(tmp_id + 2) +" = tmp" + str(tmp_id + 1)  + " ?<br>>> tmp" + str(tmp_id) + " / tmp" + str(tmp_id + 1) + " : 0;")<br>>> +    return tmp_id + 3<br>>> +<br>>> +def emit_fmax(tmp_id, args):<br>>> +    c("double tmp" + str(tmp_id) +" = " + args[1] + ";")<br>>> +    c("double tmp" + str(tmp_id + 1) +" = " + args[0] + ";")<br>>> +    c("double tmp" + str(tmp_id + 2) +" = MAX(tmp" + str(tmp_id) + ",<br>>> tmp" + str(tmp_id + 1) + ");")<br>>> +    return tmp_id + 3<br>>> +<br>>> +def emit_fmul(tmp_id, args):<br>>> +    c("double tmp" + str(tmp_id) +" = " + args[1] + " * " + args[0] +<br>>> ";")<br>>> +    return tmp_id + 1<br>>> +<br>>> +def emit_fsub(tmp_id, args):<br>>> +    c("double tmp" + str(tmp_id) +" = " + args[1] + " - " + args[0] +<br>>> ";")<br>>> +    return tmp_id + 1<br>>> +<br>>> +def emit_read(tmp_id, args):<br>>> +    type = args[1].lower()<br>>> +    c("uint64_t tmp" + str(tmp_id) + " = accumulator[query->" + type +<br>>> "_offset + " + args[0] + "];")<br>>> +    return tmp_id + 1<br>>> +<br>>> +def emit_uadd(tmp_id, args):<br>>> +    c("uint64_t tmp" + str(tmp_id) +" = " + args[1] + " + " + args[0] +<br>>> ";")<br>>> +    return tmp_id + 1<br>>> +<br>>> +# Be careful to check for divide by zero...<br>>> +def emit_udiv(tmp_id, args):<br>>> +    c("uint64_t tmp" + str(tmp_id) +" = " + args[1] + ";")<br>>> +    c("uint64_t tmp" + str(tmp_id + 1) +" = " + args[0] + ";")<br>>> +    c("uint64_t tmp" + str(tmp_id + 2) +" = tmp" + str(tmp_id + 1)  + " ?<br>>> tmp" + str(tmp_id) + " / tmp" + str(tmp_id + 1) + " : 0;")<br>>> +    return tmp_id + 3<br>>> +<br>>> +def emit_umul(tmp_id, args):<br>>> +    c("uint64_t tmp" + str(tmp_id) +" = " + args[1] + " * " + args[0] +<br>>> ";")<br>>> +    return tmp_id + 1<br>>> +<br>>> +def emit_usub(tmp_id, args):<br>>> +    c("uint64_t tmp" + str(tmp_id) +" = " + args[1] + " - " + args[0] +<br>>> ";")<br>>> +    return tmp_id + 1<br>>> +<br>>> +def emit_umin(tmp_id, args):<br>>> +    c("uint64_t tmp" + str(tmp_id) +" = MIN(" + args[1] + ", " + args[0]<br>>> + ");")<br>>> +    return tmp_id + 1<br>>> +<br>>> +ops = {}<br>>> +#             (n operands, emitter)<br>>> +ops["FADD"] = (2, emit_fadd)<br>>> +ops["FDIV"] = (2, emit_fdiv)<br>>> +ops["FMAX"] = (2, emit_fmax)<br>>> +ops["FMUL"] = (2, emit_fmul)<br>>> +ops["FSUB"] = (2, emit_fsub)<br>>> +ops["READ"] = (2, emit_read)<br>>> +ops["UADD"] = (2, emit_uadd)<br>>> +ops["UDIV"] = (2, emit_udiv)<br>>> +ops["UMUL"] = (2, emit_umul)<br>>> +ops["USUB"] = (2, emit_usub)<br>>> +ops["UMIN"] = (2, emit_umin)<br>>> +<br>>> +def brkt(subexp):<br>>> +    if " " in subexp:<br>>> +        return "(" + subexp + ")"<br>>> +    else:<br>>> +        return subexp<br>>> +<br>>> +def splice_bitwise_and(args):<br>>> +    return brkt(args[1]) + " & " + brkt(args[0])<br>>> +<br>>> +def splice_logical_and(args):<br>>> +    return brkt(args[1]) + " && " + brkt(args[0])<br>>> +<br>>> +def splice_ult(args):<br>>> +    return brkt(args[1]) + " < " + brkt(args[0])<br>>> +<br>>> +def splice_ugte(args):<br>>> +    return brkt(args[1]) + " >= " + brkt(args[0])<br>>> +<br>>> +exp_ops = {}<br>>> +#                 (n operands, splicer)<br>>> +exp_ops["AND"]  = (2, splice_bitwise_and)<br>>> +exp_ops["UGTE"] = (2, splice_ugte)<br>>> +exp_ops["ULT"]  = (2, splice_ult)<br>>> +exp_ops["&&"]   = (2, splice_logical_and)<br>>> +<br>>> +<br>>> +hw_vars = {}<br>>> +hw_vars["$EuCoresTotalCount"] = "brw->perfquery.sys_vars.n_<wbr>eus"<br>>> +hw_vars["$EuSlicesTotalCount"<wbr>] = "brw->perfquery.sys_vars.n_eu_<wbr>slices"<br>>> +hw_vars["$<wbr>EuSubslicesTotalCount"] =<br>>> "brw->perfquery.sys_vars.n_eu_<wbr>sub_slices"<br>>> +hw_vars["$EuThreadsCount"] = "brw->perfquery.sys_vars.eu_<wbr>threads_count"<br>>> +hw_vars["$SliceMask"] = "brw->perfquery.sys_vars.<wbr>slice_mask"<br>>> +hw_vars["$SubsliceMask"] = "brw->perfquery.sys_vars.<wbr>subslice_mask"<br>>> +hw_vars["$<wbr>GpuTimestampFrequency"] =<br>>> "brw->perfquery.sys_vars.<wbr>timestamp_frequency"<br>>> +hw_vars["$GpuMinFrequency"] = "brw->perfquery.sys_vars.gt_<wbr>min_freq"<br>>> +hw_vars["$GpuMaxFrequency"] = "brw->perfquery.sys_vars.gt_<wbr>max_freq"<br>>> +<br>>> +counter_vars = {}<br>><br>><br>> I guess you can remove this counter_vars as it's given as an argument to the<br>> following functions.<br>><br>><br>>> +<br>>> +def output_rpn_equation_code(set, counter, equation, counter_vars):<br>>> +    c("/* RPN equation: " + equation + " */")<br>>> +    tokens = equation.split()<br>>> +    stack = []<br>>> +    tmp_id = 0<br>>> +    tmp = None<br>>> +<br>>> +    for token in tokens:<br>>> +        stack.append(token)<br>>> +        while stack and stack[-1] in ops:<br>>> +            op = stack.pop()<br>>> +            argc, callback = ops[op]<br>>> +            args = []<br>>> +            for i in range(0, argc):<br>>> +                operand = stack.pop()<br>>> +                if operand[0] == "$":<br>>> +                    if operand in hw_vars:<br>>> +                        operand = hw_vars[operand]<br>>> +                    elif operand in counter_vars:<br>>> +                        reference = counter_vars[operand]<br>>> +                        operand = read_funcs[operand[1:]] + "(brw, query,<br>>> accumulator)"<br>>> +                    else:<br>>> +                        raise Exception("Failed to resolve variable " +<br>>> operand + " in equation " + equation + " for " + set.get('name') + " :: " +<br>>> counter.get('name'));<br>>> +                args.append(operand)<br>>> +<br>>> +            tmp_id = callback(tmp_id, args)<br>>> +<br>>> +            tmp = "tmp" + str(tmp_id - 1)<br>>> +            stack.append(tmp)<br>>> +<br>>> +    if len(stack) != 1:<br>>> +        raise Exception("Spurious empty rpn code for " + set.get('name')<br>>> + " :: " +<br>>> +                counter.get('name') + ".\nThis is probably due to some<br>>> unhandled RPN function, in the equation \"" +<br>>> +                equation + "\"")<br>>> +<br>>> +    value = stack.pop()<br>>> +<br>>> +    if value in hw_vars:<br>>> +        value = hw_vars[value];<br>>> +<br>>> +    c("\nreturn " + value + ";")<br>>> +<br>>> +def splice_rpn_expression(set, counter, expression):<br>>> +    tokens = expression.split()<br>>> +    stack = []<br>>> +<br>>> +    for token in tokens:<br>>> +        stack.append(token)<br>>> +        while stack and stack[-1] in exp_ops:<br>>> +            op = stack.pop()<br>>> +            argc, callback = exp_ops[op]<br>>> +            args = []<br>>> +            for i in range(0, argc):<br>>> +                operand = stack.pop()<br>>> +                if operand[0] == "$":<br>>> +                    if operand in hw_vars:<br>>> +                        operand = hw_vars[operand]<br>>> +                    else:<br>>> +                        raise Exception("Failed to resolve variable " +<br>>> operand + " in expression " + expression + " for " + set.get('name') + " ::<br>>> " + counter.get('name'));<br>>> +                args.append(operand)<br>>> +<br>>> +            subexp = callback(args)<br>>> +<br>>> +            stack.append(subexp)<br>>> +<br>>> +    if len(stack) != 1:<br>>> +        raise Exception("Spurious empty rpn expression for " +<br>>> set.get('name') + " :: " +<br>>> +                counter.get('name') + ".\nThis is probably due to some<br>>> unhandled RPN operation, in the expression \"" +<br>>> +                expression + "\"")<br>>> +<br>>> +    return stack.pop()<br>>> +<br>>> +def output_counter_read(set, counter, counter_vars):<br>>> +    c("\n")<br>>> +    c("/* " + set.get('name') + " :: " + counter.get('name') + " */")<br>>> +    ret_type = counter.get('data_type')<br>>> +    if ret_type == "uint64":<br>>> +        ret_type = "uint64_t"<br>>> +<br>>> +    c("static " + ret_type)<br>>> +    read_sym = set.get('chipset').lower() + "__" +<br>>> set.get('underscore_name') + "__" + counter.get('underscore_name') +<br>>> "__read"<br>>> +    c(read_sym + "(struct brw_context *brw,\n")<br>>> +    c_indent(len(read_sym) + 1)<br>>> +    c("const struct brw_perf_query_info *query,\n")<br>>> +    c("uint64_t *accumulator)\n")<br>>> +    c_outdent(len(read_sym) + 1)<br>>> +<br>>> +    c("{")<br>>> +    c_indent(3)<br>>> +<br>>> +    output_rpn_equation_code(set, counter, counter.get('equation'),<br>>> counter_vars)<br>>> +<br>>> +    c_outdent(3)<br>>> +    c("}")<br>>> +<br>>> +    return read_sym<br>>> +<br>>> +def output_counter_max(set, counter, counter_vars):<br>>> +    max_eq = counter.get('max_equation')<br>>> +<br>>> +    if not max_eq:<br>>> +        return "0; /* undefined */"<br>>> +<br>>> +    try:<br>>> +        val = float(max_eq)<br>>> +        return max_eq + ";"<br>>> +    except:<br>>> +        pass<br>>> +<br>>> +    # We can only report constant maximum values via<br>>> INTEL_performance_query<br>>> +    for token in max_eq.split():<br>>> +        if token[0] == '$' and token not in hw_vars:<br>>> +            return "0; /* unsupported (varies over time) */"<br>>> +<br>>> +    c("\n")<br>>> +    c("/* " + set.get('name') + " :: " + counter.get('name') + " */")<br>>> +    ret_type = counter.get('data_type')<br>>> +    if ret_type == "uint64":<br>>> +        ret_type = "uint64_t"<br>>> +<br>>> +    c("static " + ret_type)<br>>> +    max_sym = set.get('chipset').lower() + "__" +<br>>> set.get('underscore_name') + "__" + counter.get('underscore_name') + "__max"<br>>> +    c(max_sym + "(struct brw_context *brw)\n")<br>>> +<br>>> +    c("{")<br>>> +    c_indent(3)<br>>> +<br>>> +    output_rpn_equation_code(set, counter, max_eq, counter_vars)<br>>> +<br>>> +    c_outdent(3)<br>>> +    c("}")<br>>> +<br>>> +    return max_sym + "(brw);"<br>>> +<br>>> +c_type_sizes = { "uint32_t": 4, "uint64_t": 8, "float": 4, "double": 8,<br>>> "bool": 4 }<br>>> +def sizeof(c_type):<br>>> +    return c_type_sizes[c_type]<br>>> +<br>>> +def pot_align(base, pot_alignment):<br>>> +    return (base + pot_alignment - 1) & ~(pot_alignment - 1);<br>>> +<br>>> +semantic_type_map = {<br>>> +    "duration": "raw",<br>>> +    "ratio": "event"<br>>> +    }<br>>> +<br>>> +def output_counter_report(set, counter, current_offset):<br>>> +    data_type = counter.get('data_type')<br>>> +    data_type_uc = data_type.upper()<br>>> +    c_type = data_type<br>>> +<br>>> +    if "uint" in c_type:<br>>> +        c_type = c_type + "_t"<br>>> +<br>>> +    semantic_type = counter.get('semantic_type')<br>>> +    if semantic_type in semantic_type_map:<br>>> +        semantic_type = semantic_type_map[semantic_<wbr>type]<br>>> +<br>>> +    semantic_type_uc = semantic_type.upper()<br>>> +<br>>> +    c("\n")<br>>> +<br>>> +    availability = counter.get('availability')<br>>> +    if availability:<br>>> +        expression = splice_rpn_expression(set, counter, availability)<br>>> +        lines = expression.split(' && ')<br>>> +        n_lines = len(lines)<br>>> +        if n_lines == 1:<br>>> +            c("if (" + lines[0] + ") {")<br>>> +        else:<br>>> +            c("if (" + lines[0] + " &&")<br>>> +            c_indent(4)<br>>> +            for i in range(1, (n_lines - 1)):<br>>> +                c(lines[i] + " &&")<br>>> +            c(lines[(n_lines - 1)] + ") {")<br>>> +            c_outdent(4)<br>>> +        c_indent(3)<br>>> +<br>>> +    c("counter = &query->counters[query->n_<wbr>counters++];\n")<br>>> +    c("counter->oa_counter_read_" + data_type + " = " +<br>>> read_funcs[counter.get('<wbr>symbol_name')] + ";\n")<br>>> +    c("counter->name = \"" + counter.get('name') + "\";\n")<br>>> +    c("counter->desc = \"" + counter.get('description') + "\";\n")<br>>> +    c("counter->type = GL_PERFQUERY_COUNTER_" + semantic_type_uc +<br>>> "_INTEL;\n")<br>>> +    c("counter->data_type = GL_PERFQUERY_COUNTER_DATA_" + data_type_uc +<br>>> "_INTEL;\n")<br>>> +    c("counter->raw_max = " + max_values[counter.get('<wbr>symbol_name')] +<br>>> "\n")<br>>> +<br>>> +    current_offset = pot_align(current_offset, sizeof(c_type))<br>>> +    c("counter->offset = " + str(current_offset) + ";\n")<br>>> +    c("counter->size = sizeof(" + c_type + ");\n")<br>>> +<br>>> +    if availability:<br>>> +        c_outdent(3);<br>>> +        c("}")<br>>> +<br>>> +    return current_offset + sizeof(c_type)<br>>> +<br>>> +parser = argparse.ArgumentParser()<br>>> +parser.add_argument("xml", help="XML description of metrics")<br>>> +parser.add_argument("--<wbr>header", help="Header file to write")<br>>> +parser.add_argument("--code", help="C file to write")<br>>> +parser.add_argument("--<wbr>chipset", help="Chipset to generate code for")<br>>> +<br>>> +args = parser.parse_args()<br>>> +<br>>> +chipset = args.chipset.lower()<br>>> +<br>>> +if args.header:<br>>> +    header_file = open(args.header, 'w')<br>>> +<br>>> +if args.code:<br>>> +    c_file = open(args.code, 'w')<br>>> +<br>>> +tree = ET.parse(args.xml)<br>>> +<br>>> +<br>>> +copyright = """/* Autogenerated file, DO NOT EDIT manually!<br>>> + *<br>>> + * Copyright (c) 2015 Intel Corporation<br>>> + *<br>>> + * Permission is hereby granted, free of charge, to any person obtaining<br>>> a<br>>> + * copy of this software and associated documentation files (the<br>>> "Software"),<br>>> + * to deal in the Software without restriction, including without<br>>> limitation<br>>> + * the rights to use, copy, modify, merge, publish, distribute,<br>>> sublicense,<br>>> + * and/or sell copies of the Software, and to permit persons to whom the<br>>> + * Software is furnished to do so, subject to the following conditions:<br>>> + *<br>>> + * The above copyright notice and this permission notice (including the<br>>> next<br>>> + * paragraph) shall be included in all copies or substantial portions of<br>>> the<br>>> + * Software.<br>>> + *<br>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,<br>>> EXPRESS OR<br>>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF<br>>> MERCHANTABILITY,<br>>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT<br>>> SHALL<br>>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR<br>>> OTHER<br>>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,<br>>> ARISING<br>>> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER<br>>> + * DEALINGS IN THE SOFTWARE.<br>>> + */<br>>> +<br>>> +"""<br>>> +<br>>> +h(copyright)<br>>> +h("""#pragma once<br>>> +<br>>> +struct brw_context;<br>>> +<br>>> +""")<br>>> +<br>>> +c(copyright)<br>>> +c(<br>>> +"""<br>>> +#include <stdint.h><br>>> +#include <stdbool.h><br>>> +<br>>> +#include "util/hash_table.h"<br>>> +<br>>> +""")<br>>> +<br>>> +c("#include \"brw_oa_" + chipset + ".h\"")<br>>> +<br>>> +c(<br>>> +"""<br>>> +#include "brw_context.h"<br>>> +#include "brw_performance_query.h"<br>>> +<br>>> +<br>>> +#define MIN(a, b) ((a < b) ? (a) : (b))<br>>> +#define MAX(a, b) ((a > b) ? (a) : (b))<br>>> +<br>>> +""")<br>>> +<br>>> +for set in tree.findall(".//set"):<br>>> +    max_values = {}<br>>> +    read_funcs = {}<br>>> +    counter_vars = {}<br>>> +    counters = set.findall("counter")<br>>> +<br>>> +    assert set.get('chipset').lower() == chipset<br>>> +<br>>> +    for counter in counters:<br>>> +        empty_vars = {}<br>>> +        read_funcs[counter.get('<wbr>symbol_name')] = output_counter_read(set,<br>>> counter, counter_vars)<br>>> +        max_values[counter.get('<wbr>symbol_name')] = output_counter_max(set,<br>>> counter, empty_vars)<br>>> +        counter_vars["$" + counter.get('symbol_name')] = counter<br>>> +<br>>> +<br>>> +    c("\nstatic struct brw_perf_query_counter " + chipset + "_" +<br>>> set.get('underscore_name') + "_query_counters[" + str(len(counters)) +<br>>> "];\n")<br>>> +    c("static struct brw_perf_query_info " + chipset + "_" +<br>>> set.get('underscore_name') + "_query = {\n")<br>>> +    c_indent(3)<br>>> +<br>>> +    c(".kind = OA_COUNTERS,\n")<br>>> +    c(".name = \"" + set.get('name') + "\",\n")<br>>> +    c(".guid = \"" + set.get('hw_config_guid') + "\",\n")<br>>> +<br>>> +    c(".counters = " + chipset + "_" + set.get('underscore_name') +<br>>> "_query_counters,")<br>>> +    c(".n_counters = 0,")<br>>> +    c(".oa_metrics_set_id = 0, /* determined at runtime, via sysfs */")<br>>> +<br>>> +    if chipset == "hsw":<br>>> +        c(""".oa_format = I915_OA_FORMAT_A45_B8_C8,<br>>> +<br>>> +/* Accumulation buffer offsets... */<br>>> +.gpu_time_offset = 0,<br>>> +.a_offset = 1,<br>>> +.b_offset = 46,<br>>> +.c_offset = 54,<br>>> +""")<br>>> +    else:<br>>> +        c(""".oa_format = I915_OA_FORMAT_A32u40_A4u32_<wbr>B8_C8,<br>>> +<br>>> +/* Accumulation buffer offsets... */<br>>> +.gpu_time_offset = 0,<br>>> +.gpu_clock_offset = 1,<br>>> +.a_offset = 2,<br>>> +.b_offset = 38,<br>>> +.c_offset = 46,<br>>> +""")<br>>> +<br>>> +    c_outdent(3)<br>>> +    c("};\n")<br>>> +<br>>> +    c("\nstatic void\n")<br>>> +    c("register_" + set.get('underscore_name') + "_counter_query(struct<br>>> brw_context *brw)\n")<br>>> +    c("{\n")<br>>> +    c_indent(3)<br>>> +<br>>> +    c("static struct brw_perf_query_info *query = &" + chipset + "_" +<br>>> set.get('underscore_name') + "_query;\n")<br>>> +    c("struct brw_perf_query_counter *counter;\n")<br>>> +<br>>> +    c("\n")<br>>> +    c("/* Note: we're assuming there can't be any variation in the<br>>> definition ")<br>>> +    c(" * of a query between contexts so it's ok to describe a query<br>>> within a ")<br>>> +    c(" * global variable which only needs to be initialized once... */")<br>>> +    c("\nif (!query->data_size) {")<br>>> +    c_indent(3)<br>>> +<br>>> +    offset = 0<br>>> +    for counter in counters:<br>>> +        offset = output_counter_report(set, counter, offset)<br>>> +<br>>> +<br>>> +    c("\nquery->data_size = counter->offset + counter->size;\n")<br>>> +<br>>> +    c_outdent(3)<br>>> +    c("}");<br>>> +<br>>> +    c("\n_mesa_hash_table_insert(<wbr>brw->perfquery.oa_metrics_<wbr>table,<br>>> query->guid, query);")<br>>> +<br>>> +    c_outdent(3)<br>>> +    c("}\n")<br>>> +<br>>> +h("void brw_oa_register_queries_" + chipset + "(struct brw_context<br>>> *brw);\n")<br>>> +<br>>> +c("\nvoid")<br>>> +c("brw_oa_register_queries_" + chipset + "(struct brw_context *brw)")<br>>> +c("{")<br>>> +c_indent(3)<br>>> +<br>>> +for set in tree.findall(".//set"):<br>>> +    c("register_" + set.get('underscore_name') + "_counter_query(brw);")<br>>> +<br>>> +c_outdent(3)<br>>> +c("}")<br>>> +<br>><br>><br>></div></div>
</div>