mirror of https://gitlab.freedesktop.org/mesa/mesa
725 lines
25 KiB
Python
Executable File
725 lines
25 KiB
Python
Executable File
#!/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()
|