Edit

IABSD.fr/xenocara/lib/mesa/src/compiler/isaspec/encode.py

Branch :

  • Show log

    Commit

  • Author : jsg
    Date : 2025-06-05 11:23:11
    Hash : 67d6f117
    Message : Import Mesa 25.0.7

  • lib/mesa/src/compiler/isaspec/encode.py
  • #!/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()