mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-08 13:28:06 +02:00
isaspec: Add initial decoding support
This reuses the <map> entries in the <encode> block to go in the reverse direction and parse an instruction into a machine-readable structure. It currently assumes that <map> entries are simple l-values like "src->src[0]" or "src->flag", which is enough for afuc, but the plan for the future is to use the <decode> block to allow us to override that for more complex cases. Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/23949>
This commit is contained in:
parent
2faf344f03
commit
038680c2f6
5 changed files with 261 additions and 1 deletions
|
|
@ -27,6 +27,59 @@ import argparse
|
|||
import os
|
||||
import sys
|
||||
|
||||
class FieldDecode(object):
|
||||
def __init__(self, name, map_expr):
|
||||
self.name = name;
|
||||
self.map_expr = map_expr
|
||||
|
||||
def get_c_name(self):
|
||||
return self.name.lower().replace('-', '_')
|
||||
|
||||
# State and helpers used by the template:
|
||||
class State(object):
|
||||
def __init__(self, isa):
|
||||
self.isa = isa
|
||||
|
||||
def case_name(self, bitset, name):
|
||||
return bitset.encode.case_prefix + name.upper().replace('.', '_').replace('-', '_').replace('#', '')
|
||||
|
||||
# Return a list of all <map> entries for a leaf bitset, with the child
|
||||
# bitset overriding the parent bitset's entries. Because we can't resolve
|
||||
# which <map>s are used until we resolve which overload is used, we
|
||||
# generate code for encoding all of these and then at runtime select which
|
||||
# one to call based on the display.
|
||||
def decode_fields(self, bitset):
|
||||
if bitset.get_root().decode is None:
|
||||
return
|
||||
|
||||
seen_fields = set()
|
||||
if bitset.encode is not None:
|
||||
for name, expr in bitset.encode.maps.items():
|
||||
seen_fields.add(name)
|
||||
yield FieldDecode(name, expr)
|
||||
|
||||
if bitset.extends is not None:
|
||||
for field in self.decode_fields(self.isa.bitsets[bitset.extends]):
|
||||
if field.name not in seen_fields:
|
||||
yield field
|
||||
|
||||
# A limited resolver for field type which doesn't properly account for
|
||||
# overrides. In particular, if a field is defined differently in multiple
|
||||
# different cases, this just blindly picks the last one.
|
||||
#
|
||||
# TODO to do this properly, I don't think there is an alternative than
|
||||
# to emit code which evaluates the case.expr
|
||||
def resolve_simple_field(self, bitset, name):
|
||||
field = None
|
||||
for case in bitset.cases:
|
||||
if name in case.fields:
|
||||
field = case.fields[name]
|
||||
if field is not None:
|
||||
return field
|
||||
if bitset.extends is not None:
|
||||
return self.resolve_simple_field(bitset.isa.bitsets[bitset.extends], name)
|
||||
return None
|
||||
|
||||
template = """\
|
||||
/* Copyright (C) 2020 Google, Inc.
|
||||
*
|
||||
|
|
@ -84,6 +137,22 @@ ${expr.get_c_name()}(struct decode_scope *scope)
|
|||
}
|
||||
%endfor
|
||||
|
||||
/* forward-declarations of bitset decode functions */
|
||||
%for name, bitset in isa.all_bitsets():
|
||||
% for df in s.decode_fields(bitset):
|
||||
static void decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_${df.get_c_name()}(void *out, struct decode_scope *scope, uint64_t val);
|
||||
% endfor
|
||||
static const struct isa_field_decode decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_fields[] = {
|
||||
% for df in s.decode_fields(bitset):
|
||||
{
|
||||
.name = "${df.name}",
|
||||
.decode = decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_${df.get_c_name()},
|
||||
},
|
||||
% endfor
|
||||
};
|
||||
static void decode_${bitset.get_c_name()}_gen_${bitset.gen_min}(void *out, struct decode_scope *scope);
|
||||
%endfor
|
||||
|
||||
/*
|
||||
* Forward-declarations (so we don't have to figure out which order to
|
||||
* emit various tables when they have pointers to each other)
|
||||
|
|
@ -169,6 +238,9 @@ static const struct isa_bitset bitset_${bitset.get_c_name()}_gen_${bitset.gen_mi
|
|||
.match.bitset = { ${', '.join(isa.split_bits(pattern.match, 32))} },
|
||||
.dontcare.bitset = { ${', '.join(isa.split_bits(pattern.dontcare, 32))} },
|
||||
.mask.bitset = { ${', '.join(isa.split_bits(pattern.mask, 32))} },
|
||||
.decode = decode_${bitset.get_c_name()}_gen_${bitset.gen_min},
|
||||
.num_decode_fields = ARRAY_SIZE(decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_fields),
|
||||
.decode_fields = decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_fields,
|
||||
.num_cases = ${len(bitset.cases)},
|
||||
.cases = {
|
||||
% for case in bitset.cases:
|
||||
|
|
@ -197,6 +269,37 @@ const struct isa_bitset *${root.get_c_name()}[] = {
|
|||
|
||||
#include "isaspec_decode_impl.c"
|
||||
|
||||
%for name, bitset in isa.all_bitsets():
|
||||
% for df in s.decode_fields(bitset):
|
||||
<% field = s.resolve_simple_field(bitset, df.name) %>
|
||||
static void decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_${df.get_c_name()}(void *out, struct decode_scope *scope, uint64_t val)
|
||||
{
|
||||
% if bitset.get_root().decode is not None and field is not None:
|
||||
${bitset.get_root().encode.type} src = *(${bitset.get_root().encode.type} *)out;
|
||||
% if field.get_c_typename() == 'TYPE_BITSET':
|
||||
isa_decode_bitset(&${df.map_expr}, ${isa.roots[field.type].get_c_name()}, scope, uint64_t_to_bitmask(val));
|
||||
% elif field.get_c_typename() in ['TYPE_BRANCH', 'TYPE_INT', 'TYPE_OFFSET']:
|
||||
${df.map_expr} = util_sign_extend(val, ${field.get_size()});
|
||||
% else:
|
||||
${df.map_expr} = val;
|
||||
% endif
|
||||
*(${bitset.get_root().encode.type} *)out = src;
|
||||
% endif
|
||||
}
|
||||
|
||||
% endfor
|
||||
static void decode_${bitset.get_c_name()}_gen_${bitset.gen_min}(void *out, struct decode_scope *scope)
|
||||
{
|
||||
% if bitset.get_root().decode is not None:
|
||||
UNUSED ${bitset.get_root().encode.type} src;
|
||||
% if bitset.get_root().encode.type.endswith('*') and name in isa.leafs and bitset.get_root().encode.case_prefix is not None:
|
||||
src = ${bitset.get_root().get_c_name()}_create(${s.case_name(bitset.get_root(), bitset.name)});
|
||||
*(${bitset.get_root().encode.type} *)out = src;
|
||||
% endif
|
||||
% endif
|
||||
}
|
||||
%endfor
|
||||
|
||||
"""
|
||||
|
||||
header = """\
|
||||
|
|
@ -286,11 +389,12 @@ def main():
|
|||
args = parser.parse_args()
|
||||
|
||||
isa = ISA(args.xml)
|
||||
s = State(isa)
|
||||
|
||||
try:
|
||||
with open(args.out_c, 'w') as f:
|
||||
out_h_basename = os.path.basename(args.out_h)
|
||||
f.write(Template(template).render(isa=isa, header=out_h_basename))
|
||||
f.write(Template(template).render(isa=isa, s=s, header=out_h_basename))
|
||||
|
||||
with open(args.out_h, 'w') as f:
|
||||
f.write(Template(header).render(isa=isa, guard=guard(args.out_h)))
|
||||
|
|
|
|||
|
|
@ -242,6 +242,14 @@ class BitSetEncode(object):
|
|||
if 'force' in map.attrib and map.attrib['force'] == 'true':
|
||||
self.forced[name] = 'true'
|
||||
|
||||
class BitSetDecode(object):
|
||||
"""Additional data that may be associated with a root bitset node
|
||||
to provide additional information needed to generate helpers
|
||||
to decode the bitset.
|
||||
"""
|
||||
def __init__(self, xml):
|
||||
pass
|
||||
|
||||
class BitSet(object):
|
||||
"""Class that encapsulates a single bitset rule
|
||||
"""
|
||||
|
|
@ -266,6 +274,9 @@ class BitSet(object):
|
|||
self.encode = None
|
||||
if xml.find('encode') is not None:
|
||||
self.encode = BitSetEncode(xml.find('encode'))
|
||||
self.decode = None
|
||||
if xml.find('decode') is not None:
|
||||
self.decode = BitSetDecode(xml.find('encode'))
|
||||
|
||||
self.gen_min = 0
|
||||
self.gen_max = (1 << 32) - 1
|
||||
|
|
|
|||
|
|
@ -123,6 +123,8 @@ struct isa_decode_options {
|
|||
|
||||
void isa_disasm(void *bin, int sz, FILE *out, const struct isa_decode_options *options);
|
||||
|
||||
bool isa_decode(void *out, void *bin, const struct isa_decode_options *options);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -56,6 +56,12 @@ typedef uint64_t (*isa_expr_t)(struct decode_scope *scope);
|
|||
*/
|
||||
uint64_t isa_decode_field(struct decode_scope *scope, const char *field_name);
|
||||
|
||||
void isa_decode_bitset(void *out, const struct isa_bitset **bitsets, struct decode_scope *scope, bitmask_t val);
|
||||
|
||||
/**
|
||||
* Used by generated decode functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* For bitset fields, there are some cases where we want to "remap" field
|
||||
* names, essentially allowing one to parameterize a nested bitset when
|
||||
|
|
@ -69,6 +75,8 @@ struct isa_field_params {
|
|||
} params[];
|
||||
};
|
||||
|
||||
struct decode_scope;
|
||||
|
||||
/**
|
||||
* Description of a single field within a bitset case.
|
||||
*/
|
||||
|
|
@ -132,6 +140,11 @@ struct isa_case {
|
|||
struct isa_field fields[];
|
||||
};
|
||||
|
||||
struct isa_field_decode {
|
||||
const char *name;
|
||||
void (*decode)(void *out, struct decode_scope *scope, uint64_t val);
|
||||
};
|
||||
|
||||
/**
|
||||
* An individual bitset, the leaves of a bitset inheritance hiearchy will
|
||||
* have the match and mask to match a single instruction (or arbitrary
|
||||
|
|
@ -147,6 +160,9 @@ struct isa_bitset {
|
|||
bitmask_t match;
|
||||
bitmask_t dontcare;
|
||||
bitmask_t mask;
|
||||
void (*decode)(void *out, struct decode_scope *scope);
|
||||
unsigned num_decode_fields;
|
||||
const struct isa_field_decode *decode_fields;
|
||||
unsigned num_cases;
|
||||
const struct isa_case *cases[];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -840,6 +840,117 @@ disasm(struct decode_state *state, void *bin, int sz)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
decode_bitset_cb(void *out, struct decode_scope *scope, const struct isa_bitset *b)
|
||||
{
|
||||
while (b) {
|
||||
b->decode(out, scope);
|
||||
b = b->parent;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
decode_field(void *out, struct decode_scope *scope, const char *field_name)
|
||||
{
|
||||
const struct isa_bitset *bitset = scope->bitset;
|
||||
size_t field_name_len = strlen(field_name);
|
||||
|
||||
/* alignment handling */
|
||||
const char *align = strstr(field_name, ":align=");
|
||||
|
||||
if (align) {
|
||||
field_name_len = align - field_name;
|
||||
}
|
||||
|
||||
if (field_name == align)
|
||||
return;
|
||||
|
||||
if (!strncmp("NAME", field_name, field_name_len))
|
||||
return;
|
||||
|
||||
bitmask_t v;
|
||||
const struct isa_field *field = resolve_field(scope, field_name, field_name_len, &v);
|
||||
if (!field) {
|
||||
decode_error(scope->state, "no field '%.*s'", (int)field_name_len, field_name);
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t val = bitmask_to_uint64_t(v);
|
||||
|
||||
for (unsigned i = 0; i < bitset->num_decode_fields; i++) {
|
||||
if (!strncmp(bitset->decode_fields[i].name, field_name, field_name_len)) {
|
||||
bitset->decode_fields[i].decode(out, scope, val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
decode_bitset(void *out, struct decode_scope *scope)
|
||||
{
|
||||
const struct isa_bitset *bitset = scope->bitset;
|
||||
decode_bitset_cb(out, scope, bitset);
|
||||
|
||||
const char *display = find_display(scope, bitset);
|
||||
|
||||
if (!display) {
|
||||
decode_error(scope->state, "%s: no display template", bitset->name);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *p = display;
|
||||
|
||||
while (*p != '\0') {
|
||||
if (*p == '{') {
|
||||
const char *e = ++p;
|
||||
while (*e != '}') {
|
||||
e++;
|
||||
}
|
||||
|
||||
char *field_name = strndup(p, e-p);
|
||||
decode_field(out, scope, field_name);
|
||||
free(field_name);
|
||||
|
||||
p = e;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isa_decode_bitset(void *out, const struct isa_bitset **bitsets, struct decode_scope *scope, bitmask_t val)
|
||||
{
|
||||
struct decode_state *state = scope->state;
|
||||
|
||||
const struct isa_bitset *b = find_bitset(state, bitsets, val);
|
||||
if (!b)
|
||||
return;
|
||||
|
||||
struct decode_scope *new_scope = push_scope(state, b, val);
|
||||
|
||||
decode_bitset(out, new_scope);
|
||||
|
||||
pop_scope(new_scope);
|
||||
}
|
||||
|
||||
static bool
|
||||
decode(void *out, struct decode_state *state, void *bin)
|
||||
{
|
||||
bitmask_t instr = { 0 };
|
||||
next_instruction(&instr, bin);
|
||||
|
||||
const struct isa_bitset *b = find_bitset(state, __instruction, instr);
|
||||
if (!b)
|
||||
return false;
|
||||
|
||||
struct decode_scope *scope = push_scope(state, b, instr);
|
||||
|
||||
decode_bitset(out, scope);
|
||||
|
||||
pop_scope(scope);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
cmp_entrypoints(const void *_a, const void *_b)
|
||||
{
|
||||
|
|
@ -900,3 +1011,19 @@ isa_disasm(void *bin, int sz, FILE *out, const struct isa_decode_options *option
|
|||
|
||||
ralloc_free(state);
|
||||
}
|
||||
|
||||
bool
|
||||
isa_decode(void *out, void *bin, const struct isa_decode_options *options)
|
||||
{
|
||||
struct decode_state *state = rzalloc_size(NULL, sizeof(*state));
|
||||
state->options = options;
|
||||
|
||||
bool result = decode(out, state, bin);
|
||||
|
||||
if (flush_errors(state)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ralloc_free(state);
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue