Path: blob/21.2-virgl/src/panfrost/bifrost/bifrost_isa.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.2122# Useful for autogeneration23COPYRIGHT = """/*24* Copyright (C) 2020 Collabora, Ltd.25*26* Permission is hereby granted, free of charge, to any person obtaining a27* copy of this software and associated documentation files (the "Software"),28* to deal in the Software without restriction, including without limitation29* the rights to use, copy, modify, merge, publish, distribute, sublicense,30* and/or sell copies of the Software, and to permit persons to whom the31* Software is furnished to do so, subject to the following conditions:32*33* The above copyright notice and this permission notice (including the next34* paragraph) shall be included in all copies or substantial portions of the35* Software.36*37* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR38* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,39* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL40* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER41* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,42* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE43* SOFTWARE.44*/4546/* Autogenerated file, do not edit */4748"""4950# Parse instruction set XML into a normalized form for processing5152import xml.etree.ElementTree as ET53import copy54import itertools55from collections import OrderedDict5657def parse_cond(cond, aliased = False):58if cond.tag == 'reserved':59return None6061if cond.attrib.get('alias', False) and not aliased:62return ['alias', parse_cond(cond, True)]6364if 'left' in cond.attrib:65return [cond.tag, cond.attrib['left'], cond.attrib['right']]66else:67return [cond.tag] + [parse_cond(x) for x in cond.findall('*')]6869def parse_exact(obj):70return [int(obj.attrib['mask'], 0), int(obj.attrib['exact'], 0)]7172def parse_derived(obj):73out = []7475for deriv in obj.findall('derived'):76loc = [int(deriv.attrib['start']), int(deriv.attrib['size'])]77count = 1 << loc[1]7879opts = [parse_cond(d) for d in deriv.findall('*')]80default = [None] * count81opts_fit = (opts + default)[0:count]8283out.append([loc, opts_fit])8485return out8687def parse_modifiers(obj, include_pseudo):88out = []8990for mod in obj.findall('mod'):91if mod.attrib.get('pseudo', False) and not include_pseudo:92continue9394name = mod.attrib['name']95start = mod.attrib.get('start', None)96size = int(mod.attrib['size'])9798if start is not None:99start = int(start)100101opts = [x.text if x.tag == 'opt' else x.tag for x in mod.findall('*')]102103if len(opts) == 0:104assert('opt' in mod.attrib)105opts = ['none', mod.attrib['opt']]106107# Find suitable default108default = mod.attrib.get('default', 'none' if 'none' in opts else None)109110# Pad out as reserved111count = (1 << size)112opts = (opts + (['reserved'] * count))[0:count]113out.append([[name, start, size], default, opts])114115return out116117def parse_copy(enc, existing):118for node in enc.findall('copy'):119name = node.get('name')120for ex in existing:121if ex[0][0] == name:122ex[0][1] = node.get('start')123124def parse_instruction(ins, include_pseudo):125common = {126'srcs': [],127'modifiers': [],128'immediates': [],129'swaps': [],130'derived': [],131'staging': ins.attrib.get('staging', '').split('=')[0],132'staging_count': ins.attrib.get('staging', '=0').split('=')[1],133'dests': int(ins.attrib.get('dests', '1')),134'unused': ins.attrib.get('unused', False),135'pseudo': ins.attrib.get('pseudo', False),136'message': ins.attrib.get('message', 'none'),137'last': ins.attrib.get('last', False),138'table': ins.attrib.get('table', False),139}140141if 'exact' in ins.attrib:142common['exact'] = parse_exact(ins)143144for src in ins.findall('src'):145mask = int(src.attrib['mask'], 0) if ('mask' in src.attrib) else 0xFF146common['srcs'].append([int(src.attrib['start'], 0), mask])147148for imm in ins.findall('immediate'):149if imm.attrib.get('pseudo', False) and not include_pseudo:150continue151152start = int(imm.attrib['start']) if 'start' in imm.attrib else None153common['immediates'].append([imm.attrib['name'], start, int(imm.attrib['size'])])154155common['derived'] = parse_derived(ins)156common['modifiers'] = parse_modifiers(ins, include_pseudo)157158for swap in ins.findall('swap'):159lr = [int(swap.get('left')), int(swap.get('right'))]160cond = parse_cond(swap.findall('*')[0])161rewrites = {}162163for rw in swap.findall('rewrite'):164mp = {}165166for m in rw.findall('map'):167mp[m.attrib['from']] = m.attrib['to']168169rewrites[rw.attrib['name']] = mp170171common['swaps'].append([lr, cond, rewrites])172173encodings = ins.findall('encoding')174variants = []175176if len(encodings) == 0:177variants = [[None, common]]178else:179for enc in encodings:180variant = copy.deepcopy(common)181assert(len(variant['derived']) == 0)182183variant['exact'] = parse_exact(enc)184variant['derived'] = parse_derived(enc)185parse_copy(enc, variant['modifiers'])186187cond = parse_cond(enc.findall('*')[0])188variants.append([cond, variant])189190return variants191192def parse_instructions(xml, include_unused = False, include_pseudo = False):193final = {}194instructions = ET.parse(xml).getroot().findall('ins')195196for ins in instructions:197parsed = parse_instruction(ins, include_pseudo)198199# Some instructions are for useful disassembly only and can be stripped200# out of the compiler, particularly useful for release builds201if parsed[0][1]["unused"] and not include_unused:202continue203204# On the other hand, some instructions are only for the IR, not disassembly205if parsed[0][1]["pseudo"] and not include_pseudo:206continue207208final[ins.attrib['name']] = parsed209210return final211212# Expand out an opcode name to something C-escaped213214def opname_to_c(name):215return name.lower().replace('*', 'fma_').replace('+', 'add_').replace('.', '_')216217# Expand out distinct states to distrinct instructions, with a placeholder218# condition for instructions with a single state219220def expand_states(instructions):221out = {}222223for ins in instructions:224c = instructions[ins]225226for ((test, desc), i) in zip(c, range(len(c))):227# Construct a name for the state228name = ins + (('.' + str(i)) if len(c) > 1 else '')229230out[name] = (ins, test if test is not None else [], desc)231232return out233234# Drop keys used for packing to simplify IR representation, so we can check for235# equivalence easier236237def simplify_to_ir(ins):238return {239'staging': ins['staging'],240'srcs': len(ins['srcs']),241'dests': ins['dests'],242'modifiers': [[m[0][0], m[2]] for m in ins['modifiers']],243'immediates': [m[0] for m in ins['immediates']]244}245246247def combine_ir_variants(instructions, key):248seen = [op for op in instructions.keys() if op[1:] == key]249variant_objs = [[simplify_to_ir(Q[1]) for Q in instructions[x]] for x in seen]250variants = sum(variant_objs, [])251252# Accumulate modifiers across variants253modifiers = {}254255for s in variants[0:]:256# Check consistency257assert(s['srcs'] == variants[0]['srcs'])258assert(s['dests'] == variants[0]['dests'])259assert(s['immediates'] == variants[0]['immediates'])260assert(s['staging'] == variants[0]['staging'])261262for name, opts in s['modifiers']:263if name not in modifiers:264modifiers[name] = copy.deepcopy(opts)265else:266modifiers[name] += opts267268# Great, we've checked srcs/immediates are consistent and we've summed over269# modifiers270return {271'srcs': variants[0]['srcs'],272'dests': variants[0]['dests'],273'staging': variants[0]['staging'],274'immediates': sorted(variants[0]['immediates']),275'modifiers': modifiers,276'v': len(variants),277'ir': variants278}279280# Partition instructions to mnemonics, considering units and variants281# equivalent.282283def partition_mnemonics(instructions):284key_func = lambda x: x[1:]285sorted_instrs = sorted(instructions.keys(), key = key_func)286partitions = itertools.groupby(sorted_instrs, key_func)287return { k: combine_ir_variants(instructions, k) for k, v in partitions }288289# Generate modifier lists, by accumulating all the possible modifiers, and290# deduplicating thus assigning canonical enum values. We don't try _too_ hard291# to be clever, but by preserving as much of the original orderings as292# possible, later instruction encoding is simplified a bit. Probably a micro293# optimization but we have to pick _some_ ordering, might as well choose the294# most convenient.295#296# THIS MUST BE DETERMINISTIC297298def order_modifiers(ir_instructions):299out = {}300301# modifier name -> (list of option strings)302modifier_lists = {}303304for ins in sorted(ir_instructions):305modifiers = ir_instructions[ins]["modifiers"]306307for name in modifiers:308name_ = name[0:-1] if name[-1] in "0123" else name309310if name_ not in modifier_lists:311modifier_lists[name_] = copy.deepcopy(modifiers[name])312else:313modifier_lists[name_] += modifiers[name]314315for mod in modifier_lists:316lst = list(OrderedDict.fromkeys(modifier_lists[mod]))317318# Ensure none is false for booleans so the builder makes sense319if len(lst) == 2 and lst[1] == "none":320lst.reverse()321elif mod == "table":322# We really need a zero sentinel to materialize DTSEL323assert(lst[2] == "none")324lst[2] = lst[0]325lst[0] = "none"326327out[mod] = lst328329return out330331# Count sources for a simplified (IR) instruction, including a source for a332# staging register if necessary333def src_count(op):334staging = 1 if (op["staging"] in ["r", "rw"]) else 0335return op["srcs"] + staging336337# Parses out the size part of an opocde name338def typesize(opcode):339if opcode[-3:] == '128':340return 128341if opcode[-2:] == '48':342return 48343elif opcode[-1] == '8':344return 8345else:346try:347return int(opcode[-2:])348except:349return 32350351352