isaspec: Add support for function and entrypoint labels

Functions (i.e. labels reached from call instructions) should be printed
differently from normal labels. In addition we also need to add support
for entrypoints with user-defined names in order to show packet names in
afuc.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/23949>
This commit is contained in:
Connor Abbott
2023-06-22 14:13:25 +02:00
committed by Marge Bot
parent 569d3ac5a1
commit dc874e4654
7 changed files with 133 additions and 8 deletions

View File

@@ -276,6 +276,30 @@ This would produce a disassembly like ``jump #l42`` if the destination is 42
instructions after the start of the disassembly. The destination would be
preceded by a line with just ``l42:``.
``branch`` and ``absbranch`` fields can additionally have a ``call="true"``
attribute. For now, this just changes the disassembly. In particular the label
prefix is changed to ``fxn`` and an extra empty line before the destination is
added to visually seperate the disassembly into functions. So, for example, a
call instruction defined like this:
.. code-block:: xml
<bitset name="call" extends="#instruction">
<display>
call #{OFFSET}
</display>
<pattern low="26" high="31">110010</pattern> <!-- opcode goes here -->
<field name="OFFSET" low="0" high="25" type="branch" call="true"/>
</bitset>
will disassemble to ``call #fxn42``.
Finally, users with special knowledge about where execution may start can define
"entrypoints" when disassembling which are printed like function call
destinations, with an extra empty line, but with an arbitrary user-defined
name. Names that are ``fxn`` or ``l`` followed by a number are discouraged
because they may clash with automatically-generated names.
Encoding
--------

View File

@@ -0,0 +1,4 @@
[*.{c,h,cpp,hpp,cc,hh,y,yy}]
indent_style = tab
indent_size = 8
max_line_length = 78

View File

@@ -147,6 +147,9 @@ static const struct isa_case ${case.get_c_name()}_gen_${bitset.gen_min} = {
% endif
% if field.get_c_typename() == 'TYPE_ASSERT':
.val.bitset = { ${', '.join(isa.split_bits(field.val, 32))} },
% endif
% if field.get_c_typename() == 'TYPE_BRANCH' or field.get_c_typename() == 'TYPE_ABSBRANCH':
.call = ${str(field.call).lower()},
% endif
},
% endfor

View File

@@ -114,6 +114,7 @@ class BitSetField(object):
self.params.append([name, aas])
self.expr = None
self.display = None
self.call = 'call' in xml.attrib and xml.attrib['call'] == 'true'
if 'display' in xml.attrib:
self.display = xml.attrib['display'].strip()
@@ -176,6 +177,7 @@ class BitSetDerivedField(BitSetField):
self.display = None
if 'display' in xml.attrib:
self.display = xml.attrib['display'].strip()
self.call = 'call' in xml.attrib and xml.attrib['call'] == 'true'
class BitSetCase(object):
"""Class that encapsulates a single bitset case

View File

@@ -46,6 +46,11 @@ struct isa_decode_hook {
void (*cb)(void *data, struct isa_decode_value *val);
};
struct isa_entrypoint {
const char *name;
uint32_t offset;
};
struct isa_decode_options {
uint32_t gpu_id;
@@ -87,6 +92,12 @@ struct isa_decode_options {
* callback for undefined instructions
*/
void (*no_match_cb)(FILE *out, const BITSET_WORD *bitset, size_t size);
/**
* List of known entrypoints to treat like call targets
*/
unsigned entrypoint_count;
const struct isa_entrypoint *entrypoints;
};
void isa_decode(void *bin, int sz, FILE *out, const struct isa_decode_options *options);

View File

@@ -104,6 +104,7 @@ struct isa_field {
bitmask_t val; /* if type==ASSERT */
const struct isa_enum *enums; /* if type==ENUM */
const char *display; /* if type==BOOL */
bool call; /* if type==(BRANCH|ABSBRANCH) */
};
/**

View File

@@ -141,6 +141,16 @@ struct decode_state {
*/
BITSET_WORD *branch_targets;
/**
* Bitset of instructions that are call targets.
*/
BITSET_WORD *call_targets;
/**
* Bitset of instructions that are entrypoints.
*/
BITSET_WORD *entrypoints;
/**
* We allow a limited amount of expression evaluation recursion, but
* not recursive evaluation of any given expression, to prevent infinite
@@ -156,6 +166,12 @@ struct decode_state {
*/
struct decode_scope *scope;
/* Next entrypoint to be decoded. */
struct isa_entrypoint *next_entrypoint;
/* Sentinel value after the last entrypoint in the array. */
struct isa_entrypoint *end_entrypoint;
/**
* A small fixed upper limit on # of decode errors to capture per-
* instruction seems reasonable.
@@ -619,8 +635,13 @@ display_field(struct decode_scope *scope, const char *field_name)
offset = val;
}
if (offset < scope->state->num_instr) {
if (field->call) {
print(scope->state, "fxn%d", offset);
BITSET_SET(scope->state->call_targets, offset);
} else {
print(scope->state, "l%d", offset);
BITSET_SET(scope->state->branch_targets, offset);
}
break;
}
}
@@ -732,14 +753,49 @@ decode(struct decode_state *state, void *bin, int sz)
break;
}
if (state->options->branch_labels &&
BITSET_TEST(state->branch_targets, state->n)) {
if (state->options->branch_labels) {
bool entrypoint = state->next_entrypoint !=
state->end_entrypoint &&
state->next_entrypoint->offset == state->n;
/* Print an extra empty line before functions and
* entrypoints to more clearly separate them.
*/
if ((BITSET_TEST(state->call_targets, state->n) || entrypoint) &&
state->n != 0) {
if (state->options->instr_cb) {
state->options->instr_cb(state->options->cbdata,
state->n, instr.bitset);
}
print(state, "\n");
}
while (state->next_entrypoint != state->end_entrypoint &&
state->next_entrypoint->offset == state->n) {
if (state->options->instr_cb) {
state->options->instr_cb(state->options->cbdata,
state->n, instr.bitset);
}
print(state, "%s:\n", state->next_entrypoint->name);
state->next_entrypoint++;
}
if (BITSET_TEST(state->call_targets, state->n)) {
if (state->options->instr_cb) {
state->options->instr_cb(state->options->cbdata,
state->n, instr.bitset);
}
print(state, "fxn%d:\n", state->n);
}
if (BITSET_TEST(state->branch_targets, state->n)) {
if (state->options->instr_cb) {
state->options->instr_cb(state->options->cbdata,
state->n, instr.bitset);
}
print(state, "l%d:\n", state->n);
}
}
if (state->options->instr_cb) {
state->options->instr_cb(state->options->cbdata, state->n, instr.bitset);
@@ -774,6 +830,13 @@ decode(struct decode_state *state, void *bin, int sz)
}
}
static int
cmp_entrypoints(const void *_a, const void *_b)
{
const struct isa_entrypoint *a = _a, *b = _b;
return (int)a->offset - (int)b->offset;
}
void
isa_decode(void *bin, int sz, FILE *out, const struct isa_decode_options *options)
{
@@ -793,6 +856,8 @@ isa_decode(void *bin, int sz, FILE *out, const struct isa_decode_options *option
if (state->options->branch_labels) {
state->branch_targets = rzalloc_size(state,
sizeof(BITSET_WORD) * BITSET_WORDS(state->num_instr));
state->call_targets = rzalloc_size(state,
sizeof(BITSET_WORD) * BITSET_WORDS(state->num_instr));
/* Do a pre-pass to find all the branch targets: */
state->out = fopen("/dev/null", "w");
@@ -802,6 +867,21 @@ isa_decode(void *bin, int sz, FILE *out, const struct isa_decode_options *option
if (options) {
state->options = options;
}
/* Sort the entrypoints by offset and initialize entrypoint
* state.
*/
if (options->entrypoint_count) {
struct isa_entrypoint *entrypoints =
ralloc_array(state, struct isa_entrypoint,
options->entrypoint_count);
memcpy(entrypoints, options->entrypoints,
options->entrypoint_count * sizeof(*entrypoints));
qsort(entrypoints, options->entrypoint_count,
sizeof(*entrypoints), cmp_entrypoints);
state->next_entrypoint = entrypoints;
state->end_entrypoint = entrypoints + options->entrypoint_count;
}
}
state->out = out;