#!/usr/bin/env python3 # # Copyright © 2020 Google, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice (including the next # paragraph) shall be included in all copies or substantial portions of the # Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. from mako.template import Template from isa import ISA import argparse import os import sys class FieldDecode(object): def __init__(self, name, map_expr): self.name = name self.map_expr = map_expr def get_c_name(self): return self.name.lower().replace('-', '_') # State and helpers used by the template: class State(object): def __init__(self, isa): self.isa = isa def case_name(self, bitset, name): return bitset.encode.case_prefix + name.upper().replace('.', '_').replace('-', '_').replace('#', '') # Return a list of all entries for a leaf bitset, with the child # bitset overriding the parent bitset's entries. Because we can't resolve # which s are used until we resolve which overload is used, we # generate code for encoding all of these and then at runtime select which # one to call based on the display. def decode_fields(self, bitset): if bitset.get_root().decode is None: return seen_fields = set() if bitset.encode is not None: for name, expr in bitset.encode.maps.items(): seen_fields.add(name) yield FieldDecode(name, expr) if bitset.extends is not None: for field in self.decode_fields(self.isa.bitsets[bitset.extends]): if field.name not in seen_fields: yield field # A limited resolver for field type which doesn't properly account for # overrides. In particular, if a field is defined differently in multiple # different cases, this just blindly picks the last one. # # TODO to do this properly, I don't think there is an alternative than # to emit code which evaluates the case.expr def resolve_simple_field(self, bitset, name): field = None for case in bitset.cases: if name in case.fields: field = case.fields[name] if field is not None: return field if bitset.extends is not None: return self.resolve_simple_field(bitset.isa.bitsets[bitset.extends], name) return None template = """\ /* Copyright (C) 2020 Google, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "${header}" #include #include #define BITMASK_WORDS BITSET_WORDS(${isa.bitsize}) typedef struct { BITSET_WORD bitset[BITMASK_WORDS]; } bitmask_t; #define BITSET_FORMAT ${isa.format()} #define BITSET_VALUE(v) ${isa.value()} static inline void next_instruction(bitmask_t *instr, BITSET_WORD *start) { %for i in range(0, int(isa.bitsize / 32)): instr->bitset[${i}] = *(start + ${i}); %endfor } static inline uint64_t bitmask_to_uint64_t(bitmask_t mask) { % if isa.bitsize <= 32: return mask.bitset[0]; % else: return ((uint64_t)mask.bitset[1] << 32) | mask.bitset[0]; % endif } static inline bitmask_t uint64_t_to_bitmask(uint64_t val) { bitmask_t mask = { .bitset[0] = val & 0xffffffff, % if isa.bitsize > 32: .bitset[1] = (val >> 32) & 0xffffffff, % endif }; return mask; } #include "isaspec_decode_decl.h" static uint64_t isa_decode_field(struct decode_scope *scope, const char *field_name); /* * enum tables, these don't have any link back to other tables so just * dump them up front before the bitset tables */ %for name, enum in isa.enums.items(): static const struct isa_enum ${enum.get_c_name()} = { .num_values = ${len(enum.values)}, .values = { % for val, display in enum.values.items(): { .val = ${val}, .display = "${display}" }, % endfor }, }; %endfor /* * generated expression functions, can be linked from bitset tables, so * also dump them up front */ %for name, expr in isa.expressions.items(): static uint64_t ${expr.get_c_name()}(struct decode_scope *scope) { % for fieldname in sorted(expr.fieldnames): int64_t ${fieldname} = isa_decode_field(scope, "${fieldname}"); % endfor return ${expr.expr}; } %endfor /* forward-declarations of bitset decode functions */ %for name, bitset in isa.all_bitsets(): % for df in s.decode_fields(bitset): static void decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_${df.get_c_name()}(void *out, struct decode_scope *scope, uint64_t val); % endfor static const struct isa_field_decode decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_fields[] = { % for df in s.decode_fields(bitset): { .name = "${df.name}", .decode = decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_${df.get_c_name()}, }, % endfor }; static void decode_${bitset.get_c_name()}_gen_${bitset.gen_min}(void *out, struct decode_scope *scope); %endfor /* * Forward-declarations (so we don't have to figure out which order to * emit various tables when they have pointers to each other) */ %for name, bitset in isa.all_bitsets(): static const struct isa_bitset bitset_${bitset.get_c_name()}_gen_${bitset.gen_min}; %endfor %for root_name, root in isa.roots.items(): static const struct isa_bitset *${root.get_c_name()}[]; %endfor /* * bitset tables: */ %for name, bitset in isa.all_bitsets(): % for case in bitset.cases: % for field_name, field in case.fields.items(): % if field.get_c_typename() == 'TYPE_BITSET': % if len(field.params) > 0: static const struct isa_field_params ${case.get_c_name()}_gen_${bitset.gen_min}_${field.get_c_name()} = { .num_params = ${len(field.params)}, .params = { % for param in field.params: { .name= "${param[0]}", .as = "${param[1]}" }, % endfor }, }; % endif % endif % endfor static const struct isa_case ${case.get_c_name()}_gen_${bitset.gen_min} = { % if case.expr is not None: .expr = &${isa.expressions[case.expr].get_c_name()}, % endif % if case.display is not None: .display = "${case.display}", % endif .num_fields = ${len(case.fields)}, .fields = { % for field_name, field in case.fields.items(): { .name = "${field_name}", .low = ${field.low}, .high = ${field.high}, % if field.expr is not None: .expr = &${isa.expressions[field.expr].get_c_name()}, % endif % if field.display is not None: .display = "${field.display}", % endif .type = ${field.get_c_typename()}, % if field.get_c_typename() == 'TYPE_BITSET': .bitsets = ${isa.roots[field.type].get_c_name()}, % if len(field.params) > 0: .params = &${case.get_c_name()}_gen_${bitset.gen_min}_${field.get_c_name()}, % endif % endif % if field.get_c_typename() == 'TYPE_ENUM': .enums = &${isa.enums[field.type].get_c_name()}, % endif % if field.get_c_typename() == 'TYPE_ASSERT': .val.bitset = { ${', '.join(isa.split_bits(field.val, 32))} }, % endif % if field.get_c_typename() == 'TYPE_BRANCH' or field.get_c_typename() == 'TYPE_ABSBRANCH': .call = ${str(field.call).lower()}, % endif }, % endfor }, }; % endfor static const struct isa_bitset bitset_${bitset.get_c_name()}_gen_${bitset.gen_min} = { <% pattern = bitset.get_pattern() %> % if bitset.extends is not None: .parent = &bitset_${isa.bitsets[bitset.extends].get_c_name()}_gen_${isa.bitsets[bitset.extends].gen_min}, % endif .name = "${bitset.display_name}", .gen = { .min = ${bitset.get_gen_min()}, .max = ${bitset.get_gen_max()}, }, .match.bitset = { ${', '.join(isa.split_bits(pattern.match, 32))} }, .dontcare.bitset = { ${', '.join(isa.split_bits(pattern.dontcare, 32))} }, .mask.bitset = { ${', '.join(isa.split_bits(pattern.mask, 32))} }, .decode = decode_${bitset.get_c_name()}_gen_${bitset.gen_min}, .num_decode_fields = ARRAY_SIZE(decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_fields), .decode_fields = decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_fields, .num_cases = ${len(bitset.cases)}, .cases = { % for case in bitset.cases: &${case.get_c_name()}_gen_${bitset.gen_min}, % endfor }, }; %endfor /* * bitset hierarchy root tables (where decoding starts from): */ %for root_name, root in isa.roots.items(): static const struct isa_bitset *${root.get_c_name()}[] = { % for leaf_name, leafs in isa.leafs.items(): % for leaf in leafs: % if leaf.get_root() == root: &bitset_${leaf.get_c_name()}_gen_${leaf.gen_min}, % endif % endfor % endfor (void *)0 }; %endfor #include "isaspec_decode_impl.c" %for name, bitset in isa.all_bitsets(): % for df in s.decode_fields(bitset): <% field = s.resolve_simple_field(bitset, df.name) %> static void decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_${df.get_c_name()}(void *out, struct decode_scope *scope, uint64_t val) { % if bitset.get_root().decode is not None and field is not None: ${bitset.get_root().encode.type} src = *(${bitset.get_root().encode.type} *)out; % if field.get_c_typename() == 'TYPE_BITSET': isa_decode_bitset(&${df.map_expr}, ${isa.roots[field.type].get_c_name()}, scope, uint64_t_to_bitmask(val)); % elif field.get_c_typename() in ['TYPE_BRANCH', 'TYPE_INT', 'TYPE_OFFSET']: ${df.map_expr} = util_sign_extend(val, ${field.get_size()}); % else: ${df.map_expr} = val; % endif *(${bitset.get_root().encode.type} *)out = src; % endif } % endfor static void decode_${bitset.get_c_name()}_gen_${bitset.gen_min}(void *out, struct decode_scope *scope) { % if bitset.get_root().decode is not None: UNUSED ${bitset.get_root().encode.type} src; % if bitset.get_root().encode.type.endswith('*') and name in isa.leafs and bitset.get_root().encode.case_prefix is not None: src = ${bitset.get_root().get_c_name()}_create(${s.case_name(bitset.get_root(), bitset.name)}); *(${bitset.get_root().encode.type} *)out = src; % endif % endif } %endfor void ${prefix}_isa_disasm(void *bin, int sz, FILE *out, const struct isa_decode_options *options) { isa_disasm(bin, sz, out, options); } bool ${prefix}_isa_decode(void *out, void *bin, const struct isa_decode_options *options) { return isa_decode(out, bin, options); } uint32_t ${prefix}_isa_get_gpu_id(struct decode_scope *scope) { return isa_get_gpu_id(scope); } """ header = """\ /* Copyright (C) 2020 Google, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef _${guard}_ #define _${guard}_ #include "compiler/isaspec/isaspec.h" #ifdef __cplusplus extern "C" { #endif void ${prefix}_isa_disasm(void *bin, int sz, FILE *out, const struct isa_decode_options *options); bool ${prefix}_isa_decode(void *out, void *bin, const struct isa_decode_options *options); struct decode_scope; uint32_t ${prefix}_isa_get_gpu_id(struct decode_scope *scope); /** * Allows to use gpu_id in expr functions */ #define ISA_GPU_ID() ${prefix}_isa_get_gpu_id(scope) #ifdef __cplusplus } #endif #endif /* _${guard}_ */ """ def guard(p): return os.path.basename(p).upper().replace("-", "_").replace(".", "_") def prefix(p): return os.path.basename(p).lower().replace("-", "_").replace(".", "_").split('_')[0] def main(): parser = argparse.ArgumentParser() parser.add_argument('--xml', required=True, help='isaspec XML file.') parser.add_argument('--out-c', required=True, help='Output C file.') parser.add_argument('--out-h', required=True, help='Output H file.') args = parser.parse_args() isa = ISA(args.xml) s = State(isa) try: with open(args.out_c, 'w', encoding='utf-8') as f: out_h_basename = os.path.basename(args.out_h) f.write(Template(template).render(isa=isa, s=s, header=out_h_basename, prefix=prefix(args.out_h))) with open(args.out_h, 'w', encoding='utf-8') as f: f.write(Template(header).render(isa=isa, guard=guard(args.out_h), prefix=prefix(args.out_h))) except Exception: # In the event there's an error, this imports some helpers from mako # to print a useful stack trace and prints it, then exits with # status 1, if python is run with debug; otherwise it just raises # the exception import sys from mako import exceptions print(exceptions.text_error_template().render(), file=sys.stderr) sys.exit(1) if __name__ == '__main__': main()