2020-12-14 00:18:43 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2023-01-04 19:47:54 +00:00
|
|
|
/* This file should not be built directly. Instead, it is included in the C
|
|
|
|
* file generated by isaspec/decode.py and built along with it.
|
|
|
|
*/
|
|
|
|
|
2020-12-14 00:18:43 +00:00
|
|
|
#include <assert.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "util/bitset.h"
|
|
|
|
#include "util/compiler.h"
|
|
|
|
#include "util/half_float.h"
|
2020-12-15 21:22:37 +00:00
|
|
|
#include "util/hash_table.h"
|
2020-12-14 00:18:43 +00:00
|
|
|
#include "util/ralloc.h"
|
|
|
|
#include "util/u_debug.h"
|
|
|
|
#include "util/u_math.h"
|
|
|
|
|
|
|
|
#include "isa.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The set of leaf node bitsets in the bitset hiearchy which defines all
|
|
|
|
* the possible instructions.
|
|
|
|
*
|
|
|
|
* TODO maybe we want to pass this in as parameter so this same decoder
|
|
|
|
* can work with multiple different instruction sets.
|
|
|
|
*/
|
|
|
|
extern const struct isa_bitset *__instruction[];
|
|
|
|
|
|
|
|
struct decode_state;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Decode scope. When parsing a field that is itself a bitset, we push a
|
|
|
|
* new scope to the stack. A nested bitset is allowed to resolve fields
|
|
|
|
* from an enclosing scope (needed, for example, to decode src register
|
|
|
|
* bitsets, where half/fullness is determined by fields outset if bitset
|
|
|
|
* in the instruction containing the bitset.
|
|
|
|
*
|
|
|
|
* But the field being resolved could be a derived field, or different
|
|
|
|
* depending on an override at a higher level of the stack, requiring
|
|
|
|
* expression evaluation which could in turn reference variables which
|
|
|
|
* triggers a recursive field lookup. But those lookups should not start
|
|
|
|
* from the top of the stack, but instead the current stack level. This
|
|
|
|
* prevents a field from accidentally resolving to different values
|
|
|
|
* depending on the starting point of the lookup. (Not only causing
|
|
|
|
* confusion, but this is behavior we don't want to depend on if we
|
|
|
|
* wanted to optimize things by caching field lookup results.)
|
|
|
|
*/
|
|
|
|
struct decode_scope {
|
|
|
|
/**
|
|
|
|
* Enclosing scope
|
|
|
|
*/
|
|
|
|
struct decode_scope *parent;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Current bitset value being decoded
|
|
|
|
*/
|
2021-05-08 10:28:00 +01:00
|
|
|
bitmask_t val;
|
2020-12-14 00:18:43 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Current bitset.
|
|
|
|
*/
|
|
|
|
const struct isa_bitset *bitset;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Field name remapping.
|
|
|
|
*/
|
|
|
|
const struct isa_field_params *params;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pointer back to decode state, for convenience.
|
|
|
|
*/
|
|
|
|
struct decode_state *state;
|
2020-12-15 21:22:37 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Cache expression evaluation results. Expressions for overrides can
|
|
|
|
* be repeatedly evaluated for each field being resolved. And each
|
|
|
|
* field reference to a derived field (potentially from another expr)
|
|
|
|
* would require re-evaluation. But for a given scope, each evaluation
|
|
|
|
* of an expression gives the same result. So we can cache to speed
|
|
|
|
* things up.
|
|
|
|
*
|
|
|
|
* TODO we could maybe be clever and assign a unique idx to each expr
|
|
|
|
* and use a direct lookup table? Would be a bit more clever if it was
|
|
|
|
* smart enough to allow unrelated expressions that are never involved
|
|
|
|
* in a given scope to have overlapping cache lookup idx's.
|
|
|
|
*/
|
|
|
|
struct hash_table *cache;
|
2020-12-14 00:18:43 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Current decode state
|
|
|
|
*/
|
|
|
|
struct decode_state {
|
|
|
|
const struct isa_decode_options *options;
|
|
|
|
FILE *out;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Current instruction being decoded:
|
|
|
|
*/
|
|
|
|
unsigned n;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Number of instructions being decoded
|
|
|
|
*/
|
|
|
|
unsigned num_instr;
|
|
|
|
|
2021-07-14 22:23:49 +01:00
|
|
|
/**
|
|
|
|
* Column number of current line
|
|
|
|
*/
|
|
|
|
unsigned line_column;
|
|
|
|
|
2020-12-14 00:18:43 +00:00
|
|
|
/**
|
|
|
|
* Bitset of instructions that are branch targets (if options->branch_labels
|
|
|
|
* is enabled)
|
|
|
|
*/
|
|
|
|
BITSET_WORD *branch_targets;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* We allow a limited amount of expression evaluation recursion, but
|
|
|
|
* not recursive evaluation of any given expression, to prevent infinite
|
|
|
|
* recursion.
|
|
|
|
*/
|
|
|
|
int expr_sp;
|
|
|
|
isa_expr_t expr_stack[8];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Current topmost/innermost level of scope used for decoding fields,
|
|
|
|
* including derived fields which may in turn rely on decoding other
|
|
|
|
* fields, potentially from a lower/out level in the stack.
|
|
|
|
*/
|
|
|
|
struct decode_scope *scope;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A small fixed upper limit on # of decode errors to capture per-
|
|
|
|
* instruction seems reasonable.
|
|
|
|
*/
|
|
|
|
unsigned num_errors;
|
|
|
|
char *errors[4];
|
|
|
|
};
|
|
|
|
|
2021-06-18 20:26:29 +01:00
|
|
|
static void
|
|
|
|
print(struct decode_state *state, const char *fmt, ...)
|
|
|
|
{
|
2021-07-14 22:30:59 +01:00
|
|
|
char *buffer;
|
2021-06-18 20:26:29 +01:00
|
|
|
va_list args;
|
2021-07-14 22:30:59 +01:00
|
|
|
int ret;
|
2021-06-18 20:26:29 +01:00
|
|
|
|
|
|
|
va_start(args, fmt);
|
2021-07-14 22:30:59 +01:00
|
|
|
ret = vasprintf(&buffer, fmt, args);
|
2021-06-18 20:26:29 +01:00
|
|
|
va_end(args);
|
2021-07-14 22:30:59 +01:00
|
|
|
|
|
|
|
if (ret != -1) {
|
|
|
|
const size_t len = strlen(buffer);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < len; i++) {
|
|
|
|
const char c = buffer[i];
|
|
|
|
|
|
|
|
fputc(c, state->out);
|
|
|
|
state->line_column++;
|
|
|
|
|
|
|
|
if (c == '\n') {
|
|
|
|
state->line_column = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(buffer);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2021-06-18 20:26:29 +01:00
|
|
|
}
|
|
|
|
|
2020-12-14 00:18:43 +00:00
|
|
|
static void display(struct decode_scope *scope);
|
|
|
|
static void decode_error(struct decode_state *state, const char *fmt, ...) _util_printf_format(2,3);
|
|
|
|
|
|
|
|
static void
|
|
|
|
decode_error(struct decode_state *state, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
if (!state->options->show_errors) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->num_errors == ARRAY_SIZE(state->errors)) {
|
|
|
|
/* too many errors, bail */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vasprintf(&state->errors[state->num_errors++], fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
flush_errors(struct decode_state *state)
|
|
|
|
{
|
|
|
|
unsigned num_errors = state->num_errors;
|
|
|
|
if (num_errors > 0)
|
2021-06-18 20:26:29 +01:00
|
|
|
print(state, "\t; ");
|
2020-12-14 00:18:43 +00:00
|
|
|
for (unsigned i = 0; i < num_errors; i++) {
|
2021-06-18 20:26:29 +01:00
|
|
|
print(state, "%s%s", (i > 0) ? ", " : "", state->errors[i]);
|
2020-12-14 00:18:43 +00:00
|
|
|
free(state->errors[i]);
|
|
|
|
}
|
|
|
|
state->num_errors = 0;
|
|
|
|
return num_errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
push_expr(struct decode_state *state, isa_expr_t expr)
|
|
|
|
{
|
|
|
|
for (int i = state->expr_sp - 1; i > 0; i--) {
|
|
|
|
if (state->expr_stack[i] == expr) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
state->expr_stack[state->expr_sp++] = expr;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pop_expr(struct decode_state *state)
|
|
|
|
{
|
|
|
|
assert(state->expr_sp > 0);
|
|
|
|
state->expr_sp--;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct decode_scope *
|
2021-05-08 10:28:00 +01:00
|
|
|
push_scope(struct decode_state *state, const struct isa_bitset *bitset, bitmask_t val)
|
2020-12-14 00:18:43 +00:00
|
|
|
{
|
|
|
|
struct decode_scope *scope = rzalloc_size(state, sizeof(*scope));
|
|
|
|
|
2021-05-08 10:28:00 +01:00
|
|
|
BITSET_COPY(scope->val.bitset, val.bitset);
|
2020-12-14 00:18:43 +00:00
|
|
|
scope->bitset = bitset;
|
|
|
|
scope->parent = state->scope;
|
|
|
|
scope->state = state;
|
|
|
|
|
|
|
|
state->scope = scope;
|
|
|
|
|
|
|
|
return scope;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pop_scope(struct decode_scope *scope)
|
|
|
|
{
|
|
|
|
assert(scope->state->scope == scope); /* must be top of stack */
|
|
|
|
|
|
|
|
scope->state->scope = scope->parent;
|
|
|
|
ralloc_free(scope);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Evaluate an expression, returning it's resulting value
|
|
|
|
*/
|
|
|
|
static uint64_t
|
|
|
|
evaluate_expr(struct decode_scope *scope, isa_expr_t expr)
|
|
|
|
{
|
2020-12-15 21:22:37 +00:00
|
|
|
if (scope->cache) {
|
|
|
|
struct hash_entry *entry = _mesa_hash_table_search(scope->cache, expr);
|
|
|
|
if (entry) {
|
|
|
|
return *(uint64_t *)entry->data;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
scope->cache = _mesa_pointer_hash_table_create(scope);
|
|
|
|
}
|
|
|
|
|
2020-12-14 00:18:43 +00:00
|
|
|
if (!push_expr(scope->state, expr))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
uint64_t ret = expr(scope);
|
|
|
|
|
|
|
|
pop_expr(scope->state);
|
|
|
|
|
2020-12-15 21:22:37 +00:00
|
|
|
uint64_t *retp = ralloc_size(scope->cache, sizeof(*retp));
|
|
|
|
*retp = ret;
|
|
|
|
_mesa_hash_table_insert(scope->cache, expr, retp);
|
|
|
|
|
2020-12-14 00:18:43 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find the bitset in NULL terminated bitset hiearchy root table which
|
|
|
|
* matches against 'val'
|
|
|
|
*/
|
|
|
|
static const struct isa_bitset *
|
|
|
|
find_bitset(struct decode_state *state, const struct isa_bitset **bitsets,
|
2021-05-08 10:28:00 +01:00
|
|
|
bitmask_t val)
|
2020-12-14 00:18:43 +00:00
|
|
|
{
|
|
|
|
const struct isa_bitset *match = NULL;
|
|
|
|
for (int n = 0; bitsets[n]; n++) {
|
|
|
|
if (state->options->gpu_id > bitsets[n]->gen.max)
|
|
|
|
continue;
|
|
|
|
if (state->options->gpu_id < bitsets[n]->gen.min)
|
|
|
|
continue;
|
|
|
|
|
2021-05-08 10:28:00 +01:00
|
|
|
// m = (val & bitsets[n]->mask) & ~bitsets[n]->dontcare;
|
|
|
|
bitmask_t m = { 0 };
|
|
|
|
bitmask_t not_dontcare;
|
|
|
|
|
|
|
|
BITSET_AND(m.bitset, val.bitset, bitsets[n]->mask.bitset);
|
|
|
|
|
|
|
|
BITSET_COPY(not_dontcare.bitset, bitsets[n]->dontcare.bitset);
|
|
|
|
BITSET_NOT(not_dontcare.bitset);
|
2020-12-14 00:18:43 +00:00
|
|
|
|
2021-05-08 10:28:00 +01:00
|
|
|
BITSET_AND(m.bitset, m.bitset, not_dontcare.bitset);
|
|
|
|
|
|
|
|
if (!BITSET_EQUAL(m.bitset, bitsets[n]->match.bitset)) {
|
2020-12-14 00:18:43 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We should only have exactly one match
|
|
|
|
*
|
|
|
|
* TODO more complete/formal way to validate that any given
|
|
|
|
* bit pattern will only have a single match?
|
|
|
|
*/
|
|
|
|
if (match) {
|
|
|
|
decode_error(state, "bitset conflict: %s vs %s", match->name,
|
|
|
|
bitsets[n]->name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
match = bitsets[n];
|
|
|
|
}
|
|
|
|
|
2021-05-08 10:28:00 +01:00
|
|
|
if (match) {
|
|
|
|
bitmask_t m = { 0 };
|
|
|
|
BITSET_AND(m.bitset, match->dontcare.bitset, val.bitset);
|
|
|
|
|
|
|
|
if (BITSET_COUNT(m.bitset)) {
|
|
|
|
decode_error(state, "dontcare bits in %s: %"BITSET_FORMAT,
|
|
|
|
match->name, BITSET_VALUE(m.bitset));
|
|
|
|
}
|
2020-12-14 00:18:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return match;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct isa_field *
|
|
|
|
find_field(struct decode_scope *scope, const struct isa_bitset *bitset,
|
2021-07-14 22:30:59 +01:00
|
|
|
const char *name, size_t name_len)
|
2020-12-14 00:18:43 +00:00
|
|
|
{
|
|
|
|
for (unsigned i = 0; i < bitset->num_cases; i++) {
|
|
|
|
const struct isa_case *c = bitset->cases[i];
|
|
|
|
|
|
|
|
if (c->expr) {
|
|
|
|
struct decode_state *state = scope->state;
|
|
|
|
|
|
|
|
/* When resolving a field for evaluating an expression,
|
|
|
|
* temporarily assume the expression evaluates to true.
|
|
|
|
* This allows <override/>'s to speculatively refer to
|
|
|
|
* fields defined within the override:
|
|
|
|
*/
|
|
|
|
isa_expr_t cur_expr = NULL;
|
|
|
|
if (state->expr_sp > 0)
|
|
|
|
cur_expr = state->expr_stack[state->expr_sp - 1];
|
|
|
|
if ((cur_expr != c->expr) && !evaluate_expr(scope, c->expr))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < c->num_fields; i++) {
|
2021-07-14 22:30:59 +01:00
|
|
|
if (!strncmp(name, c->fields[i].name, name_len) &&
|
|
|
|
(c->fields[i].name[name_len] == '\0')) {
|
2020-12-14 00:18:43 +00:00
|
|
|
return &c->fields[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bitset->parent) {
|
2021-07-14 22:30:59 +01:00
|
|
|
const struct isa_field *f = find_field(scope, bitset->parent, name, name_len);
|
2020-12-14 00:18:43 +00:00
|
|
|
if (f) {
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-05-08 10:28:00 +01:00
|
|
|
static bitmask_t
|
2020-12-14 00:18:43 +00:00
|
|
|
extract_field(struct decode_scope *scope, const struct isa_field *field)
|
|
|
|
{
|
2021-05-08 10:28:00 +01:00
|
|
|
bitmask_t val, mask;
|
|
|
|
|
|
|
|
BITSET_COPY(val.bitset, scope->val.bitset);
|
|
|
|
BITSET_ZERO(mask.bitset);
|
|
|
|
|
|
|
|
BITSET_SET_RANGE(mask.bitset, field->low, field->high);
|
|
|
|
BITSET_AND(val.bitset, val.bitset, mask.bitset);
|
|
|
|
BITSET_SHR(val.bitset, field->low);
|
|
|
|
|
|
|
|
return val;
|
2020-12-14 00:18:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find the display template for a given bitset, recursively searching
|
|
|
|
* parents in the bitset hierarchy.
|
|
|
|
*/
|
|
|
|
static const char *
|
|
|
|
find_display(struct decode_scope *scope, const struct isa_bitset *bitset)
|
|
|
|
{
|
|
|
|
for (unsigned i = 0; i < bitset->num_cases; i++) {
|
|
|
|
const struct isa_case *c = bitset->cases[i];
|
|
|
|
if (c->expr && !evaluate_expr(scope, c->expr))
|
|
|
|
continue;
|
|
|
|
/* since this is the chosen case, it seems like a good place
|
|
|
|
* to check asserted bits:
|
|
|
|
*/
|
|
|
|
for (unsigned j = 0; j < c->num_fields; j++) {
|
|
|
|
if (c->fields[j].type == TYPE_ASSERT) {
|
|
|
|
const struct isa_field *f = &c->fields[j];
|
2021-05-08 10:28:00 +01:00
|
|
|
bitmask_t val;
|
|
|
|
|
|
|
|
val = extract_field(scope, f);
|
|
|
|
if (!BITSET_EQUAL(val.bitset, f->val.bitset)) {
|
2020-12-14 00:18:43 +00:00
|
|
|
decode_error(scope->state, "WARNING: unexpected "
|
2021-05-25 08:19:17 +01:00
|
|
|
"bits[%u:%u] in %s: %"BITSET_FORMAT" vs %"BITSET_FORMAT,
|
2020-12-14 00:18:43 +00:00
|
|
|
f->low, f->high, bitset->name,
|
2021-05-08 10:28:00 +01:00
|
|
|
BITSET_VALUE(val.bitset), BITSET_VALUE(f->val.bitset));
|
2020-12-14 00:18:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!c->display)
|
|
|
|
continue;
|
|
|
|
return c->display;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If we didn't find something check up the bitset hierarchy.
|
|
|
|
*/
|
|
|
|
if (bitset->parent) {
|
|
|
|
return find_display(scope, bitset->parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Decode a field that is itself another bitset type
|
|
|
|
*/
|
|
|
|
static void
|
2021-05-08 10:28:00 +01:00
|
|
|
display_bitset_field(struct decode_scope *scope, const struct isa_field *field, bitmask_t val)
|
2020-12-14 00:18:43 +00:00
|
|
|
{
|
|
|
|
const struct isa_bitset *b = find_bitset(scope->state, field->bitsets, val);
|
|
|
|
if (!b) {
|
2021-05-25 08:19:17 +01:00
|
|
|
decode_error(scope->state, "no match: FIELD: '%s.%s': %"BITSET_FORMAT,
|
2021-05-08 10:28:00 +01:00
|
|
|
scope->bitset->name, field->name, BITSET_VALUE(val.bitset));
|
2020-12-14 00:18:43 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct decode_scope *nested_scope =
|
|
|
|
push_scope(scope->state, b, val);
|
|
|
|
nested_scope->params = field->params;
|
|
|
|
display(nested_scope);
|
|
|
|
pop_scope(nested_scope);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2021-05-08 10:28:00 +01:00
|
|
|
display_enum_field(struct decode_scope *scope, const struct isa_field *field, bitmask_t val)
|
2020-12-14 00:18:43 +00:00
|
|
|
{
|
|
|
|
const struct isa_enum *e = field->enums;
|
2021-05-08 10:28:00 +01:00
|
|
|
const uint64_t ui = bitmask_to_uint64_t(val);
|
|
|
|
|
2020-12-14 00:18:43 +00:00
|
|
|
for (unsigned i = 0; i < e->num_values; i++) {
|
2021-05-08 10:28:00 +01:00
|
|
|
if (e->values[i].val == ui) {
|
2021-06-18 20:26:29 +01:00
|
|
|
print(scope->state, "%s", e->values[i].display);
|
2020-12-14 00:18:43 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-18 20:26:29 +01:00
|
|
|
print(scope->state, "%u", (unsigned)ui);
|
2020-12-14 00:18:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct isa_field *
|
2021-07-14 22:30:59 +01:00
|
|
|
resolve_field(struct decode_scope *scope, const char *field_name, size_t field_name_len, bitmask_t *valp)
|
2020-12-14 00:18:43 +00:00
|
|
|
{
|
|
|
|
if (!scope) {
|
|
|
|
/* We've reached the bottom of the stack! */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct isa_field *field =
|
2021-07-14 22:30:59 +01:00
|
|
|
find_field(scope, scope->bitset, field_name, field_name_len);
|
2020-12-14 00:18:43 +00:00
|
|
|
|
|
|
|
if (!field && scope->params) {
|
|
|
|
for (unsigned i = 0; i < scope->params->num_params; i++) {
|
2021-07-14 22:30:59 +01:00
|
|
|
if (!strncmp(field_name, scope->params->params[i].as, field_name_len) &&
|
|
|
|
(scope->params->params[i].as[field_name_len] == '\0')) {
|
2020-12-14 00:18:43 +00:00
|
|
|
const char *param_name = scope->params->params[i].name;
|
2021-07-14 22:30:59 +01:00
|
|
|
return resolve_field(scope->parent, param_name, strlen(param_name), valp);
|
2020-12-14 00:18:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!field) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* extract out raw field value: */
|
|
|
|
if (field->expr) {
|
2021-05-08 10:28:00 +01:00
|
|
|
uint64_t val = evaluate_expr(scope, field->expr);
|
|
|
|
|
|
|
|
*valp = uint64_t_to_bitmask(val);
|
2020-12-14 00:18:43 +00:00
|
|
|
} else {
|
|
|
|
*valp = extract_field(scope, field);
|
|
|
|
}
|
|
|
|
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This is also used from generated expr functions */
|
|
|
|
uint64_t
|
|
|
|
isa_decode_field(struct decode_scope *scope, const char *field_name)
|
|
|
|
{
|
2021-05-08 10:28:00 +01:00
|
|
|
bitmask_t val;
|
2021-07-14 22:30:59 +01:00
|
|
|
const struct isa_field *field = resolve_field(scope, field_name, strlen(field_name), &val);
|
2020-12-14 00:18:43 +00:00
|
|
|
if (!field) {
|
|
|
|
decode_error(scope->state, "no field '%s'", field_name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-05-08 10:28:00 +01:00
|
|
|
return bitmask_to_uint64_t(val);
|
2020-12-14 00:18:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
display_field(struct decode_scope *scope, const char *field_name)
|
|
|
|
{
|
|
|
|
const struct isa_decode_options *options = scope->state->options;
|
2021-07-14 22:30:59 +01:00
|
|
|
struct decode_state *state = scope->state;
|
|
|
|
size_t field_name_len = strlen(field_name);
|
|
|
|
int num_align = 0;
|
|
|
|
|
|
|
|
/* alignment handling */
|
|
|
|
const char *align = strstr(field_name, ":align=");
|
|
|
|
|
|
|
|
if (align) {
|
|
|
|
const char *value = strstr(align, "=") + 1;
|
|
|
|
|
|
|
|
field_name_len = align - field_name;
|
|
|
|
num_align = atoi(value);
|
|
|
|
}
|
2020-12-14 00:18:43 +00:00
|
|
|
|
2022-01-23 14:48:27 +00:00
|
|
|
/* Special case ':algin=' should only do alignment */
|
|
|
|
if (field_name == align) {
|
|
|
|
while (scope->state->line_column < num_align)
|
|
|
|
print(state, " ");
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-14 00:18:43 +00:00
|
|
|
/* Special case 'NAME' maps to instruction/bitset name: */
|
2021-07-14 22:30:59 +01:00
|
|
|
if (!strncmp("NAME", field_name, field_name_len)) {
|
2020-12-14 00:18:43 +00:00
|
|
|
if (options->field_cb) {
|
|
|
|
options->field_cb(options->cbdata, field_name, &(struct isa_decode_value){
|
|
|
|
.str = scope->bitset->name,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-07-14 22:30:59 +01:00
|
|
|
while (scope->state->line_column < num_align)
|
|
|
|
print(state, " ");
|
|
|
|
|
2021-06-18 20:26:29 +01:00
|
|
|
print(scope->state, "%s", scope->bitset->name);
|
2020-12-14 00:18:43 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-08 10:28:00 +01:00
|
|
|
bitmask_t v;
|
2021-07-14 22:30:59 +01:00
|
|
|
const struct isa_field *field = resolve_field(scope, field_name, field_name_len, &v);
|
2020-12-14 00:18:43 +00:00
|
|
|
if (!field) {
|
2021-07-14 22:30:59 +01:00
|
|
|
decode_error(scope->state, "no field '%.*s'", (int)field_name_len, field_name);
|
2020-12-14 00:18:43 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-08 10:28:00 +01:00
|
|
|
uint64_t val = bitmask_to_uint64_t(v);
|
|
|
|
|
2020-12-14 00:18:43 +00:00
|
|
|
if (options->field_cb) {
|
|
|
|
options->field_cb(options->cbdata, field_name, &(struct isa_decode_value){
|
|
|
|
.num = val,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned width = 1 + field->high - field->low;
|
|
|
|
|
2021-07-14 22:30:59 +01:00
|
|
|
while (scope->state->line_column < num_align)
|
|
|
|
print(state, " ");
|
|
|
|
|
2020-12-14 00:18:43 +00:00
|
|
|
switch (field->type) {
|
|
|
|
/* Basic types: */
|
|
|
|
case TYPE_BRANCH:
|
|
|
|
if (scope->state->options->branch_labels) {
|
2021-04-19 10:43:30 +01:00
|
|
|
int offset = util_sign_extend(val, width) + scope->state->n;
|
2020-12-14 00:18:43 +00:00
|
|
|
if (offset < scope->state->num_instr) {
|
2021-06-18 20:26:29 +01:00
|
|
|
print(scope->state, "l%d", offset);
|
2020-12-14 00:18:43 +00:00
|
|
|
BITSET_SET(scope->state->branch_targets, offset);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FALLTHROUGH;
|
|
|
|
case TYPE_INT:
|
2021-06-18 20:26:29 +01:00
|
|
|
print(scope->state, "%"PRId64, util_sign_extend(val, width));
|
2020-12-14 00:18:43 +00:00
|
|
|
break;
|
|
|
|
case TYPE_UINT:
|
2021-06-18 20:26:29 +01:00
|
|
|
print(scope->state, "%"PRIu64, val);
|
2020-12-14 00:18:43 +00:00
|
|
|
break;
|
|
|
|
case TYPE_HEX:
|
|
|
|
// TODO format # of digits based on field width?
|
2021-06-18 20:26:29 +01:00
|
|
|
print(scope->state, "%"PRIx64, val);
|
2020-12-14 00:18:43 +00:00
|
|
|
break;
|
|
|
|
case TYPE_OFFSET:
|
|
|
|
if (val != 0) {
|
2021-06-18 20:26:29 +01:00
|
|
|
print(scope->state, "%+"PRId64, util_sign_extend(val, width));
|
2020-12-14 00:18:43 +00:00
|
|
|
}
|
|
|
|
break;
|
2021-06-18 16:09:01 +01:00
|
|
|
case TYPE_UOFFSET:
|
|
|
|
if (val != 0) {
|
2021-06-18 20:26:29 +01:00
|
|
|
print(scope->state, "+%"PRIu64, val);
|
2021-06-18 16:09:01 +01:00
|
|
|
}
|
|
|
|
break;
|
2020-12-14 00:18:43 +00:00
|
|
|
case TYPE_FLOAT:
|
|
|
|
if (width == 16) {
|
2021-06-18 20:26:29 +01:00
|
|
|
print(scope->state, "%f", _mesa_half_to_float(val));
|
2020-12-14 00:18:43 +00:00
|
|
|
} else {
|
|
|
|
assert(width == 32);
|
2021-06-18 20:26:29 +01:00
|
|
|
print(scope->state, "%f", uif(val));
|
2020-12-14 00:18:43 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TYPE_BOOL:
|
|
|
|
if (field->display) {
|
|
|
|
if (val) {
|
2021-06-18 20:26:29 +01:00
|
|
|
print(scope->state, "%s", field->display);
|
2020-12-14 00:18:43 +00:00
|
|
|
}
|
|
|
|
} else {
|
2021-06-18 20:26:29 +01:00
|
|
|
print(scope->state, "%u", (unsigned)val);
|
2020-12-14 00:18:43 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TYPE_ENUM:
|
2021-05-08 10:28:00 +01:00
|
|
|
display_enum_field(scope, field, v);
|
2020-12-14 00:18:43 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case TYPE_ASSERT:
|
|
|
|
/* assert fields are not for display */
|
|
|
|
assert(0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* For fields that are decoded with another bitset hierarchy: */
|
|
|
|
case TYPE_BITSET:
|
2021-05-08 10:28:00 +01:00
|
|
|
display_bitset_field(scope, field, v);
|
2020-12-14 00:18:43 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
decode_error(scope->state, "Bad field type: %d (%s)",
|
|
|
|
field->type, field->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
display(struct decode_scope *scope)
|
|
|
|
{
|
|
|
|
const struct isa_bitset *bitset = 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);
|
|
|
|
display_field(scope, field_name);
|
|
|
|
free(field_name);
|
|
|
|
|
|
|
|
p = e;
|
|
|
|
} else {
|
|
|
|
fputc(*p, scope->state->out);
|
2021-07-14 22:23:49 +01:00
|
|
|
scope->state->line_column++;
|
2020-12-14 00:18:43 +00:00
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
decode(struct decode_state *state, void *bin, int sz)
|
|
|
|
{
|
2021-05-08 10:28:00 +01:00
|
|
|
BITSET_WORD *instrs = bin;
|
2020-12-14 00:18:43 +00:00
|
|
|
unsigned errors = 0; /* number of consecutive unmatched instructions */
|
|
|
|
|
2021-05-08 10:28:00 +01:00
|
|
|
assert(sz % BITMASK_WORDS == 0);
|
|
|
|
|
2020-12-14 00:18:43 +00:00
|
|
|
for (state->n = 0; state->n < state->num_instr; state->n++) {
|
2021-05-17 20:50:55 +01:00
|
|
|
bitmask_t instr = { 0 };
|
|
|
|
|
2021-05-08 10:28:00 +01:00
|
|
|
next_instruction(&instr, &instrs[state->n * BITMASK_WORDS]);
|
2021-07-14 22:23:49 +01:00
|
|
|
state->line_column = 0;
|
2020-12-14 00:18:43 +00:00
|
|
|
|
|
|
|
if (state->options->max_errors && (errors > state->options->max_errors)) {
|
|
|
|
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,
|
2021-05-08 10:28:00 +01:00
|
|
|
state->n, instr.bitset);
|
2020-12-14 00:18:43 +00:00
|
|
|
}
|
2021-06-18 20:26:29 +01:00
|
|
|
print(state, "l%d:\n", state->n);
|
2020-12-14 00:18:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (state->options->instr_cb) {
|
2021-05-08 10:28:00 +01:00
|
|
|
state->options->instr_cb(state->options->cbdata, state->n, instr.bitset);
|
2020-12-14 00:18:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const struct isa_bitset *b = find_bitset(state, __instruction, instr);
|
|
|
|
if (!b) {
|
2023-01-19 12:59:09 +00:00
|
|
|
if (state->options->no_match_cb) {
|
|
|
|
state->options->no_match_cb(state->out, instr.bitset, BITMASK_WORDS);
|
|
|
|
} else {
|
|
|
|
print(state, "no match: %"BITSET_FORMAT"\n", BITSET_VALUE(instr.bitset));
|
|
|
|
}
|
2020-12-14 00:18:43 +00:00
|
|
|
errors++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct decode_scope *scope = push_scope(state, b, instr);
|
|
|
|
|
|
|
|
display(scope);
|
|
|
|
if (flush_errors(state)) {
|
|
|
|
errors++;
|
|
|
|
} else {
|
|
|
|
errors = 0;
|
|
|
|
}
|
2021-06-18 20:26:29 +01:00
|
|
|
print(state, "\n");
|
2020-12-14 00:18:43 +00:00
|
|
|
|
|
|
|
pop_scope(scope);
|
|
|
|
|
|
|
|
if (state->options->stop) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
isa_decode(void *bin, int sz, FILE *out, const struct isa_decode_options *options)
|
|
|
|
{
|
2021-03-09 13:49:28 +00:00
|
|
|
const struct isa_decode_options default_options = {
|
2021-10-21 19:58:08 +01:00
|
|
|
.gpu_id = options ? options->gpu_id : 0,
|
2021-03-09 13:49:28 +00:00
|
|
|
.branch_labels = options ? options->branch_labels : false
|
|
|
|
};
|
2020-12-14 00:18:43 +00:00
|
|
|
struct decode_state *state;
|
|
|
|
|
|
|
|
if (!options)
|
|
|
|
options = &default_options;
|
|
|
|
|
|
|
|
state = rzalloc_size(NULL, sizeof(*state));
|
|
|
|
state->options = options;
|
2021-05-08 10:28:00 +01:00
|
|
|
state->num_instr = sz / (BITMASK_WORDS * sizeof(BITSET_WORD));
|
2020-12-14 00:18:43 +00:00
|
|
|
|
|
|
|
if (state->options->branch_labels) {
|
|
|
|
state->branch_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");
|
|
|
|
state->options = &default_options; /* skip hooks for prepass */
|
|
|
|
decode(state, bin, sz);
|
|
|
|
fclose(state->out);
|
|
|
|
if (options) {
|
|
|
|
state->options = options;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
state->out = out;
|
|
|
|
|
|
|
|
decode(state, bin, sz);
|
|
|
|
|
|
|
|
ralloc_free(state);
|
|
|
|
}
|