decodetree.py (41623B)
1#!/usr/bin/env python3 2# Copyright (c) 2018 Linaro Limited 3# 4# This library is free software; you can redistribute it and/or 5# modify it under the terms of the GNU Lesser General Public 6# License as published by the Free Software Foundation; either 7# version 2.1 of the License, or (at your option) any later version. 8# 9# This library is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12# Lesser General Public License for more details. 13# 14# You should have received a copy of the GNU Lesser General Public 15# License along with this library; if not, see <http://www.gnu.org/licenses/>. 16# 17 18# 19# Generate a decoding tree from a specification file. 20# See the syntax and semantics in docs/devel/decodetree.rst. 21# 22 23import io 24import os 25import re 26import sys 27import getopt 28 29insnwidth = 32 30bitop_width = 32 31insnmask = 0xffffffff 32variablewidth = False 33fields = {} 34arguments = {} 35formats = {} 36allpatterns = [] 37anyextern = False 38 39translate_prefix = 'trans' 40translate_scope = 'static ' 41input_file = '' 42output_file = None 43output_fd = None 44insntype = 'uint32_t' 45decode_function = 'decode' 46 47# An identifier for C. 48re_C_ident = '[a-zA-Z][a-zA-Z0-9_]*' 49 50# Identifiers for Arguments, Fields, Formats and Patterns. 51re_arg_ident = '&[a-zA-Z0-9_]*' 52re_fld_ident = '%[a-zA-Z0-9_]*' 53re_fmt_ident = '@[a-zA-Z0-9_]*' 54re_pat_ident = '[a-zA-Z0-9_]*' 55 56def error_with_file(file, lineno, *args): 57 """Print an error message from file:line and args and exit.""" 58 global output_file 59 global output_fd 60 61 prefix = '' 62 if file: 63 prefix += f'{file}:' 64 if lineno: 65 prefix += f'{lineno}:' 66 if prefix: 67 prefix += ' ' 68 print(prefix, end='error: ', file=sys.stderr) 69 print(*args, file=sys.stderr) 70 71 if output_file and output_fd: 72 output_fd.close() 73 os.remove(output_file) 74 exit(1) 75# end error_with_file 76 77 78def error(lineno, *args): 79 error_with_file(input_file, lineno, *args) 80# end error 81 82 83def output(*args): 84 global output_fd 85 for a in args: 86 output_fd.write(a) 87 88 89def output_autogen(): 90 output('/* This file is autogenerated by scripts/decodetree.py. */\n\n') 91 92 93def str_indent(c): 94 """Return a string with C spaces""" 95 return ' ' * c 96 97 98def str_fields(fields): 99 """Return a string uniquely identifying FIELDS""" 100 r = '' 101 for n in sorted(fields.keys()): 102 r += '_' + n 103 return r[1:] 104 105 106def whex(val): 107 """Return a hex string for val padded for insnwidth""" 108 global insnwidth 109 return f'0x{val:0{insnwidth // 4}x}' 110 111 112def whexC(val): 113 """Return a hex string for val padded for insnwidth, 114 and with the proper suffix for a C constant.""" 115 suffix = '' 116 if val >= 0x100000000: 117 suffix = 'ull' 118 elif val >= 0x80000000: 119 suffix = 'u' 120 return whex(val) + suffix 121 122 123def str_match_bits(bits, mask): 124 """Return a string pretty-printing BITS/MASK""" 125 global insnwidth 126 127 i = 1 << (insnwidth - 1) 128 space = 0x01010100 129 r = '' 130 while i != 0: 131 if i & mask: 132 if i & bits: 133 r += '1' 134 else: 135 r += '0' 136 else: 137 r += '.' 138 if i & space: 139 r += ' ' 140 i >>= 1 141 return r 142 143 144def is_pow2(x): 145 """Return true iff X is equal to a power of 2.""" 146 return (x & (x - 1)) == 0 147 148 149def ctz(x): 150 """Return the number of times 2 factors into X.""" 151 assert x != 0 152 r = 0 153 while ((x >> r) & 1) == 0: 154 r += 1 155 return r 156 157 158def is_contiguous(bits): 159 if bits == 0: 160 return -1 161 shift = ctz(bits) 162 if is_pow2((bits >> shift) + 1): 163 return shift 164 else: 165 return -1 166 167 168def eq_fields_for_args(flds_a, arg): 169 if len(flds_a) != len(arg.fields): 170 return False 171 # Only allow inference on default types 172 for t in arg.types: 173 if t != 'int': 174 return False 175 for k, a in flds_a.items(): 176 if k not in arg.fields: 177 return False 178 return True 179 180 181def eq_fields_for_fmts(flds_a, flds_b): 182 if len(flds_a) != len(flds_b): 183 return False 184 for k, a in flds_a.items(): 185 if k not in flds_b: 186 return False 187 b = flds_b[k] 188 if a.__class__ != b.__class__ or a != b: 189 return False 190 return True 191 192 193class Field: 194 """Class representing a simple instruction field""" 195 def __init__(self, sign, pos, len): 196 self.sign = sign 197 self.pos = pos 198 self.len = len 199 self.mask = ((1 << len) - 1) << pos 200 201 def __str__(self): 202 if self.sign: 203 s = 's' 204 else: 205 s = '' 206 return str(self.pos) + ':' + s + str(self.len) 207 208 def str_extract(self): 209 global bitop_width 210 s = 's' if self.sign else '' 211 return f'{s}extract{bitop_width}(insn, {self.pos}, {self.len})' 212 213 def __eq__(self, other): 214 return self.sign == other.sign and self.mask == other.mask 215 216 def __ne__(self, other): 217 return not self.__eq__(other) 218# end Field 219 220 221class MultiField: 222 """Class representing a compound instruction field""" 223 def __init__(self, subs, mask): 224 self.subs = subs 225 self.sign = subs[0].sign 226 self.mask = mask 227 228 def __str__(self): 229 return str(self.subs) 230 231 def str_extract(self): 232 global bitop_width 233 ret = '0' 234 pos = 0 235 for f in reversed(self.subs): 236 ext = f.str_extract() 237 if pos == 0: 238 ret = ext 239 else: 240 ret = f'deposit{bitop_width}({ret}, {pos}, {bitop_width - pos}, {ext})' 241 pos += f.len 242 return ret 243 244 def __ne__(self, other): 245 if len(self.subs) != len(other.subs): 246 return True 247 for a, b in zip(self.subs, other.subs): 248 if a.__class__ != b.__class__ or a != b: 249 return True 250 return False 251 252 def __eq__(self, other): 253 return not self.__ne__(other) 254# end MultiField 255 256 257class ConstField: 258 """Class representing an argument field with constant value""" 259 def __init__(self, value): 260 self.value = value 261 self.mask = 0 262 self.sign = value < 0 263 264 def __str__(self): 265 return str(self.value) 266 267 def str_extract(self): 268 return str(self.value) 269 270 def __cmp__(self, other): 271 return self.value - other.value 272# end ConstField 273 274 275class FunctionField: 276 """Class representing a field passed through a function""" 277 def __init__(self, func, base): 278 self.mask = base.mask 279 self.sign = base.sign 280 self.base = base 281 self.func = func 282 283 def __str__(self): 284 return self.func + '(' + str(self.base) + ')' 285 286 def str_extract(self): 287 return self.func + '(ctx, ' + self.base.str_extract() + ')' 288 289 def __eq__(self, other): 290 return self.func == other.func and self.base == other.base 291 292 def __ne__(self, other): 293 return not self.__eq__(other) 294# end FunctionField 295 296 297class ParameterField: 298 """Class representing a pseudo-field read from a function""" 299 def __init__(self, func): 300 self.mask = 0 301 self.sign = 0 302 self.func = func 303 304 def __str__(self): 305 return self.func 306 307 def str_extract(self): 308 return self.func + '(ctx)' 309 310 def __eq__(self, other): 311 return self.func == other.func 312 313 def __ne__(self, other): 314 return not self.__eq__(other) 315# end ParameterField 316 317 318class Arguments: 319 """Class representing the extracted fields of a format""" 320 def __init__(self, nm, flds, types, extern): 321 self.name = nm 322 self.extern = extern 323 self.fields = flds 324 self.types = types 325 326 def __str__(self): 327 return self.name + ' ' + str(self.fields) 328 329 def struct_name(self): 330 return 'arg_' + self.name 331 332 def output_def(self): 333 if not self.extern: 334 output('typedef struct {\n') 335 for (n, t) in zip(self.fields, self.types): 336 output(f' {t} {n};\n') 337 output('} ', self.struct_name(), ';\n\n') 338# end Arguments 339 340 341class General: 342 """Common code between instruction formats and instruction patterns""" 343 def __init__(self, name, lineno, base, fixb, fixm, udfm, fldm, flds, w): 344 self.name = name 345 self.file = input_file 346 self.lineno = lineno 347 self.base = base 348 self.fixedbits = fixb 349 self.fixedmask = fixm 350 self.undefmask = udfm 351 self.fieldmask = fldm 352 self.fields = flds 353 self.width = w 354 355 def __str__(self): 356 return self.name + ' ' + str_match_bits(self.fixedbits, self.fixedmask) 357 358 def str1(self, i): 359 return str_indent(i) + self.__str__() 360# end General 361 362 363class Format(General): 364 """Class representing an instruction format""" 365 366 def extract_name(self): 367 global decode_function 368 return decode_function + '_extract_' + self.name 369 370 def output_extract(self): 371 output('static void ', self.extract_name(), '(DisasContext *ctx, ', 372 self.base.struct_name(), ' *a, ', insntype, ' insn)\n{\n') 373 for n, f in self.fields.items(): 374 output(' a->', n, ' = ', f.str_extract(), ';\n') 375 output('}\n\n') 376# end Format 377 378 379class Pattern(General): 380 """Class representing an instruction pattern""" 381 382 def output_decl(self): 383 global translate_scope 384 global translate_prefix 385 output('typedef ', self.base.base.struct_name(), 386 ' arg_', self.name, ';\n') 387 output(translate_scope, 'bool ', translate_prefix, '_', self.name, 388 '(DisasContext *ctx, arg_', self.name, ' *a);\n') 389 390 def output_code(self, i, extracted, outerbits, outermask): 391 global translate_prefix 392 ind = str_indent(i) 393 arg = self.base.base.name 394 output(ind, '/* ', self.file, ':', str(self.lineno), ' */\n') 395 if not extracted: 396 output(ind, self.base.extract_name(), 397 '(ctx, &u.f_', arg, ', insn);\n') 398 for n, f in self.fields.items(): 399 output(ind, 'u.f_', arg, '.', n, ' = ', f.str_extract(), ';\n') 400 output(ind, 'if (', translate_prefix, '_', self.name, 401 '(ctx, &u.f_', arg, ')) return true;\n') 402 403 # Normal patterns do not have children. 404 def build_tree(self): 405 return 406 def prop_masks(self): 407 return 408 def prop_format(self): 409 return 410 def prop_width(self): 411 return 412 413# end Pattern 414 415 416class MultiPattern(General): 417 """Class representing a set of instruction patterns""" 418 419 def __init__(self, lineno): 420 self.file = input_file 421 self.lineno = lineno 422 self.pats = [] 423 self.base = None 424 self.fixedbits = 0 425 self.fixedmask = 0 426 self.undefmask = 0 427 self.width = None 428 429 def __str__(self): 430 r = 'group' 431 if self.fixedbits is not None: 432 r += ' ' + str_match_bits(self.fixedbits, self.fixedmask) 433 return r 434 435 def output_decl(self): 436 for p in self.pats: 437 p.output_decl() 438 439 def prop_masks(self): 440 global insnmask 441 442 fixedmask = insnmask 443 undefmask = insnmask 444 445 # Collect fixedmask/undefmask for all of the children. 446 for p in self.pats: 447 p.prop_masks() 448 fixedmask &= p.fixedmask 449 undefmask &= p.undefmask 450 451 # Widen fixedmask until all fixedbits match 452 repeat = True 453 fixedbits = 0 454 while repeat and fixedmask != 0: 455 fixedbits = None 456 for p in self.pats: 457 thisbits = p.fixedbits & fixedmask 458 if fixedbits is None: 459 fixedbits = thisbits 460 elif fixedbits != thisbits: 461 fixedmask &= ~(fixedbits ^ thisbits) 462 break 463 else: 464 repeat = False 465 466 self.fixedbits = fixedbits 467 self.fixedmask = fixedmask 468 self.undefmask = undefmask 469 470 def build_tree(self): 471 for p in self.pats: 472 p.build_tree() 473 474 def prop_format(self): 475 for p in self.pats: 476 p.build_tree() 477 478 def prop_width(self): 479 width = None 480 for p in self.pats: 481 p.prop_width() 482 if width is None: 483 width = p.width 484 elif width != p.width: 485 error_with_file(self.file, self.lineno, 486 'width mismatch in patterns within braces') 487 self.width = width 488 489# end MultiPattern 490 491 492class IncMultiPattern(MultiPattern): 493 """Class representing an overlapping set of instruction patterns""" 494 495 def output_code(self, i, extracted, outerbits, outermask): 496 global translate_prefix 497 ind = str_indent(i) 498 for p in self.pats: 499 if outermask != p.fixedmask: 500 innermask = p.fixedmask & ~outermask 501 innerbits = p.fixedbits & ~outermask 502 output(ind, f'if ((insn & {whexC(innermask)}) == {whexC(innerbits)}) {{\n') 503 output(ind, f' /* {str_match_bits(p.fixedbits, p.fixedmask)} */\n') 504 p.output_code(i + 4, extracted, p.fixedbits, p.fixedmask) 505 output(ind, '}\n') 506 else: 507 p.output_code(i, extracted, p.fixedbits, p.fixedmask) 508#end IncMultiPattern 509 510 511class Tree: 512 """Class representing a node in a decode tree""" 513 514 def __init__(self, fm, tm): 515 self.fixedmask = fm 516 self.thismask = tm 517 self.subs = [] 518 self.base = None 519 520 def str1(self, i): 521 ind = str_indent(i) 522 r = ind + whex(self.fixedmask) 523 if self.format: 524 r += ' ' + self.format.name 525 r += ' [\n' 526 for (b, s) in self.subs: 527 r += ind + f' {whex(b)}:\n' 528 r += s.str1(i + 4) + '\n' 529 r += ind + ']' 530 return r 531 532 def __str__(self): 533 return self.str1(0) 534 535 def output_code(self, i, extracted, outerbits, outermask): 536 ind = str_indent(i) 537 538 # If we identified all nodes below have the same format, 539 # extract the fields now. 540 if not extracted and self.base: 541 output(ind, self.base.extract_name(), 542 '(ctx, &u.f_', self.base.base.name, ', insn);\n') 543 extracted = True 544 545 # Attempt to aid the compiler in producing compact switch statements. 546 # If the bits in the mask are contiguous, extract them. 547 sh = is_contiguous(self.thismask) 548 if sh > 0: 549 # Propagate SH down into the local functions. 550 def str_switch(b, sh=sh): 551 return f'(insn >> {sh}) & {b >> sh:#x}' 552 553 def str_case(b, sh=sh): 554 return hex(b >> sh) 555 else: 556 def str_switch(b): 557 return f'insn & {whexC(b)}' 558 559 def str_case(b): 560 return whexC(b) 561 562 output(ind, 'switch (', str_switch(self.thismask), ') {\n') 563 for b, s in sorted(self.subs): 564 assert (self.thismask & ~s.fixedmask) == 0 565 innermask = outermask | self.thismask 566 innerbits = outerbits | b 567 output(ind, 'case ', str_case(b), ':\n') 568 output(ind, ' /* ', 569 str_match_bits(innerbits, innermask), ' */\n') 570 s.output_code(i + 4, extracted, innerbits, innermask) 571 output(ind, ' break;\n') 572 output(ind, '}\n') 573# end Tree 574 575 576class ExcMultiPattern(MultiPattern): 577 """Class representing a non-overlapping set of instruction patterns""" 578 579 def output_code(self, i, extracted, outerbits, outermask): 580 # Defer everything to our decomposed Tree node 581 self.tree.output_code(i, extracted, outerbits, outermask) 582 583 @staticmethod 584 def __build_tree(pats, outerbits, outermask): 585 # Find the intersection of all remaining fixedmask. 586 innermask = ~outermask & insnmask 587 for i in pats: 588 innermask &= i.fixedmask 589 590 if innermask == 0: 591 # Edge condition: One pattern covers the entire insnmask 592 if len(pats) == 1: 593 t = Tree(outermask, innermask) 594 t.subs.append((0, pats[0])) 595 return t 596 597 text = 'overlapping patterns:' 598 for p in pats: 599 text += '\n' + p.file + ':' + str(p.lineno) + ': ' + str(p) 600 error_with_file(pats[0].file, pats[0].lineno, text) 601 602 fullmask = outermask | innermask 603 604 # Sort each element of pats into the bin selected by the mask. 605 bins = {} 606 for i in pats: 607 fb = i.fixedbits & innermask 608 if fb in bins: 609 bins[fb].append(i) 610 else: 611 bins[fb] = [i] 612 613 # We must recurse if any bin has more than one element or if 614 # the single element in the bin has not been fully matched. 615 t = Tree(fullmask, innermask) 616 617 for b, l in bins.items(): 618 s = l[0] 619 if len(l) > 1 or s.fixedmask & ~fullmask != 0: 620 s = ExcMultiPattern.__build_tree(l, b | outerbits, fullmask) 621 t.subs.append((b, s)) 622 623 return t 624 625 def build_tree(self): 626 super().prop_format() 627 self.tree = self.__build_tree(self.pats, self.fixedbits, 628 self.fixedmask) 629 630 @staticmethod 631 def __prop_format(tree): 632 """Propagate Format objects into the decode tree""" 633 634 # Depth first search. 635 for (b, s) in tree.subs: 636 if isinstance(s, Tree): 637 ExcMultiPattern.__prop_format(s) 638 639 # If all entries in SUBS have the same format, then 640 # propagate that into the tree. 641 f = None 642 for (b, s) in tree.subs: 643 if f is None: 644 f = s.base 645 if f is None: 646 return 647 if f is not s.base: 648 return 649 tree.base = f 650 651 def prop_format(self): 652 super().prop_format() 653 self.__prop_format(self.tree) 654 655# end ExcMultiPattern 656 657 658def parse_field(lineno, name, toks): 659 """Parse one instruction field from TOKS at LINENO""" 660 global fields 661 global insnwidth 662 663 # A "simple" field will have only one entry; 664 # a "multifield" will have several. 665 subs = [] 666 width = 0 667 func = None 668 for t in toks: 669 if re.match('^!function=', t): 670 if func: 671 error(lineno, 'duplicate function') 672 func = t.split('=') 673 func = func[1] 674 continue 675 676 if re.fullmatch('[0-9]+:s[0-9]+', t): 677 # Signed field extract 678 subtoks = t.split(':s') 679 sign = True 680 elif re.fullmatch('[0-9]+:[0-9]+', t): 681 # Unsigned field extract 682 subtoks = t.split(':') 683 sign = False 684 else: 685 error(lineno, f'invalid field token "{t}"') 686 po = int(subtoks[0]) 687 le = int(subtoks[1]) 688 if po + le > insnwidth: 689 error(lineno, f'field {t} too large') 690 f = Field(sign, po, le) 691 subs.append(f) 692 width += le 693 694 if width > insnwidth: 695 error(lineno, 'field too large') 696 if len(subs) == 0: 697 if func: 698 f = ParameterField(func) 699 else: 700 error(lineno, 'field with no value') 701 else: 702 if len(subs) == 1: 703 f = subs[0] 704 else: 705 mask = 0 706 for s in subs: 707 if mask & s.mask: 708 error(lineno, 'field components overlap') 709 mask |= s.mask 710 f = MultiField(subs, mask) 711 if func: 712 f = FunctionField(func, f) 713 714 if name in fields: 715 error(lineno, 'duplicate field', name) 716 fields[name] = f 717# end parse_field 718 719 720def parse_arguments(lineno, name, toks): 721 """Parse one argument set from TOKS at LINENO""" 722 global arguments 723 global re_C_ident 724 global anyextern 725 726 flds = [] 727 types = [] 728 extern = False 729 for n in toks: 730 if re.fullmatch('!extern', n): 731 extern = True 732 anyextern = True 733 continue 734 if re.fullmatch(re_C_ident + ':' + re_C_ident, n): 735 (n, t) = n.split(':') 736 elif re.fullmatch(re_C_ident, n): 737 t = 'int' 738 else: 739 error(lineno, f'invalid argument set token "{n}"') 740 if n in flds: 741 error(lineno, f'duplicate argument "{n}"') 742 flds.append(n) 743 types.append(t) 744 745 if name in arguments: 746 error(lineno, 'duplicate argument set', name) 747 arguments[name] = Arguments(name, flds, types, extern) 748# end parse_arguments 749 750 751def lookup_field(lineno, name): 752 global fields 753 if name in fields: 754 return fields[name] 755 error(lineno, 'undefined field', name) 756 757 758def add_field(lineno, flds, new_name, f): 759 if new_name in flds: 760 error(lineno, 'duplicate field', new_name) 761 flds[new_name] = f 762 return flds 763 764 765def add_field_byname(lineno, flds, new_name, old_name): 766 return add_field(lineno, flds, new_name, lookup_field(lineno, old_name)) 767 768 769def infer_argument_set(flds): 770 global arguments 771 global decode_function 772 773 for arg in arguments.values(): 774 if eq_fields_for_args(flds, arg): 775 return arg 776 777 name = decode_function + str(len(arguments)) 778 arg = Arguments(name, flds.keys(), ['int'] * len(flds), False) 779 arguments[name] = arg 780 return arg 781 782 783def infer_format(arg, fieldmask, flds, width): 784 global arguments 785 global formats 786 global decode_function 787 788 const_flds = {} 789 var_flds = {} 790 for n, c in flds.items(): 791 if c is ConstField: 792 const_flds[n] = c 793 else: 794 var_flds[n] = c 795 796 # Look for an existing format with the same argument set and fields 797 for fmt in formats.values(): 798 if arg and fmt.base != arg: 799 continue 800 if fieldmask != fmt.fieldmask: 801 continue 802 if width != fmt.width: 803 continue 804 if not eq_fields_for_fmts(flds, fmt.fields): 805 continue 806 return (fmt, const_flds) 807 808 name = decode_function + '_Fmt_' + str(len(formats)) 809 if not arg: 810 arg = infer_argument_set(flds) 811 812 fmt = Format(name, 0, arg, 0, 0, 0, fieldmask, var_flds, width) 813 formats[name] = fmt 814 815 return (fmt, const_flds) 816# end infer_format 817 818 819def parse_generic(lineno, parent_pat, name, toks): 820 """Parse one instruction format from TOKS at LINENO""" 821 global fields 822 global arguments 823 global formats 824 global allpatterns 825 global re_arg_ident 826 global re_fld_ident 827 global re_fmt_ident 828 global re_C_ident 829 global insnwidth 830 global insnmask 831 global variablewidth 832 833 is_format = parent_pat is None 834 835 fixedmask = 0 836 fixedbits = 0 837 undefmask = 0 838 width = 0 839 flds = {} 840 arg = None 841 fmt = None 842 for t in toks: 843 # '&Foo' gives a format an explicit argument set. 844 if re.fullmatch(re_arg_ident, t): 845 tt = t[1:] 846 if arg: 847 error(lineno, 'multiple argument sets') 848 if tt in arguments: 849 arg = arguments[tt] 850 else: 851 error(lineno, 'undefined argument set', t) 852 continue 853 854 # '@Foo' gives a pattern an explicit format. 855 if re.fullmatch(re_fmt_ident, t): 856 tt = t[1:] 857 if fmt: 858 error(lineno, 'multiple formats') 859 if tt in formats: 860 fmt = formats[tt] 861 else: 862 error(lineno, 'undefined format', t) 863 continue 864 865 # '%Foo' imports a field. 866 if re.fullmatch(re_fld_ident, t): 867 tt = t[1:] 868 flds = add_field_byname(lineno, flds, tt, tt) 869 continue 870 871 # 'Foo=%Bar' imports a field with a different name. 872 if re.fullmatch(re_C_ident + '=' + re_fld_ident, t): 873 (fname, iname) = t.split('=%') 874 flds = add_field_byname(lineno, flds, fname, iname) 875 continue 876 877 # 'Foo=number' sets an argument field to a constant value 878 if re.fullmatch(re_C_ident + '=[+-]?[0-9]+', t): 879 (fname, value) = t.split('=') 880 value = int(value) 881 flds = add_field(lineno, flds, fname, ConstField(value)) 882 continue 883 884 # Pattern of 0s, 1s, dots and dashes indicate required zeros, 885 # required ones, or dont-cares. 886 if re.fullmatch('[01.-]+', t): 887 shift = len(t) 888 fms = t.replace('0', '1') 889 fms = fms.replace('.', '0') 890 fms = fms.replace('-', '0') 891 fbs = t.replace('.', '0') 892 fbs = fbs.replace('-', '0') 893 ubm = t.replace('1', '0') 894 ubm = ubm.replace('.', '0') 895 ubm = ubm.replace('-', '1') 896 fms = int(fms, 2) 897 fbs = int(fbs, 2) 898 ubm = int(ubm, 2) 899 fixedbits = (fixedbits << shift) | fbs 900 fixedmask = (fixedmask << shift) | fms 901 undefmask = (undefmask << shift) | ubm 902 # Otherwise, fieldname:fieldwidth 903 elif re.fullmatch(re_C_ident + ':s?[0-9]+', t): 904 (fname, flen) = t.split(':') 905 sign = False 906 if flen[0] == 's': 907 sign = True 908 flen = flen[1:] 909 shift = int(flen, 10) 910 if shift + width > insnwidth: 911 error(lineno, f'field {fname} exceeds insnwidth') 912 f = Field(sign, insnwidth - width - shift, shift) 913 flds = add_field(lineno, flds, fname, f) 914 fixedbits <<= shift 915 fixedmask <<= shift 916 undefmask <<= shift 917 else: 918 error(lineno, f'invalid token "{t}"') 919 width += shift 920 921 if variablewidth and width < insnwidth and width % 8 == 0: 922 shift = insnwidth - width 923 fixedbits <<= shift 924 fixedmask <<= shift 925 undefmask <<= shift 926 undefmask |= (1 << shift) - 1 927 928 # We should have filled in all of the bits of the instruction. 929 elif not (is_format and width == 0) and width != insnwidth: 930 error(lineno, f'definition has {width} bits') 931 932 # Do not check for fields overlapping fields; one valid usage 933 # is to be able to duplicate fields via import. 934 fieldmask = 0 935 for f in flds.values(): 936 fieldmask |= f.mask 937 938 # Fix up what we've parsed to match either a format or a pattern. 939 if is_format: 940 # Formats cannot reference formats. 941 if fmt: 942 error(lineno, 'format referencing format') 943 # If an argument set is given, then there should be no fields 944 # without a place to store it. 945 if arg: 946 for f in flds.keys(): 947 if f not in arg.fields: 948 error(lineno, f'field {f} not in argument set {arg.name}') 949 else: 950 arg = infer_argument_set(flds) 951 if name in formats: 952 error(lineno, 'duplicate format name', name) 953 fmt = Format(name, lineno, arg, fixedbits, fixedmask, 954 undefmask, fieldmask, flds, width) 955 formats[name] = fmt 956 else: 957 # Patterns can reference a format ... 958 if fmt: 959 # ... but not an argument simultaneously 960 if arg: 961 error(lineno, 'pattern specifies both format and argument set') 962 if fixedmask & fmt.fixedmask: 963 error(lineno, 'pattern fixed bits overlap format fixed bits') 964 if width != fmt.width: 965 error(lineno, 'pattern uses format of different width') 966 fieldmask |= fmt.fieldmask 967 fixedbits |= fmt.fixedbits 968 fixedmask |= fmt.fixedmask 969 undefmask |= fmt.undefmask 970 else: 971 (fmt, flds) = infer_format(arg, fieldmask, flds, width) 972 arg = fmt.base 973 for f in flds.keys(): 974 if f not in arg.fields: 975 error(lineno, f'field {f} not in argument set {arg.name}') 976 if f in fmt.fields.keys(): 977 error(lineno, f'field {f} set by format and pattern') 978 for f in arg.fields: 979 if f not in flds.keys() and f not in fmt.fields.keys(): 980 error(lineno, f'field {f} not initialized') 981 pat = Pattern(name, lineno, fmt, fixedbits, fixedmask, 982 undefmask, fieldmask, flds, width) 983 parent_pat.pats.append(pat) 984 allpatterns.append(pat) 985 986 # Validate the masks that we have assembled. 987 if fieldmask & fixedmask: 988 error(lineno, 'fieldmask overlaps fixedmask ', 989 f'({whex(fieldmask)} & {whex(fixedmask)})') 990 if fieldmask & undefmask: 991 error(lineno, 'fieldmask overlaps undefmask ', 992 f'({whex(fieldmask)} & {whex(undefmask)})') 993 if fixedmask & undefmask: 994 error(lineno, 'fixedmask overlaps undefmask ', 995 f'({whex(fixedmask)} & {whex(undefmask)})') 996 if not is_format: 997 allbits = fieldmask | fixedmask | undefmask 998 if allbits != insnmask: 999 error(lineno, 'bits left unspecified ', 1000 f'({whex(allbits ^ insnmask)})') 1001# end parse_general 1002 1003 1004def parse_file(f, parent_pat): 1005 """Parse all of the patterns within a file""" 1006 global re_arg_ident 1007 global re_fld_ident 1008 global re_fmt_ident 1009 global re_pat_ident 1010 1011 # Read all of the lines of the file. Concatenate lines 1012 # ending in backslash; discard empty lines and comments. 1013 toks = [] 1014 lineno = 0 1015 nesting = 0 1016 nesting_pats = [] 1017 1018 for line in f: 1019 lineno += 1 1020 1021 # Expand and strip spaces, to find indent. 1022 line = line.rstrip() 1023 line = line.expandtabs() 1024 len1 = len(line) 1025 line = line.lstrip() 1026 len2 = len(line) 1027 1028 # Discard comments 1029 end = line.find('#') 1030 if end >= 0: 1031 line = line[:end] 1032 1033 t = line.split() 1034 if len(toks) != 0: 1035 # Next line after continuation 1036 toks.extend(t) 1037 else: 1038 # Allow completely blank lines. 1039 if len1 == 0: 1040 continue 1041 indent = len1 - len2 1042 # Empty line due to comment. 1043 if len(t) == 0: 1044 # Indentation must be correct, even for comment lines. 1045 if indent != nesting: 1046 error(lineno, 'indentation ', indent, ' != ', nesting) 1047 continue 1048 start_lineno = lineno 1049 toks = t 1050 1051 # Continuation? 1052 if toks[-1] == '\\': 1053 toks.pop() 1054 continue 1055 1056 name = toks[0] 1057 del toks[0] 1058 1059 # End nesting? 1060 if name == '}' or name == ']': 1061 if len(toks) != 0: 1062 error(start_lineno, 'extra tokens after close brace') 1063 1064 # Make sure { } and [ ] nest properly. 1065 if (name == '}') != isinstance(parent_pat, IncMultiPattern): 1066 error(lineno, 'mismatched close brace') 1067 1068 try: 1069 parent_pat = nesting_pats.pop() 1070 except: 1071 error(lineno, 'extra close brace') 1072 1073 nesting -= 2 1074 if indent != nesting: 1075 error(lineno, 'indentation ', indent, ' != ', nesting) 1076 1077 toks = [] 1078 continue 1079 1080 # Everything else should have current indentation. 1081 if indent != nesting: 1082 error(start_lineno, 'indentation ', indent, ' != ', nesting) 1083 1084 # Start nesting? 1085 if name == '{' or name == '[': 1086 if len(toks) != 0: 1087 error(start_lineno, 'extra tokens after open brace') 1088 1089 if name == '{': 1090 nested_pat = IncMultiPattern(start_lineno) 1091 else: 1092 nested_pat = ExcMultiPattern(start_lineno) 1093 parent_pat.pats.append(nested_pat) 1094 nesting_pats.append(parent_pat) 1095 parent_pat = nested_pat 1096 1097 nesting += 2 1098 toks = [] 1099 continue 1100 1101 # Determine the type of object needing to be parsed. 1102 if re.fullmatch(re_fld_ident, name): 1103 parse_field(start_lineno, name[1:], toks) 1104 elif re.fullmatch(re_arg_ident, name): 1105 parse_arguments(start_lineno, name[1:], toks) 1106 elif re.fullmatch(re_fmt_ident, name): 1107 parse_generic(start_lineno, None, name[1:], toks) 1108 elif re.fullmatch(re_pat_ident, name): 1109 parse_generic(start_lineno, parent_pat, name, toks) 1110 else: 1111 error(lineno, f'invalid token "{name}"') 1112 toks = [] 1113 1114 if nesting != 0: 1115 error(lineno, 'missing close brace') 1116# end parse_file 1117 1118 1119class SizeTree: 1120 """Class representing a node in a size decode tree""" 1121 1122 def __init__(self, m, w): 1123 self.mask = m 1124 self.subs = [] 1125 self.base = None 1126 self.width = w 1127 1128 def str1(self, i): 1129 ind = str_indent(i) 1130 r = ind + whex(self.mask) + ' [\n' 1131 for (b, s) in self.subs: 1132 r += ind + f' {whex(b)}:\n' 1133 r += s.str1(i + 4) + '\n' 1134 r += ind + ']' 1135 return r 1136 1137 def __str__(self): 1138 return self.str1(0) 1139 1140 def output_code(self, i, extracted, outerbits, outermask): 1141 ind = str_indent(i) 1142 1143 # If we need to load more bytes to test, do so now. 1144 if extracted < self.width: 1145 output(ind, f'insn = {decode_function}_load_bytes', 1146 f'(ctx, insn, {extracted // 8}, {self.width // 8});\n') 1147 extracted = self.width 1148 1149 # Attempt to aid the compiler in producing compact switch statements. 1150 # If the bits in the mask are contiguous, extract them. 1151 sh = is_contiguous(self.mask) 1152 if sh > 0: 1153 # Propagate SH down into the local functions. 1154 def str_switch(b, sh=sh): 1155 return f'(insn >> {sh}) & {b >> sh:#x}' 1156 1157 def str_case(b, sh=sh): 1158 return hex(b >> sh) 1159 else: 1160 def str_switch(b): 1161 return f'insn & {whexC(b)}' 1162 1163 def str_case(b): 1164 return whexC(b) 1165 1166 output(ind, 'switch (', str_switch(self.mask), ') {\n') 1167 for b, s in sorted(self.subs): 1168 innermask = outermask | self.mask 1169 innerbits = outerbits | b 1170 output(ind, 'case ', str_case(b), ':\n') 1171 output(ind, ' /* ', 1172 str_match_bits(innerbits, innermask), ' */\n') 1173 s.output_code(i + 4, extracted, innerbits, innermask) 1174 output(ind, '}\n') 1175 output(ind, 'return insn;\n') 1176# end SizeTree 1177 1178class SizeLeaf: 1179 """Class representing a leaf node in a size decode tree""" 1180 1181 def __init__(self, m, w): 1182 self.mask = m 1183 self.width = w 1184 1185 def str1(self, i): 1186 return str_indent(i) + whex(self.mask) 1187 1188 def __str__(self): 1189 return self.str1(0) 1190 1191 def output_code(self, i, extracted, outerbits, outermask): 1192 global decode_function 1193 ind = str_indent(i) 1194 1195 # If we need to load more bytes, do so now. 1196 if extracted < self.width: 1197 output(ind, f'insn = {decode_function}_load_bytes', 1198 f'(ctx, insn, {extracted // 8}, {self.width // 8});\n') 1199 extracted = self.width 1200 output(ind, 'return insn;\n') 1201# end SizeLeaf 1202 1203 1204def build_size_tree(pats, width, outerbits, outermask): 1205 global insnwidth 1206 1207 # Collect the mask of bits that are fixed in this width 1208 innermask = 0xff << (insnwidth - width) 1209 innermask &= ~outermask 1210 minwidth = None 1211 onewidth = True 1212 for i in pats: 1213 innermask &= i.fixedmask 1214 if minwidth is None: 1215 minwidth = i.width 1216 elif minwidth != i.width: 1217 onewidth = False; 1218 if minwidth < i.width: 1219 minwidth = i.width 1220 1221 if onewidth: 1222 return SizeLeaf(innermask, minwidth) 1223 1224 if innermask == 0: 1225 if width < minwidth: 1226 return build_size_tree(pats, width + 8, outerbits, outermask) 1227 1228 pnames = [] 1229 for p in pats: 1230 pnames.append(p.name + ':' + p.file + ':' + str(p.lineno)) 1231 error_with_file(pats[0].file, pats[0].lineno, 1232 f'overlapping patterns size {width}:', pnames) 1233 1234 bins = {} 1235 for i in pats: 1236 fb = i.fixedbits & innermask 1237 if fb in bins: 1238 bins[fb].append(i) 1239 else: 1240 bins[fb] = [i] 1241 1242 fullmask = outermask | innermask 1243 lens = sorted(bins.keys()) 1244 if len(lens) == 1: 1245 b = lens[0] 1246 return build_size_tree(bins[b], width + 8, b | outerbits, fullmask) 1247 1248 r = SizeTree(innermask, width) 1249 for b, l in bins.items(): 1250 s = build_size_tree(l, width, b | outerbits, fullmask) 1251 r.subs.append((b, s)) 1252 return r 1253# end build_size_tree 1254 1255 1256def prop_size(tree): 1257 """Propagate minimum widths up the decode size tree""" 1258 1259 if isinstance(tree, SizeTree): 1260 min = None 1261 for (b, s) in tree.subs: 1262 width = prop_size(s) 1263 if min is None or min > width: 1264 min = width 1265 assert min >= tree.width 1266 tree.width = min 1267 else: 1268 min = tree.width 1269 return min 1270# end prop_size 1271 1272 1273def main(): 1274 global arguments 1275 global formats 1276 global allpatterns 1277 global translate_scope 1278 global translate_prefix 1279 global output_fd 1280 global output_file 1281 global input_file 1282 global insnwidth 1283 global insntype 1284 global insnmask 1285 global decode_function 1286 global bitop_width 1287 global variablewidth 1288 global anyextern 1289 1290 decode_scope = 'static ' 1291 1292 long_opts = ['decode=', 'translate=', 'output=', 'insnwidth=', 1293 'static-decode=', 'varinsnwidth='] 1294 try: 1295 (opts, args) = getopt.gnu_getopt(sys.argv[1:], 'o:vw:', long_opts) 1296 except getopt.GetoptError as err: 1297 error(0, err) 1298 for o, a in opts: 1299 if o in ('-o', '--output'): 1300 output_file = a 1301 elif o == '--decode': 1302 decode_function = a 1303 decode_scope = '' 1304 elif o == '--static-decode': 1305 decode_function = a 1306 elif o == '--translate': 1307 translate_prefix = a 1308 translate_scope = '' 1309 elif o in ('-w', '--insnwidth', '--varinsnwidth'): 1310 if o == '--varinsnwidth': 1311 variablewidth = True 1312 insnwidth = int(a) 1313 if insnwidth == 16: 1314 insntype = 'uint16_t' 1315 insnmask = 0xffff 1316 elif insnwidth == 64: 1317 insntype = 'uint64_t' 1318 insnmask = 0xffffffffffffffff 1319 bitop_width = 64 1320 elif insnwidth != 32: 1321 error(0, 'cannot handle insns of width', insnwidth) 1322 else: 1323 assert False, 'unhandled option' 1324 1325 if len(args) < 1: 1326 error(0, 'missing input file') 1327 1328 toppat = ExcMultiPattern(0) 1329 1330 for filename in args: 1331 input_file = filename 1332 f = open(filename, 'rt', encoding='utf-8') 1333 parse_file(f, toppat) 1334 f.close() 1335 1336 # We do not want to compute masks for toppat, because those masks 1337 # are used as a starting point for build_tree. For toppat, we must 1338 # insist that decode begins from naught. 1339 for i in toppat.pats: 1340 i.prop_masks() 1341 1342 toppat.build_tree() 1343 toppat.prop_format() 1344 1345 if variablewidth: 1346 for i in toppat.pats: 1347 i.prop_width() 1348 stree = build_size_tree(toppat.pats, 8, 0, 0) 1349 prop_size(stree) 1350 1351 if output_file: 1352 output_fd = open(output_file, 'wt', encoding='utf-8') 1353 else: 1354 output_fd = io.TextIOWrapper(sys.stdout.buffer, 1355 encoding=sys.stdout.encoding, 1356 errors="ignore") 1357 1358 output_autogen() 1359 for n in sorted(arguments.keys()): 1360 f = arguments[n] 1361 f.output_def() 1362 1363 # A single translate function can be invoked for different patterns. 1364 # Make sure that the argument sets are the same, and declare the 1365 # function only once. 1366 # 1367 # If we're sharing formats, we're likely also sharing trans_* functions, 1368 # but we can't tell which ones. Prevent issues from the compiler by 1369 # suppressing redundant declaration warnings. 1370 if anyextern: 1371 output("#pragma GCC diagnostic push\n", 1372 "#pragma GCC diagnostic ignored \"-Wredundant-decls\"\n", 1373 "#ifdef __clang__\n" 1374 "# pragma GCC diagnostic ignored \"-Wtypedef-redefinition\"\n", 1375 "#endif\n\n") 1376 1377 out_pats = {} 1378 for i in allpatterns: 1379 if i.name in out_pats: 1380 p = out_pats[i.name] 1381 if i.base.base != p.base.base: 1382 error(0, i.name, ' has conflicting argument sets') 1383 else: 1384 i.output_decl() 1385 out_pats[i.name] = i 1386 output('\n') 1387 1388 if anyextern: 1389 output("#pragma GCC diagnostic pop\n\n") 1390 1391 for n in sorted(formats.keys()): 1392 f = formats[n] 1393 f.output_extract() 1394 1395 output(decode_scope, 'bool ', decode_function, 1396 '(DisasContext *ctx, ', insntype, ' insn)\n{\n') 1397 1398 i4 = str_indent(4) 1399 1400 if len(allpatterns) != 0: 1401 output(i4, 'union {\n') 1402 for n in sorted(arguments.keys()): 1403 f = arguments[n] 1404 output(i4, i4, f.struct_name(), ' f_', f.name, ';\n') 1405 output(i4, '} u;\n\n') 1406 toppat.output_code(4, False, 0, 0) 1407 1408 output(i4, 'return false;\n') 1409 output('}\n') 1410 1411 if variablewidth: 1412 output('\n', decode_scope, insntype, ' ', decode_function, 1413 '_load(DisasContext *ctx)\n{\n', 1414 ' ', insntype, ' insn = 0;\n\n') 1415 stree.output_code(4, 0, 0, 0) 1416 output('}\n') 1417 1418 if output_file: 1419 output_fd.close() 1420# end main 1421 1422 1423if __name__ == '__main__': 1424 main()