• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
23from xml.etree import ElementTree
24import os
25import re
26
27def dbg(str):
28    if False:
29        print(str)
30
31class 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
59def 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
71def 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
98def get_c_name(name):
99    return name.lower().replace('#', '__').replace('-', '_').replace('.', '_')
100
101class 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
136class 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
157class 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
180class 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
219class 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
243class 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        # Used for generated encoder, to de-duplicate encoding for
252        # similar instructions:
253        self.snippets = {}
254
255        if 'size' in xml.attrib:
256            assert('extends' not in xml.attrib)
257            self.size = int(xml.attrib['size'])
258            self.extends = None
259        else:
260            self.size = None
261            self.extends = xml.attrib['extends']
262
263        self.encode = None
264        if xml.find('encode') is not None:
265            self.encode = BitSetEncode(xml.find('encode'))
266
267        self.gen_min = 0
268        self.gen_max = ~0
269
270        for gen in xml.findall('gen'):
271            if 'min' in gen.attrib:
272                self.gen_min = gen.attrib['min']
273            if 'max' in gen.attrib:
274                self.gen_max = gen.attrib['max']
275
276        # Collect up the match/dontcare/mask bitmasks for
277        # this bitset case:
278        self.match = 0
279        self.dontcare = 0
280        self.mask = 0
281        self.field_mask = 0
282
283        self.cases = []
284
285        # Helper to check for redefined bits:
286        def is_defined_bits(m):
287            return ((self.field_mask | self.mask | self.dontcare | self.match) & m) != 0
288
289        def update_default_bitmask_field(bs, field):
290            m = field.mask()
291            dbg("field: {}.{} => {:016x}".format(self.name, field.name, m))
292            # For default case, we don't expect any bits to be doubly defined:
293            assert not is_defined_bits(m), "Redefined bits in field {}.{}: {}..{}".format(
294                self.name, field.name, field.low, field.high);
295            self.field_mask |= m
296
297        def update_override_bitmask_field(bs, field):
298            m = field.mask()
299            dbg("field: {}.{} => {:016x}".format(self.name, field.name, m))
300            assert self.field_mask ^ ~m
301
302        dflt = BitSetCase(self, xml, update_default_bitmask_field)
303
304        for override in xml.findall('override'):
305            if 'expr' in override.attrib:
306                expr = override.attrib['expr']
307            else:
308                e = isa.parse_one_expression(override, self.name)
309                expr = e.name
310            c = BitSetCase(self, override, update_override_bitmask_field, expr)
311            self.cases.append(c)
312
313        # Default case is expected to be the last one:
314        self.cases.append(dflt)
315
316        for pattern in xml.findall('pattern'):
317            match, dontcare, mask = extract_pattern(pattern, self.name, is_defined_bits)
318
319            self.match    |= match
320            self.dontcare |= dontcare
321            self.mask     |= mask
322
323    def get_pattern(self):
324        if self.extends is not None:
325            parent = self.isa.bitsets[self.extends]
326            ppat = parent.get_pattern()
327            pat  = BitSetPattern(self)
328
329            assert ((ppat.defined_bits() & pat.defined_bits()) == 0), "bitset conflict in {}: {:x}".format(self.name, (ppat.defined_bits() & pat.defined_bits()))
330
331            return pat.merge(ppat)
332
333        return BitSetPattern(self)
334
335    def get_size(self):
336        if self.extends is not None:
337            parent = self.isa.bitsets[self.extends]
338            return parent.get_size()
339        return self.size
340
341    def get_c_name(self):
342        return get_c_name(self.name)
343
344    def get_root(self):
345        if self.extends is not None:
346            return self.isa.bitsets[self.extends].get_root()
347        return self
348
349class BitSetEnum(object):
350    """Class that encapsulates an enum declaration
351    """
352    def __init__(self, isa, xml):
353        self.isa = isa
354        self.name = xml.attrib['name']
355        # Table mapping value to name
356        # TODO currently just mapping to 'display' name, but if we
357        # need more attributes then maybe need BitSetEnumValue?
358        self.values = {}
359        for value in xml.findall('value'):
360            self.values[value.attrib['val']] = value.attrib['display']
361
362    def get_c_name(self):
363        return 'enum_' + get_c_name(self.name)
364
365class BitSetExpression(object):
366    """Class that encapsulates an <expr> declaration
367    """
368    def __init__(self, isa, xml):
369        self.isa = isa
370        if 'name' in xml.attrib:
371            self.name = xml.attrib['name']
372        else:
373            self.name = 'anon_' + str(isa.anon_expression_count)
374            isa.anon_expression_count = isa.anon_expression_count + 1
375        expr = xml.text.strip()
376        self.fieldnames = list(set(re.findall(r"{([a-zA-Z0-9_]+)}", expr)))
377        self.expr = re.sub(r"{([a-zA-Z0-9_]+)}", r"\1", expr)
378        dbg("'{}' -> '{}'".format(expr, self.expr))
379
380    def get_c_name(self):
381        return 'expr_' + get_c_name(self.name)
382
383class ISA(object):
384    """Class that encapsulates all the parsed bitset rules
385    """
386    def __init__(self, xmlpath):
387        self.base_path = os.path.dirname(xmlpath)
388
389        # Counter used to name inline (anonymous) expressions:
390        self.anon_expression_count = 0
391
392        # Table of (globally defined) expressions:
393        self.expressions = {}
394
395        # Table of enums:
396        self.enums = {}
397
398        # Table of toplevel bitset hierarchies:
399        self.roots = {}
400
401        # Table of leaf nodes of bitset hierarchies:
402        self.leafs = {}
403
404        # Table of all bitsets:
405        self.bitsets = {}
406
407        # Max needed bitsize for one instruction
408        self.bitsize = 0
409
410        root = ElementTree.parse(xmlpath).getroot()
411        self.parse_file(root)
412        self.validate_isa()
413
414    def parse_expressions(self, root):
415        e = None
416        for expr in root.findall('expr'):
417            e = BitSetExpression(self, expr)
418            self.expressions[e.name] = e
419        return e
420
421    def parse_one_expression(self, root, name):
422        assert len(root.findall('expr')) == 1, "expected a single expression in: {}".format(name)
423        return self.parse_expressions(root)
424
425    def parse_file(self, root):
426        # Handle imports up-front:
427        for imprt in root.findall('import'):
428            p = os.path.join(self.base_path, imprt.attrib['file'])
429            self.parse_file(ElementTree.parse(p))
430
431        # Extract expressions:
432        self.parse_expressions(root)
433
434        # Extract enums:
435        for enum in root.findall('enum'):
436            e = BitSetEnum(self, enum)
437            self.enums[e.name] = e
438
439        # Extract bitsets:
440        for bitset in root.findall('bitset'):
441            b = BitSet(self, bitset)
442            if b.size is not None:
443                dbg("toplevel: " + b.name)
444                self.roots[b.name] = b
445                self.bitsize = max(self.bitsize, b.size)
446            else:
447                dbg("derived: " + b.name)
448            self.bitsets[b.name] = b
449            self.leafs[b.name]  = b
450
451        # Remove non-leaf nodes from the leafs table:
452        for name, bitset in self.bitsets.items():
453            if bitset.extends is not None:
454                if bitset.extends in self.leafs:
455                    del self.leafs[bitset.extends]
456
457    def validate_isa(self):
458        # Validate that all bitset fields have valid types, and in
459        # the case of bitset type, the sizes match:
460        builtin_types = ['branch', 'int', 'uint', 'hex', 'offset', 'uoffset', 'float', 'bool', 'enum']
461        for bitset_name, bitset in self.bitsets.items():
462            if bitset.extends is not None:
463                assert bitset.extends in self.bitsets, "{} extends invalid type: {}".format(
464                    bitset_name, bitset.extends)
465            for case in bitset.cases:
466                for field_name, field in case.fields.items():
467                    if field.type == 'float':
468                        assert field.get_size() == 32 or field.get_size() == 16
469
470                    if not isinstance(field, BitSetDerivedField):
471                        assert field.high < bitset.get_size(), \
472                            "{}.{}: invalid bit range: [{}, {}] is not in [{}, {}]".format(
473                            bitset_name, field_name, field.low, field.high, 0, bitset.get_size() - 1)
474
475                    if field.type in builtin_types:
476                        continue
477                    if field.type in self.enums:
478                        continue
479                    assert field.type in self.bitsets, "{}.{}: invalid type: {}".format(
480                        bitset_name, field_name, field.type)
481                    bs = self.bitsets[field.type]
482                    assert field.get_size() == bs.get_size(), "{}.{}: invalid size: {} vs {}".format(
483                        bitset_name, field_name, field.get_size(), bs.get_size())
484
485        # Validate that all the leaf node bitsets have no remaining
486        # undefined bits
487        for name, bitset in self.leafs.items():
488            pat = bitset.get_pattern()
489            sz  = bitset.get_size()
490            assert ((pat.mask | pat.field_mask) == (1 << sz) - 1), "leaf bitset {} has undefined bits: {:x}".format(
491                bitset.name, ~(pat.mask | pat.field_mask) & ((1 << sz) - 1))
492
493        # TODO somehow validating that only one bitset in a hierarchy
494        # matches any given bit pattern would be useful.
495
496        # TODO we should probably be able to look at the contexts where
497        # an expression is evaluated and verify that it doesn't have any
498        # {VARNAME} references that would be unresolved at evaluation time
499
500    def format(self):
501        ''' Generate format string used by printf(..) and friends '''
502        parts = []
503        words = self.bitsize / 32
504
505        for i in range(int(words)):
506            parts.append('%08x')
507
508        fmt = ''.join(parts)
509
510        return f"\"{fmt[1:]}\""
511
512    def value(self):
513        ''' Generate format values used by printf(..) and friends '''
514        parts = []
515        words = self.bitsize / 32
516
517        for i in range(int(words) - 1, -1, -1):
518            parts.append('v[' + str(i) + ']')
519
520        return ', '.join(parts)
521
522    def split_bits(self, value):
523        ''' Split `value` into a list of 32-bit integers '''
524        mask, parts = (1 << 32) - 1, []
525        words = self.bitsize / 32
526
527        while value:
528            parts.append(hex(value & mask))
529            value >>= 32
530
531        # Add 'missing' words
532        while len(parts) < words:
533            parts.append('0x0')
534
535        return parts
536