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