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