summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Romanick <ian.d.romanick@intel.com>2019-02-19 20:53:09 -0800
committerIan Romanick <ian.d.romanick@intel.com>2019-03-26 10:29:22 -0700
commitd1a9aba61bd7f4c8a6d61821fe98df5faaa5a488 (patch)
tree4941cbd1736e8544aee763c1f9d24cbc4ad9063d
parent63e9b863613fc1c03018410122b2f57f45320ab1 (diff)
WIP: intel/compiler: Add code generator generator
-rw-r--r--src/intel/compiler/gen_gen_codegen.py862
1 files changed, 862 insertions, 0 deletions
diff --git a/src/intel/compiler/gen_gen_codegen.py b/src/intel/compiler/gen_gen_codegen.py
new file mode 100644
index 000000000000..7488d87f3e0a
--- /dev/null
+++ b/src/intel/compiler/gen_gen_codegen.py
@@ -0,0 +1,862 @@
+#
+# Copyright (C) 2019 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.
+
+from __future__ import print_function
+import ast
+from collections import defaultdict
+import itertools
+import struct
+import sys
+import mako.template
+import re
+import traceback
+from enum import Enum, unique
+
+# Generate an Intel Gen architecture code generator.
+
+# These opcodes are only employed by nir_search. This provides a mapping from
+# opcode to destination type.
+conv_opcode_types = {
+ 'i2f' : 'float',
+ 'u2f' : 'float',
+ 'f2f' : 'float',
+ 'f2u' : 'uint',
+ 'f2i' : 'int',
+ 'u2u' : 'uint',
+ 'i2i' : 'int',
+ 'b2f' : 'float',
+ 'b2i' : 'int',
+ 'i2b' : 'bool',
+ 'f2b' : 'bool',
+}
+
+if sys.version_info < (3, 0):
+ integer_types = (int, long)
+ string_type = unicode
+
+else:
+ integer_types = (int, )
+ string_type = str
+
+_type_re = re.compile(r"(?P<type>int|uint|bool|float)?(?P<bits>\d+)?")
+
+def type_bits(type_str):
+ m = _type_re.match(type_str)
+ assert m.group('type')
+
+ if m.group('bits') is None:
+ return 0
+ else:
+ return int(m.group('bits'))
+
+# Represents a set of variables, each with a unique id
+class VarSet(object):
+ def __init__(self):
+ self.names = {}
+ self.ids = itertools.count()
+ self.immutable = False;
+
+ def __getitem__(self, name):
+ if name not in self.names:
+ assert not self.immutable, "Unknown replacement variable: {}".format(name)
+ self.names[name] = next(self.ids)
+
+ return self.names[name]
+
+ def lock(self):
+ self.immutable = True
+
+class Value(object):
+ @staticmethod
+ def create(val, name_base, varset):
+ if isinstance(val, bytes):
+ val = val.decode('utf-8')
+
+ if isinstance(val, tuple):
+ return Expression(val, name_base, varset)
+ elif isinstance(val, Expression):
+ return val
+ elif isinstance(val, string_type):
+ return Variable(val, name_base, varset)
+ elif isinstance(val, (bool, float) + integer_types):
+ return Constant(val, name_base)
+
+ __template = mako.template.Template("""
+static const ${val.c_type} ${val.name} = {
+ { ${val.type_enum}, ${val.c_bit_size} },
+% if isinstance(val, Constant):
+ ${val.type()}, { ${val.hex()} /* ${val.value} */ },
+% elif isinstance(val, Variable):
+ ${val.index}, /* ${val.var_name} */
+ ${'true' if val.is_constant else 'false'},
+ ${val.type() or 'nir_type_invalid' },
+ ${val.cond if val.cond else 'NULL'},
+% elif isinstance(val, Expression):
+ ${'true' if val.inexact else 'false'},
+ nir_op_${val.opcode},
+ { ${', '.join(src.c_ptr for src in val.sources)} },
+ ${val.cond if val.cond else 'NULL'},
+% endif
+};""")
+
+ def __init__(self, val, name, type_str):
+ self.in_val = str(val)
+ self.name = name
+ self.type_str = type_str
+
+ def __str__(self):
+ return self.in_val
+
+ def get_bit_size(self):
+ """Get the physical bit-size that has been chosen for this value, or if
+ there is none, the canonical value which currently represents this
+ bit-size class. Variables will be preferred, i.e. if there are any
+ variables in the equivalence class, the canonical value will be a
+ variable. We do this since we'll need to know which variable each value
+ is equivalent to when constructing the replacement expression. This is
+ the "find" part of the union-find algorithm.
+ """
+ bit_size = self
+
+ while isinstance(bit_size, Value):
+ if bit_size._bit_size is None:
+ break
+ bit_size = bit_size._bit_size
+
+ if bit_size is not self:
+ self._bit_size = bit_size
+ return bit_size
+
+ @property
+ def type_enum(self):
+ return "nir_search_value_" + self.type_str
+
+ @property
+ def c_type(self):
+ return "nir_search_" + self.type_str
+
+ @property
+ def c_ptr(self):
+ return "&{0}.value".format(self.name)
+
+ @property
+ def c_bit_size(self):
+ bit_size = self.get_bit_size()
+ if isinstance(bit_size, int):
+ return bit_size
+ elif isinstance(bit_size, Variable):
+ return -bit_size.index - 1
+ else:
+ # If the bit-size class is neither a variable, nor an actual bit-size, then
+ # - If it's in the search expression, we don't need to check anything
+ # - If it's in the replace expression, either it's ambiguous (in which
+ # case we'd reject it), or it equals the bit-size of the search value
+ # We represent these cases with a 0 bit-size.
+ return 0
+
+ def render(self):
+ return self.__template.render(val=self,
+ Constant=Constant,
+ Variable=Variable,
+ Expression=Expression)
+
+_constant_re = re.compile(r"(?P<value>[^@\(]+)(?:@(?P<bits>\d+))?")
+
+class Constant(Value):
+ def __init__(self, val, name):
+ Value.__init__(self, val, name, "constant")
+
+ if isinstance(val, (str)):
+ m = _constant_re.match(val)
+ self.value = ast.literal_eval(m.group('value'))
+ self._bit_size = int(m.group('bits')) if m.group('bits') else None
+ else:
+ self.value = val
+ self._bit_size = None
+
+ if isinstance(self.value, bool):
+ assert self._bit_size is None or self._bit_size == 1
+ self._bit_size = 1
+
+ def hex(self):
+ if isinstance(self.value, (bool)):
+ return 'NIR_TRUE' if self.value else 'NIR_FALSE'
+ if isinstance(self.value, integer_types):
+ return hex(self.value)
+ elif isinstance(self.value, float):
+ i = struct.unpack('Q', struct.pack('d', self.value))[0]
+ h = hex(i)
+
+ # On Python 2 this 'L' suffix is automatically added, but not on Python 3
+ # Adding it explicitly makes the generated file identical, regardless
+ # of the Python version running this script.
+ if h[-1] != 'L' and i > sys.maxsize:
+ h += 'L'
+
+ return h
+ else:
+ assert False
+
+ def type(self):
+ if isinstance(self.value, (bool)):
+ return "nir_type_bool"
+ elif isinstance(self.value, integer_types):
+ return "nir_type_int"
+ elif isinstance(self.value, float):
+ return "nir_type_float"
+
+_var_name_re = re.compile(r"(?P<const>#)?(?P<name>\w+)"
+ r"(?:@(?P<type>int|uint|bool|float)?(?P<bits>\d+)?)?"
+ r"(?P<cond>\([^\)]+\))?")
+
+class Variable(Value):
+ def __init__(self, val, name, varset):
+ Value.__init__(self, val, name, "variable")
+
+ m = _var_name_re.match(val)
+ assert m and m.group('name') is not None
+
+ self.var_name = m.group('name')
+
+ # Prevent common cases where someone puts quotes around a literal
+ # constant. If we want to support names that have numeric or
+ # punctuation characters, we can me the first assertion more flexible.
+ assert self.var_name.isalpha()
+ assert self.var_name is not 'True'
+ assert self.var_name is not 'False'
+
+ self.is_constant = m.group('const') is not None
+ self.cond = m.group('cond')
+ self.required_type = m.group('type')
+ self._bit_size = int(m.group('bits')) if m.group('bits') else None
+
+ if self.required_type == 'bool':
+ if self._bit_size is not None:
+ assert self._bit_size in type_sizes(self.required_type)
+ else:
+ self._bit_size = 1
+
+ if self.required_type is not None:
+ assert self.required_type in ('float', 'bool', 'int', 'uint')
+
+ self.index = varset[self.var_name]
+
+ def type(self):
+ if self.required_type == 'bool':
+ return "nir_type_bool"
+ elif self.required_type in ('int', 'uint'):
+ return "nir_type_int"
+ elif self.required_type == 'float':
+ return "nir_type_float"
+
+_opcode_re = re.compile(r"(?P<inexact>~)?(?P<opcode>\w+)(?:@(?P<bits>\d+))?"
+ r"(?P<cond>\([^\)]+\))?")
+
+class Expression(Value):
+ def __init__(self, expr, name_base, varset):
+ Value.__init__(self, expr, name_base, "expression")
+ assert isinstance(expr, tuple)
+
+ m = _opcode_re.match(expr[0])
+ assert m and m.group('opcode') is not None
+
+ self.opcode = m.group('opcode')
+ self._bit_size = int(m.group('bits')) if m.group('bits') else None
+ self.inexact = m.group('inexact') is not None
+ self.cond = m.group('cond')
+ self.sources = [ Value.create(src, "{0}_{1}".format(name_base, i), varset)
+ for (i, src) in enumerate(expr[1:]) ]
+
+ if self.opcode in conv_opcode_types:
+ assert self._bit_size is None, \
+ 'Expression cannot use an unsized conversion opcode with ' \
+ 'an explicit size; that\'s silly.'
+
+
+ def render(self):
+ srcs = "\n".join(src.render() for src in self.sources)
+ return srcs + super(Expression, self).render()
+
+
+@unique
+class OperandType(Enum):
+ # Inferred means the type of the operand is inferred from expected source
+ # type of the NIR instruction.
+ inferred = 0
+ B, W, D, Q, UB, UW, UD, UQ, HF, F, DF, VF = range(1, 13)
+
+ def __str__(self):
+ return "BRW_REGISTER_TYPE_" + self.name
+
+
+@unique
+class OperandFile(Enum):
+ unknown = 0
+ destination = 1
+ input = 2
+ temporary = 3
+ constant = 4
+ null = 5
+ grf = 6
+
+
+def grf(number, subnumber, width):
+ operand = RegisterOperand("g{}.{}".format(number, subnumber))
+ operand.nr = number
+ operand.subnr = subnumber
+ operand.size = width
+ operand.file = OperandFile.grf
+ return operand
+
+
+def retype(operand, new_type):
+ if not isinstance(operand, Operand):
+ operand = Operand.create(operand)
+
+ operand.retype(new_type)
+ return operand
+
+
+def subscript(operand, type, index):
+ if not isinstance(operand, Operand):
+ operand = Operand.create(operand)
+
+ operand.subscript(type, index)
+ return operand
+
+
+def abs(operand):
+ if not isinstance(operand, Operand):
+ operand = Operand.create(operand)
+
+ operand.abs()
+ return operand
+
+
+def neg(operand):
+ assert not isinstance(operand, Instruction)
+
+ if not isinstance(operand, Operand):
+ operand = Operand.create(operand)
+
+ operand.negate()
+ return operand
+
+
+def imm(value, type):
+ return ConstantOperand(value, type)
+
+
+def null(type):
+ return retype(RegisterOperand(None), type)
+
+
+class Operand(object):
+ @staticmethod
+ def create(val):
+ if isinstance(val, bytes):
+ val = val.decode('utf-8')
+
+ if isinstance(val, Operand):
+ return val
+ elif isinstance(val, string_type):
+ return RegisterOperand(val)
+ else:
+ return ConstantOperand(val)
+
+
+class ConstantOperand(Operand):
+ all_constants = []
+
+ def __init__(self, val, type):
+ self.val = val
+ self.file = OperandFile.constant
+ self.type = type if isinstance(type, OperandType) else OperandType[type]
+ self.index = len(ConstantOperand.all_constants)
+
+ assert self.type != OperandType.inferred
+
+ ConstantOperand.all_constants.append(self)
+
+
+ def negate(self):
+ assert False
+
+
+ def abs(self):
+ assert False
+
+
+ def retype(self, new_type):
+ assert False
+
+
+ def subscript(self, type, index):
+ assert False
+
+
+ def remap_names(self, file, varset):
+ pass
+
+
+ def bytecode_instruction_list(self):
+ return ["bytecode_instruction(append_constant, {}, {})".format(self.type, self.index)]
+
+
+ def __str__(self):
+ if self.type == OperandType.Q:
+ return "{{ .q = {} }}".format(self.val)
+ elif self.type == OperandType.UQ:
+ return "{{ .uq = {} }}".format(self.val)
+ elif self.type == OperandType.D:
+ return "{{ .d = {} }}".format(self.val)
+ elif self.type == OperandType.UD:
+ return "{{ .ud = {} }}".format(self.val)
+ elif self.type == OperandType.W:
+ return "{{ .w = {} }}".format(self.val)
+ elif self.type == OperandType.UW:
+ return "{{ .uw = {} }}".format(self.val)
+ elif self.type == OperandType.B:
+ return "{{ .b = {} }}".format(self.val)
+ elif self.type == OperandType.UB:
+ return "{{ .ub = {} }}".format(self.val)
+ elif self.type == OperandType.F:
+ return "{{ .f = {} }}".format(self.val)
+ elif self.type == OperandType.DF:
+ return "{{ .df = {}d }}".format(self.val)
+ elif self.type == OperandType.HF:
+ return "{{ .uw = {} /* .hf = {} */ }}".format(hex(struct.unpack('<H', struct.pack('<e', self.val))[0]), self.val)
+ else:
+ # Unsupported types for constants
+ assert False
+
+
+class RegisterOperand(Operand):
+ def __init__(self, name):
+ self.type = OperandType.inferred
+ self._abs = False
+ self.neg = False
+ self.subregion = None
+ self.size = 0
+
+ if name == "r":
+ self.name = None
+ self.file = OperandFile.destination
+ self.index = 0
+ elif name is None:
+ self.name = None
+ self.file = OperandFile.null
+ else:
+ self.name = name
+ self.file = OperandFile.unknown
+ self.index = 0
+
+
+ def negate(self):
+ self.neg = not self.neg
+
+
+ def abs(self):
+ self.neg = False
+ self._abs = True
+
+
+ def retype(self, new_type):
+ if isinstance(new_type, OperandType):
+ self.type = new_type
+ else:
+ self.type = OperandType[new_type]
+
+
+ def subscript(self, type, index):
+ assert self.subregion is None
+
+ if not isinstance(type, OperandType):
+ type = OperandType[type]
+
+ self.subregion = (type, index)
+
+
+ def remap_names(self, file, varset):
+ if self.name is not None and self.file != OperandFile.grf:
+ if file == OperandFile.input:
+ self.file = file
+ self.index = varset[self.name]
+ self.name = None
+ elif self.name in varset:
+ self.file = file
+ self.index, _ = varset[self.name]
+ self.name = None
+
+
+ def bytecode_instruction_list(self):
+ assert self.file != OperandFile.unknown
+ assert self.file != OperandFile.constant
+
+ if self.file == OperandFile.destination:
+ op = [ "bytecode_instruction(append_output)" ]
+ elif self.file == OperandFile.input:
+ op = [ "bytecode_instruction(append_input, {})".format(self.index) ]
+ elif self.file == OperandFile.temporary:
+ op = [ "bytecode_instruction(append_temporary, {})".format(self.index) ]
+ elif self.file == OperandFile.null:
+ assert self.type != OperandType.inferred
+ op = [ "bytecode_instruction(append_null_reg, {})".format(self.type) ]
+ elif self.file == OperandFile.grf:
+ assert self.size != 0
+ op = [ "bytecode_instruction(append_vec{}_grf, {}, {})".format(self.size, self.subnr, self.nr) ]
+
+
+ if self.file != OperandFile.null:
+ if self.type != OperandType.inferred:
+ op.append("bytecode_instruction(retype_operand, {})".format(self.type))
+
+ if self._abs:
+ op.append("bytecode_instruction(abs_operand)")
+
+ if self.neg:
+ op.append("bytecode_instruction(neg_operand)")
+
+ if self.subregion is not None:
+ op.append("bytecode_instruction(subscript_operand, {}, {})".format(self.subregion[0], self.subregion[1]))
+
+ return op
+
+
+class Instruction(object):
+ def __init__(self, opcode, dest, src0=None, src1=None, src2=None):
+ self.opcode = opcode
+ self.saturate_mode = False
+ self.conditional_modifier = None
+ self.pred = None
+ self.dest = Operand.create(dest)
+
+ srcs = []
+ if src0 is not None:
+ srcs.append(src0)
+
+ if src1 is not None:
+ srcs.append(src1)
+
+ if src2 is not None:
+ srcs.append(src2)
+
+ self.sources = [Operand.create(s) for s in srcs]
+
+
+ def remap_names(self, file, varset):
+ if file == OperandFile.temporary:
+ self.dest.remap_names(file, varset)
+
+ [src.remap_names(file, varset) for src in self.sources]
+
+
+ def saturate(self):
+ self.saturate_mode = True
+ return self
+
+
+ def cmod(self, m):
+ # Don't support the "round increment" R cmod from Gen4/Gen5.
+ assert m in ['Z', 'NZ', 'L', 'LE', 'G', 'GE', 'O', 'U']
+ self.conditional_modifier = m
+ return self
+
+
+ def predicate(self, pred="NORMAL"):
+ self.pred = pred
+ return self
+
+
+ def bytecode_instruction_list(self):
+ opcode_name = self.opcode if "_OPCODE_" in self.opcode else "BRW_OPCODE_" + self.opcode
+ instr = [ "bytecode_instruction(emit_instruction, {})".format(opcode_name) ]
+
+ if self.saturate_mode:
+ instr.append("bytecode_instruction(saturate_instruction)")
+
+ if self.conditional_modifier is not None:
+ instr.append("bytecode_instruction(conditional_mod, BRW_CONDITIONAL_{})".format(self.conditional_modifier))
+
+ if self.pred is not None:
+ instr.append("bytecode_instruction(predicate_instruction, BRW_PREDICATE_{})".format(self.pred))
+
+ return self.dest.bytecode_instruction_list() +\
+ [x for src in self.sources for x in src.bytecode_instruction_list() ] +\
+ instr
+
+
+class InstructionList(object):
+ """Represents a list of instructions and any temporary registers used by those
+ instructions.
+
+ """
+
+ def __init__(self, temporary_list, instruction_list):
+ self.instructions = instruction_list
+
+ self.temporaries = {}
+ temp_ids = itertools.count()
+ for (name, type) in temporary_list:
+ self.temporaries[name] = (next(temp_ids), OperandType[type])
+
+ [instr.remap_names(OperandFile.temporary, self.temporaries) for instr in self.instructions]
+
+
+ def remap_names(self, varset):
+ [instr.remap_names(OperandFile.input, varset) for instr in self.instructions]
+
+
+ def bytecode_instruction_list(self):
+ declare_temps = ["bytecode_instruction(declare_temporary, {})".format(type) for (_, (_, type)) in self.temporaries.items()]
+ instr = [x for instr in self.instructions for x in instr.bytecode_instruction_list()]
+
+ return declare_temps + instr
+
+
+_optimization_ids = itertools.count()
+condition_list = ['true']
+
+class SearchAndReplace(object):
+ def __init__(self, transform):
+ self.id = next(_optimization_ids)
+
+ search = transform[0]
+ replace = transform[1]
+
+ self.condition = transform[2] if len(transform) > 2 else 'true'
+ if self.condition not in condition_list:
+ condition_list.append(self.condition)
+ self.condition_index = condition_list.index(self.condition)
+
+ varset = VarSet()
+ if isinstance(search, Expression):
+ self.search = search
+ else:
+ self.search = Expression(search, "search{0}".format(self.id), varset)
+
+ varset.lock()
+
+ assert isinstance(replace, Instruction) or isinstance(replace, InstructionList)
+
+ self.replace = InstructionList([], (replace,)) if isinstance(replace, Instruction) else replace
+ self.replace.remap_names(varset)
+
+_code_generator_template = mako.template.Template("""
+#ifndef STRUCT_TRANSFORM
+#define STRUCT_TRANSFORM
+
+struct transform {
+ const nir_search_expression *search;
+ unsigned bytecode;
+ unsigned condition_offset;
+};
+
+#endif /* STRUCT_TRANSFORM */
+
+static const struct bytecode_instruction ${pass_name}_derp[] = {
+% for b in bytecode:
+ ${b},
+% endfor
+};
+
+static const union immediate_value ${pass_name}_immediates[] = {
+% for i in immediates:
+ ${i},
+% endfor
+};
+
+% for (opcode, xform_list) in sorted(xform_dict.items()):
+ % for xform in xform_list:
+ ${xform.search.render()}
+ % endfor
+% endfor
+
+
+static const struct transform ${pass_name}_xforms[] = {
+% for (name, first_bytecode, condition_index) in expression_bytecode_tuples:
+ { &${name}, ${first_bytecode}, ${condition_index} },
+% endfor
+};
+
+static bool
+nir_emit_alu_${pass_name}(fs_visitor *v, const struct gen_device_info *devinfo,
+ const fs_builder &bld, nir_alu_instr *alu)
+{
+ const struct brw_wm_prog_key *const fs_key = (struct brw_wm_prog_key *) v->key;
+
+ const bool condition_flags[${len(condition_list)}] = {
+ % for index, condition in enumerate(condition_list):
+ ${condition}, /* ${index} */
+ % endfor
+ };
+
+ unsigned first = 0;
+ unsigned count = 0;
+
+ switch (alu->op) {
+ % for opcode in opcode_dict.keys():
+ case nir_op_${opcode}:
+ first = ${opcode_dict[opcode][0]};
+ count = ${opcode_dict[opcode][1]};
+ break;
+
+ % endfor
+ % for opcode, reason in unsupported:
+ % if not isinstance(opcode, str):
+ % for o in opcode:
+ case nir_op_${o}:
+ % endfor
+ % else:
+ case nir_op_${opcode}:
+ % endif
+ unreachable("${reason}");
+ % endfor
+ default:
+ /* unreachable("unhandled instruction"); */
+ break;
+ }
+
+ for (unsigned i = 0; i < count; i++) {
+ uint8_t swizzle[NIR_MAX_VEC_COMPONENTS];
+ unsigned num_variables;
+ nir_alu_src variables[NIR_SEARCH_MAX_VARIABLES];
+ uint8_t which_src[NIR_SEARCH_MAX_VARIABLES];
+
+ if (condition_flags[${pass_name}_xforms[first + i].condition_offset] &&
+ nir_match_instr(alu, ${pass_name}_xforms[first + i].search,
+ swizzle, &num_variables, variables, which_src)) {
+ /* Deal with the destination of the instruction. */
+ fs_reg result = v->get_nir_dest(alu->dest.dest);
+ result.type = brw_type_for_nir_type(devinfo,
+ (nir_alu_type)(nir_op_infos[alu->op].output_type |
+ nir_dest_bit_size(alu->dest.dest)));
+
+ /* This code path should never see an instruction that operates on
+ * more than a single channel. Therefore, we can just adjust the
+ * source and destination registers for that channel and emit the
+ * instruction.
+ */
+ unsigned channel = 0;
+ if (nir_op_infos[alu->op].output_size == 0) {
+ /* Since NIR is doing the scalarizing for us, we should only ever
+ * see vectorized operations with a single channel.
+ */
+ assert(util_bitcount(alu->dest.write_mask) == 1);
+ channel = ffs(alu->dest.write_mask) - 1;
+
+ result = offset(result, bld, channel);
+ }
+
+ /* Deal with the sources of the "instruction." */
+ fs_reg op[NIR_SEARCH_MAX_VARIABLES];
+ for (unsigned i = 0; i < num_variables; i++) {
+ /* Instruction that originally used this variable. */
+ assert(variables[i].src.parent_instr->type == nir_instr_type_alu);
+ nir_alu_instr *const orig_user =
+ nir_instr_as_alu(variables[i].src.parent_instr);
+
+ op[i] = v->get_nir_src(variables[i].src);
+ op[i].type = brw_type_for_nir_type(devinfo,
+ (nir_alu_type)(nir_op_infos[orig_user->op].input_types[which_src[i]] |
+ nir_src_bit_size(variables[i].src)));
+ op[i].abs = variables[i].abs;
+ op[i].negate = variables[i].negate;
+
+ assert(nir_op_infos[orig_user->op].input_sizes[i] < 2);
+ op[i] = offset(op[i], bld, orig_user->src[which_src[i]].swizzle[channel]);
+ }
+
+ emit_instructions_from_bytecode(bld,
+ &${pass_name}_derp[${pass_name}_xforms[first + i].bytecode],
+ result,
+ op,
+ ${pass_name}_immediates,
+ alu->dest.saturate);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+""")
+
+class CodeGeneratorGenerator(object):
+ def __init__(self, pass_name, transforms, unsupported):
+ self.opcode_xforms = defaultdict(lambda : [])
+ self.pass_name = pass_name
+ self.unsupported = unsupported
+
+ error = False
+
+ for xform in transforms:
+ if not isinstance(xform, SearchAndReplace):
+ try:
+ xform = SearchAndReplace(xform)
+ except:
+ print("Failed to parse transformation:", file=sys.stderr)
+ print(" " + str(xform), file=sys.stderr)
+ traceback.print_exc(file=sys.stderr)
+ print('', file=sys.stderr)
+ error = True
+ continue
+
+ if xform.search.opcode not in self.opcode_xforms:
+ self.opcode_xforms[xform.search.opcode] = []
+
+ self.opcode_xforms[xform.search.opcode].append(xform)
+
+ if error:
+ sys.exit(1)
+
+
+ def render(self):
+ # Generate two tables reprensenting the data related to the code
+ # transformations:
+ #
+ # 1. Table of bytecode instructions.
+ # 2. Table of <expression, bytecode> tuples.
+
+ bytecode = []
+ expression_bytecode_tuples = []
+ opcode_dict = defaultdict(lambda : [])
+
+ for (opcode, xform_list) in sorted(self.opcode_xforms.items()):
+ first = len(expression_bytecode_tuples)
+
+ for xform in xform_list:
+ expression_bytecode_tuples.append((xform.search.name, len(bytecode), xform.condition_index))
+ bytecode = bytecode + xform.replace.bytecode_instruction_list() + ["bytecode_instruction(end_of_stream)"]
+
+ count = len(expression_bytecode_tuples) - first
+ opcode_dict[opcode] = (first, count)
+
+ return _code_generator_template.render(pass_name=self.pass_name,
+ xform_dict=self.opcode_xforms,
+ bytecode=bytecode,
+ expression_bytecode_tuples=expression_bytecode_tuples,
+ opcode_dict=opcode_dict,
+ immediates=ConstantOperand.all_constants,
+ condition_list=condition_list,
+ unsupported=self.unsupported)
+