17ec681f3Smrg#
27ec681f3Smrg# Copyright (C) 2020 Collabora, Ltd.
37ec681f3Smrg#
47ec681f3Smrg# Permission is hereby granted, free of charge, to any person obtaining a
57ec681f3Smrg# copy of this software and associated documentation files (the "Software"),
67ec681f3Smrg# to deal in the Software without restriction, including without limitation
77ec681f3Smrg# the rights to use, copy, modify, merge, publish, distribute, sublicense,
87ec681f3Smrg# and/or sell copies of the Software, and to permit persons to whom the
97ec681f3Smrg# Software is furnished to do so, subject to the following conditions:
107ec681f3Smrg#
117ec681f3Smrg# The above copyright notice and this permission notice (including the next
127ec681f3Smrg# paragraph) shall be included in all copies or substantial portions of the
137ec681f3Smrg# Software.
147ec681f3Smrg#
157ec681f3Smrg# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
167ec681f3Smrg# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
177ec681f3Smrg# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
187ec681f3Smrg# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
197ec681f3Smrg# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
207ec681f3Smrg# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
217ec681f3Smrg# IN THE SOFTWARE.
227ec681f3Smrg
237ec681f3Smrg# Useful for autogeneration
247ec681f3SmrgCOPYRIGHT = """/*
257ec681f3Smrg * Copyright (C) 2020 Collabora, Ltd.
267ec681f3Smrg *
277ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
287ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
297ec681f3Smrg * to deal in the Software without restriction, including without limitation
307ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
317ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the
327ec681f3Smrg * Software is furnished to do so, subject to the following conditions:
337ec681f3Smrg *
347ec681f3Smrg * The above copyright notice and this permission notice (including the next
357ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
367ec681f3Smrg * Software.
377ec681f3Smrg *
387ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
397ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
407ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
417ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
427ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
437ec681f3Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
447ec681f3Smrg * SOFTWARE.
457ec681f3Smrg */
467ec681f3Smrg
477ec681f3Smrg/* Autogenerated file, do not edit */
487ec681f3Smrg
497ec681f3Smrg"""
507ec681f3Smrg
517ec681f3Smrg# Parse instruction set XML into a normalized form for processing
527ec681f3Smrg
537ec681f3Smrgimport xml.etree.ElementTree as ET
547ec681f3Smrgimport copy
557ec681f3Smrgimport itertools
567ec681f3Smrgfrom collections import OrderedDict
577ec681f3Smrg
587ec681f3Smrgdef parse_cond(cond, aliased = False):
597ec681f3Smrg    if cond.tag == 'reserved':
607ec681f3Smrg        return None
617ec681f3Smrg
627ec681f3Smrg    if cond.attrib.get('alias', False) and not aliased:
637ec681f3Smrg        return ['alias', parse_cond(cond, True)]
647ec681f3Smrg
657ec681f3Smrg    if 'left' in cond.attrib:
667ec681f3Smrg        return [cond.tag, cond.attrib['left'], cond.attrib['right']]
677ec681f3Smrg    else:
687ec681f3Smrg        return [cond.tag] + [parse_cond(x) for x in cond.findall('*')]
697ec681f3Smrg
707ec681f3Smrgdef parse_exact(obj):
717ec681f3Smrg    return [int(obj.attrib['mask'], 0), int(obj.attrib['exact'], 0)]
727ec681f3Smrg
737ec681f3Smrgdef parse_derived(obj):
747ec681f3Smrg    out = []
757ec681f3Smrg
767ec681f3Smrg    for deriv in obj.findall('derived'):
777ec681f3Smrg        loc = [int(deriv.attrib['start']), int(deriv.attrib['size'])]
787ec681f3Smrg        count = 1 << loc[1]
797ec681f3Smrg
807ec681f3Smrg        opts = [parse_cond(d) for d in deriv.findall('*')]
817ec681f3Smrg        default = [None] * count
827ec681f3Smrg        opts_fit = (opts + default)[0:count]
837ec681f3Smrg
847ec681f3Smrg        out.append([loc, opts_fit])
857ec681f3Smrg
867ec681f3Smrg    return out
877ec681f3Smrg
887ec681f3Smrgdef parse_modifiers(obj, include_pseudo):
897ec681f3Smrg    out = []
907ec681f3Smrg
917ec681f3Smrg    for mod in obj.findall('mod'):
927ec681f3Smrg        if mod.attrib.get('pseudo', False) and not include_pseudo:
937ec681f3Smrg            continue
947ec681f3Smrg
957ec681f3Smrg        name = mod.attrib['name']
967ec681f3Smrg        start = mod.attrib.get('start', None)
977ec681f3Smrg        size = int(mod.attrib['size'])
987ec681f3Smrg
997ec681f3Smrg        if start is not None:
1007ec681f3Smrg            start = int(start)
1017ec681f3Smrg
1027ec681f3Smrg        opts = [x.text if x.tag == 'opt' else x.tag for x in mod.findall('*')]
1037ec681f3Smrg
1047ec681f3Smrg        if len(opts) == 0:
1057ec681f3Smrg            assert('opt' in mod.attrib)
1067ec681f3Smrg            opts = ['none', mod.attrib['opt']]
1077ec681f3Smrg
1087ec681f3Smrg        # Find suitable default
1097ec681f3Smrg        default = mod.attrib.get('default', 'none' if 'none' in opts else None)
1107ec681f3Smrg
1117ec681f3Smrg        # Pad out as reserved
1127ec681f3Smrg        count = (1 << size)
1137ec681f3Smrg        opts = (opts + (['reserved'] * count))[0:count]
1147ec681f3Smrg        out.append([[name, start, size], default, opts])
1157ec681f3Smrg
1167ec681f3Smrg    return out
1177ec681f3Smrg
1187ec681f3Smrgdef parse_copy(enc, existing):
1197ec681f3Smrg    for node in enc.findall('copy'):
1207ec681f3Smrg        name = node.get('name')
1217ec681f3Smrg        for ex in existing:
1227ec681f3Smrg            if ex[0][0] == name:
1237ec681f3Smrg                ex[0][1] = node.get('start')
1247ec681f3Smrg
1257ec681f3Smrgdef parse_instruction(ins, include_pseudo):
1267ec681f3Smrg    common = {
1277ec681f3Smrg            'srcs': [],
1287ec681f3Smrg            'modifiers': [],
1297ec681f3Smrg            'immediates': [],
1307ec681f3Smrg            'swaps': [],
1317ec681f3Smrg            'derived': [],
1327ec681f3Smrg            'staging': ins.attrib.get('staging', '').split('=')[0],
1337ec681f3Smrg            'staging_count': ins.attrib.get('staging', '=0').split('=')[1],
1347ec681f3Smrg            'dests': int(ins.attrib.get('dests', '1')),
1357ec681f3Smrg            'unused': ins.attrib.get('unused', False),
1367ec681f3Smrg            'pseudo': ins.attrib.get('pseudo', False),
1377ec681f3Smrg            'message': ins.attrib.get('message', 'none'),
1387ec681f3Smrg            'last': ins.attrib.get('last', False),
1397ec681f3Smrg            'table': ins.attrib.get('table', False),
1407ec681f3Smrg    }
1417ec681f3Smrg
1427ec681f3Smrg    if 'exact' in ins.attrib:
1437ec681f3Smrg        common['exact'] = parse_exact(ins)
1447ec681f3Smrg
1457ec681f3Smrg    for src in ins.findall('src'):
1467ec681f3Smrg        mask = int(src.attrib['mask'], 0) if ('mask' in src.attrib) else 0xFF
1477ec681f3Smrg        common['srcs'].append([int(src.attrib['start'], 0), mask])
1487ec681f3Smrg
1497ec681f3Smrg    for imm in ins.findall('immediate'):
1507ec681f3Smrg        if imm.attrib.get('pseudo', False) and not include_pseudo:
1517ec681f3Smrg            continue
1527ec681f3Smrg
1537ec681f3Smrg        start = int(imm.attrib['start']) if 'start' in imm.attrib else None
1547ec681f3Smrg        common['immediates'].append([imm.attrib['name'], start, int(imm.attrib['size'])])
1557ec681f3Smrg
1567ec681f3Smrg    common['derived'] = parse_derived(ins)
1577ec681f3Smrg    common['modifiers'] = parse_modifiers(ins, include_pseudo)
1587ec681f3Smrg
1597ec681f3Smrg    for swap in ins.findall('swap'):
1607ec681f3Smrg        lr = [int(swap.get('left')), int(swap.get('right'))]
1617ec681f3Smrg        cond = parse_cond(swap.findall('*')[0])
1627ec681f3Smrg        rewrites = {}
1637ec681f3Smrg
1647ec681f3Smrg        for rw in swap.findall('rewrite'):
1657ec681f3Smrg            mp = {}
1667ec681f3Smrg
1677ec681f3Smrg            for m in rw.findall('map'):
1687ec681f3Smrg                mp[m.attrib['from']] = m.attrib['to']
1697ec681f3Smrg
1707ec681f3Smrg            rewrites[rw.attrib['name']] = mp
1717ec681f3Smrg
1727ec681f3Smrg        common['swaps'].append([lr, cond, rewrites])
1737ec681f3Smrg
1747ec681f3Smrg    encodings = ins.findall('encoding')
1757ec681f3Smrg    variants = []
1767ec681f3Smrg
1777ec681f3Smrg    if len(encodings) == 0:
1787ec681f3Smrg        variants = [[None, common]]
1797ec681f3Smrg    else:
1807ec681f3Smrg        for enc in encodings:
1817ec681f3Smrg            variant = copy.deepcopy(common)
1827ec681f3Smrg            assert(len(variant['derived']) == 0)
1837ec681f3Smrg
1847ec681f3Smrg            variant['exact'] = parse_exact(enc)
1857ec681f3Smrg            variant['derived'] = parse_derived(enc)
1867ec681f3Smrg            parse_copy(enc, variant['modifiers'])
1877ec681f3Smrg
1887ec681f3Smrg            cond = parse_cond(enc.findall('*')[0])
1897ec681f3Smrg            variants.append([cond, variant])
1907ec681f3Smrg
1917ec681f3Smrg    return variants
1927ec681f3Smrg
1937ec681f3Smrgdef parse_instructions(xml, include_unused = False, include_pseudo = False):
1947ec681f3Smrg    final = {}
1957ec681f3Smrg    instructions = ET.parse(xml).getroot().findall('ins')
1967ec681f3Smrg
1977ec681f3Smrg    for ins in instructions:
1987ec681f3Smrg        parsed = parse_instruction(ins, include_pseudo)
1997ec681f3Smrg
2007ec681f3Smrg        # Some instructions are for useful disassembly only and can be stripped
2017ec681f3Smrg        # out of the compiler, particularly useful for release builds
2027ec681f3Smrg        if parsed[0][1]["unused"] and not include_unused:
2037ec681f3Smrg            continue
2047ec681f3Smrg
2057ec681f3Smrg        # On the other hand, some instructions are only for the IR, not disassembly
2067ec681f3Smrg        if parsed[0][1]["pseudo"] and not include_pseudo:
2077ec681f3Smrg            continue
2087ec681f3Smrg
2097ec681f3Smrg        final[ins.attrib['name']] = parsed
2107ec681f3Smrg
2117ec681f3Smrg    return final
2127ec681f3Smrg
2137ec681f3Smrg# Expand out an opcode name to something C-escaped
2147ec681f3Smrg
2157ec681f3Smrgdef opname_to_c(name):
2167ec681f3Smrg    return name.lower().replace('*', 'fma_').replace('+', 'add_').replace('.', '_')
2177ec681f3Smrg
2187ec681f3Smrg# Expand out distinct states to distrinct instructions, with a placeholder
2197ec681f3Smrg# condition for instructions with a single state
2207ec681f3Smrg
2217ec681f3Smrgdef expand_states(instructions):
2227ec681f3Smrg    out = {}
2237ec681f3Smrg
2247ec681f3Smrg    for ins in instructions:
2257ec681f3Smrg        c = instructions[ins]
2267ec681f3Smrg
2277ec681f3Smrg        for ((test, desc), i) in zip(c, range(len(c))):
2287ec681f3Smrg            # Construct a name for the state
2297ec681f3Smrg            name = ins + (('.' + str(i)) if len(c) > 1 else '')
2307ec681f3Smrg
2317ec681f3Smrg            out[name] = (ins, test if test is not None else [], desc)
2327ec681f3Smrg
2337ec681f3Smrg    return out
2347ec681f3Smrg
2357ec681f3Smrg# Drop keys used for packing to simplify IR representation, so we can check for
2367ec681f3Smrg# equivalence easier
2377ec681f3Smrg
2387ec681f3Smrgdef simplify_to_ir(ins):
2397ec681f3Smrg    return {
2407ec681f3Smrg            'staging': ins['staging'],
2417ec681f3Smrg            'srcs': len(ins['srcs']),
2427ec681f3Smrg            'dests': ins['dests'],
2437ec681f3Smrg            'modifiers': [[m[0][0], m[2]] for m in ins['modifiers']],
2447ec681f3Smrg            'immediates': [m[0] for m in ins['immediates']]
2457ec681f3Smrg        }
2467ec681f3Smrg
2477ec681f3Smrg
2487ec681f3Smrgdef combine_ir_variants(instructions, key):
2497ec681f3Smrg    seen = [op for op in instructions.keys() if op[1:] == key]
2507ec681f3Smrg    variant_objs = [[simplify_to_ir(Q[1]) for Q in instructions[x]] for x in seen]
2517ec681f3Smrg    variants = sum(variant_objs, [])
2527ec681f3Smrg
2537ec681f3Smrg    # Accumulate modifiers across variants
2547ec681f3Smrg    modifiers = {}
2557ec681f3Smrg
2567ec681f3Smrg    for s in variants[0:]:
2577ec681f3Smrg        # Check consistency
2587ec681f3Smrg        assert(s['srcs'] == variants[0]['srcs'])
2597ec681f3Smrg        assert(s['dests'] == variants[0]['dests'])
2607ec681f3Smrg        assert(s['immediates'] == variants[0]['immediates'])
2617ec681f3Smrg        assert(s['staging'] == variants[0]['staging'])
2627ec681f3Smrg
2637ec681f3Smrg        for name, opts in s['modifiers']:
2647ec681f3Smrg            if name not in modifiers:
2657ec681f3Smrg                modifiers[name] = copy.deepcopy(opts)
2667ec681f3Smrg            else:
2677ec681f3Smrg                modifiers[name] += opts
2687ec681f3Smrg
2697ec681f3Smrg    # Great, we've checked srcs/immediates are consistent and we've summed over
2707ec681f3Smrg    # modifiers
2717ec681f3Smrg    return {
2727ec681f3Smrg            'srcs': variants[0]['srcs'],
2737ec681f3Smrg            'dests': variants[0]['dests'],
2747ec681f3Smrg            'staging': variants[0]['staging'],
2757ec681f3Smrg            'immediates': sorted(variants[0]['immediates']),
2767ec681f3Smrg            'modifiers': modifiers,
2777ec681f3Smrg            'v': len(variants),
2787ec681f3Smrg            'ir': variants
2797ec681f3Smrg        }
2807ec681f3Smrg
2817ec681f3Smrg# Partition instructions to mnemonics, considering units and variants
2827ec681f3Smrg# equivalent.
2837ec681f3Smrg
2847ec681f3Smrgdef partition_mnemonics(instructions):
2857ec681f3Smrg    key_func = lambda x: x[1:]
2867ec681f3Smrg    sorted_instrs = sorted(instructions.keys(), key = key_func)
2877ec681f3Smrg    partitions = itertools.groupby(sorted_instrs, key_func)
2887ec681f3Smrg    return { k: combine_ir_variants(instructions, k) for k, v in partitions }
2897ec681f3Smrg
2907ec681f3Smrg# Generate modifier lists, by accumulating all the possible modifiers, and
2917ec681f3Smrg# deduplicating thus assigning canonical enum values. We don't try _too_ hard
2927ec681f3Smrg# to be clever, but by preserving as much of the original orderings as
2937ec681f3Smrg# possible, later instruction encoding is simplified a bit.  Probably a micro
2947ec681f3Smrg# optimization but we have to pick _some_ ordering, might as well choose the
2957ec681f3Smrg# most convenient.
2967ec681f3Smrg#
2977ec681f3Smrg# THIS MUST BE DETERMINISTIC
2987ec681f3Smrg
2997ec681f3Smrgdef order_modifiers(ir_instructions):
3007ec681f3Smrg    out = {}
3017ec681f3Smrg
3027ec681f3Smrg    # modifier name -> (list of option strings)
3037ec681f3Smrg    modifier_lists = {}
3047ec681f3Smrg
3057ec681f3Smrg    for ins in sorted(ir_instructions):
3067ec681f3Smrg        modifiers = ir_instructions[ins]["modifiers"]
3077ec681f3Smrg
3087ec681f3Smrg        for name in modifiers:
3097ec681f3Smrg            name_ = name[0:-1] if name[-1] in "0123" else name
3107ec681f3Smrg
3117ec681f3Smrg            if name_ not in modifier_lists:
3127ec681f3Smrg                modifier_lists[name_] = copy.deepcopy(modifiers[name])
3137ec681f3Smrg            else:
3147ec681f3Smrg                modifier_lists[name_] += modifiers[name]
3157ec681f3Smrg
3167ec681f3Smrg    for mod in modifier_lists:
3177ec681f3Smrg        lst = list(OrderedDict.fromkeys(modifier_lists[mod]))
3187ec681f3Smrg
3197ec681f3Smrg        # Ensure none is false for booleans so the builder makes sense
3207ec681f3Smrg        if len(lst) == 2 and lst[1] == "none":
3217ec681f3Smrg            lst.reverse()
3227ec681f3Smrg        elif mod == "table":
3237ec681f3Smrg            # We really need a zero sentinel to materialize DTSEL
3247ec681f3Smrg            assert(lst[2] == "none")
3257ec681f3Smrg            lst[2] = lst[0]
3267ec681f3Smrg            lst[0] = "none"
3277ec681f3Smrg
3287ec681f3Smrg        out[mod] = lst
3297ec681f3Smrg
3307ec681f3Smrg    return out
3317ec681f3Smrg
3327ec681f3Smrg# Count sources for a simplified (IR) instruction, including a source for a
3337ec681f3Smrg# staging register if necessary
3347ec681f3Smrgdef src_count(op):
3357ec681f3Smrg    staging = 1 if (op["staging"] in ["r", "rw"]) else 0
3367ec681f3Smrg    return op["srcs"] + staging
3377ec681f3Smrg
3387ec681f3Smrg# Parses out the size part of an opocde name
3397ec681f3Smrgdef typesize(opcode):
3407ec681f3Smrg    if opcode[-3:] == '128':
3417ec681f3Smrg        return 128
3427ec681f3Smrg    if opcode[-2:] == '48':
3437ec681f3Smrg        return 48
3447ec681f3Smrg    elif opcode[-1] == '8':
3457ec681f3Smrg        return 8
3467ec681f3Smrg    else:
3477ec681f3Smrg        try:
3487ec681f3Smrg            return int(opcode[-2:])
3497ec681f3Smrg        except:
3507ec681f3Smrg            return 32
351