• 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, force_enum = None):
77        self.name = name
78        self.start = start
79        self.size = size
80        self.implied = implied
81        self.is_enum = (force_enum is not None) or size > 1
82        self.enum = force_enum or name
83
84        if not self.is_enum:
85            self.bare_values = ['', name]
86            self.default = 0
87        else:
88            self.bare_values = [x.value for x in enums[self.enum].values]
89            defaults = [x for x in enums[self.enum].values if x.default]
90            assert(len(defaults) <= 1)
91
92            if len(defaults) > 0:
93                self.default = self.bare_values.index(defaults[0].value)
94            else:
95                self.default = None
96
97def Flag(name, start):
98    return Modifier(name, start, 1)
99
100# Model a single instruction
101class Source:
102    def __init__(self, index, size, is_float = False, swizzle = False,
103            halfswizzle = False, widen = False, lanes = False, combine = False, lane = None, absneg = False, notted = False, name = ""):
104        self.is_float = is_float or absneg
105        self.start = (index * 8)
106        self.size = size
107        self.absneg = absneg
108        self.notted = notted
109        self.swizzle = swizzle
110        self.halfswizzle = halfswizzle
111        self.widen = widen
112        self.lanes = lanes
113        self.lane = lane
114        self.combine = combine
115        self.name = name
116
117        self.offset = {}
118        self.bits = {}
119        if absneg:
120            self.offset['neg'] = 32 + 2 + ((2 - index) * 2)
121            self.offset['abs'] = 33 + 2 + ((2 - index) * 2)
122            self.bits['neg'] = 1
123            self.bits['abs'] = 1
124        if notted:
125            self.offset['not'] = 35
126            self.bits['not'] = 1
127        if widen or lanes or halfswizzle:
128            self.offset['widen'] = 26 if index == 1 else 36
129            self.bits['widen'] = 4 # XXX: too much?
130        if lane:
131            self.offset['lane'] = self.lane
132            self.bits['lane'] = 2 if size in (8, 32) else 1
133        if swizzle:
134            assert(size in [16, 32])
135            self.offset['swizzle'] = 24 + ((2 - index) * 2)
136            self.bits['swizzle'] = 2
137        if combine:
138            self.offset['combine'] = 37
139            self.bits['combine'] = 3
140
141class Dest:
142    def __init__(self, name = ""):
143        self.name = name
144
145class Staging:
146    def __init__(self, read = False, write = False, count = 0, flags = 'true', name = ""):
147        self.name = name
148        self.read = read
149        self.write = write
150        self.count = count
151        self.flags = (flags != 'false')
152        self.start = 40
153
154        if write and not self.flags:
155            self.start = 16
156
157        # For compatibility
158        self.absneg = False
159        self.swizzle = False
160        self.notted = False
161        self.widen = False
162        self.lanes = False
163        self.lane = False
164        self.halfswizzle = False
165        self.combine = False
166        self.size = 32
167
168        if not self.flags:
169            self.encoded_flags = 0
170        elif flags == 'rw':
171            self.encoded_flags = 0xc0
172        else:
173            assert(flags == 'true')
174            self.encoded_flags = (0x80 if write else 0) | (0x40 if read else 0)
175
176class Immediate:
177    def __init__(self, name, start, size, signed):
178        self.name = name
179        self.start = start
180        self.size = size
181        self.signed = signed
182
183class Instruction:
184    def __init__(self, name, opcode, opcode2, srcs = [], dests = [], immediates = [], modifiers = [], staging = None, unit = None):
185        self.name = name
186        self.srcs = srcs
187        self.dests = dests
188        self.opcode = opcode
189        self.opcode2 = opcode2 or 0
190        self.immediates = immediates
191        self.modifiers = modifiers
192        self.staging = staging
193        self.unit = unit
194        self.is_signed = len(name.split(".")) > 1 and ('s' in name.split(".")[1])
195
196        # Message-passing instruction <===> not ALU instruction
197        self.message = unit not in ["FMA", "CVT", "SFU"]
198
199        self.secondary_shift = max(len(self.srcs) * 8, 16)
200        self.secondary_mask = 0xF if opcode2 is not None else 0x0
201        if "left" in [x.name for x in self.modifiers]:
202            self.secondary_mask |= 0x100
203        if len(srcs) == 3 and (srcs[1].widen or srcs[1].lanes):
204            self.secondary_mask &= ~0xC # conflicts
205        if opcode == 0x90:
206            # XXX: XMLify this, but disambiguates sign of conversions
207            self.secondary_mask |= 0x10
208        if name.startswith("LOAD.i") or name.startswith("STORE.i") or name.startswith("LD_BUFFER.i"):
209            self.secondary_shift = 27 # Alias with memory_size
210            self.secondary_mask = 0x7
211        if "descriptor_type" in [x.name for x in self.modifiers]:
212            self.secondary_mask = 0x3
213            self.secondary_shift = 37
214        elif "memory_width" in [x.name for x in self.modifiers]:
215            self.secondary_mask = 0x7
216            self.secondary_shift = 27
217
218        assert(len(dests) == 0 or not staging)
219        assert(not opcode2 or (opcode2 & self.secondary_mask) == opcode2)
220
221    def __str__(self):
222        return self.name
223
224# Build a single source from XML
225def build_source(el, i, size):
226    lane = el.get('lane', None)
227    if lane == "true":
228        lane = 38 if i == 0 else 36
229    elif lane is not None:
230        lane = int(lane)
231
232    return Source(i, int(el.get('size', size)),
233            absneg = el.get('absneg', False),
234            is_float = el.get('float', False),
235            swizzle = el.get('swizzle', False),
236            halfswizzle = el.get('halfswizzle', False),
237            widen = el.get('widen', False),
238            lanes = el.get('lanes', False),
239            combine = el.get('combine', False),
240            lane = lane,
241            notted = el.get('not', False),
242            name = el.text or "")
243
244def build_imm(el):
245    return Immediate(el.attrib['name'], int(el.attrib['start']),
246            int(el.attrib['size']), bool(el.attrib.get('signed', False)))
247
248def build_staging(i, el):
249    r = xmlbool(el.attrib.get('read', 'false'))
250    w = xmlbool(el.attrib.get('write', 'false'))
251    count = int(el.attrib.get('count', '0'))
252    flags = el.attrib.get('flags', 'true')
253
254    return Staging(r, w, count, flags, el.text or '')
255
256def build_modifier(el):
257    name = el.attrib['name']
258    start = int(el.attrib['start'])
259    size = int(el.attrib['size'])
260    implied = xmlbool(el.get('implied', 'false'))
261
262    return Modifier(name, start, size, implied)
263
264# Build a single instruction from XML and group based overrides
265def build_instr(el, overrides = {}):
266    # Get overridables
267    name = overrides.get('name') or el.attrib.get('name')
268    opcode = overrides.get('opcode') or el.attrib.get('opcode')
269    opcode2 = overrides.get('opcode2') or el.attrib.get('opcode2')
270    unit = overrides.get('unit') or el.attrib.get('unit')
271    opcode = int(opcode, base=0)
272    opcode2 = int(opcode2, base=0) if opcode2 else None
273
274    # Get explicit sources/dests
275    tsize = typesize(name)
276    sources = []
277    i = 0
278
279    for src in el.findall('src'):
280        built = build_source(src, i, tsize)
281        sources += [built]
282
283        # 64-bit sources in a 32-bit (message) instruction count as two slots
284        # Affects BLEND, ST_CVT
285        if tsize != 64 and built.size == 64:
286            i = i + 2
287        else:
288            i = i + 1
289
290    dests = [Dest(dest.text or '') for dest in el.findall('dest')]
291
292    # Get implicit ones
293    sources = sources + ([Source(i, int(tsize)) for i in range(int(el.attrib.get('srcs', 0)))])
294    dests = dests + ([Dest()] * int(el.attrib.get('dests', 0)))
295
296    # Get staging registers
297    staging = [build_staging(i, el) for i, el in enumerate(el.findall('sr'))]
298
299    # Get immediates
300    imms = [build_imm(imm) for imm in el.findall('imm')]
301
302    modifiers = []
303    for mod in el:
304        if mod.tag in MODIFIERS:
305            modifiers.append(MODIFIERS[mod.tag])
306        elif mod.tag =='mod':
307            modifiers.append(build_modifier(mod))
308
309    instr = Instruction(name, opcode, opcode2, srcs = sources, dests = dests, immediates = imms, modifiers = modifiers, staging = staging, unit = unit)
310
311    instructions.append(instr)
312
313# Build all the instructions in a group by duplicating the group itself with
314# overrides for each distinct instruction
315def build_group(el):
316    for ins in el.findall('ins'):
317        build_instr(el, overrides = {
318            'name': ins.attrib['name'],
319            'opcode': ins.attrib.get('opcode'),
320            'opcode2': ins.attrib.get('opcode2'),
321            'unit': ins.attrib.get('unit'),
322        })
323
324def to_alphanum(name):
325    substitutions = {
326        ' ': '_',
327        '/': '_',
328        '[': '',
329        ']': '',
330        '(': '',
331        ')': '',
332        '-': '_',
333        ':': '',
334        '.': '',
335        ',': '',
336        '=': '',
337        '>': '',
338        '#': '',
339        '&': '',
340        '*': '',
341        '"': '',
342        '+': '',
343        '\'': '',
344    }
345
346    for i, j in substitutions.items():
347        name = name.replace(i, j)
348
349    return name
350
351def safe_name(name):
352    name = to_alphanum(name)
353    if not name[0].isalpha():
354        name = '_' + name
355
356    return name.lower()
357
358# Parses out the size part of an opocde name
359def typesize(opcode):
360    if opcode[-3:] == '128':
361        return 128
362    if opcode[-2:] == '48':
363        return 48
364    elif opcode[-1] == '8':
365        return 8
366    else:
367        try:
368            return int(opcode[-2:])
369        except:
370            return 32
371
372for child in root.findall('enum'):
373    enums[safe_name(child.attrib['name'])] = build_enum(child)
374
375MODIFIERS = {
376    # Texture instructions share a common encoding
377    "wide_indices": Flag("wide_indices", 8),
378    "array_enable": Flag("array_enable", 10),
379    "texel_offset": Flag("texel_offset", 11),
380    "shadow": Flag("shadow", 12),
381    "integer_coordinates": Flag("integer_coordinates", 13),
382    "fetch_component": Modifier("fetch_component", 14, 2),
383    "lod_mode": Modifier("lod_mode", 13, 3),
384    "lod_bias_disable": Modifier("lod_mode", 13, 1),
385    "lod_clamp_disable": Modifier("lod_mode", 14, 1),
386    "write_mask": Modifier("write_mask", 22, 4),
387    "register_type": Modifier("register_type", 26, 2),
388    "dimension": Modifier("dimension", 28, 2),
389    "skip": Flag("skip", 39),
390    "register_width": Modifier("register_width", 46, 1, force_enum = "register_width"),
391    "secondary_register_width": Modifier("secondary_register_width", 47, 1, force_enum = "register_width"),
392    "vartex_register_width": Modifier("varying_texture_register_width", 24, 2),
393
394    "atom_opc": Modifier("atomic_operation", 22, 4),
395    "atom_opc_1": Modifier("atomic_operation_with_1", 22, 4),
396    "inactive_result": Modifier("inactive_result", 22, 4),
397    "memory_access": Modifier("memory_access", 24, 2),
398    "regfmt": Modifier("register_format", 24, 3),
399    "source_format": Modifier("source_format", 24, 4),
400    "vecsize": Modifier("vector_size", 28, 2),
401
402    "slot": Modifier("slot", 30, 3),
403    "roundmode": Modifier("round_mode", 30, 2),
404    "result_type": Modifier("result_type", 30, 2),
405    "saturate": Flag("saturate", 30),
406    "not_result": Flag("not_result", 30),
407
408    "lane_op": Modifier("lane_operation", 32, 2),
409    "cmp": Modifier("condition", 32, 3),
410    "clamp": Modifier("clamp", 32, 2),
411    "sr_count": Modifier("staging_register_count", 33, 3, implied = True),
412    "sample_and_update": Modifier("sample_and_update_mode", 33, 3),
413    "sr_write_count": Modifier("staging_register_write_count", 36, 3, implied = True),
414
415    "conservative": Flag("conservative", 35),
416    "subgroup": Modifier("subgroup_size", 36, 4),
417    "update": Modifier("update_mode", 36, 2),
418    "sample": Modifier("sample_mode", 38, 2),
419}
420
421# Parse the ISA
422for child in root:
423    if child.tag == 'group':
424        build_group(child)
425    elif child.tag == 'ins':
426        build_instr(child)
427
428instruction_dict = { ins.name: ins for ins in instructions }
429
430# Validate there are no duplicated instructions
431if len(instruction_dict) != len(instructions):
432    import collections
433    counts = collections.Counter([i.name for i in instructions])
434    for c in counts:
435        if counts[c] != 1:
436            print(f'{c} appeared {counts[c]} times.')
437
438assert(len(instruction_dict) == len(instructions))
439