Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/freedreno/isa/isa.py
4564 views
1
#
2
# Copyright © 2020 Google, Inc.
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
from xml.etree import ElementTree
24
import os
25
import re
26
27
def dbg(str):
28
if False:
29
print(str)
30
31
class BitSetPattern(object):
32
"""Class that encapsulated the pattern matching, ie.
33
the match/dontcare/mask bitmasks. The following
34
rules should hold
35
36
(match ^ dontcare) == 0
37
(match || dontcare) == mask
38
39
For a leaf node, the mask should be (1 << size) - 1
40
(ie. all bits set)
41
"""
42
def __init__(self, bitset):
43
self.match = bitset.match
44
self.dontcare = bitset.dontcare
45
self.mask = bitset.mask
46
self.field_mask = bitset.field_mask;
47
48
def merge(self, pattern):
49
p = BitSetPattern(pattern)
50
p.match = p.match | self.match
51
p.dontcare = p.dontcare | self.dontcare
52
p.mask = p.mask | self.mask
53
p.field_mask = p.field_mask | self.field_mask
54
return p
55
56
def defined_bits(self):
57
return self.match | self.dontcare | self.mask | self.field_mask
58
59
def get_bitrange(field):
60
if 'pos' in field.attrib:
61
assert('low' not in field.attrib)
62
assert('high' not in field.attrib)
63
low = int(field.attrib['pos'])
64
high = low
65
else:
66
low = int(field.attrib['low'])
67
high = int(field.attrib['high'])
68
assert low <= high
69
return low, high
70
71
def extract_pattern(xml, name, is_defined_bits=None):
72
low, high = get_bitrange(xml)
73
mask = ((1 << (1 + high - low)) - 1) << low
74
75
patstr = xml.text.strip()
76
77
assert (len(patstr) == (1 + high - low)), "Invalid {} length in {}: {}..{}".format(xml.tag, name, low, high)
78
if is_defined_bits is not None:
79
assert not is_defined_bits(mask), "Redefined bits in {} {}: {}..{}".format(xml.tag, name, low, high);
80
81
match = 0;
82
dontcare = 0
83
84
for n in range(0, len(patstr)):
85
match = match << 1
86
dontcare = dontcare << 1
87
if patstr[n] == '1':
88
match |= 1
89
elif patstr[n] == 'x':
90
dontcare |= 1
91
elif patstr[n] != '0':
92
assert 0, "Invalid {} character in {}: {}".format(xml.tag, name, patstr[n])
93
94
dbg("{}: {}.{} => {:016x} / {:016x} / {:016x}".format(xml.tag, name, patstr, match << low, dontcare << low, mask))
95
96
return match << low, dontcare << low, mask
97
98
def get_c_name(name):
99
return name.lower().replace('#', '__').replace('-', '_').replace('.', '_')
100
101
class BitSetField(object):
102
"""Class that encapsulates a field defined in a bitset
103
"""
104
def __init__(self, isa, xml):
105
self.isa = isa
106
self.low, self.high = get_bitrange(xml)
107
self.name = xml.attrib['name']
108
self.type = xml.attrib['type']
109
self.params = []
110
for param in xml.findall('param'):
111
aas = name = param.attrib['name']
112
if 'as' in param.attrib:
113
aas = param.attrib['as']
114
self.params.append([name, aas])
115
self.expr = None
116
self.display = None
117
if 'display' in xml.attrib:
118
self.display = xml.attrib['display'].strip()
119
120
def get_c_name(self):
121
return get_c_name(self.name)
122
123
def get_c_typename(self):
124
if self.type in self.isa.enums:
125
return 'TYPE_ENUM'
126
if self.type in self.isa.bitsets:
127
return 'TYPE_BITSET'
128
return 'TYPE_' + self.type.upper()
129
130
def mask(self):
131
return ((1 << self.get_size()) - 1) << self.low
132
133
def get_size(self):
134
return 1 + self.high - self.low
135
136
class BitSetAssertField(BitSetField):
137
"""Similar to BitSetField, but for <assert/>s, which can be
138
used to specify that a certain bitpattern is expected in
139
place of (for example) unused bitfields
140
"""
141
def __init__(self, case, xml):
142
self.isa = case.bitset.isa
143
self.low, self.high = get_bitrange(xml)
144
self.name = case.bitset.name + '#assert' + str(len(case.fields))
145
self.type = 'uint'
146
self.expr = None
147
self.display = None
148
149
match, dontcare, mask = extract_pattern(xml, case.bitset.name)
150
self.val = match >> self.low
151
152
assert dontcare == 0, "'x' (dontcare) is not valid in an assert"
153
154
def get_c_typename(self):
155
return 'TYPE_ASSERT'
156
157
class BitSetDerivedField(BitSetField):
158
"""Similar to BitSetField, but for derived fields
159
"""
160
def __init__(self, isa, xml):
161
self.isa = isa
162
self.low = 0
163
self.high = 0
164
# NOTE: a width should be provided for 'int' derived fields, ie.
165
# where sign extension is needed. We just repurpose the 'high'
166
# field for that to make '1 + high - low' work out
167
if 'width' in xml.attrib:
168
self.high = xml.attrib['width'] + ' - 1'
169
self.name = xml.attrib['name']
170
self.type = xml.attrib['type']
171
if 'expr' in xml.attrib:
172
self.expr = xml.attrib['expr']
173
else:
174
e = isa.parse_one_expression(xml, self.name)
175
self.expr = e.name
176
self.display = None
177
if 'display' in xml.attrib:
178
self.display = xml.attrib['display'].strip()
179
180
class BitSetCase(object):
181
"""Class that encapsulates a single bitset case
182
"""
183
def __init__(self, bitset, xml, update_field_mask, expr=None):
184
self.bitset = bitset
185
if expr is not None:
186
self.name = bitset.name + '#case' + str(len(bitset.cases))
187
else:
188
self.name = bitset.name + "#default"
189
self.expr = expr
190
self.fields = {}
191
192
for derived in xml.findall('derived'):
193
f = BitSetDerivedField(bitset.isa, derived)
194
self.fields[f.name] = f
195
196
for assrt in xml.findall('assert'):
197
f = BitSetAssertField(self, assrt)
198
update_field_mask(self, f)
199
self.fields[f.name] = f
200
201
for field in xml.findall('field'):
202
dbg("{}.{}".format(self.name, field.attrib['name']))
203
f = BitSetField(bitset.isa, field)
204
update_field_mask(self, f)
205
self.fields[f.name] = f
206
207
self.display = None
208
for d in xml.findall('display'):
209
# Allow <display/> for empty display string:
210
if d.text is not None:
211
self.display = d.text.strip()
212
else:
213
self.display = ''
214
dbg("found display: '{}'".format(self.display))
215
216
def get_c_name(self):
217
return get_c_name(self.name)
218
219
class BitSetEncode(object):
220
"""Additional data that may be associated with a root bitset node
221
to provide additional information needed to generate helpers
222
to encode the bitset, such as source data type and "opcode"
223
case prefix (ie. how to choose/enumerate which leaf node bitset
224
to use to encode the source data
225
"""
226
def __init__(self, xml):
227
self.type = None
228
if 'type' in xml.attrib:
229
self.type = xml.attrib['type']
230
self.case_prefix = None
231
if 'case-prefix' in xml.attrib:
232
self.case_prefix = xml.attrib['case-prefix']
233
# The encode element may also contain mappings from encode src
234
# to individual field names:
235
self.maps = {}
236
self.forced = {}
237
for map in xml.findall('map'):
238
name = map.attrib['name']
239
self.maps[name] = map.text.strip()
240
if 'force' in map.attrib and map.attrib['force'] == 'true':
241
self.forced[name] = 'true'
242
243
class BitSet(object):
244
"""Class that encapsulates a single bitset rule
245
"""
246
def __init__(self, isa, xml):
247
self.isa = isa
248
self.xml = xml
249
self.name = xml.attrib['name']
250
251
if 'size' in xml.attrib:
252
assert('extends' not in xml.attrib)
253
self.size = int(xml.attrib['size'])
254
self.extends = None
255
else:
256
self.size = None
257
self.extends = xml.attrib['extends']
258
259
self.encode = None
260
if xml.find('encode') is not None:
261
self.encode = BitSetEncode(xml.find('encode'))
262
263
self.gen_min = 0
264
self.gen_max = ~0
265
266
for gen in xml.findall('gen'):
267
if 'min' in gen.attrib:
268
self.gen_min = gen.attrib['min']
269
if 'max' in gen.attrib:
270
self.gen_max = gen.attrib['max']
271
272
# Collect up the match/dontcare/mask bitmasks for
273
# this bitset case:
274
self.match = 0
275
self.dontcare = 0
276
self.mask = 0
277
self.field_mask = 0
278
279
self.cases = []
280
281
# Helper to check for redefined bits:
282
def is_defined_bits(m):
283
return ((self.field_mask | self.mask | self.dontcare | self.match) & m) != 0
284
285
def update_default_bitmask_field(bs, field):
286
m = field.mask()
287
dbg("field: {}.{} => {:016x}".format(self.name, field.name, m))
288
# For default case, we don't expect any bits to be doubly defined:
289
assert not is_defined_bits(m), "Redefined bits in field {}.{}: {}..{}".format(
290
self.name, field.name, field.low, field.high);
291
self.field_mask |= m
292
293
def update_override_bitmask_field(bs, field):
294
m = field.mask()
295
dbg("field: {}.{} => {:016x}".format(self.name, field.name, m))
296
assert self.field_mask ^ ~m
297
298
dflt = BitSetCase(self, xml, update_default_bitmask_field)
299
300
for override in xml.findall('override'):
301
if 'expr' in override.attrib:
302
expr = override.attrib['expr']
303
else:
304
e = isa.parse_one_expression(override, self.name)
305
expr = e.name
306
c = BitSetCase(self, override, update_override_bitmask_field, expr)
307
self.cases.append(c)
308
309
# Default case is expected to be the last one:
310
self.cases.append(dflt)
311
312
for pattern in xml.findall('pattern'):
313
match, dontcare, mask = extract_pattern(pattern, self.name, is_defined_bits)
314
315
self.match |= match
316
self.dontcare |= dontcare
317
self.mask |= mask
318
319
def get_pattern(self):
320
if self.extends is not None:
321
parent = self.isa.bitsets[self.extends]
322
ppat = parent.get_pattern()
323
pat = BitSetPattern(self)
324
325
assert ((ppat.defined_bits() & pat.defined_bits()) == 0), "bitset conflict in {}: {:x}".format(self.name, (ppat.defined_bits() & pat.defined_bits()))
326
327
return pat.merge(ppat)
328
329
return BitSetPattern(self)
330
331
def get_size(self):
332
if self.extends is not None:
333
parent = self.isa.bitsets[self.extends]
334
return parent.get_size()
335
return self.size
336
337
def get_c_name(self):
338
return get_c_name(self.name)
339
340
def get_root(self):
341
if self.extends is not None:
342
return self.isa.bitsets[self.extends].get_root()
343
return self
344
345
class BitSetEnum(object):
346
"""Class that encapsulates an enum declaration
347
"""
348
def __init__(self, isa, xml):
349
self.isa = isa
350
self.name = xml.attrib['name']
351
# Table mapping value to name
352
# TODO currently just mapping to 'display' name, but if we
353
# need more attributes then maybe need BitSetEnumValue?
354
self.values = {}
355
for value in xml.findall('value'):
356
self.values[value.attrib['val']] = value.attrib['display']
357
358
def get_c_name(self):
359
return 'enum_' + get_c_name(self.name)
360
361
class BitSetExpression(object):
362
"""Class that encapsulates an <expr> declaration
363
"""
364
def __init__(self, isa, xml):
365
self.isa = isa
366
if 'name' in xml.attrib:
367
self.name = xml.attrib['name']
368
else:
369
self.name = 'anon_' + str(isa.anon_expression_count)
370
isa.anon_expression_count = isa.anon_expression_count + 1
371
expr = xml.text.strip()
372
self.fieldnames = list(set(re.findall(r"{([a-zA-Z0-9_]+)}", expr)))
373
self.expr = re.sub(r"{([a-zA-Z0-9_]+)}", r"\1", expr)
374
dbg("'{}' -> '{}'".format(expr, self.expr))
375
376
def get_c_name(self):
377
return 'expr_' + get_c_name(self.name)
378
379
class ISA(object):
380
"""Class that encapsulates all the parsed bitset rules
381
"""
382
def __init__(self, xmlpath):
383
self.base_path = os.path.dirname(xmlpath)
384
385
# Counter used to name inline (anonymous) expressions:
386
self.anon_expression_count = 0
387
388
# Table of (globally defined) expressions:
389
self.expressions = {}
390
391
# Table of enums:
392
self.enums = {}
393
394
# Table of toplevel bitset hierarchies:
395
self.roots = {}
396
397
# Table of leaf nodes of bitset hierarchies:
398
self.leafs = {}
399
400
# Table of all bitsets:
401
self.bitsets = {}
402
403
root = ElementTree.parse(xmlpath).getroot()
404
self.parse_file(root)
405
self.validate_isa()
406
407
def parse_expressions(self, root):
408
e = None
409
for expr in root.findall('expr'):
410
e = BitSetExpression(self, expr)
411
self.expressions[e.name] = e
412
return e
413
414
def parse_one_expression(self, root, name):
415
assert len(root.findall('expr')) == 1, "expected a single expression in: {}".format(name)
416
return self.parse_expressions(root)
417
418
def parse_file(self, root):
419
# Handle imports up-front:
420
for imprt in root.findall('import'):
421
p = os.path.join(self.base_path, imprt.attrib['file'])
422
self.parse_file(ElementTree.parse(p))
423
424
# Extract expressions:
425
self.parse_expressions(root)
426
427
# Extract enums:
428
for enum in root.findall('enum'):
429
e = BitSetEnum(self, enum)
430
self.enums[e.name] = e
431
432
# Extract bitsets:
433
for bitset in root.findall('bitset'):
434
b = BitSet(self, bitset)
435
if b.size is not None:
436
dbg("toplevel: " + b.name)
437
self.roots[b.name] = b
438
else:
439
dbg("derived: " + b.name)
440
self.bitsets[b.name] = b
441
self.leafs[b.name] = b
442
443
# Remove non-leaf nodes from the leafs table:
444
for name, bitset in self.bitsets.items():
445
if bitset.extends is not None:
446
if bitset.extends in self.leafs:
447
del self.leafs[bitset.extends]
448
449
def validate_isa(self):
450
# Validate that all bitset fields have valid types, and in
451
# the case of bitset type, the sizes match:
452
builtin_types = ['branch', 'int', 'uint', 'hex', 'offset', 'uoffset', 'float', 'bool', 'enum']
453
for bitset_name, bitset in self.bitsets.items():
454
if bitset.extends is not None:
455
assert bitset.extends in self.bitsets, "{} extends invalid type: {}".format(
456
bitset_name, bitset.extends)
457
for case in bitset.cases:
458
for field_name, field in case.fields.items():
459
if field.type == 'float':
460
assert field.get_size() == 32 or field.get_size() == 16
461
462
if not isinstance(field, BitSetDerivedField):
463
assert field.high < bitset.get_size(), \
464
"{}.{}: invalid bit range: [{}, {}] is not in [{}, {}]".format(
465
bitset_name, field_name, field.low, field.high, 0, bitset.get_size() - 1)
466
467
if field.type in builtin_types:
468
continue
469
if field.type in self.enums:
470
continue
471
assert field.type in self.bitsets, "{}.{}: invalid type: {}".format(
472
bitset_name, field_name, field.type)
473
bs = self.bitsets[field.type]
474
assert field.get_size() == bs.get_size(), "{}.{}: invalid size: {} vs {}".format(
475
bitset_name, field_name, field.get_size(), bs.get_size())
476
477
# Validate that all the leaf node bitsets have no remaining
478
# undefined bits
479
for name, bitset in self.leafs.items():
480
pat = bitset.get_pattern()
481
sz = bitset.get_size()
482
assert ((pat.mask | pat.field_mask) == (1 << sz) - 1), "leaf bitset {} has undefined bits: {:x}".format(
483
bitset.name, ~(pat.mask | pat.field_mask) & ((1 << sz) - 1))
484
485
# TODO somehow validating that only one bitset in a hierarchy
486
# matches any given bit pattern would be useful.
487
488
# TODO we should probably be able to look at the contexts where
489
# an expression is evaluated and verify that it doesn't have any
490
# {VARNAME} references that would be unresolved at evaluation time
491