Mesa (main): pan/va: Add ISA.xml parser and support code

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Tue Jul 27 20:33:38 UTC 2021


Module: Mesa
Branch: main
Commit: 02e378b628f8f76e5688f134569d333ebd9180dd
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=02e378b628f8f76e5688f134569d333ebd9180dd

Author: Alyssa Rosenzweig <alyssa at collabora.com>
Date:   Fri Jul 16 10:49:35 2021 -0400

pan/va: Add ISA.xml parser and support code

This Python module parses the ISA.xml file added in the previous
commits, extracts all the useful information, and combines it with
extra annotations. In total, it provides a programmatic way to interface
with the Valhall instruction set.

   from valhall import instructions, enums

Signed-off-by: Alyssa Rosenzweig <alyssa at collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/12025>

---

 src/panfrost/bifrost/valhall/valhall.py | 362 ++++++++++++++++++++++++++++++++
 1 file changed, 362 insertions(+)

diff --git a/src/panfrost/bifrost/valhall/valhall.py b/src/panfrost/bifrost/valhall/valhall.py
new file mode 100644
index 00000000000..b1e4f06193f
--- /dev/null
+++ b/src/panfrost/bifrost/valhall/valhall.py
@@ -0,0 +1,362 @@
+#encoding=utf-8
+
+# Copyright (C) 2016 Intel Corporation
+# Copyright (C) 2016 Broadcom
+# Copyright (C) 2020 Collabora, Ltd.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+import os
+import textwrap
+import xml.etree.ElementTree as ET
+import sys
+
+tree = ET.parse(os.path.join(os.path.dirname(__file__), 'ISA.xml'))
+root = tree.getroot()
+
+# All instructions in the ISA
+instructions = []
+
+# All immediates in the ISA
+ilut = root.findall('lut')[0]
+assert(ilut.attrib['name'] == "Immediates")
+immediates = [int(imm.text, base=0) for imm in ilut.findall('constant')]
+enums = {}
+
+def xmlbool(s):
+    assert(s.lower() in ["false", "true"])
+    return False if s.lower() == "false" else True
+
+class EnumValue:
+    def __init__(self, value, default):
+        self.value = value
+        self.default = default
+
+class Enum:
+    def __init__(self, name, values):
+        self.name = name
+        self.values = values
+        self.bare_values = [x.value for x in values]
+
+        defaults = [x.value for x in values if x.default]
+        if len(defaults) > 0:
+            assert(len(defaults) == 1)
+            self.default = defaults[0]
+
+def build_enum(el):
+    values = []
+
+    for child in el:
+        if child.tag == 'value':
+            is_default = child.attrib.get('default', False)
+            values.append(EnumValue(child.text, is_default))
+        elif child.tag == 'reserved':
+            values.append(EnumValue("reserved", False))
+
+    return Enum(el.attrib['name'], values)
+
+class Modifier:
+    def __init__(self, name, start, size, implied = False):
+        self.name = name
+        self.start = start
+        self.size = size
+        self.implied = implied
+
+        if size == 1:
+            self.bare_values = ['', name]
+            self.default = 0
+        else:
+            enum = enums[name]
+            self.bare_values = [x.value for x in enum.values]
+            defaults = [x for x in enum.values if x.default]
+            assert(len(defaults) <= 1)
+            
+            if len(defaults) > 0:
+                self.default = self.bare_values.index(defaults[0].value)
+            else:
+                self.default = None
+
+def Flag(name, start):
+    return Modifier(name, start, 1)
+
+# Model a single instruction
+class Source:
+    def __init__(self, index, size, is_float = False, swizzle = False, widen = False, lanes = False, lane = None, absneg = False, notted = False, name = ""):
+        self.is_float = is_float or absneg
+        self.size = size
+        self.absneg = absneg
+        self.notted = notted
+        self.swizzle = swizzle
+        self.widen = widen
+        self.lanes = lanes
+        self.lane = lane
+        self.name = name
+
+        self.offset = {}
+        self.bits = {}
+        if absneg:
+            self.offset['neg'] = 32 + 2 + ((2 - index) * 2)
+            self.offset['abs'] = 33 + 2 + ((2 - index) * 2)
+            self.bits['neg'] = 1
+            self.bits['abs'] = 1
+        if notted:
+            self.offset['not'] = 35
+            self.bits['not'] = 1
+        if widen or lanes:
+            self.offset['widen'] = 26 if index == 1 else 36
+            self.bits['widen'] = 4 # XXX: too much?
+        if lane:
+            self.offset['lane'] = self.lane
+            self.bits['lane'] = 2 if size in (8, 32) else 1
+        if swizzle:
+            assert(size in [16, 32])
+            self.offset['swizzle'] = 24 + ((2 - index) * 2)
+            self.bits['swizzle'] = 2
+
+class Dest:
+    def __init__(self, name = ""):
+        self.name = name
+
+class Staging:
+    def __init__(self, read = False, write = False, index = 0, count = 0, flags = True, name = ""):
+        self.name = name
+        self.read = read
+        self.write = write
+        self.count = count
+        self.flags = flags
+
+        # For compatibility
+        self.absneg = False
+        self.swizzle = False
+        self.notted = False
+        self.widen = False
+        self.lanes = False
+        self.lane = False
+        self.size = 32
+
+        assert(index < 2)
+        self.start = 40 if index == 0 else 16
+
+        if not flags:
+            self.encoded_flags = 0
+        elif index > 0:
+            self.encoded_flags = 0xC0
+        else:
+            self.encoded_flags = (0x80 if write else 0) | (0x40 if read else 0)
+
+class Immediate:
+    def __init__(self, name, start, size, signed):
+        self.name = name
+        self.start = start
+        self.size = size
+        self.signed = signed
+
+class Instruction:
+    def __init__(self, name, opcode, opcode2, srcs = [], dests = [], immediates = [], modifiers = [], staging = None):
+        self.name = name
+        self.srcs = srcs
+        self.dests = dests
+        self.opcode = opcode
+        self.opcode2 = opcode2 or 0
+        self.immediates = immediates
+        self.modifiers = modifiers
+        self.staging = staging
+
+        self.secondary_shift = max(len(self.srcs) * 8, 16)
+        self.secondary_mask = 0xF if opcode2 is not None else 0x0
+        if "left" in [x.name for x in self.modifiers]:
+            self.secondary_mask |= 0x100
+        if len(srcs) == 3 and (srcs[1].widen or srcs[1].lanes):
+            self.secondary_mask &= ~0xC # conflicts
+        if opcode == 0x90:
+            # XXX: XMLify this, but disambiguates sign of conversions
+            self.secondary_mask |= 0x10
+        if name.startswith("LOAD.i") or name.startswith("STORE.i"):
+            self.secondary_shift = 27 # Alias with memory_size
+            self.secondary_mask = 0x7
+
+        assert(len(dests) == 0 or not staging)
+        assert(not opcode2 or (opcode2 & self.secondary_mask) == opcode2)
+
+    def __str__(self):
+        return self.name
+
+# Build a single source from XML
+def build_source(el, i, size):
+    lane = el.get('lane', None)
+    if lane == "true":
+        lane = 38 if i == 0 else 36
+    elif lane is not None:
+        lane = int(lane)
+
+    return Source(i, int(el.get('size', size)),
+            absneg = el.get('absneg', False),
+            is_float = el.get('float', False),
+            swizzle = el.get('swizzle', False),
+            widen = el.get('widen', False),
+            lanes = el.get('lanes', False),
+            lane = lane,
+            notted = el.get('not', False),
+            name = el.text or "")
+
+def build_imm(el):
+    return Immediate(el.attrib['name'], int(el.attrib['start']),
+            int(el.attrib['size']), bool(el.attrib.get('signed', False)))
+
+def build_staging(i, el):
+    r = xmlbool(el.attrib.get('read', 'false'))
+    w = xmlbool(el.attrib.get('write', 'false'))
+    count = int(el.attrib.get('count', '0'))
+    flags = xmlbool(el.attrib.get('flags', 'true'))
+
+    return Staging(r, w, i, count, flags, el.text or '')
+
+def build_modifier(el):
+    name = el.attrib['name']
+    start = int(el.attrib['start'])
+    size = int(el.attrib['size'])
+    implied = xmlbool(el.get('implied', 'false'))
+
+    return Modifier(name, start, size, implied)
+
+# Build a single instruction from XML and group based overrides
+def build_instr(el, overrides = {}):
+    # Get overridables
+    name = overrides.get('name') or el.attrib.get('name')
+    opcode = overrides.get('opcode') or el.attrib.get('opcode')
+    opcode2 = overrides.get('opcode2') or el.attrib.get('opcode2')
+    opcode = int(opcode, base=0)
+    opcode2 = int(opcode2, base=0) if opcode2 else None
+
+    # Get explicit sources/dests
+    tsize = typesize(name)
+    sources = [build_source(src, i, tsize) for i, src in enumerate(el.findall('src'))]
+    dests = [Dest(dest.text or '') for dest in el.findall('dest')]
+
+    # Get implicit ones
+    sources = sources + ([Source(i, int(tsize)) for i in range(int(el.attrib.get('srcs', 0)))])
+    dests = dests + ([Dest()] * int(el.attrib.get('dests', 0)))
+
+    # Get staging registers
+    staging = [build_staging(i, el) for i, el in enumerate(el.findall('sr'))]
+
+    # Get immediates
+    imms = [build_imm(imm) for imm in el.findall('imm')]
+
+    modifiers = []
+    for mod in el:
+        if mod.tag in MODIFIERS:
+            modifiers.append(MODIFIERS[mod.tag])
+        elif mod.tag =='mod':
+            modifiers.append(build_modifier(mod))
+
+    instr = Instruction(name, opcode, opcode2, srcs = sources, dests = dests, immediates = imms, modifiers = modifiers, staging = staging)
+
+    instructions.append(instr)
+
+# Build all the instructions in a group by duplicating the group itself with
+# overrides for each distinct instruction
+def build_group(el):
+    for ins in el.findall('ins'):
+        build_instr(el, overrides = {
+            'name': ins.attrib['name'],
+            'opcode': ins.attrib.get('opcode'),
+            'opcode2': ins.attrib.get('opcode2'),
+        })
+
+def to_alphanum(name):
+    substitutions = {
+        ' ': '_',
+        '/': '_',
+        '[': '',
+        ']': '',
+        '(': '',
+        ')': '',
+        '-': '_',
+        ':': '',
+        '.': '',
+        ',': '',
+        '=': '',
+        '>': '',
+        '#': '',
+        '&': '',
+        '*': '',
+        '"': '',
+        '+': '',
+        '\'': '',
+    }
+
+    for i, j in substitutions.items():
+        name = name.replace(i, j)
+
+    return name
+
+def safe_name(name):
+    name = to_alphanum(name)
+    if not name[0].isalpha():
+        name = '_' + name
+
+    return name.lower()
+
+# Parses out the size part of an opocde name
+def typesize(opcode):
+    if opcode[-3:] == '128':
+        return 128
+    if opcode[-2:] == '48':
+        return 48
+    elif opcode[-1] == '8':
+        return 8
+    else:
+        try:
+            return int(opcode[-2:])
+        except:
+            return 32
+
+for child in root.findall('enum'):
+    enums[safe_name(child.attrib['name'])] = build_enum(child)
+
+MODIFIERS = {
+    "inactive_result": Modifier("inactive_result", 22, 4),
+    "store_segment": Modifier("store_segment", 24, 2),
+    "regfmt": Modifier("register_format", 24, 3),
+    "vecsize": Modifier("vector_size", 28, 2),
+
+    "slot": Modifier("slot", 30, 3),
+    "roundmode": Modifier("round_mode", 30, 2),
+    "result_type": Modifier("result_type", 30, 2),
+    "saturate": Flag("saturate", 30),
+    "not_result": Flag("not_result", 30),
+
+    "lane_op": Modifier("lane_operation", 32, 2),
+    "cmp": Modifier("condition", 32, 3),
+    "clamp": Modifier("clamp", 32, 2),
+    "sr_count": Modifier("staging_register_count", 33, 3, implied = True),
+
+    "subgroup": Modifier("subgroup_size", 36, 2),
+}
+
+# Parse the ISA
+for child in root:
+    if child.tag == 'group':
+        build_group(child)
+    elif child.tag == 'ins':
+        build_instr(child)
+
+instruction_dict = { ins.name: ins for ins in instructions }



More information about the mesa-commit mailing list