| #!/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, BitSetDerivedField, BitSetAssertField |
| import argparse |
| import sys |
| import re |
| |
| # Encoding is driven by the display template that would be used |
| # to decode any given instruction, essentially working backwards |
| # from the decode case. (Or put another way, the decoded bitset |
| # should contain enough information to re-encode it again.) |
| # |
| # In the xml, we can have multiple override cases per bitset, |
| # which can override display template and/or fields. Iterating |
| # all this from within the template is messy, so use helpers |
| # outside of the template for this. |
| # |
| # The hierarchy of iterators for encoding is: |
| # |
| # // First level - Case() (s.bitset_cases() iterator) |
| # if (caseA.expression()) { // maps to <override/> in xml |
| # // Second level - DisplayField() (case.display_fields() iterator) |
| # ... encode field A ... |
| # ... encode field B ... |
| # |
| # // Third level - each display field can be potentially resolved |
| # // by multiple different overrides, you can end up with |
| # // an if/else ladder for an individual display field |
| # if (field_c_case1.expression()) { |
| # ... encode field C ... |
| # } else if (field_c_case2.expression() { |
| # ... encode field C ... |
| # } else { |
| # } |
| # |
| # } else if (caseB.expression())( |
| # } else { // maps to the default case in bitset, ie. outside <override/> |
| # } |
| |
| |
| # Represents a concrete field, ie. a field can be overriden |
| # by an override, so the exact choice to encode a given field |
| # in a bitset may be conditional |
| class FieldCase(object): |
| def __init__(self, bitset, field, case): |
| self.field = field |
| self.expr = None |
| if case.expr is not None: |
| self.expr = bitset.isa.expressions[case.expr] |
| |
| def signed(self): |
| if self.field.type in ['int', 'offset', 'branch']: |
| return 'true' |
| return 'false' |
| |
| class AssertField(object): |
| def __init__(self, bitset, field, case): |
| self.field = field |
| self.expr = None |
| if case.expr is not None: |
| self.expr = bitset.isa.expressions[case.expr] |
| |
| def signed(self): |
| return 'false' |
| |
| # Represents a field to be encoded: |
| class DisplayField(object): |
| def __init__(self, bitset, case, name): |
| self.bitset = bitset # leaf bitset |
| self.case = case |
| self.name = name |
| |
| def fields(self, bitset=None): |
| if bitset is None: |
| bitset = self.bitset |
| # resolving the various cases for encoding a given |
| # field is similar to resolving the display template |
| # string |
| for case in bitset.cases: |
| if case.expr is not None: |
| expr = bitset.isa.expressions[case.expr] |
| self.case.append_expr_fields(expr) |
| if self.name in case.fields: |
| field = case.fields[self.name] |
| # For bitset fields, the bitset type could reference |
| # fields in this (the containing) bitset, in addition |
| # to the ones which are directly used to encode the |
| # field itself. |
| if field.get_c_typename() == 'TYPE_BITSET': |
| for param in field.params: |
| self.case.append_field(param[0]) |
| # For derived fields, we want to consider any other |
| # fields that are referenced by the expr |
| if isinstance(field, BitSetDerivedField): |
| expr = bitset.isa.expressions[field.expr] |
| self.case.append_expr_fields(expr) |
| elif not isinstance(field, BitSetAssertField): |
| yield FieldCase(bitset, field, case) |
| # if we've found an unconditional case specifying |
| # the named field, we are done |
| if case.expr is None: |
| return |
| if bitset.extends is not None: |
| yield from self.fields(bitset.isa.bitsets[bitset.extends]) |
| |
| # Represents an if/else case in bitset encoding which has a display |
| # template string: |
| class Case(object): |
| def __init__(self, bitset, case): |
| self.bitset = bitset # leaf bitset |
| self.case = case |
| self.expr = None |
| if case.expr is not None: |
| self.expr = bitset.isa.expressions[case.expr] |
| self.fieldnames = re.findall(r"{([a-zA-Z0-9_:=]+)}", case.display) |
| self.append_forced(bitset) |
| |
| # remove special fieldname properties e.g. :align= |
| self.fieldnames = list(map(lambda name: name.split(':')[0], self.fieldnames)) |
| |
| # Handle fields which don't appear in display template but have |
| # force="true" |
| def append_forced(self, bitset): |
| if bitset.encode is not None: |
| for name, val in bitset.encode.forced.items(): |
| self.append_field(name) |
| if bitset.extends is not None: |
| self.append_forced(bitset.isa.bitsets[bitset.extends]) |
| |
| # In the process of resolving a field, we might discover additional |
| # fields that need resolving: |
| # |
| # a) a derived field which maps to one or more other concrete fields |
| # b) a bitset field, which may be "parameterized".. for example a |
| # #multisrc field which refers back to SRC1_R/SRC2_R outside of |
| # the range of bits covered by the #multisrc field itself |
| def append_field(self, fieldname): |
| if fieldname not in self.fieldnames: |
| self.fieldnames.append(fieldname) |
| |
| def append_expr_fields(self, expr): |
| for fieldname in expr.fieldnames: |
| self.append_field(fieldname) |
| |
| def display_fields(self): |
| for fieldname in self.fieldnames: |
| yield DisplayField(self.bitset, self, fieldname) |
| |
| def assert_cases(self, bitset=None): |
| if bitset is None: |
| bitset = self.bitset |
| for case in bitset.cases: |
| for name, field in case.fields.items(): |
| if field.get_c_typename() == 'TYPE_ASSERT': |
| yield AssertField(bitset, field, case) |
| if bitset.extends is not None: |
| yield from self.assert_cases(bitset.isa.bitsets[bitset.extends]) |
| |
| # State and helpers used by the template: |
| class State(object): |
| def __init__(self, isa): |
| self.isa = isa |
| self.warned_missing_extractors = [] |
| |
| def bitset_cases(self, bitset, leaf_bitset=None): |
| if leaf_bitset is None: |
| leaf_bitset = bitset |
| for case in bitset.cases: |
| if case.display is None: |
| # if this is the last case (ie. case.expr is None) |
| # then we need to go up the inheritance chain: |
| if case.expr is None and bitset.extends is not None: |
| parent_bitset = bitset.isa.bitsets[bitset.extends] |
| yield from self.bitset_cases(parent_bitset, leaf_bitset) |
| continue |
| yield Case(leaf_bitset, case) |
| |
| # Find unique bitset remap/parameter names, to generate a struct |
| # used to pass "parameters" to bitset fields: |
| def unique_param_names(self): |
| unique_names = [] |
| for root in self.encode_roots(): |
| for leaf in self.encode_leafs(root): |
| for case in self.bitset_cases(leaf): |
| for df in case.display_fields(): |
| for f in df.fields(): |
| if f.field.get_c_typename() == 'TYPE_BITSET': |
| for param in f.field.params: |
| target_name = param[1] |
| if target_name not in unique_names: |
| yield target_name |
| unique_names.append(target_name) |
| |
| def case_name(self, bitset, name): |
| return bitset.encode.case_prefix + name.upper().replace('.', '_').replace('-', '_').replace('#', '') |
| |
| def encode_roots(self): |
| for name, root in self.isa.roots.items(): |
| if root.encode is None: |
| continue |
| yield root |
| |
| def encode_leafs(self, root): |
| for name, leafs in self.isa.leafs.items(): |
| for leaf in leafs: |
| if leaf.get_root() != root: |
| continue |
| yield leaf |
| |
| def encode_leaf_groups(self, root): |
| for name, leafs in self.isa.leafs.items(): |
| if leafs[0].get_root() != root: |
| continue |
| yield leafs |
| |
| # expressions used in a bitset (case or field or recursively parent bitsets) |
| def bitset_used_exprs(self, bitset): |
| for case in bitset.cases: |
| if case.expr: |
| yield self.isa.expressions[case.expr] |
| for name, field in case.fields.items(): |
| if isinstance(field, BitSetDerivedField): |
| yield self.isa.expressions[field.expr] |
| if bitset.extends is not None: |
| yield from self.bitset_used_exprs(self.isa.bitsets[bitset.extends]) |
| |
| def extractor_impl(self, bitset, name): |
| if bitset.encode is not None: |
| if name in bitset.encode.maps: |
| return bitset.encode.maps[name] |
| if bitset.extends is not None: |
| return self.extractor_impl(self.isa.bitsets[bitset.extends], name) |
| return None |
| |
| # Default fallback when no mapping is defined, simply to avoid |
| # having to deal with encoding at the same time as r/e new |
| # instruction decoding.. but we can at least print warnings: |
| def extractor_fallback(self, bitset, name): |
| extr_name = bitset.name + '.' + name |
| if extr_name not in self.warned_missing_extractors: |
| print('WARNING: no encode mapping for {}.{}'.format(bitset.name, name)) |
| self.warned_missing_extractors.append(extr_name) |
| return '0 /* XXX */' |
| |
| def extractor(self, bitset, name): |
| extr = self.extractor_impl(bitset, name) |
| if extr is not None: |
| return extr |
| return self.extractor_fallback(bitset, name) |
| |
| # In the special case of needing to access a field with bitset type |
| # for an expr, we need to encode the field so we end up with an |
| # integer, and not some pointer to a thing that will be encoded to |
| # an integer |
| def expr_extractor(self, bitset, name, p): |
| extr = self.extractor_impl(bitset, name) |
| field = self.resolve_simple_field(bitset, name) |
| if isinstance(field, BitSetDerivedField): |
| expr = self.isa.expressions[field.expr] |
| return self.expr_name(bitset.get_root(), expr) + '(s, p, src)' |
| if extr is None: |
| if name in self.unique_param_names(): |
| extr = 'p->' + name |
| else: |
| extr = self.extractor_fallback(bitset, name) |
| if field and field.get_c_typename() == 'TYPE_BITSET': |
| extr = 'encode' + self.isa.roots[field.type].get_c_name() + '(s, ' + p + ', ' + extr + ')' |
| return extr |
| |
| # 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 |
| |
| def encode_type(self, bitset): |
| if bitset.encode is not None: |
| if bitset.encode.type is not None: |
| return bitset.encode.type |
| if bitset.extends is not None: |
| return self.encode_type(bitset.isa.bitsets[bitset.extends]) |
| return None |
| |
| def expr_name(self, root, expr): |
| return root.get_c_name() + '_' + expr.get_c_name() |
| |
| 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 <assert.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <util/bitset.h> |
| #include <util/log.h> |
| |
| <% |
| isa = s.isa |
| %> |
| |
| #define BITMASK_WORDS BITSET_WORDS(${isa.bitsize}) |
| |
| typedef struct { |
| BITSET_WORD bitset[BITMASK_WORDS]; |
| } bitmask_t; |
| |
| 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; |
| } |
| |
| static inline void |
| store_instruction(BITSET_WORD *dst, bitmask_t instr) |
| { |
| % for i in range(0, int(isa.bitsize / 32)): |
| *(dst + ${i}) = instr.bitset[${i}]; |
| % endfor |
| } |
| |
| /** |
| * Opaque type from the PoV of generated code, but allows state to be passed |
| * thru to the hand written helpers used by the generated code. |
| */ |
| struct encode_state; |
| |
| /** |
| * Allows to use gpu_id in expr functions |
| */ |
| #define ISA_GPU_ID() s->gen |
| |
| struct bitset_params; |
| |
| static bitmask_t |
| pack_field(unsigned low, unsigned high, int64_t val, bool is_signed) |
| { |
| bitmask_t field, mask; |
| |
| if (is_signed) { |
| /* NOTE: Don't assume val is already sign-extended to 64b, |
| * just check that the bits above the valid range are either |
| * all zero or all one: |
| */ |
| assert(!(( val & ~BITFIELD64_MASK(1 + high - low)) && |
| (~val & ~BITFIELD64_MASK(1 + high - low)))); |
| } else { |
| assert(!(val & ~BITFIELD64_MASK(1 + high - low))); |
| } |
| |
| BITSET_ZERO(field.bitset); |
| |
| if (!val) |
| return field; |
| |
| BITSET_ZERO(mask.bitset); |
| BITSET_SET_RANGE(mask.bitset, 0, high - low); |
| |
| field = uint64_t_to_bitmask(val); |
| BITSET_AND(field.bitset, field.bitset, mask.bitset); |
| BITSET_SHL(field.bitset, low); |
| |
| return field; |
| } |
| |
| /* |
| * Forward-declarations (so we don't have to figure out which order to |
| * emit various encoders when they have reference each other) |
| */ |
| |
| %for root in s.encode_roots(): |
| static bitmask_t encode${root.get_c_name()}(struct encode_state *s, const struct bitset_params *p, const ${root.encode.type} src); |
| %endfor |
| |
| ## TODO before the expr evaluators, we should generate extract_FOO() for |
| ## derived fields.. which probably also need to be in the context of the |
| ## respective root so they take the correct src arg?? |
| |
| /* |
| * Expression evaluators: |
| */ |
| |
| struct bitset_params { |
| %for name in s.unique_param_names(): |
| int64_t ${name}; |
| %endfor |
| }; |
| |
| ## TODO can we share this def between the two templates somehow? |
| <%def name="encode_params(leaf, field)"> |
| struct bitset_params bp = { |
| %for param in field.params: |
| .${param[1]} = ${s.expr_extractor(leaf, param[0], 'p')}, /* ${param[0]} */ |
| %endfor |
| }; |
| </%def> |
| |
| <%def name="render_expr(leaf, expr)"> |
| static inline int64_t |
| ${s.expr_name(leaf.get_root(), expr)}(struct encode_state *s, const struct bitset_params *p, const ${leaf.get_root().encode.type} src) |
| { |
| % for fieldname in expr.fieldnames: |
| int64_t ${fieldname}; |
| % endfor |
| % for fieldname in expr.fieldnames: |
| <% field = s.resolve_simple_field(leaf, fieldname) %> |
| % if field is not None and field.get_c_typename() == 'TYPE_BITSET': |
| { ${encode_params(leaf, field)} |
| const bitmask_t tmp = ${s.expr_extractor(leaf, fieldname, '&bp')}; |
| ${fieldname} = bitmask_to_uint64_t(tmp); |
| } |
| % else: |
| ${fieldname} = ${s.expr_extractor(leaf, fieldname, 'p')}; |
| % endif |
| % endfor |
| return ${expr.expr}; |
| } |
| </%def> |
| |
| ## note, we can't just iterate all the expressions, but we need to find |
| ## the context in which they are used to know the correct src type |
| |
| %for root in s.encode_roots(): |
| % for leaf in s.encode_leafs(root): |
| % for expr in s.bitset_used_exprs(leaf): |
| static inline int64_t ${s.expr_name(leaf.get_root(), expr)}(struct encode_state *s, const struct bitset_params *p, const ${leaf.get_root().encode.type} src); |
| % endfor |
| % endfor |
| %endfor |
| |
| %for root in s.encode_roots(): |
| <% |
| rendered_exprs = [] |
| %> |
| % for leaf in s.encode_leafs(root): |
| % for expr in s.bitset_used_exprs(leaf): |
| <% |
| if expr in rendered_exprs: |
| continue |
| rendered_exprs.append(expr) |
| %> |
| ${render_expr(leaf, expr)} |
| % endfor |
| % endfor |
| %endfor |
| |
| |
| /* |
| * The actual encoder definitions |
| */ |
| |
| %for root in s.encode_roots(): |
| % for leaf in s.encode_leafs(root): |
| <% snippet = encode_bitset.render(s=s, root=root, leaf=leaf) %> |
| % if snippet not in root.snippets.keys(): |
| <% snippet_name = "snippet" + root.get_c_name() + "_" + str(len(root.snippets)) %> |
| static bitmask_t |
| ${snippet_name}(struct encode_state *s, const struct bitset_params *p, const ${root.encode.type} src) |
| { |
| bitmask_t val = uint64_t_to_bitmask(0); |
| ${snippet} |
| return val; |
| } |
| <% root.snippets[snippet] = snippet_name %> |
| % endif |
| % endfor |
| |
| static bitmask_t |
| encode${root.get_c_name()}(struct encode_state *s, const struct bitset_params *p, const ${root.encode.type} src) |
| { |
| % if root.encode.case_prefix is not None: |
| switch (${root.get_c_name()}_case(s, src)) { |
| % for leafs in s.encode_leaf_groups(root): |
| case ${s.case_name(root, leafs[0].name)}: { |
| % for leaf in leafs: |
| % if leaf.has_gen_restriction(): |
| if (s->gen >= ${leaf.gen_min} && s->gen <= ${leaf.gen_max}) { |
| % endif |
| <% snippet = encode_bitset.render(s=s, root=root, leaf=leaf) %> |
| <% words = isa.split_bits((leaf.get_pattern().match), 64) %> |
| bitmask_t val = uint64_t_to_bitmask(${words[-1]}); |
| |
| <% words.pop() %> |
| |
| % for x in reversed(range(len(words))): |
| { |
| bitmask_t word = uint64_t_to_bitmask(${words[x]}); |
| BITSET_SHL(val.bitset, 64); |
| BITSET_OR(val.bitset, val.bitset, word.bitset); |
| } |
| % endfor |
| |
| BITSET_OR(val.bitset, val.bitset, ${root.snippets[snippet]}(s, p, src).bitset); |
| return val; |
| % if leaf.has_gen_restriction(): |
| } |
| % endif |
| % endfor |
| % if leaf.has_gen_restriction(): |
| break; |
| % endif |
| } |
| % endfor |
| default: |
| /* Note that we need the default case, because there are |
| * instructions which we never expect to be encoded, (ie. |
| * meta/macro instructions) as they are removed/replace |
| * in earlier stages of the compiler. |
| */ |
| break; |
| } |
| mesa_loge("Unhandled ${root.name} encode case: 0x%x\\n", ${root.get_c_name()}_case(s, src)); |
| return uint64_t_to_bitmask(0); |
| % else: # single case bitset, no switch |
| % for leaf in s.encode_leafs(root): |
| <% snippet = encode_bitset.render(s=s, root=root, leaf=leaf) %> |
| bitmask_t val = uint64_t_to_bitmask(${hex(leaf.get_pattern().match)}); |
| BITSET_OR(val.bitset, val.bitset, ${root.snippets[snippet]}(s, p, src).bitset); |
| return val; |
| % endfor |
| % endif |
| } |
| %endfor |
| """ |
| |
| encode_bitset_template = """ |
| <% |
| isa = s.isa |
| %> |
| |
| <%def name="case_pre(root, expr)"> |
| %if expr is not None: |
| if (${s.expr_name(root, expr)}(s, p, src)) { |
| %else: |
| { |
| %endif |
| </%def> |
| |
| <%def name="case_post(root, expr)"> |
| %if expr is not None: |
| } else |
| %else: |
| } |
| %endif |
| </%def> |
| |
| <%def name="encode_params(leaf, field)"> |
| struct bitset_params bp = { |
| %for param in field.params: |
| .${param[1]} = ${s.expr_extractor(leaf, param[0], 'p')}, /* ${param[0]} */ |
| %endfor |
| }; |
| </%def> |
| |
| uint64_t fld; |
| |
| (void)fld; |
| <% visited_exprs = [] %> |
| %for case in s.bitset_cases(leaf): |
| <% |
| if case.expr is not None: |
| visited_exprs.append(case.expr) |
| |
| # per-expression-case track display-field-names that we have |
| # already emitted encoding for. It is possible that an |
| # <override> case overrides a given field (for ex. #cat5-src3) |
| # and we don't want to emit encoding for both the override and |
| # the fallback |
| seen_fields = {} |
| %> |
| ${case_pre(root, case.expr)} |
| % for df in case.display_fields(): |
| % for f in df.fields(): |
| <% |
| # simplify the control flow a bit to give the compiler a bit |
| # less to clean up |
| expr = f.expr |
| if expr == case.expr: |
| # Don't need to evaluate the same condition twice: |
| expr = None |
| elif expr in visited_exprs: |
| # We are in an 'else'/'else-if' leg that we wouldn't |
| # go down due to passing an earlier if() |
| continue |
| |
| if not expr in seen_fields.keys(): |
| seen_fields[expr] = [] |
| |
| if f.field.name in seen_fields[expr]: |
| continue |
| seen_fields[expr].append(f.field.name) |
| %> |
| ${case_pre(root, expr)} |
| % if f.field.get_c_typename() == 'TYPE_BITSET': |
| { ${encode_params(leaf, f.field)} |
| bitmask_t tmp = encode${isa.roots[f.field.type].get_c_name()}(s, &bp, ${s.extractor(leaf, f.field.name)}); |
| fld = bitmask_to_uint64_t(tmp); |
| } |
| % else: |
| fld = ${s.extractor(leaf, f.field.name)}; |
| % endif |
| const bitmask_t packed = pack_field(${f.field.low}, ${f.field.high}, fld, ${f.signed()}); /* ${f.field.name} */ |
| BITSET_OR(val.bitset, val.bitset, packed.bitset); |
| ${case_post(root, expr)} |
| % endfor |
| % endfor |
| |
| % for f in case.assert_cases(): |
| <% |
| # simplify the control flow a bit to give the compiler a bit |
| # less to clean up |
| expr = f.expr |
| if expr == case.expr: |
| # Don't need to evaluate the same condition twice: |
| expr = None |
| elif expr in visited_exprs: |
| # We are in an 'else'/'else-if' leg that we wouldn't |
| # go down due to passing an earlier if() |
| continue |
| %> |
| ${case_pre(root, expr)} |
| const bitmask_t packed = pack_field(${f.field.low}, ${f.field.high}, ${f.field.val}, ${f.signed()}); |
| BITSET_OR(val.bitset, val.bitset, packed.bitset); |
| ${case_post(root, None)} |
| % endfor |
| {} /* in case no unconditional field to close out last '} else' */ |
| ${case_post(root, case.expr)} |
| %endfor |
| """ |
| |
| def main(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument('--xml', required=True, help='isaspec XML 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_h, 'w', encoding='utf-8') as f: |
| encode_bitset = Template(encode_bitset_template) |
| f.write(Template(template).render(s=s, encode_bitset=encode_bitset)) |
| |
| 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() |