17ec681f3Smrg#encoding=utf-8
27ec681f3Smrg
37ec681f3Smrg# Copyright (C) 2016 Intel Corporation
47ec681f3Smrg# Copyright (C) 2016 Broadcom
57ec681f3Smrg# Copyright (C) 2020 Collabora, Ltd.
67ec681f3Smrg#
77ec681f3Smrg# Permission is hereby granted, free of charge, to any person obtaining a
87ec681f3Smrg# copy of this software and associated documentation files (the "Software"),
97ec681f3Smrg# to deal in the Software without restriction, including without limitation
107ec681f3Smrg# the rights to use, copy, modify, merge, publish, distribute, sublicense,
117ec681f3Smrg# and/or sell copies of the Software, and to permit persons to whom the
127ec681f3Smrg# Software is furnished to do so, subject to the following conditions:
137ec681f3Smrg#
147ec681f3Smrg# The above copyright notice and this permission notice (including the next
157ec681f3Smrg# paragraph) shall be included in all copies or substantial portions of the
167ec681f3Smrg# Software.
177ec681f3Smrg#
187ec681f3Smrg# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
197ec681f3Smrg# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
207ec681f3Smrg# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
217ec681f3Smrg# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
227ec681f3Smrg# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
237ec681f3Smrg# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
247ec681f3Smrg# IN THE SOFTWARE.
257ec681f3Smrg
267ec681f3Smrgimport os
277ec681f3Smrgimport textwrap
287ec681f3Smrgimport xml.etree.ElementTree as ET
297ec681f3Smrgimport sys
307ec681f3Smrg
317ec681f3Smrgtree = ET.parse(os.path.join(os.path.dirname(__file__), 'ISA.xml'))
327ec681f3Smrgroot = tree.getroot()
337ec681f3Smrg
347ec681f3Smrg# All instructions in the ISA
357ec681f3Smrginstructions = []
367ec681f3Smrg
377ec681f3Smrg# All immediates in the ISA
387ec681f3Smrgilut = root.findall('lut')[0]
397ec681f3Smrgassert(ilut.attrib['name'] == "Immediates")
407ec681f3Smrgimmediates = [int(imm.text, base=0) for imm in ilut.findall('constant')]
417ec681f3Smrgenums = {}
427ec681f3Smrg
437ec681f3Smrgdef xmlbool(s):
447ec681f3Smrg    assert(s.lower() in ["false", "true"])
457ec681f3Smrg    return False if s.lower() == "false" else True
467ec681f3Smrg
477ec681f3Smrgclass EnumValue:
487ec681f3Smrg    def __init__(self, value, default):
497ec681f3Smrg        self.value = value
507ec681f3Smrg        self.default = default
517ec681f3Smrg
527ec681f3Smrgclass Enum:
537ec681f3Smrg    def __init__(self, name, values):
547ec681f3Smrg        self.name = name
557ec681f3Smrg        self.values = values
567ec681f3Smrg        self.bare_values = [x.value for x in values]
577ec681f3Smrg
587ec681f3Smrg        defaults = [x.value for x in values if x.default]
597ec681f3Smrg        if len(defaults) > 0:
607ec681f3Smrg            assert(len(defaults) == 1)
617ec681f3Smrg            self.default = defaults[0]
627ec681f3Smrg
637ec681f3Smrgdef build_enum(el):
647ec681f3Smrg    values = []
657ec681f3Smrg
667ec681f3Smrg    for child in el:
677ec681f3Smrg        if child.tag == 'value':
687ec681f3Smrg            is_default = child.attrib.get('default', False)
697ec681f3Smrg            values.append(EnumValue(child.text, is_default))
707ec681f3Smrg        elif child.tag == 'reserved':
717ec681f3Smrg            values.append(EnumValue("reserved", False))
727ec681f3Smrg
737ec681f3Smrg    return Enum(el.attrib['name'], values)
747ec681f3Smrg
757ec681f3Smrgclass Modifier:
767ec681f3Smrg    def __init__(self, name, start, size, implied = False):
777ec681f3Smrg        self.name = name
787ec681f3Smrg        self.start = start
797ec681f3Smrg        self.size = size
807ec681f3Smrg        self.implied = implied
817ec681f3Smrg
827ec681f3Smrg        if size == 1:
837ec681f3Smrg            self.bare_values = ['', name]
847ec681f3Smrg            self.default = 0
857ec681f3Smrg        else:
867ec681f3Smrg            enum = enums[name]
877ec681f3Smrg            self.bare_values = [x.value for x in enum.values]
887ec681f3Smrg            defaults = [x for x in enum.values if x.default]
897ec681f3Smrg            assert(len(defaults) <= 1)
907ec681f3Smrg
917ec681f3Smrg            if len(defaults) > 0:
927ec681f3Smrg                self.default = self.bare_values.index(defaults[0].value)
937ec681f3Smrg            else:
947ec681f3Smrg                self.default = None
957ec681f3Smrg
967ec681f3Smrgdef Flag(name, start):
977ec681f3Smrg    return Modifier(name, start, 1)
987ec681f3Smrg
997ec681f3Smrg# Model a single instruction
1007ec681f3Smrgclass Source:
1017ec681f3Smrg    def __init__(self, index, size, is_float = False, swizzle = False, widen = False, lanes = False, lane = None, absneg = False, notted = False, name = ""):
1027ec681f3Smrg        self.is_float = is_float or absneg
1037ec681f3Smrg        self.size = size
1047ec681f3Smrg        self.absneg = absneg
1057ec681f3Smrg        self.notted = notted
1067ec681f3Smrg        self.swizzle = swizzle
1077ec681f3Smrg        self.widen = widen
1087ec681f3Smrg        self.lanes = lanes
1097ec681f3Smrg        self.lane = lane
1107ec681f3Smrg        self.name = name
1117ec681f3Smrg
1127ec681f3Smrg        self.offset = {}
1137ec681f3Smrg        self.bits = {}
1147ec681f3Smrg        if absneg:
1157ec681f3Smrg            self.offset['neg'] = 32 + 2 + ((2 - index) * 2)
1167ec681f3Smrg            self.offset['abs'] = 33 + 2 + ((2 - index) * 2)
1177ec681f3Smrg            self.bits['neg'] = 1
1187ec681f3Smrg            self.bits['abs'] = 1
1197ec681f3Smrg        if notted:
1207ec681f3Smrg            self.offset['not'] = 35
1217ec681f3Smrg            self.bits['not'] = 1
1227ec681f3Smrg        if widen or lanes:
1237ec681f3Smrg            self.offset['widen'] = 26 if index == 1 else 36
1247ec681f3Smrg            self.bits['widen'] = 4 # XXX: too much?
1257ec681f3Smrg        if lane:
1267ec681f3Smrg            self.offset['lane'] = self.lane
1277ec681f3Smrg            self.bits['lane'] = 2 if size in (8, 32) else 1
1287ec681f3Smrg        if swizzle:
1297ec681f3Smrg            assert(size in [16, 32])
1307ec681f3Smrg            self.offset['swizzle'] = 24 + ((2 - index) * 2)
1317ec681f3Smrg            self.bits['swizzle'] = 2
1327ec681f3Smrg
1337ec681f3Smrgclass Dest:
1347ec681f3Smrg    def __init__(self, name = ""):
1357ec681f3Smrg        self.name = name
1367ec681f3Smrg
1377ec681f3Smrgclass Staging:
1387ec681f3Smrg    def __init__(self, read = False, write = False, index = 0, count = 0, flags = True, name = ""):
1397ec681f3Smrg        self.name = name
1407ec681f3Smrg        self.read = read
1417ec681f3Smrg        self.write = write
1427ec681f3Smrg        self.count = count
1437ec681f3Smrg        self.flags = flags
1447ec681f3Smrg
1457ec681f3Smrg        # For compatibility
1467ec681f3Smrg        self.absneg = False
1477ec681f3Smrg        self.swizzle = False
1487ec681f3Smrg        self.notted = False
1497ec681f3Smrg        self.widen = False
1507ec681f3Smrg        self.lanes = False
1517ec681f3Smrg        self.lane = False
1527ec681f3Smrg        self.size = 32
1537ec681f3Smrg
1547ec681f3Smrg        assert(index < 2)
1557ec681f3Smrg        self.start = 40 if index == 0 else 16
1567ec681f3Smrg
1577ec681f3Smrg        if not flags:
1587ec681f3Smrg            self.encoded_flags = 0
1597ec681f3Smrg        elif index > 0:
1607ec681f3Smrg            self.encoded_flags = 0xC0
1617ec681f3Smrg        else:
1627ec681f3Smrg            self.encoded_flags = (0x80 if write else 0) | (0x40 if read else 0)
1637ec681f3Smrg
1647ec681f3Smrgclass Immediate:
1657ec681f3Smrg    def __init__(self, name, start, size, signed):
1667ec681f3Smrg        self.name = name
1677ec681f3Smrg        self.start = start
1687ec681f3Smrg        self.size = size
1697ec681f3Smrg        self.signed = signed
1707ec681f3Smrg
1717ec681f3Smrgclass Instruction:
1727ec681f3Smrg    def __init__(self, name, opcode, opcode2, srcs = [], dests = [], immediates = [], modifiers = [], staging = None):
1737ec681f3Smrg        self.name = name
1747ec681f3Smrg        self.srcs = srcs
1757ec681f3Smrg        self.dests = dests
1767ec681f3Smrg        self.opcode = opcode
1777ec681f3Smrg        self.opcode2 = opcode2 or 0
1787ec681f3Smrg        self.immediates = immediates
1797ec681f3Smrg        self.modifiers = modifiers
1807ec681f3Smrg        self.staging = staging
1817ec681f3Smrg
1827ec681f3Smrg        self.secondary_shift = max(len(self.srcs) * 8, 16)
1837ec681f3Smrg        self.secondary_mask = 0xF if opcode2 is not None else 0x0
1847ec681f3Smrg        if "left" in [x.name for x in self.modifiers]:
1857ec681f3Smrg            self.secondary_mask |= 0x100
1867ec681f3Smrg        if len(srcs) == 3 and (srcs[1].widen or srcs[1].lanes):
1877ec681f3Smrg            self.secondary_mask &= ~0xC # conflicts
1887ec681f3Smrg        if opcode == 0x90:
1897ec681f3Smrg            # XXX: XMLify this, but disambiguates sign of conversions
1907ec681f3Smrg            self.secondary_mask |= 0x10
1917ec681f3Smrg        if name.startswith("LOAD.i") or name.startswith("STORE.i"):
1927ec681f3Smrg            self.secondary_shift = 27 # Alias with memory_size
1937ec681f3Smrg            self.secondary_mask = 0x7
1947ec681f3Smrg
1957ec681f3Smrg        assert(len(dests) == 0 or not staging)
1967ec681f3Smrg        assert(not opcode2 or (opcode2 & self.secondary_mask) == opcode2)
1977ec681f3Smrg
1987ec681f3Smrg    def __str__(self):
1997ec681f3Smrg        return self.name
2007ec681f3Smrg
2017ec681f3Smrg# Build a single source from XML
2027ec681f3Smrgdef build_source(el, i, size):
2037ec681f3Smrg    lane = el.get('lane', None)
2047ec681f3Smrg    if lane == "true":
2057ec681f3Smrg        lane = 38 if i == 0 else 36
2067ec681f3Smrg    elif lane is not None:
2077ec681f3Smrg        lane = int(lane)
2087ec681f3Smrg
2097ec681f3Smrg    return Source(i, int(el.get('size', size)),
2107ec681f3Smrg            absneg = el.get('absneg', False),
2117ec681f3Smrg            is_float = el.get('float', False),
2127ec681f3Smrg            swizzle = el.get('swizzle', False),
2137ec681f3Smrg            widen = el.get('widen', False),
2147ec681f3Smrg            lanes = el.get('lanes', False),
2157ec681f3Smrg            lane = lane,
2167ec681f3Smrg            notted = el.get('not', False),
2177ec681f3Smrg            name = el.text or "")
2187ec681f3Smrg
2197ec681f3Smrgdef build_imm(el):
2207ec681f3Smrg    return Immediate(el.attrib['name'], int(el.attrib['start']),
2217ec681f3Smrg            int(el.attrib['size']), bool(el.attrib.get('signed', False)))
2227ec681f3Smrg
2237ec681f3Smrgdef build_staging(i, el):
2247ec681f3Smrg    r = xmlbool(el.attrib.get('read', 'false'))
2257ec681f3Smrg    w = xmlbool(el.attrib.get('write', 'false'))
2267ec681f3Smrg    count = int(el.attrib.get('count', '0'))
2277ec681f3Smrg    flags = xmlbool(el.attrib.get('flags', 'true'))
2287ec681f3Smrg
2297ec681f3Smrg    return Staging(r, w, i, count, flags, el.text or '')
2307ec681f3Smrg
2317ec681f3Smrgdef build_modifier(el):
2327ec681f3Smrg    name = el.attrib['name']
2337ec681f3Smrg    start = int(el.attrib['start'])
2347ec681f3Smrg    size = int(el.attrib['size'])
2357ec681f3Smrg    implied = xmlbool(el.get('implied', 'false'))
2367ec681f3Smrg
2377ec681f3Smrg    return Modifier(name, start, size, implied)
2387ec681f3Smrg
2397ec681f3Smrg# Build a single instruction from XML and group based overrides
2407ec681f3Smrgdef build_instr(el, overrides = {}):
2417ec681f3Smrg    # Get overridables
2427ec681f3Smrg    name = overrides.get('name') or el.attrib.get('name')
2437ec681f3Smrg    opcode = overrides.get('opcode') or el.attrib.get('opcode')
2447ec681f3Smrg    opcode2 = overrides.get('opcode2') or el.attrib.get('opcode2')
2457ec681f3Smrg    opcode = int(opcode, base=0)
2467ec681f3Smrg    opcode2 = int(opcode2, base=0) if opcode2 else None
2477ec681f3Smrg
2487ec681f3Smrg    # Get explicit sources/dests
2497ec681f3Smrg    tsize = typesize(name)
2507ec681f3Smrg    sources = [build_source(src, i, tsize) for i, src in enumerate(el.findall('src'))]
2517ec681f3Smrg    dests = [Dest(dest.text or '') for dest in el.findall('dest')]
2527ec681f3Smrg
2537ec681f3Smrg    # Get implicit ones
2547ec681f3Smrg    sources = sources + ([Source(i, int(tsize)) for i in range(int(el.attrib.get('srcs', 0)))])
2557ec681f3Smrg    dests = dests + ([Dest()] * int(el.attrib.get('dests', 0)))
2567ec681f3Smrg
2577ec681f3Smrg    # Get staging registers
2587ec681f3Smrg    staging = [build_staging(i, el) for i, el in enumerate(el.findall('sr'))]
2597ec681f3Smrg
2607ec681f3Smrg    # Get immediates
2617ec681f3Smrg    imms = [build_imm(imm) for imm in el.findall('imm')]
2627ec681f3Smrg
2637ec681f3Smrg    modifiers = []
2647ec681f3Smrg    for mod in el:
2657ec681f3Smrg        if mod.tag in MODIFIERS:
2667ec681f3Smrg            modifiers.append(MODIFIERS[mod.tag])
2677ec681f3Smrg        elif mod.tag =='mod':
2687ec681f3Smrg            modifiers.append(build_modifier(mod))
2697ec681f3Smrg
2707ec681f3Smrg    instr = Instruction(name, opcode, opcode2, srcs = sources, dests = dests, immediates = imms, modifiers = modifiers, staging = staging)
2717ec681f3Smrg
2727ec681f3Smrg    instructions.append(instr)
2737ec681f3Smrg
2747ec681f3Smrg# Build all the instructions in a group by duplicating the group itself with
2757ec681f3Smrg# overrides for each distinct instruction
2767ec681f3Smrgdef build_group(el):
2777ec681f3Smrg    for ins in el.findall('ins'):
2787ec681f3Smrg        build_instr(el, overrides = {
2797ec681f3Smrg            'name': ins.attrib['name'],
2807ec681f3Smrg            'opcode': ins.attrib.get('opcode'),
2817ec681f3Smrg            'opcode2': ins.attrib.get('opcode2'),
2827ec681f3Smrg        })
2837ec681f3Smrg
2847ec681f3Smrgdef to_alphanum(name):
2857ec681f3Smrg    substitutions = {
2867ec681f3Smrg        ' ': '_',
2877ec681f3Smrg        '/': '_',
2887ec681f3Smrg        '[': '',
2897ec681f3Smrg        ']': '',
2907ec681f3Smrg        '(': '',
2917ec681f3Smrg        ')': '',
2927ec681f3Smrg        '-': '_',
2937ec681f3Smrg        ':': '',
2947ec681f3Smrg        '.': '',
2957ec681f3Smrg        ',': '',
2967ec681f3Smrg        '=': '',
2977ec681f3Smrg        '>': '',
2987ec681f3Smrg        '#': '',
2997ec681f3Smrg        '&': '',
3007ec681f3Smrg        '*': '',
3017ec681f3Smrg        '"': '',
3027ec681f3Smrg        '+': '',
3037ec681f3Smrg        '\'': '',
3047ec681f3Smrg    }
3057ec681f3Smrg
3067ec681f3Smrg    for i, j in substitutions.items():
3077ec681f3Smrg        name = name.replace(i, j)
3087ec681f3Smrg
3097ec681f3Smrg    return name
3107ec681f3Smrg
3117ec681f3Smrgdef safe_name(name):
3127ec681f3Smrg    name = to_alphanum(name)
3137ec681f3Smrg    if not name[0].isalpha():
3147ec681f3Smrg        name = '_' + name
3157ec681f3Smrg
3167ec681f3Smrg    return name.lower()
3177ec681f3Smrg
3187ec681f3Smrg# Parses out the size part of an opocde name
3197ec681f3Smrgdef typesize(opcode):
3207ec681f3Smrg    if opcode[-3:] == '128':
3217ec681f3Smrg        return 128
3227ec681f3Smrg    if opcode[-2:] == '48':
3237ec681f3Smrg        return 48
3247ec681f3Smrg    elif opcode[-1] == '8':
3257ec681f3Smrg        return 8
3267ec681f3Smrg    else:
3277ec681f3Smrg        try:
3287ec681f3Smrg            return int(opcode[-2:])
3297ec681f3Smrg        except:
3307ec681f3Smrg            return 32
3317ec681f3Smrg
3327ec681f3Smrgfor child in root.findall('enum'):
3337ec681f3Smrg    enums[safe_name(child.attrib['name'])] = build_enum(child)
3347ec681f3Smrg
3357ec681f3SmrgMODIFIERS = {
3367ec681f3Smrg    "inactive_result": Modifier("inactive_result", 22, 4),
3377ec681f3Smrg    "store_segment": Modifier("store_segment", 24, 2),
3387ec681f3Smrg    "regfmt": Modifier("register_format", 24, 3),
3397ec681f3Smrg    "vecsize": Modifier("vector_size", 28, 2),
3407ec681f3Smrg
3417ec681f3Smrg    "slot": Modifier("slot", 30, 3),
3427ec681f3Smrg    "roundmode": Modifier("round_mode", 30, 2),
3437ec681f3Smrg    "result_type": Modifier("result_type", 30, 2),
3447ec681f3Smrg    "saturate": Flag("saturate", 30),
3457ec681f3Smrg    "not_result": Flag("not_result", 30),
3467ec681f3Smrg
3477ec681f3Smrg    "lane_op": Modifier("lane_operation", 32, 2),
3487ec681f3Smrg    "cmp": Modifier("condition", 32, 3),
3497ec681f3Smrg    "clamp": Modifier("clamp", 32, 2),
3507ec681f3Smrg    "sr_count": Modifier("staging_register_count", 33, 3, implied = True),
3517ec681f3Smrg
3527ec681f3Smrg    "subgroup": Modifier("subgroup_size", 36, 2),
3537ec681f3Smrg}
3547ec681f3Smrg
3557ec681f3Smrg# Parse the ISA
3567ec681f3Smrgfor child in root:
3577ec681f3Smrg    if child.tag == 'group':
3587ec681f3Smrg        build_group(child)
3597ec681f3Smrg    elif child.tag == 'ins':
3607ec681f3Smrg        build_instr(child)
3617ec681f3Smrg
3627ec681f3Smrginstruction_dict = { ins.name: ins for ins in instructions }
3637ec681f3Smrg
3647ec681f3Smrg# Validate there are no duplicated instructions
3657ec681f3Smrgif len(instruction_dict) != len(instructions):
3667ec681f3Smrg    import collections
3677ec681f3Smrg    counts = collections.Counter([i.name for i in instructions])
3687ec681f3Smrg    for c in counts:
3697ec681f3Smrg        if counts[c] != 1:
3707ec681f3Smrg            print(f'{c} appeared {counts[c]} times.')
3717ec681f3Smrg
3727ec681f3Smrgassert(len(instruction_dict) == len(instructions))
373