| #!/usr/bin/env python3 |
| |
| ## |
| ## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. |
| ## |
| ## This program is free software; you can redistribute it and/or modify |
| ## it under the terms of the GNU General Public License as published by |
| ## the Free Software Foundation; either version 2 of the License, or |
| ## (at your option) any later version. |
| ## |
| ## This program is distributed in the hope that it will be useful, |
| ## but WITHOUT ANY WARRANTY; without even the implied warranty of |
| ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| ## GNU General Public License for more details. |
| ## |
| ## You should have received a copy of the GNU General Public License |
| ## along with this program; if not, see <http://www.gnu.org/licenses/>. |
| ## |
| |
| import io |
| import re |
| |
| import sys |
| import iset |
| |
| encs = { |
| tag: "".join(reversed(iset.iset[tag]["enc"].replace(" ", ""))) |
| for tag in iset.tags |
| if iset.iset[tag]["enc"] != "MISSING ENCODING" |
| } |
| |
| enc_classes = set([iset.iset[tag]["enc_class"] for tag in encs.keys()]) |
| subinsn_enc_classes = set( |
| [enc_class for enc_class in enc_classes if enc_class.startswith("SUBINSN_")] |
| ) |
| ext_enc_classes = set( |
| [ |
| enc_class |
| for enc_class in enc_classes |
| if enc_class not in ("NORMAL", "16BIT") and not enc_class.startswith("SUBINSN_") |
| ] |
| ) |
| |
| try: |
| subinsn_groupings = iset.subinsn_groupings |
| except AttributeError: |
| subinsn_groupings = {} |
| |
| for tag, subinsn_grouping in subinsn_groupings.items(): |
| encs[tag] = "".join(reversed(subinsn_grouping["enc"].replace(" ", ""))) |
| |
| dectree_normal = {"leaves": set()} |
| dectree_16bit = {"leaves": set()} |
| dectree_subinsn_groupings = {"leaves": set()} |
| dectree_subinsns = {name: {"leaves": set()} for name in subinsn_enc_classes} |
| dectree_extensions = {name: {"leaves": set()} for name in ext_enc_classes} |
| |
| for tag in encs.keys(): |
| if tag in subinsn_groupings: |
| dectree_subinsn_groupings["leaves"].add(tag) |
| continue |
| enc_class = iset.iset[tag]["enc_class"] |
| if enc_class.startswith("SUBINSN_"): |
| if len(encs[tag]) != 32: |
| encs[tag] = encs[tag] + "0" * (32 - len(encs[tag])) |
| dectree_subinsns[enc_class]["leaves"].add(tag) |
| elif enc_class == "16BIT": |
| if len(encs[tag]) != 16: |
| raise Exception( |
| 'Tag "{}" has enc_class "{}" and not an encoding ' |
| + "width of 16 bits!".format(tag, enc_class) |
| ) |
| dectree_16bit["leaves"].add(tag) |
| else: |
| if len(encs[tag]) != 32: |
| raise Exception( |
| 'Tag "{}" has enc_class "{}" and not an encoding ' |
| + "width of 32 bits!".format(tag, enc_class) |
| ) |
| if enc_class == "NORMAL": |
| dectree_normal["leaves"].add(tag) |
| else: |
| dectree_extensions[enc_class]["leaves"].add(tag) |
| |
| faketags = set() |
| for tag, enc in iset.enc_ext_spaces.items(): |
| faketags.add(tag) |
| encs[tag] = "".join(reversed(enc.replace(" ", ""))) |
| dectree_normal["leaves"].add(tag) |
| |
| faketags |= set(subinsn_groupings.keys()) |
| |
| |
| def every_bit_counts(bitset): |
| for i in range(1, len(next(iter(bitset)))): |
| if len(set([bits[:i] + bits[i + 1 :] for bits in bitset])) == len(bitset): |
| return False |
| return True |
| |
| |
| def auto_separate(node): |
| tags = node["leaves"] |
| if len(tags) <= 1: |
| return |
| enc_width = len(encs[next(iter(tags))]) |
| opcode_bit_for_all = [ |
| all([encs[tag][i] in "01" for tag in tags]) for i in range(enc_width) |
| ] |
| opcode_bit_is_0_for_all = [ |
| opcode_bit_for_all[i] and all([encs[tag][i] == "0" for tag in tags]) |
| for i in range(enc_width) |
| ] |
| opcode_bit_is_1_for_all = [ |
| opcode_bit_for_all[i] and all([encs[tag][i] == "1" for tag in tags]) |
| for i in range(enc_width) |
| ] |
| differentiator_opcode_bit = [ |
| opcode_bit_for_all[i] |
| and not (opcode_bit_is_0_for_all[i] or opcode_bit_is_1_for_all[i]) |
| for i in range(enc_width) |
| ] |
| best_width = 0 |
| for width in range(4, 0, -1): |
| for lsb in range(enc_width - width, -1, -1): |
| bitset = set([encs[tag][lsb : lsb + width] for tag in tags]) |
| if all(differentiator_opcode_bit[lsb : lsb + width]) and ( |
| len(bitset) == len(tags) or every_bit_counts(bitset) |
| ): |
| best_width = width |
| best_lsb = lsb |
| caught_all_tags = len(bitset) == len(tags) |
| break |
| if best_width != 0: |
| break |
| if best_width == 0: |
| raise Exception( |
| "Could not find a way to differentiate the encodings " |
| + "of the following tags:\n{}".format("\n".join(tags)) |
| ) |
| if caught_all_tags: |
| for width in range(1, best_width): |
| for lsb in range(enc_width - width, -1, -1): |
| bitset = set([encs[tag][lsb : lsb + width] for tag in tags]) |
| if all(differentiator_opcode_bit[lsb : lsb + width]) and len( |
| bitset |
| ) == len(tags): |
| best_width = width |
| best_lsb = lsb |
| break |
| else: |
| continue |
| break |
| node["separator_lsb"] = best_lsb |
| node["separator_width"] = best_width |
| node["children"] = [] |
| for value in range(2**best_width): |
| child = {} |
| bits = "".join(reversed("{:0{}b}".format(value, best_width))) |
| child["leaves"] = set( |
| [tag for tag in tags if encs[tag][best_lsb : best_lsb + best_width] == bits] |
| ) |
| node["children"].append(child) |
| for child in node["children"]: |
| auto_separate(child) |
| |
| |
| auto_separate(dectree_normal) |
| auto_separate(dectree_16bit) |
| if subinsn_groupings: |
| auto_separate(dectree_subinsn_groupings) |
| for dectree_subinsn in dectree_subinsns.values(): |
| auto_separate(dectree_subinsn) |
| for dectree_ext in dectree_extensions.values(): |
| auto_separate(dectree_ext) |
| |
| for tag in faketags: |
| del encs[tag] |
| |
| |
| def table_name(parents, node): |
| path = parents + [node] |
| root = path[0] |
| tag = next(iter(node["leaves"])) |
| if tag in subinsn_groupings: |
| enc_width = len(subinsn_groupings[tag]["enc"].replace(" ", "")) |
| else: |
| tag = next(iter(node["leaves"] - faketags)) |
| enc_width = len(encs[tag]) |
| determining_bits = ["_"] * enc_width |
| for parent, child in zip(path[:-1], path[1:]): |
| lsb = parent["separator_lsb"] |
| width = parent["separator_width"] |
| value = parent["children"].index(child) |
| determining_bits[lsb : lsb + width] = list( |
| reversed("{:0{}b}".format(value, width)) |
| ) |
| if tag in subinsn_groupings: |
| name = "DECODE_ROOT_EE" |
| else: |
| enc_class = iset.iset[tag]["enc_class"] |
| if enc_class in ext_enc_classes: |
| name = "DECODE_EXT_{}".format(enc_class) |
| elif enc_class in subinsn_enc_classes: |
| name = "DECODE_SUBINSN_{}".format(enc_class) |
| else: |
| name = "DECODE_ROOT_{}".format(enc_width) |
| if node != root: |
| name += "_" + "".join(reversed(determining_bits)) |
| return name |
| |
| |
| def print_node(f, node, parents): |
| if len(node["leaves"]) <= 1: |
| return |
| name = table_name(parents, node) |
| lsb = node["separator_lsb"] |
| width = node["separator_width"] |
| print( |
| "DECODE_NEW_TABLE({},{},DECODE_SEPARATOR_BITS({},{}))".format( |
| name, 2**width, lsb, width |
| ), |
| file=f, |
| ) |
| for child in node["children"]: |
| if len(child["leaves"]) == 0: |
| print("INVALID()", file=f) |
| elif len(child["leaves"]) == 1: |
| (tag,) = child["leaves"] |
| if tag in subinsn_groupings: |
| class_a = subinsn_groupings[tag]["class_a"] |
| class_b = subinsn_groupings[tag]["class_b"] |
| enc = subinsn_groupings[tag]["enc"].replace(" ", "") |
| if "RESERVED" in tag: |
| print("INVALID()", file=f) |
| else: |
| print( |
| 'SUBINSNS({},{},{},"{}")'.format(tag, class_a, class_b, enc), |
| file=f, |
| ) |
| elif tag in iset.enc_ext_spaces: |
| enc = iset.enc_ext_spaces[tag].replace(" ", "") |
| print('EXTSPACE({},"{}")'.format(tag, enc), file=f) |
| else: |
| enc = "".join(reversed(encs[tag])) |
| print('TERMINAL({},"{}")'.format(tag, enc), file=f) |
| else: |
| print("TABLE_LINK({})".format(table_name(parents + [node], child)), file=f) |
| print( |
| "DECODE_END_TABLE({},{},DECODE_SEPARATOR_BITS({},{}))".format( |
| name, 2**width, lsb, width |
| ), |
| file=f, |
| ) |
| print(file=f) |
| parents.append(node) |
| for child in node["children"]: |
| print_node(f, child, parents) |
| parents.pop() |
| |
| |
| def print_tree(f, tree): |
| print_node(f, tree, []) |
| |
| |
| def print_match_info(f): |
| for tag in sorted(encs.keys(), key=iset.tags.index): |
| enc = "".join(reversed(encs[tag])) |
| mask = int(re.sub(r"[^1]", r"0", enc.replace("0", "1")), 2) |
| match = int(re.sub(r"[^01]", r"0", enc), 2) |
| suffix = "" |
| print( |
| "DECODE{}_MATCH_INFO({},0x{:x}U,0x{:x}U)".format(suffix, tag, mask, match), |
| file=f, |
| ) |
| |
| |
| regre = re.compile(r"((?<!DUP)[MNORCPQXSGVZA])([stuvwxyzdefg]+)([.]?[LlHh]?)(\d+S?)") |
| immre = re.compile(r"[#]([rRsSuUm])(\d+)(?:[:](\d+))?") |
| |
| |
| def ordered_unique(l): |
| return sorted(set(l), key=l.index) |
| |
| |
| implicit_registers = {"SP": 29, "FP": 30, "LR": 31} |
| |
| num_registers = {"R": 32, "V": 32} |
| |
| |
| def print_op_info(f): |
| for tag in sorted(encs.keys(), key=iset.tags.index): |
| enc = encs[tag] |
| print(file=f) |
| print("DECODE_OPINFO({},".format(tag), file=f) |
| regs = ordered_unique(regre.findall(iset.iset[tag]["syntax"])) |
| imms = ordered_unique(immre.findall(iset.iset[tag]["syntax"])) |
| regno = 0 |
| for reg in regs: |
| reg_type = reg[0] |
| reg_letter = reg[1][0] |
| reg_num_choices = int(reg[3].rstrip("S")) |
| reg_mapping = reg[0] + "".join(["_" for letter in reg[1]]) + reg[3] |
| reg_enc_fields = re.findall(reg_letter + "+", enc) |
| if len(reg_enc_fields) == 0: |
| raise Exception('Tag "{}" missing register field!'.format(tag)) |
| if len(reg_enc_fields) > 1: |
| raise Exception('Tag "{}" has split register field!'.format(tag)) |
| reg_enc_field = reg_enc_fields[0] |
| if 2 ** len(reg_enc_field) != reg_num_choices: |
| raise Exception( |
| 'Tag "{}" has incorrect register field width!'.format(tag) |
| ) |
| print( |
| " DECODE_REG({},{},{})".format( |
| regno, len(reg_enc_field), enc.index(reg_enc_field) |
| ), |
| file=f, |
| ) |
| if reg_type in num_registers and reg_num_choices != num_registers[reg_type]: |
| print( |
| " DECODE_MAPPED_REG({},{})".format(regno, reg_mapping), |
| file=f, |
| ) |
| regno += 1 |
| |
| def implicit_register_key(reg): |
| return implicit_registers[reg] |
| |
| for reg in sorted( |
| set( |
| [ |
| r |
| for r in ( |
| iset.iset[tag]["rregs"].split(",") |
| + iset.iset[tag]["wregs"].split(",") |
| ) |
| if r in implicit_registers |
| ] |
| ), |
| key=implicit_register_key, |
| ): |
| print( |
| " DECODE_IMPL_REG({},{})".format(regno, implicit_registers[reg]), |
| file=f, |
| ) |
| regno += 1 |
| if imms and imms[0][0].isupper(): |
| imms = reversed(imms) |
| for imm in imms: |
| if imm[0].isupper(): |
| immno = 1 |
| else: |
| immno = 0 |
| imm_type = imm[0] |
| imm_width = int(imm[1]) |
| imm_shift = imm[2] |
| if imm_shift: |
| imm_shift = int(imm_shift) |
| else: |
| imm_shift = 0 |
| if imm_type.islower(): |
| imm_letter = "i" |
| else: |
| imm_letter = "I" |
| remainder = imm_width |
| for m in reversed(list(re.finditer(imm_letter + "+", enc))): |
| remainder -= m.end() - m.start() |
| print( |
| " DECODE_IMM({},{},{},{})".format( |
| immno, m.end() - m.start(), m.start(), remainder |
| ), |
| file=f, |
| ) |
| if remainder != 0: |
| if imm[2]: |
| imm[2] = ":" + imm[2] |
| raise Exception( |
| 'Tag "{}" has an incorrect number of ' |
| + 'encoding bits for immediate "{}"'.format(tag, "".join(imm)) |
| ) |
| if imm_type.lower() in "sr": |
| print(" DECODE_IMM_SXT({},{})".format(immno, imm_width), file=f) |
| if imm_type.lower() == "n": |
| print(" DECODE_IMM_NEG({},{})".format(immno, imm_width), file=f) |
| if imm_shift: |
| print( |
| " DECODE_IMM_SHIFT({},{})".format(immno, imm_shift), file=f |
| ) |
| print(")", file=f) |
| |
| |
| if __name__ == "__main__": |
| with open(sys.argv[1], "w") as f: |
| print_tree(f, dectree_normal) |
| print_tree(f, dectree_16bit) |
| if subinsn_groupings: |
| print_tree(f, dectree_subinsn_groupings) |
| for name, dectree_subinsn in sorted(dectree_subinsns.items()): |
| print_tree(f, dectree_subinsn) |
| for name, dectree_ext in sorted(dectree_extensions.items()): |
| print_tree(f, dectree_ext) |
| print_match_info(f) |
| print_op_info(f) |