isaspec: Add support for function and entrypoint labels
authorConnor Abbott <cwabbott0@gmail.com>
Thu, 22 Jun 2023 12:13:25 +0000 (14:13 +0200)
committerMarge Bot <emma+marge@anholt.net>
Fri, 28 Jul 2023 18:41:58 +0000 (18:41 +0000)
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>

docs/drivers/freedreno/isaspec.rst
src/compiler/isaspec/.editorconfig [new file with mode: 0644]
src/compiler/isaspec/decode.py
src/compiler/isaspec/isa.py
src/compiler/isaspec/isaspec.h
src/compiler/isaspec/isaspec_decode_decl.h
src/compiler/isaspec/isaspec_decode_impl.c

index e512d2f..34e01e7 100644 (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
 --------
 
diff --git a/src/compiler/isaspec/.editorconfig b/src/compiler/isaspec/.editorconfig
new file mode 100644 (file)
index 0000000..321b435
--- /dev/null
@@ -0,0 +1,4 @@
+[*.{c,h,cpp,hpp,cc,hh,y,yy}]
+indent_style = tab
+indent_size = 8
+max_line_length = 78
index 9e05fa0..6e1a9d0 100755 (executable)
@@ -148,6 +148,9 @@ static const struct isa_case ${case.get_c_name()}_gen_${bitset.gen_min} = {
 %      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
        },
index 3e70c34..501cb6d 100644 (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
index 2867a0e..fdfc281 100644 (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);
index 8ae61a7..93367f1 100644 (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) */
        };
 
        /**
index b5e8d54..3b2c59a 100644 (file)
@@ -142,6 +142,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
         * recursion.
@@ -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;