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