Path: blob/21.2-virgl/src/panfrost/bifrost/gen_disasm.py
4564 views
#1# Copyright (C) 2020 Collabora, Ltd.2#3# Permission is hereby granted, free of charge, to any person obtaining a4# copy of this software and associated documentation files (the "Software"),5# to deal in the Software without restriction, including without limitation6# the rights to use, copy, modify, merge, publish, distribute, sublicense,7# and/or sell copies of the Software, and to permit persons to whom the8# Software is furnished to do so, subject to the following conditions:9#10# The above copyright notice and this permission notice (including the next11# paragraph) shall be included in all copies or substantial portions of the12# Software.13#14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING19# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS20# IN THE SOFTWARE.2122import sys23import itertools24from bifrost_isa import parse_instructions, opname_to_c, expand_states25from mako.template import Template2627instructions = parse_instructions(sys.argv[1], include_unused = True)2829# Constructs a reserved mask for a derived to cull impossible encodings3031def reserved_mask(derived):32((pos, width), opts) = derived33reserved = [x is None for x in opts]34mask = sum([(y << x) for x, y in enumerate(reserved)])35return (pos, width, mask)3637def reserved_masks(op):38masks = [reserved_mask(m) for m in op[2].get("derived", [])]39return [m for m in masks if m[2] != 0]4041# To decode instructions, pattern match based on the rules:42#43# 1. Execution unit (FMA or ADD) must line up.44# 2. All exact bits must match.45# 3. No fields should be reserved in a legal encoding.46# 4. Tiebreaker: Longer exact masks (greater unsigned bitwise inverses) win.47#48# To implement, filter the execution unit and check for exact bits in49# descending order of exact mask length. Check for reserved fields per50# candidate and succeed if it matches.51# found.5253def decode_op(instructions, is_fma):54# Filter out the desired execution unit55options = [n for n in instructions.keys() if (n[0] == '*') == is_fma]5657# Sort by exact masks, descending58MAX_MASK = (1 << (23 if is_fma else 20)) - 159options.sort(key = lambda n: (MAX_MASK ^ instructions[n][2]["exact"][0]))6061# Map to what we need to template62mapped = [(opname_to_c(op), instructions[op][2]["exact"], reserved_masks(instructions[op])) for op in options]6364# Generate checks in order65template = """void66bi_disasm_${unit}(FILE *fp, unsigned bits, struct bifrost_regs *srcs, struct bifrost_regs *next_regs, unsigned staging_register, unsigned branch_offset, struct bi_constants *consts, bool last)67{68fputs(" ", fp);6970% for (i, (name, (emask, ebits), derived)) in enumerate(options):71% if len(derived) > 0:72${"else " if i > 0 else ""}if (unlikely(((bits & ${hex(emask)}) == ${hex(ebits)})73% for (pos, width, reserved) in derived:74&& !(${hex(reserved)} & (1 << _BITS(bits, ${pos}, ${width})))75% endfor76))77% else:78${"else " if i > 0 else ""}if (unlikely(((bits & ${hex(emask)}) == ${hex(ebits)})))79% endif80bi_disasm_${name}(fp, bits, srcs, next_regs, staging_register, branch_offset, consts, last);81% endfor82else83fprintf(fp, "INSTR_INVALID_ENC ${unit} %X", bits);8485fputs("\\n", fp);86}"""8788return Template(template).render(options = mapped, unit = "fma" if is_fma else "add")8990# Decoding emits a series of function calls to e.g. `fma_fadd_v2f16`. We need to91# emit functions to disassemble a single decoded instruction in a particular92# state. Sync prototypes to avoid moves when calling.9394disasm_op_template = Template("""static void95bi_disasm_${c_name}(FILE *fp, unsigned bits, struct bifrost_regs *srcs, struct bifrost_regs *next_regs, unsigned staging_register, unsigned branch_offset, struct bi_constants *consts, bool last)96{97${body.strip()}98}99""")100101lut_template_only = Template(""" static const char *${field}[] = {102${", ".join(['"' + x + '"' for x in table])}103};104""")105106# Given a lookup table written logically, generate an accessor107lut_template = Template(""" static const char *${field}_table[] = {108${", ".join(['"' + x + '"' for x in table])}109};110111const char *${field} = ${field}_table[_BITS(bits, ${pos}, ${width})];112""")113114# Helpers for decoding follow. pretty_mods applies dot syntax115116def pretty_mods(opts, default):117return [('.' + (opt or 'reserved') if opt != default else '') for opt in opts]118119# Recursively searches for the set of free variables required by an expression120121def find_context_keys_expr(expr):122if isinstance(expr, list):123return set.union(*[find_context_keys_expr(x) for x in expr[1:]])124elif expr[0] == '#':125return set()126else:127return set([expr])128129def find_context_keys(desc, test):130keys = set()131132if len(test) > 0:133keys |= find_context_keys_expr(test)134135for i, (_, vals) in enumerate(desc.get('derived', [])):136for j, val in enumerate(vals):137if val is not None:138keys |= find_context_keys_expr(val)139140return keys141142# Compiles a logic expression to Python expression, ctx -> { T, F }143144EVALUATORS = {145'and': ' and ',146'or': ' or ',147'eq': ' == ',148'neq': ' != ',149}150151def compile_derived_inner(expr, keys):152if expr == []:153return 'True'154elif expr is None or expr[0] == 'alias':155return 'False'156elif isinstance(expr, list):157args = [compile_derived_inner(arg, keys) for arg in expr[1:]]158return '(' + EVALUATORS[expr[0]].join(args) + ')'159elif expr[0] == '#':160return "'{}'".format(expr[1:])161elif expr == 'ordering':162return expr163else:164return "ctx[{}]".format(keys.index(expr))165166def compile_derived(expr, keys):167return eval('lambda ctx, ordering: ' + compile_derived_inner(expr, keys))168169# Generate all possible combinations of values and evaluate the derived values170# by bruteforce evaluation to generate a forward mapping (values -> deriveds)171172def evaluate_forward_derived(vals, ctx, ordering):173for j, expr in enumerate(vals):174if expr(ctx, ordering):175return j176177return None178179def evaluate_forward(keys, derivf, testf, ctx, ordering):180if not testf(ctx, ordering):181return None182183deriv = []184185for vals in derivf:186evaled = evaluate_forward_derived(vals, ctx, ordering)187188if evaled is None:189return None190191deriv.append(evaled)192193return deriv194195def evaluate_forwards(keys, derivf, testf, mod_vals, ordered):196orderings = ["lt", "gt"] if ordered else [None]197return [[evaluate_forward(keys, derivf, testf, i, order) for i in itertools.product(*mod_vals)] for order in orderings]198199# Invert the forward mapping (values -> deriveds) of finite sets to produce a200# backwards mapping (deriveds -> values), suitable for disassembly. This is201# possible since the encoding is unambiguous, so this mapping is a bijection202# (after reserved/impossible encodings)203204def invert_lut(value_size, forward, derived, mod_map, keys, mod_vals):205backwards = [None] * (1 << value_size)206for (i, deriveds), ctx in zip(enumerate(forward), itertools.product(*mod_vals)):207# Skip reserved208if deriveds == None:209continue210211shift = 0212param = 0213for j, ((x, width), y) in enumerate(derived):214param += (deriveds[j] << shift)215shift += width216217assert(param not in backwards)218backwards[param] = ctx219220return backwards221222# Compute the value of all indirectly specified modifiers by using the223# backwards mapping (deriveds -> values) as a run-time lookup table.224225def build_lut(mnemonic, desc, test):226# Construct the system227facts = []228229mod_map = {}230231for ((name, pos, width), default, values) in desc.get('modifiers', []):232mod_map[name] = (width, values, pos, default)233234derived = desc.get('derived', [])235236# Find the keys and impose an order237key_set = find_context_keys(desc, test)238ordered = 'ordering' in key_set239key_set.discard('ordering')240keys = list(key_set)241242# Evaluate the deriveds for every possible state, forming a (state -> deriveds) map243testf = compile_derived(test, keys)244derivf = [[compile_derived(expr, keys) for expr in v] for (_, v) in derived]245mod_vals = [mod_map[k][1] for k in keys]246forward = evaluate_forwards(keys, derivf, testf, mod_vals, ordered)247248# Now invert that map to get a (deriveds -> state) map249value_size = sum([width for ((x, width), y) in derived])250backwards = [invert_lut(value_size, f, derived, mod_map, keys, mod_vals) for f in forward]251252# From that map, we can generate LUTs253output = ""254255if ordered:256output += "bool ordering = (_BITS(bits, {}, 3) > _BITS(bits, {}, 3));\n".format(desc["srcs"][0][0], desc["srcs"][1][0])257258for j, key in enumerate(keys):259# Only generate tables for indirect specifiers260if mod_map[key][2] is not None:261continue262263idx_parts = []264shift = 0265266for ((pos, width), _) in derived:267idx_parts.append("(_BITS(bits, {}, {}) << {})".format(pos, width, shift))268shift += width269270built_idx = (" | ".join(idx_parts)) if len(idx_parts) > 0 else "0"271272default = mod_map[key][3]273274if ordered:275for i, order in enumerate(backwards):276options = [ctx[j] if ctx is not None and ctx[j] is not None else "reserved" for ctx in order]277output += lut_template_only.render(field = key + "_" + str(i), table = pretty_mods(options, default))278279output += " const char *{} = ordering ? {}_1[{}] : {}_0[{}];\n".format(key, key, built_idx, key, built_idx)280else:281options = [ctx[j] if ctx is not None and ctx[j] is not None else "reserved" for ctx in backwards[0]]282output += lut_template_only.render(field = key + "_table", table = pretty_mods(options, default))283output += " const char *{} = {}_table[{}];\n".format(key, key, built_idx)284285return output286287def disasm_mod(mod, skip_mods):288if mod[0][0] in skip_mods:289return ''290else:291return ' fputs({}, fp);\n'.format(mod[0][0])292293def disasm_op(name, op):294(mnemonic, test, desc) = op295is_fma = mnemonic[0] == '*'296297# Modifiers may be either direct (pos is not None) or indirect (pos is298# None). If direct, we just do the bit lookup. If indirect, we use a LUT.299300body = ""301skip_mods = []302303body += build_lut(mnemonic, desc, test)304305for ((mod, pos, width), default, opts) in desc.get('modifiers', []):306if pos is not None:307body += lut_template.render(field = mod, table = pretty_mods(opts, default), pos = pos, width = width) + "\n"308309# Mnemonic, followed by modifiers310body += ' fputs("{}", fp);\n'.format(mnemonic)311312srcs = desc.get('srcs', [])313314for mod in desc.get('modifiers', []):315# Skip per-source until next block316if mod[0][0][-1] in "0123" and int(mod[0][0][-1]) < len(srcs):317continue318319body += disasm_mod(mod, skip_mods)320321body += ' fputs(" ", fp);\n'322body += ' bi_disasm_dest_{}(fp, next_regs, last);\n'.format('fma' if is_fma else 'add')323324# Next up, each source. Source modifiers are inserterd here325326for i, (pos, mask) in enumerate(srcs):327body += ' fputs(", ", fp);\n'328body += ' dump_src(fp, _BITS(bits, {}, 3), *srcs, consts, {});\n'.format(pos, "true" if is_fma else "false")329330# Error check if needed331if (mask != 0xFF):332body += ' if (!({} & (1 << _BITS(bits, {}, 3)))) fputs("(INVALID)", fp);\n'.format(hex(mask), pos, 3)333334# Print modifiers suffixed with this src number (e.g. abs0 for src0)335for mod in desc.get('modifiers', []):336if mod[0][0][-1] == str(i):337body += disasm_mod(mod, skip_mods)338339# And each immediate340for (imm, pos, width) in desc.get('immediates', []):341body += ' fprintf(fp, ", {}:%u", _BITS(bits, {}, {}));\n'.format(imm, pos, width)342343# Attach a staging register if one is used344if desc.get('staging'):345body += ' fprintf(fp, ", @r%u", staging_register);\n'346347return disasm_op_template.render(c_name = opname_to_c(name), body = body)348349print('#include "util/macros.h"')350print('#include "disassemble.h"')351352states = expand_states(instructions)353print('#define _BITS(bits, pos, width) (((bits) >> (pos)) & ((1 << (width)) - 1))')354355for st in states:356print(disasm_op(st, states[st]))357358print(decode_op(states, True))359print(decode_op(states, False))360361362