I have a couple of comments, but they're just suggestions. On Monday, December 15, 2014 10:12:54 PM Jason Ekstrand wrote: > This commit builds on the nir_search.h infastructure by adds a bit of > python code that makes it stupid easy to write an algebraic transformation > pass. The nir_algebraic.py file contains four python classes that > correspond directly to the datastructures in nir_search.c and allow you to > easily generate the C code to represent them. Given a list of > search-and-replace operations, it can then generate a function that applies > those transformations to a shader. > > The transformations can be specified manually, or they can be specified > using nested tuples. The nested tuples make a neat little language for > specifying expression trees and search-and-replace operations in a very > readable and easy-to-edit fasion. > > The generated code is also fairly efficient. Insteady of blindly calling > nir_replace_instr with every single transformation and on every single > instruction, it uses a switch statement on the instruction opcode to do a > first-order culling and only calls nir_replace_instr if the opcode is known > to match the first opcode in the search expression. > --- > src/glsl/nir/nir_algebraic.py | 249 > ++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 249 insertions(+) > create mode 100644 src/glsl/nir/nir_algebraic.py > > diff --git a/src/glsl/nir/nir_algebraic.py b/src/glsl/nir/nir_algebraic.py > new file mode 100644 > index 0000000..7718a6d > --- /dev/null > +++ b/src/glsl/nir/nir_algebraic.py > @@ -0,0 +1,249 @@ > +#! /usr/bin/env python > +# > +# Copyright (C) 2014 Intel Corporation > +# > +# Permission is hereby granted, free of charge, to any person obtaining a > +# copy of this software and associated documentation files (the "Software"), > +# to deal in the Software without restriction, including without limitation > +# the rights to use, copy, modify, merge, publish, distribute, sublicense, > +# and/or sell copies of the Software, and to permit persons to whom the > +# Software is furnished to do so, subject to the following conditions: > +# > +# The above copyright notice and this permission notice (including the next > +# paragraph) shall be included in all copies or substantial portions of the > +# Software. > +# > +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING > +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER > DEALINGS > +# IN THE SOFTWARE. > +# > +# Authors: > +# Jason Ekstrand (ja...@jlekstrand.net) > + > +import itertools, struct, sys, mako.template
It's idiomatic to have one import per line > + > +# Represents a set of variables, each with a unique id > +class VarSet(object): > + def __init__(self): > + self.names = {} > + self.ids = itertools.count() > + > + def __getitem__(self, name): > + if name not in self.names: > + self.names[name] = self.ids.next() > + > + return self.names[name] > + > +class Value(object): > + @staticmethod > + def create(val, name_base, varset): > + if isinstance(val, tuple): > + return Expression(val, name_base, varset) > + elif isinstance(val, Expression): > + return val > + elif isinstance(val, str): (str, unicode) > + return Variable(val, name_base, varset) > + elif isinstance(val, (bool, int, long, float)): > + return Constant(val, name_base) > + > + __template = mako.template.Template(""" > +static const ${val.c_type()} ${val.name} = { > + { ${val.type_enum()} }, > +% if isinstance(val, Constant): > + { ${hex(val)} /* ${val.value} */ }, > +% elif isinstance(val, Variable): > + ${val.index}, /* ${val.var_name} */ > +% elif isinstance(val, Expression): > + nir_op_${val.opcode}, > + { ${', '.join([src.c_ptr() for src in val.sources])} }, > +% endif > +};""") > + > + def __init__(self, name, type_str): > + self.name = name > + self.type_str = type_str > + > + def type_enum(self): > + return "nir_search_value_" + self.type_str You could use the property decorator on these (the one above and two below) to access them as attributes rather than as functions > + > + def c_type(self): > + return "nir_search_" + self.type_str > + > + def c_ptr(self): > + return "&{0}.value".format(self.name) > + > + def render(self): > + return self.__template.render(val=self, > + Constant=Constant, > + Variable=Variable, > + Expression=Expression) > + > +class Constant(Value): > + def __init__(self, val, name): > + Value.__init__(self, name, "constant") > + self.value = val > + > + def __hex__(self): > + # Even if it's an integer, we still need to unpack as an unsigned > + # int. This is because, without C99, we can only assign to the first > + # element of a union in an initializer. > + if isinstance(self.value, (bool)): > + return 'NIR_TRUE' if self.value else 'NIR_FALSE' > + if isinstance(self.value, (int, long)): > + return hex(struct.unpack('I', struct.pack('i', self.value))[0]) > + elif isinstance(self.value, float): > + return hex(struct.unpack('I', struct.pack('f', self.value))[0]) > + else: > + assert False > + > +class Variable(Value): > + def __init__(self, val, name, varset): > + Value.__init__(self, name, "variable") > + self.var_name = val > + self.index = varset[val] > + self.name = name > + > +class Expression(Value): > + def __init__(self, expr, name_base, varset): > + Value.__init__(self, name_base, "expression") > + assert isinstance(expr, tuple) > + > + self.opcode = expr[0] > + self.sources = [ Value.create(src, "{0}_{1}".format(name_base, i), > varset) > + for (i, src) in enumerate(expr[1:]) ] > + > + def render(self): > + srcs = "\n".join([src.render() for src in self.sources]) > + return srcs + super(Expression, self).render() > + > +_optimization_ids = itertools.count() > + > +class SearchAndReplace(object): > + def __init__(self, search, replace): > + self.id = _optimization_ids.next() > + > + varset = VarSet() > + if isinstance(search, Expression): > + self.search = search > + else: > + self.search = Expression(search, "search{0}".format(self.id), > varset) > + > + if isinstance(replace, Value): > + self.replace = replace > + else: > + self.replace = Value.create(replace, "replace{0}".format(self.id), > varset) > + > +_algebraic_pass_template = mako.template.Template(""" > +#include "nir.h" > +#include "nir_search.h" > + > +struct transform { > + const nir_search_expression *search; > + const nir_search_value *replace; > +}; > + > +% for (opcode, xform_list) in xform_dict.iteritems(): > +% for xform in xform_list: mako doesn't care about spaces around it's control flow, you can indent either before or after the % to make the code more readable > + ${xform.search.render()} > + ${xform.replace.render()} > +% endfor > + > +static const struct { > + const nir_search_expression *search; > + const nir_search_value *replace; > +} ${pass_name}_${opcode}_xforms[] = { > +% for xform in xform_list: > + { &${xform.search.name}, ${xform.replace.c_ptr()} }, > +% endfor > +}; > +% endfor > + > +struct opt_state { > + void *mem_ctx; > + bool progress; > +}; > + > +static bool > +${pass_name}_block(nir_block *block, void *void_state) > +{ > + struct opt_state *state = void_state; > + > + nir_foreach_instr_safe(block, instr) { > + if (instr->type != nir_instr_type_alu) > + continue; > + > + nir_alu_instr *alu = nir_instr_as_alu(instr); > + if (!alu->dest.dest.is_ssa) > + continue; > + > + switch (alu->op) { > + % for opcode in xform_dict.keys(): You could also dedent this by a space or two for readability > + case nir_op_${opcode}: > + for (unsigned i = 0; i < ARRAY_SIZE(${pass_name}_${opcode}_xforms); > i++) { > + if (nir_replace_instr(alu, > ${pass_name}_${opcode}_xforms[i].search, > + ${pass_name}_${opcode}_xforms[i].replace, > + state->mem_ctx)) > + state->progress = true; > + } > + break; > + % endfor > + default: > + break; > + } > + } > + > + return true; > +} > + > +static bool > +${pass_name}_impl(nir_function_impl *impl) > +{ > + struct opt_state state; > + > + state.mem_ctx = ralloc_parent(impl); > + state.progress = false; > + > + nir_foreach_block(impl, ${pass_name}_block, &state); > + > + if (state.progress) > + nir_metadata_dirty(impl, nir_metadata_block_index | > + nir_metadata_dominance); > + > + return state.progress; > +} > + > +bool > +${pass_name}(nir_shader *shader) > +{ > + bool progress = false; > + > + nir_foreach_overload(shader, overload) { > + if (overload->impl) > + progress |= ${pass_name}_impl(overload->impl); > + } > + > + return progress; > +} > +""") > + > +class AlgebraicPass(object): > + def __init__(self, pass_name, transforms): > + self.xform_dict = {} > + self.pass_name = pass_name > + > + for xform in transforms: > + if not isinstance(xform, SearchAndReplace): > + xform = SearchAndReplace(*xform) > + > + if xform.search.opcode not in self.xform_dict: > + self.xform_dict[xform.search.opcode] = [] Rather than doing this consider using collections.defaultdict, > + > + self.xform_dict[xform.search.opcode].append(xform) > + > + def render(self): > + return _algebraic_pass_template.render(pass_name=self.pass_name, > + xform_dict=self.xform_dict) > -- > 2.2.0 > > _______________________________________________ > mesa-dev mailing list > mesa-dev@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/mesa-dev >
signature.asc
Description: This is a digitally signed message part.
_______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev