• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#encoding=utf-8
2
3# Copyright (C) 2016 Intel Corporation
4# Copyright (C) 2016 Broadcom
5# Copyright (C) 2020 Collabora, Ltd.
6#
7# Permission is hereby granted, free of charge, to any person obtaining a
8# copy of this software and associated documentation files (the "Software"),
9# to deal in the Software without restriction, including without limitation
10# the rights to use, copy, modify, merge, publish, distribute, sublicense,
11# and/or sell copies of the Software, and to permit persons to whom the
12# Software is furnished to do so, subject to the following conditions:
13#
14# The above copyright notice and this permission notice (including the next
15# paragraph) shall be included in all copies or substantial portions of the
16# Software.
17#
18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
21# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24# IN THE SOFTWARE.
25
26import os
27import textwrap
28import xml.etree.ElementTree as ET
29import sys
30
31tree = ET.parse(os.path.join(os.path.dirname(__file__), 'ISA.xml'))
32root = tree.getroot()
33
34# All instructions in the ISA
35instructions = []
36
37# All immediates in the ISA
38ilut = root.findall('lut')[0]
39assert(ilut.attrib['name'] == "Immediates")
40immediates = [int(imm.text, base=0) for imm in ilut.findall('constant')]
41enums = {}
42
43def xmlbool(s):
44    assert(s.lower() in ["false", "true"])
45    return False if s.lower() == "false" else True
46
47class EnumValue:
48    def __init__(self, value, default):
49        self.value = value
50        self.default = default
51
52class Enum:
53    def __init__(self, name, values):
54        self.name = name
55        self.values = values
56        self.bare_values = [x.value for x in values]
57
58        defaults = [x.value for x in values if x.default]
59        if len(defaults) > 0:
60            assert(len(defaults) == 1)
61            self.default = defaults[0]
62
63def build_enum(el):
64    values = []
65
66    for child in el:
67        if child.tag == 'value':
68            is_default = child.attrib.get('default', False)
69            values.append(EnumValue(child.text, is_default))
70        elif child.tag == 'reserved':
71            values.append(EnumValue("reserved", False))
72
73    return Enum(el.attrib['name'], values)
74
75class Modifier:
76    def __init__(self, name, start, size, implied = False):
77        self.name = name
78        self.start = start
79        self.size = size
80        self.implied = implied
81
82        if size == 1:
83            self.bare_values = ['', name]
84            self.default = 0
85        else:
86            enum = enums[name]
87            self.bare_values = [x.value for x in enum.values]
88            defaults = [x for x in enum.values if x.default]
89            assert(len(defaults) <= 1)
90
91            if len(defaults) > 0:
92                self.default = self.bare_values.index(defaults[0].value)
93            else:
94                self.default = None
95
96def Flag(name, start):
97    return Modifier(name, start, 1)
98
99# Model a single instruction
100class Source:
101    def __init__(self, index, size, is_float = False, swizzle = False, widen = False, lanes = False, lane = None, absneg = False, notted = False, name = ""):
102        self.is_float = is_float or absneg
103        self.size = size
104        self.absneg = absneg
105        self.notted = notted
106        self.swizzle = swizzle
107        self.widen = widen
108        self.lanes = lanes
109        self.lane = lane
110        self.name = name
111
112        self.offset = {}
113        self.bits = {}
114        if absneg:
115            self.offset['neg'] = 32 + 2 + ((2 - index) * 2)
116            self.offset['abs'] = 33 + 2 + ((2 - index) * 2)
117            self.bits['neg'] = 1
118            self.bits['abs'] = 1
119        if notted:
120            self.offset['not'] = 35
121            self.bits['not'] = 1
122        if widen or lanes:
123            self.offset['widen'] = 26 if index == 1 else 36
124            self.bits['widen'] = 4 # XXX: too much?
125        if lane:
126            self.offset['lane'] = self.lane
127            self.bits['lane'] = 2 if size in (8, 32) else 1
128        if swizzle:
129            assert(size in [16, 32])
130            self.offset['swizzle'] = 24 + ((2 - index) * 2)
131            self.bits['swizzle'] = 2
132
133class Dest:
134    def __init__(self, name = ""):
135        self.name = name
136
137class Staging:
138    def __init__(self, read = False, write = False, index = 0, count = 0, flags = True, name = ""):
139        self.name = name
140        self.read = read
141        self.write = write
142        self.count = count
143        self.flags = flags
144
145        # For compatibility
146        self.absneg = False
147        self.swizzle = False
148        self.notted = False
149        self.widen = False
150        self.lanes = False
151        self.lane = False
152        self.size = 32
153
154        assert(index < 2)
155        self.start = 40 if index == 0 else 16
156
157        if not flags:
158            self.encoded_flags = 0
159        elif index > 0:
160            self.encoded_flags = 0xC0
161        else:
162            self.encoded_flags = (0x80 if write else 0) | (0x40 if read else 0)
163
164class Immediate:
165    def __init__(self, name, start, size, signed):
166        self.name = name
167        self.start = start
168        self.size = size
169        self.signed = signed
170
171class Instruction:
172    def __init__(self, name, opcode, opcode2, srcs = [], dests = [], immediates = [], modifiers = [], staging = None):
173        self.name = name
174        self.srcs = srcs
175        self.dests = dests
176        self.opcode = opcode
177        self.opcode2 = opcode2 or 0
178        self.immediates = immediates
179        self.modifiers = modifiers
180        self.staging = staging
181
182        self.secondary_shift = max(len(self.srcs) * 8, 16)
183        self.secondary_mask = 0xF if opcode2 is not None else 0x0
184        if "left" in [x.name for x in self.modifiers]:
185            self.secondary_mask |= 0x100
186        if len(srcs) == 3 and (srcs[1].widen or srcs[1].lanes):
187            self.secondary_mask &= ~0xC # conflicts
188        if opcode == 0x90:
189            # XXX: XMLify this, but disambiguates sign of conversions
190            self.secondary_mask |= 0x10
191        if name.startswith("LOAD.i") or name.startswith("STORE.i"):
192            self.secondary_shift = 27 # Alias with memory_size
193            self.secondary_mask = 0x7
194
195        assert(len(dests) == 0 or not staging)
196        assert(not opcode2 or (opcode2 & self.secondary_mask) == opcode2)
197
198    def __str__(self):
199        return self.name
200
201# Build a single source from XML
202def build_source(el, i, size):
203    lane = el.get('lane', None)
204    if lane == "true":
205        lane = 38 if i == 0 else 36
206    elif lane is not None:
207        lane = int(lane)
208
209    return Source(i, int(el.get('size', size)),
210            absneg = el.get('absneg', False),
211            is_float = el.get('float', False),
212            swizzle = el.get('swizzle', False),
213            widen = el.get('widen', False),
214            lanes = el.get('lanes', False),
215            lane = lane,
216            notted = el.get('not', False),
217            name = el.text or "")
218
219def build_imm(el):
220    return Immediate(el.attrib['name'], int(el.attrib['start']),
221            int(el.attrib['size']), bool(el.attrib.get('signed', False)))
222
223def build_staging(i, el):
224    r = xmlbool(el.attrib.get('read', 'false'))
225    w = xmlbool(el.attrib.get('write', 'false'))
226    count = int(el.attrib.get('count', '0'))
227    flags = xmlbool(el.attrib.get('flags', 'true'))
228
229    return Staging(r, w, i, count, flags, el.text or '')
230
231def build_modifier(el):
232    name = el.attrib['name']
233    start = int(el.attrib['start'])
234    size = int(el.attrib['size'])
235    implied = xmlbool(el.get('implied', 'false'))
236
237    return Modifier(name, start, size, implied)
238
239# Build a single instruction from XML and group based overrides
240def build_instr(el, overrides = {}):
241    # Get overridables
242    name = overrides.get('name') or el.attrib.get('name')
243    opcode = overrides.get('opcode') or el.attrib.get('opcode')
244    opcode2 = overrides.get('opcode2') or el.attrib.get('opcode2')
245    opcode = int(opcode, base=0)
246    opcode2 = int(opcode2, base=0) if opcode2 else None
247
248    # Get explicit sources/dests
249    tsize = typesize(name)
250    sources = [build_source(src, i, tsize) for i, src in enumerate(el.findall('src'))]
251    dests = [Dest(dest.text or '') for dest in el.findall('dest')]
252
253    # Get implicit ones
254    sources = sources + ([Source(i, int(tsize)) for i in range(int(el.attrib.get('srcs', 0)))])
255    dests = dests + ([Dest()] * int(el.attrib.get('dests', 0)))
256
257    # Get staging registers
258    staging = [build_staging(i, el) for i, el in enumerate(el.findall('sr'))]
259
260    # Get immediates
261    imms = [build_imm(imm) for imm in el.findall('imm')]
262
263    modifiers = []
264    for mod in el:
265        if mod.tag in MODIFIERS:
266            modifiers.append(MODIFIERS[mod.tag])
267        elif mod.tag =='mod':
268            modifiers.append(build_modifier(mod))
269
270    instr = Instruction(name, opcode, opcode2, srcs = sources, dests = dests, immediates = imms, modifiers = modifiers, staging = staging)
271
272    instructions.append(instr)
273
274# Build all the instructions in a group by duplicating the group itself with
275# overrides for each distinct instruction
276def build_group(el):
277    for ins in el.findall('ins'):
278        build_instr(el, overrides = {
279            'name': ins.attrib['name'],
280            'opcode': ins.attrib.get('opcode'),
281            'opcode2': ins.attrib.get('opcode2'),
282        })
283
284def to_alphanum(name):
285    substitutions = {
286        ' ': '_',
287        '/': '_',
288        '[': '',
289        ']': '',
290        '(': '',
291        ')': '',
292        '-': '_',
293        ':': '',
294        '.': '',
295        ',': '',
296        '=': '',
297        '>': '',
298        '#': '',
299        '&': '',
300        '*': '',
301        '"': '',
302        '+': '',
303        '\'': '',
304    }
305
306    for i, j in substitutions.items():
307        name = name.replace(i, j)
308
309    return name
310
311def safe_name(name):
312    name = to_alphanum(name)
313    if not name[0].isalpha():
314        name = '_' + name
315
316    return name.lower()
317
318# Parses out the size part of an opocde name
319def typesize(opcode):
320    if opcode[-3:] == '128':
321        return 128
322    if opcode[-2:] == '48':
323        return 48
324    elif opcode[-1] == '8':
325        return 8
326    else:
327        try:
328            return int(opcode[-2:])
329        except:
330            return 32
331
332for child in root.findall('enum'):
333    enums[safe_name(child.attrib['name'])] = build_enum(child)
334
335MODIFIERS = {
336    "inactive_result": Modifier("inactive_result", 22, 4),
337    "store_segment": Modifier("store_segment", 24, 2),
338    "regfmt": Modifier("register_format", 24, 3),
339    "vecsize": Modifier("vector_size", 28, 2),
340
341    "slot": Modifier("slot", 30, 3),
342    "roundmode": Modifier("round_mode", 30, 2),
343    "result_type": Modifier("result_type", 30, 2),
344    "saturate": Flag("saturate", 30),
345    "not_result": Flag("not_result", 30),
346
347    "lane_op": Modifier("lane_operation", 32, 2),
348    "cmp": Modifier("condition", 32, 3),
349    "clamp": Modifier("clamp", 32, 2),
350    "sr_count": Modifier("staging_register_count", 33, 3, implied = True),
351
352    "subgroup": Modifier("subgroup_size", 36, 2),
353}
354
355# Parse the ISA
356for child in root:
357    if child.tag == 'group':
358        build_group(child)
359    elif child.tag == 'ins':
360        build_instr(child)
361
362instruction_dict = { ins.name: ins for ins in instructions }
363
364# Validate there are no duplicated instructions
365if len(instruction_dict) != len(instructions):
366    import collections
367    counts = collections.Counter([i.name for i in instructions])
368    for c in counts:
369        if counts[c] != 1:
370            print(f'{c} appeared {counts[c]} times.')
371
372assert(len(instruction_dict) == len(instructions))
373