<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Jan 5, 2015 at 10:00 PM, Connor Abbott <span dir="ltr"><<a href="mailto:cwabbott0@gmail.com" target="_blank">cwabbott0@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On Tue, Dec 16, 2014 at 1:12 AM, Jason Ekstrand <<a href="mailto:jason@jlekstrand.net">jason@jlekstrand.net</a>> wrote:<br>
> This commit builds on the nir_search.h infastructure by adds a bit of<br>
<br>
</span>adding<br></blockquote><div><br></div><div>fixed.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div class="h5"><br>
> python code that makes it stupid easy to write an algebraic transformation<br>
> pass.  The nir_algebraic.py file contains four python classes that<br>
> correspond directly to the datastructures in nir_search.c and allow you to<br>
> easily generate the C code to represent them.  Given a list of<br>
> search-and-replace operations, it can then generate a function that applies<br>
> those transformations to a shader.<br>
><br>
> The transformations can be specified manually, or they can be specified<br>
> using nested tuples.  The nested tuples make a neat little language for<br>
> specifying expression trees and search-and-replace operations in a very<br>
> readable and easy-to-edit fasion.<br>
><br>
> The generated code is also fairly efficient.  Insteady of blindly calling<br>
> nir_replace_instr with every single transformation and on every single<br>
> instruction, it uses a switch statement on the instruction opcode to do a<br>
> first-order culling and only calls nir_replace_instr if the opcode is known<br>
> to match the first opcode in the search expression.<br>
> ---<br>
>  src/glsl/nir/nir_algebraic.py | 249 ++++++++++++++++++++++++++++++++++++++++++<br>
>  1 file changed, 249 insertions(+)<br>
>  create mode 100644 src/glsl/nir/nir_algebraic.py<br>
><br>
> diff --git a/src/glsl/nir/nir_algebraic.py b/src/glsl/nir/nir_algebraic.py<br>
> new file mode 100644<br>
> index 0000000..7718a6d<br>
> --- /dev/null<br>
> +++ b/src/glsl/nir/nir_algebraic.py<br>
> @@ -0,0 +1,249 @@<br>
> +#! /usr/bin/env python<br>
> +#<br>
> +# Copyright (C) 2014 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 "Software"),<br>
> +# to deal in the Software without restriction, including without limitation<br>
> +# the rights to use, copy, modify, merge, publish, distribute, 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 next<br>
> +# paragraph) shall be included in all copies or substantial portions of the<br>
> +# Software.<br>
> +#<br>
> +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br>
> +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br>
> +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL<br>
> +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 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 DEALINGS<br>
> +# IN THE SOFTWARE.<br>
> +#<br>
> +# Authors:<br>
> +#    Jason Ekstrand (<a href="mailto:jason@jlekstrand.net">jason@jlekstrand.net</a>)<br>
> +<br>
> +import itertools, struct, sys, mako.template<br>
> +<br>
> +# Represents a set of variables, each with a unique id<br>
> +class VarSet(object):<br>
> +   def __init__(self):<br>
> +      self.names = {}<br>
> +      self.ids = itertools.count()<br>
> +<br>
> +   def __getitem__(self, name):<br>
> +      if name not in self.names:<br>
> +         self.names[name] = self.ids.next()<br>
> +<br>
> +      return self.names[name]<br>
> +<br>
> +class Value(object):<br>
> +   @staticmethod<br>
> +   def create(val, name_base, varset):<br>
> +      if isinstance(val, tuple):<br>
> +         return Expression(val, name_base, varset)<br>
> +      elif isinstance(val, Expression):<br>
> +         return val<br>
> +      elif isinstance(val, str):<br>
> +         return Variable(val, name_base, varset)<br>
> +      elif isinstance(val, (bool, int, long, float)):<br>
> +         return Constant(val, name_base)<br>
> +<br>
> +   __template = mako.template.Template("""<br>
> +static const ${val.c_type()} ${<a href="http://val.name" target="_blank">val.name</a>} = {<br>
> +   { ${val.type_enum()} },<br>
> +% if isinstance(val, Constant):<br>
> +   { ${hex(val)} /* ${val.value} */ },<br>
> +% elif isinstance(val, Variable):<br>
> +   ${val.index}, /* ${val.var_name} */<br>
> +% elif isinstance(val, Expression):<br>
> +   nir_op_${val.opcode},<br>
> +   { ${', '.join([src.c_ptr() for src in val.sources])} },<br>
> +% endif<br>
> +};""")<br>
<br>
</div></div>This stuff seems like it's subclass-specific... maybe make a subclass<br>
method called get_initializer_list() or something, and then just do<br>
<br>
 { ${val.type_enum()} },<br>
 ${val.get_initializer_list()}<br></blockquote><div><br></div><div>Yes we could, and I thought about doing so.  However, I didn't because I'd like to keep the C stuff in the template so it's easier to verify "yes, this prints the right C".<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div class="h5"><br>
> +<br>
> +   def __init__(self, name, type_str):<br>
> +      <a href="http://self.name" target="_blank">self.name</a> = name<br>
> +      self.type_str = type_str<br>
> +<br>
> +   def type_enum(self):<br>
> +      return "nir_search_value_" + self.type_str<br>
> +<br>
> +   def c_type(self):<br>
> +      return "nir_search_" + self.type_str<br>
> +<br>
> +   def c_ptr(self):<br>
> +      return "&{0}.value".format(<a href="http://self.name" target="_blank">self.name</a>)<br>
> +<br>
> +   def render(self):<br>
> +      return self.__template.render(val=self,<br>
> +                                    Constant=Constant,<br>
> +                                    Variable=Variable,<br>
> +                                    Expression=Expression)<br>
> +<br>
> +class Constant(Value):<br>
> +   def __init__(self, val, name):<br>
> +      Value.__init__(self, name, "constant")<br>
> +      self.value = val<br>
> +<br>
> +   def __hex__(self):<br>
> +      # Even if it's an integer, we still need to unpack as an unsigned<br>
> +      # int.  This is because, without C99, we can only assign to the first<br>
> +      # element of a union in an initializer.<br>
> +      if isinstance(self.value, (bool)):<br>
> +         return 'NIR_TRUE' if self.value else 'NIR_FALSE'<br>
> +      if isinstance(self.value, (int, long)):<br>
> +         return hex(struct.unpack('I', struct.pack('i', self.value))[0])<br>
> +      elif isinstance(self.value, float):<br>
> +         return hex(struct.unpack('I', struct.pack('f', self.value))[0])<br>
> +      else:<br>
> +         assert False<br>
> +<br>
> +class Variable(Value):<br>
> +   def __init__(self, val, name, varset):<br>
> +      Value.__init__(self, name, "variable")<br>
> +      self.var_name = val<br>
> +      self.index = varset[val]<br>
> +      <a href="http://self.name" target="_blank">self.name</a> = name<br>
> +<br>
> +class Expression(Value):<br>
> +   def __init__(self, expr, name_base, varset):<br>
> +      Value.__init__(self, name_base, "expression")<br>
> +      assert isinstance(expr, tuple)<br>
> +<br>
> +      self.opcode = expr[0]<br>
> +      self.sources = [ Value.create(src, "{0}_{1}".format(name_base, i), varset)<br>
> +                       for (i, src) in enumerate(expr[1:]) ]<br>
> +<br>
> +   def render(self):<br>
> +      srcs = "\n".join([src.render() for src in self.sources])<br>
<br>
</div></div>You can just say<br>
<br>
 "\n".join(src.render() for src in self.sources)<br>
<br>
here and avoid creating a list (as well as the extra brackets).<br></blockquote><div><br></div><div>Sure.  Easy enough.  I also fixed that one other place.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div class="h5"><br>
> +      return srcs + super(Expression, self).render()<br>
> +<br>
> +_optimization_ids = itertools.count()<br>
> +<br>
> +class SearchAndReplace(object):<br>
> +   def __init__(self, search, replace):<br>
> +      <a href="http://self.id" target="_blank">self.id</a> = _optimization_ids.next()<br>
> +<br>
> +      varset = VarSet()<br>
> +      if isinstance(search, Expression):<br>
> +         self.search = search<br>
> +      else:<br>
> +         self.search = Expression(search, "search{0}".format(<a href="http://self.id" target="_blank">self.id</a>), varset)<br>
> +<br>
> +      if isinstance(replace, Value):<br>
> +         self.replace = replace<br>
> +      else:<br>
> +         self.replace = Value.create(replace, "replace{0}".format(<a href="http://self.id" target="_blank">self.id</a>), varset)<br>
> +<br>
> +_algebraic_pass_template = mako.template.Template("""<br>
> +#include "nir.h"<br>
> +#include "nir_search.h"<br>
> +<br>
> +struct transform {<br>
> +   const nir_search_expression *search;<br>
> +   const nir_search_value *replace;<br>
> +};<br>
> +<br>
> +% for (opcode, xform_list) in xform_dict.iteritems():<br>
> +% for xform in xform_list:<br>
> +   ${xform.search.render()}<br>
> +   ${xform.replace.render()}<br>
> +% endfor<br>
> +<br>
> +static const struct {<br>
> +   const nir_search_expression *search;<br>
> +   const nir_search_value *replace;<br>
> +} ${pass_name}_${opcode}_xforms[] = {<br>
> +% for xform in xform_list:<br>
> +   { &${<a href="http://xform.search.name" target="_blank">xform.search.name</a>}, ${xform.replace.c_ptr()} },<br>
> +% endfor<br>
> +};<br>
> +% endfor<br>
> +<br>
> +struct opt_state {<br>
> +   void *mem_ctx;<br>
> +   bool progress;<br>
> +};<br>
> +<br>
> +static bool<br>
> +${pass_name}_block(nir_block *block, void *void_state)<br>
> +{<br>
> +   struct opt_state *state = void_state;<br>
> +<br>
> +   nir_foreach_instr_safe(block, instr) {<br>
> +      if (instr->type != nir_instr_type_alu)<br>
> +         continue;<br>
> +<br>
> +      nir_alu_instr *alu = nir_instr_as_alu(instr);<br>
> +      if (!alu->dest.dest.is_ssa)<br>
> +         continue;<br>
> +<br>
> +      switch (alu->op) {<br>
> +      % for opcode in xform_dict.keys():<br>
> +      case nir_op_${opcode}:<br>
> +         for (unsigned i = 0; i < ARRAY_SIZE(${pass_name}_${opcode}_xforms); i++) {<br>
> +            if (nir_replace_instr(alu, ${pass_name}_${opcode}_xforms[i].search,<br>
> +                                  ${pass_name}_${opcode}_xforms[i].replace,<br>
> +                                  state->mem_ctx))<br>
> +               state->progress = true;<br>
> +         }<br>
> +         break;<br>
> +      % endfor<br>
> +      default:<br>
> +         break;<br>
> +      }<br>
> +   }<br>
> +<br>
> +   return true;<br>
> +}<br>
> +<br>
> +static bool<br>
> +${pass_name}_impl(nir_function_impl *impl)<br>
> +{<br>
> +   struct opt_state state;<br>
> +<br>
> +   state.mem_ctx = ralloc_parent(impl);<br>
> +   state.progress = false;<br>
> +<br>
> +   nir_foreach_block(impl, ${pass_name}_block, &state);<br>
> +<br>
> +   if (state.progress)<br>
> +      nir_metadata_dirty(impl, nir_metadata_block_index |<br>
> +                               nir_metadata_dominance);<br>
> +<br>
> +   return state.progress;<br>
> +}<br>
> +<br>
> +bool<br>
> +${pass_name}(nir_shader *shader)<br>
> +{<br>
> +   bool progress = false;<br>
> +<br>
> +   nir_foreach_overload(shader, overload) {<br>
> +      if (overload->impl)<br>
> +         progress |= ${pass_name}_impl(overload->impl);<br>
> +   }<br>
> +<br>
> +   return progress;<br>
> +}<br>
> +""")<br>
> +<br>
> +class AlgebraicPass(object):<br>
> +   def __init__(self, pass_name, transforms):<br>
> +      self.xform_dict = {}<br>
> +      self.pass_name = pass_name<br>
> +<br>
> +      for xform in transforms:<br>
> +         if not isinstance(xform, SearchAndReplace):<br>
> +            xform = SearchAndReplace(*xform)<br>
> +<br>
> +         if xform.search.opcode not in self.xform_dict:<br>
> +            self.xform_dict[xform.search.opcode] = []<br>
> +<br>
> +         self.xform_dict[xform.search.opcode].append(xform)<br>
> +<br>
> +   def render(self):<br>
> +      return _algebraic_pass_template.render(pass_name=self.pass_name,<br>
> +                                             xform_dict=self.xform_dict)<br>
> --<br>
> 2.2.0<br>
><br>
</div></div>> _______________________________________________<br>
> mesa-dev mailing list<br>
> <a href="mailto:mesa-dev@lists.freedesktop.org">mesa-dev@lists.freedesktop.org</a><br>
> <a href="http://lists.freedesktop.org/mailman/listinfo/mesa-dev" target="_blank">http://lists.freedesktop.org/mailman/listinfo/mesa-dev</a><br>
</blockquote></div><br></div></div>