summaryrefslogtreecommitdiff
path: root/src/compiler/isaspec/encode.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/isaspec/encode.py')
-rwxr-xr-xsrc/compiler/isaspec/encode.py724
1 files changed, 724 insertions, 0 deletions
diff --git a/src/compiler/isaspec/encode.py b/src/compiler/isaspec/encode.py
new file mode 100755
index 00000000000..7de7bb7fde2
--- /dev/null
+++ b/src/compiler/isaspec/encode.py
@@ -0,0 +1,724 @@
+#!/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()