• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#
2# Copyright (C) 2020 Collabora, Ltd.
3#
4# Permission is hereby granted, free of charge, to any person obtaining a
5# copy of this software and associated documentation files (the "Software"),
6# to deal in the Software without restriction, including without limitation
7# the rights to use, copy, modify, merge, publish, distribute, sublicense,
8# and/or sell copies of the Software, and to permit persons to whom the
9# Software is furnished to do so, subject to the following conditions:
10#
11# The above copyright notice and this permission notice (including the next
12# paragraph) shall be included in all copies or substantial portions of the
13# Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21# IN THE SOFTWARE.
22
23# Useful for autogeneration
24COPYRIGHT = """/*
25 * Copyright (C) 2020 Collabora, Ltd.
26 *
27 * Permission is hereby granted, free of charge, to any person obtaining a
28 * copy of this software and associated documentation files (the "Software"),
29 * to deal in the Software without restriction, including without limitation
30 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
31 * and/or sell copies of the Software, and to permit persons to whom the
32 * Software is furnished to do so, subject to the following conditions:
33 *
34 * The above copyright notice and this permission notice (including the next
35 * paragraph) shall be included in all copies or substantial portions of the
36 * Software.
37 *
38 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
41 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
42 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
43 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
44 * SOFTWARE.
45 */
46
47/* Autogenerated file, do not edit */
48
49"""
50
51# Parse instruction set XML into a normalized form for processing
52
53import xml.etree.ElementTree as ET
54import copy
55import itertools
56from collections import OrderedDict
57
58def parse_cond(cond, aliased = False):
59    if cond.tag == 'reserved':
60        return None
61
62    if cond.attrib.get('alias', False) and not aliased:
63        return ['alias', parse_cond(cond, True)]
64
65    if 'left' in cond.attrib:
66        return [cond.tag, cond.attrib['left'], cond.attrib['right']]
67    else:
68        return [cond.tag] + [parse_cond(x) for x in cond.findall('*')]
69
70def parse_exact(obj):
71    return [int(obj.attrib['mask'], 0), int(obj.attrib['exact'], 0)]
72
73def parse_derived(obj):
74    out = []
75
76    for deriv in obj.findall('derived'):
77        loc = [int(deriv.attrib['start']), int(deriv.attrib['size'])]
78        count = 1 << loc[1]
79
80        opts = [parse_cond(d) for d in deriv.findall('*')]
81        default = [None] * count
82        opts_fit = (opts + default)[0:count]
83
84        out.append([loc, opts_fit])
85
86    return out
87
88def parse_modifiers(obj, include_pseudo):
89    out = []
90
91    for mod in obj.findall('mod'):
92        if mod.attrib.get('pseudo', False) and not include_pseudo:
93            continue
94
95        name = mod.attrib['name']
96        start = mod.attrib.get('start', None)
97        size = int(mod.attrib['size'])
98
99        if start is not None:
100            start = int(start)
101
102        opts = [x.text if x.tag == 'opt' else x.tag for x in mod.findall('*')]
103
104        if len(opts) == 0:
105            assert('opt' in mod.attrib)
106            opts = ['none', mod.attrib['opt']]
107
108        # Find suitable default
109        default = mod.attrib.get('default', 'none' if 'none' in opts else None)
110
111        # Pad out as reserved
112        count = (1 << size)
113        opts = (opts + (['reserved'] * count))[0:count]
114        out.append([[name, start, size], default, opts])
115
116    return out
117
118def parse_copy(enc, existing):
119    for node in enc.findall('copy'):
120        name = node.get('name')
121        for ex in existing:
122            if ex[0][0] == name:
123                ex[0][1] = node.get('start')
124
125def parse_instruction(ins, include_pseudo):
126    common = {
127            'srcs': [],
128            'modifiers': [],
129            'immediates': [],
130            'swaps': [],
131            'derived': [],
132            'staging': ins.attrib.get('staging', '').split('=')[0],
133            'staging_count': ins.attrib.get('staging', '=0').split('=')[1],
134            'dests': int(ins.attrib.get('dests', '1')),
135            'unused': ins.attrib.get('unused', False),
136            'pseudo': ins.attrib.get('pseudo', False),
137            'message': ins.attrib.get('message', 'none'),
138            'last': ins.attrib.get('last', False),
139            'table': ins.attrib.get('table', False),
140    }
141
142    if 'exact' in ins.attrib:
143        common['exact'] = parse_exact(ins)
144
145    for src in ins.findall('src'):
146        if src.attrib.get('pseudo', False) and not include_pseudo:
147            continue
148
149        mask = int(src.attrib['mask'], 0) if ('mask' in src.attrib) else 0xFF
150        common['srcs'].append([int(src.attrib['start'], 0), mask])
151
152    for imm in ins.findall('immediate'):
153        if imm.attrib.get('pseudo', False) and not include_pseudo:
154            continue
155
156        start = int(imm.attrib['start']) if 'start' in imm.attrib else None
157        common['immediates'].append([imm.attrib['name'], start, int(imm.attrib['size'])])
158
159    common['derived'] = parse_derived(ins)
160    common['modifiers'] = parse_modifiers(ins, include_pseudo)
161
162    for swap in ins.findall('swap'):
163        lr = [int(swap.get('left')), int(swap.get('right'))]
164        cond = parse_cond(swap.findall('*')[0])
165        rewrites = {}
166
167        for rw in swap.findall('rewrite'):
168            mp = {}
169
170            for m in rw.findall('map'):
171                mp[m.attrib['from']] = m.attrib['to']
172
173            rewrites[rw.attrib['name']] = mp
174
175        common['swaps'].append([lr, cond, rewrites])
176
177    encodings = ins.findall('encoding')
178    variants = []
179
180    if len(encodings) == 0:
181        variants = [[None, common]]
182    else:
183        for enc in encodings:
184            variant = copy.deepcopy(common)
185            assert(len(variant['derived']) == 0)
186
187            variant['exact'] = parse_exact(enc)
188            variant['derived'] = parse_derived(enc)
189            parse_copy(enc, variant['modifiers'])
190
191            cond = parse_cond(enc.findall('*')[0])
192            variants.append([cond, variant])
193
194    return variants
195
196def parse_instructions(xml, include_unused = False, include_pseudo = False):
197    final = {}
198    instructions = ET.parse(xml).getroot().findall('ins')
199
200    for ins in instructions:
201        parsed = parse_instruction(ins, include_pseudo)
202
203        # Some instructions are for useful disassembly only and can be stripped
204        # out of the compiler, particularly useful for release builds
205        if parsed[0][1]["unused"] and not include_unused:
206            continue
207
208        # On the other hand, some instructions are only for the IR, not disassembly
209        if parsed[0][1]["pseudo"] and not include_pseudo:
210            continue
211
212        final[ins.attrib['name']] = parsed
213
214    return final
215
216# Expand out an opcode name to something C-escaped
217
218def opname_to_c(name):
219    return name.lower().replace('*', 'fma_').replace('+', 'add_').replace('.', '_')
220
221# Expand out distinct states to distrinct instructions, with a placeholder
222# condition for instructions with a single state
223
224def expand_states(instructions):
225    out = {}
226
227    for ins in instructions:
228        c = instructions[ins]
229
230        for ((test, desc), i) in zip(c, range(len(c))):
231            # Construct a name for the state
232            name = ins + (('.' + str(i)) if len(c) > 1 else '')
233
234            out[name] = (ins, test if test is not None else [], desc)
235
236    return out
237
238# Drop keys used for packing to simplify IR representation, so we can check for
239# equivalence easier
240
241def simplify_to_ir(ins):
242    return {
243            'staging': ins['staging'],
244            'srcs': len(ins['srcs']),
245            'dests': ins['dests'],
246            'modifiers': [[m[0][0], m[2]] for m in ins['modifiers']],
247            'immediates': [m[0] for m in ins['immediates']]
248        }
249
250# Converstions to integers default to rounding-to-zero
251# All other opcodes default to rounding to nearest even
252def default_round_to_zero(name):
253    # 8-bit int to float is exact
254    subs = ['_TO_U', '_TO_S', '_TO_V2U', '_TO_V2S', '_TO_V4U', '_TO_V4S']
255    return any([x in name for x in subs])
256
257def combine_ir_variants(instructions, key):
258    seen = [op for op in instructions.keys() if op[1:] == key]
259    variant_objs = [[simplify_to_ir(Q[1]) for Q in instructions[x]] for x in seen]
260    variants = sum(variant_objs, [])
261
262    # Accumulate modifiers across variants
263    modifiers = {}
264
265    for s in variants[0:]:
266        # Check consistency
267        assert(s['srcs'] == variants[0]['srcs'])
268        assert(s['dests'] == variants[0]['dests'])
269        assert(s['immediates'] == variants[0]['immediates'])
270        assert(s['staging'] == variants[0]['staging'])
271
272        for name, opts in s['modifiers']:
273            if name not in modifiers:
274                modifiers[name] = copy.deepcopy(opts)
275            else:
276                modifiers[name] += opts
277
278    # Great, we've checked srcs/immediates are consistent and we've summed over
279    # modifiers
280    return {
281            'key': key,
282            'srcs': variants[0]['srcs'],
283            'dests': variants[0]['dests'],
284            'staging': variants[0]['staging'],
285            'immediates': sorted(variants[0]['immediates']),
286            'modifiers': modifiers,
287            'v': len(variants),
288            'ir': variants,
289            'rtz': default_round_to_zero(key)
290        }
291
292# Partition instructions to mnemonics, considering units and variants
293# equivalent.
294
295def partition_mnemonics(instructions):
296    key_func = lambda x: x[1:]
297    sorted_instrs = sorted(instructions.keys(), key = key_func)
298    partitions = itertools.groupby(sorted_instrs, key_func)
299    return { k: combine_ir_variants(instructions, k) for k, v in partitions }
300
301# Generate modifier lists, by accumulating all the possible modifiers, and
302# deduplicating thus assigning canonical enum values. We don't try _too_ hard
303# to be clever, but by preserving as much of the original orderings as
304# possible, later instruction encoding is simplified a bit.  Probably a micro
305# optimization but we have to pick _some_ ordering, might as well choose the
306# most convenient.
307#
308# THIS MUST BE DETERMINISTIC
309
310def order_modifiers(ir_instructions):
311    out = {}
312
313    # modifier name -> (list of option strings)
314    modifier_lists = {}
315
316    for ins in sorted(ir_instructions):
317        modifiers = ir_instructions[ins]["modifiers"]
318
319        for name in modifiers:
320            name_ = name[0:-1] if name[-1] in "0123" else name
321
322            if name_ not in modifier_lists:
323                modifier_lists[name_] = copy.deepcopy(modifiers[name])
324            else:
325                modifier_lists[name_] += modifiers[name]
326
327    for mod in modifier_lists:
328        lst = list(OrderedDict.fromkeys(modifier_lists[mod]))
329
330        # Ensure none is false for booleans so the builder makes sense
331        if len(lst) == 2 and lst[1] == "none":
332            lst.reverse()
333        elif mod == "table":
334            # We really need a zero sentinel to materialize DTSEL
335            assert(lst[2] == "none")
336            lst[2] = lst[0]
337            lst[0] = "none"
338
339        out[mod] = lst
340
341    return out
342
343# Count sources for a simplified (IR) instruction, including a source for a
344# staging register if necessary
345def src_count(op):
346    staging = 1 if (op["staging"] in ["r", "rw"]) else 0
347    return op["srcs"] + staging
348
349# Parses out the size part of an opocde name
350def typesize(opcode):
351    if opcode[-3:] == '128':
352        return 128
353    if opcode[-2:] == '48':
354        return 48
355    elif opcode[-1] == '8':
356        return 8
357    else:
358        try:
359            return int(opcode[-2:])
360        except:
361            return 32
362