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:
@@ -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
|
||||
--------
|
||||
|
||||
|
4
src/compiler/isaspec/.editorconfig
Normal file
4
src/compiler/isaspec/.editorconfig
Normal file
@@ -0,0 +1,4 @@
|
||||
[*.{c,h,cpp,hpp,cc,hh,y,yy}]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
max_line_length = 78
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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) */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -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) {
|
||||
print(scope->state, "l%d", offset);
|
||||
BITSET_SET(scope->state->branch_targets, offset);
|
||||
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,13 +753,48 @@ 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->instr_cb) {
|
||||
state->options->instr_cb(state->options->cbdata,
|
||||
state->n, instr.bitset);
|
||||
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);
|
||||
}
|
||||
print(state, "l%d:\n", state->n);
|
||||
}
|
||||
|
||||
if (state->options->instr_cb) {
|
||||
@@ -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;
|
||||
|
Reference in New Issue
Block a user