2019-04-22 04:41:09 +01:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2019 Connor Abbott <cwabbott0@gmail.com>
|
|
|
|
* Copyright (C) 2019 Lyude Paul <thatslyude@gmail.com>
|
|
|
|
* Copyright (C) 2019 Ryan Houdek <Sonicadvance1@gmail.com>
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "bifrost.h"
|
|
|
|
#include "disassemble.h"
|
2020-08-05 22:25:27 +01:00
|
|
|
#include "bi_print_common.h"
|
2019-04-22 04:41:09 +01:00
|
|
|
#include "util/macros.h"
|
|
|
|
|
|
|
|
// return bits (high, lo]
|
|
|
|
static uint64_t bits(uint32_t word, unsigned lo, unsigned high)
|
|
|
|
{
|
|
|
|
if (high == 32)
|
|
|
|
return word >> lo;
|
|
|
|
return (word & ((1 << high) - 1)) >> lo;
|
|
|
|
}
|
|
|
|
|
|
|
|
// each of these structs represents an instruction that's dispatched in one
|
|
|
|
// cycle. Note that these instructions are packed in funny ways within the
|
|
|
|
// clause, hence the need for a separate struct.
|
|
|
|
struct bifrost_alu_inst {
|
|
|
|
uint32_t fma_bits;
|
|
|
|
uint32_t add_bits;
|
|
|
|
uint64_t reg_bits;
|
|
|
|
};
|
|
|
|
|
|
|
|
static unsigned get_reg0(struct bifrost_regs regs)
|
|
|
|
{
|
|
|
|
if (regs.ctrl == 0)
|
|
|
|
return regs.reg0 | ((regs.reg1 & 0x1) << 5);
|
|
|
|
|
|
|
|
return regs.reg0 <= regs.reg1 ? regs.reg0 : 63 - regs.reg0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned get_reg1(struct bifrost_regs regs)
|
|
|
|
{
|
|
|
|
return regs.reg0 <= regs.reg1 ? regs.reg1 : 63 - regs.reg1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// this represents the decoded version of the ctrl register field.
|
2019-08-14 20:28:01 +01:00
|
|
|
struct bifrost_reg_ctrl {
|
2019-04-22 04:41:09 +01:00
|
|
|
bool read_reg0;
|
|
|
|
bool read_reg1;
|
|
|
|
bool read_reg3;
|
|
|
|
enum bifrost_reg_write_unit fma_write_unit;
|
|
|
|
enum bifrost_reg_write_unit add_write_unit;
|
|
|
|
bool clause_start;
|
|
|
|
};
|
|
|
|
|
2020-09-14 18:21:33 +01:00
|
|
|
static void dump_header(FILE *fp, struct bifrost_header header, bool verbose)
|
2019-08-14 20:28:01 +01:00
|
|
|
{
|
2020-03-18 17:23:00 +00:00
|
|
|
fprintf(fp, "id(%du) ", header.scoreboard_index);
|
|
|
|
|
2019-04-22 04:41:09 +01:00
|
|
|
if (header.clause_type != 0) {
|
2020-03-18 17:23:00 +00:00
|
|
|
const char *name = bi_clause_type_name(header.clause_type);
|
|
|
|
|
|
|
|
if (name[0] == '?')
|
|
|
|
fprintf(fp, "unk%u ", header.clause_type);
|
|
|
|
else
|
|
|
|
fprintf(fp, "%s ", name);
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (header.scoreboard_deps != 0) {
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "next-wait(");
|
2019-04-22 04:41:09 +01:00
|
|
|
bool first = true;
|
|
|
|
for (unsigned i = 0; i < 8; i++) {
|
|
|
|
if (header.scoreboard_deps & (1 << i)) {
|
|
|
|
if (!first) {
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, ", ");
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "%d", i);
|
2019-04-22 04:41:09 +01:00
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
}
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, ") ");
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (header.datareg_writebarrier)
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "data-reg-barrier ");
|
2019-04-22 04:41:09 +01:00
|
|
|
|
|
|
|
if (!header.no_end_of_shader)
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "eos ");
|
2019-04-22 04:41:09 +01:00
|
|
|
|
|
|
|
if (!header.back_to_back) {
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "nbb ");
|
2019-04-22 04:41:09 +01:00
|
|
|
if (header.branch_cond)
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "branch-cond ");
|
2019-04-22 04:41:09 +01:00
|
|
|
else
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "branch-uncond ");
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (header.elide_writes)
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "we ");
|
2019-04-22 04:41:09 +01:00
|
|
|
|
|
|
|
if (header.suppress_inf)
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "suppress-inf ");
|
2019-04-22 04:41:09 +01:00
|
|
|
if (header.suppress_nan)
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "suppress-nan ");
|
2019-04-22 04:41:09 +01:00
|
|
|
|
|
|
|
if (header.unk0)
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "unk0 ");
|
2019-04-22 04:41:09 +01:00
|
|
|
if (header.unk1)
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "unk1 ");
|
2019-04-22 04:41:09 +01:00
|
|
|
if (header.unk2)
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "unk2 ");
|
2019-04-22 04:41:09 +01:00
|
|
|
if (header.unk3)
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "unk3 ");
|
2019-04-22 04:41:09 +01:00
|
|
|
if (header.unk4)
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "unk4 ");
|
2019-04-22 04:41:09 +01:00
|
|
|
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "\n");
|
2019-04-22 04:41:09 +01:00
|
|
|
|
|
|
|
if (verbose) {
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "# clause type %d, next clause type %d\n",
|
2019-08-14 20:28:01 +01:00
|
|
|
header.clause_type, header.next_clause_type);
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-22 20:59:57 +00:00
|
|
|
static struct bifrost_reg_ctrl DecodeRegCtrl(FILE *fp, struct bifrost_regs regs)
|
2019-04-22 04:41:09 +01:00
|
|
|
{
|
|
|
|
struct bifrost_reg_ctrl decoded = {};
|
|
|
|
unsigned ctrl;
|
|
|
|
if (regs.ctrl == 0) {
|
|
|
|
ctrl = regs.reg1 >> 2;
|
|
|
|
decoded.read_reg0 = !(regs.reg1 & 0x2);
|
|
|
|
decoded.read_reg1 = false;
|
|
|
|
} else {
|
|
|
|
ctrl = regs.ctrl;
|
|
|
|
decoded.read_reg0 = decoded.read_reg1 = true;
|
|
|
|
}
|
|
|
|
switch (ctrl) {
|
2019-08-14 20:28:01 +01:00
|
|
|
case 1:
|
|
|
|
decoded.fma_write_unit = REG_WRITE_TWO;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
decoded.fma_write_unit = REG_WRITE_TWO;
|
|
|
|
decoded.read_reg3 = true;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
decoded.read_reg3 = true;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
decoded.add_write_unit = REG_WRITE_TWO;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
decoded.add_write_unit = REG_WRITE_TWO;
|
|
|
|
decoded.read_reg3 = true;
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
decoded.clause_start = true;
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
decoded.fma_write_unit = REG_WRITE_TWO;
|
|
|
|
decoded.clause_start = true;
|
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
decoded.read_reg3 = true;
|
|
|
|
decoded.clause_start = true;
|
|
|
|
break;
|
|
|
|
case 13:
|
|
|
|
decoded.add_write_unit = REG_WRITE_TWO;
|
|
|
|
decoded.clause_start = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7:
|
|
|
|
case 15:
|
|
|
|
decoded.fma_write_unit = REG_WRITE_THREE;
|
|
|
|
decoded.add_write_unit = REG_WRITE_TWO;
|
|
|
|
break;
|
|
|
|
default:
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "# unknown reg ctrl %d\n", ctrl);
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return decoded;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pass in the add_write_unit or fma_write_unit, and this returns which register
|
|
|
|
// the ADD/FMA units are writing to
|
|
|
|
static unsigned GetRegToWrite(enum bifrost_reg_write_unit unit, struct bifrost_regs regs)
|
|
|
|
{
|
|
|
|
switch (unit) {
|
2019-08-14 20:28:01 +01:00
|
|
|
case REG_WRITE_TWO:
|
|
|
|
return regs.reg2;
|
|
|
|
case REG_WRITE_THREE:
|
|
|
|
return regs.reg3;
|
|
|
|
default: /* REG_WRITE_NONE */
|
|
|
|
assert(0);
|
|
|
|
return 0;
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-22 20:59:57 +00:00
|
|
|
static void dump_regs(FILE *fp, struct bifrost_regs srcs)
|
2019-04-22 04:41:09 +01:00
|
|
|
{
|
2020-01-22 20:59:57 +00:00
|
|
|
struct bifrost_reg_ctrl ctrl = DecodeRegCtrl(fp, srcs);
|
|
|
|
fprintf(fp, "# ");
|
2019-04-22 04:41:09 +01:00
|
|
|
if (ctrl.read_reg0)
|
2020-09-08 15:47:13 +01:00
|
|
|
fprintf(fp, "port 0: r%d ", get_reg0(srcs));
|
2019-04-22 04:41:09 +01:00
|
|
|
if (ctrl.read_reg1)
|
2020-09-08 15:47:13 +01:00
|
|
|
fprintf(fp, "port 1: r%d ", get_reg1(srcs));
|
2019-04-22 04:41:09 +01:00
|
|
|
|
|
|
|
if (ctrl.fma_write_unit == REG_WRITE_TWO)
|
2020-09-08 15:47:13 +01:00
|
|
|
fprintf(fp, "port 2: r%d (write FMA) ", srcs.reg2);
|
2019-04-22 04:41:09 +01:00
|
|
|
else if (ctrl.add_write_unit == REG_WRITE_TWO)
|
2020-09-08 15:47:13 +01:00
|
|
|
fprintf(fp, "port 2: r%d (write ADD) ", srcs.reg2);
|
2019-04-22 04:41:09 +01:00
|
|
|
|
|
|
|
if (ctrl.fma_write_unit == REG_WRITE_THREE)
|
2020-09-08 15:47:13 +01:00
|
|
|
fprintf(fp, "port 3: r%d (write FMA) ", srcs.reg3);
|
2019-04-22 04:41:09 +01:00
|
|
|
else if (ctrl.add_write_unit == REG_WRITE_THREE)
|
2020-09-08 15:47:13 +01:00
|
|
|
fprintf(fp, "port 3: r%d (write ADD) ", srcs.reg3);
|
2019-04-22 04:41:09 +01:00
|
|
|
else if (ctrl.read_reg3)
|
2020-09-08 15:47:13 +01:00
|
|
|
fprintf(fp, "port 3: r%d (read) ", srcs.reg3);
|
2019-04-22 04:41:09 +01:00
|
|
|
|
|
|
|
if (srcs.uniform_const) {
|
|
|
|
if (srcs.uniform_const & 0x80) {
|
2020-09-08 15:47:13 +01:00
|
|
|
fprintf(fp, "uniform: u%d", (srcs.uniform_const & 0x7f) * 2);
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "\n");
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
2020-09-14 18:08:44 +01:00
|
|
|
|
|
|
|
void
|
|
|
|
bi_disasm_dest_fma(FILE *fp, struct bifrost_regs *next_regs)
|
|
|
|
{
|
|
|
|
struct bifrost_reg_ctrl next_ctrl = DecodeRegCtrl(fp, *next_regs);
|
|
|
|
if (next_ctrl.fma_write_unit != REG_WRITE_NONE)
|
|
|
|
fprintf(fp, "r%u:t0", GetRegToWrite(next_ctrl.fma_write_unit, *next_regs));
|
|
|
|
else
|
|
|
|
fprintf(fp, "t0");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bi_disasm_dest_add(FILE *fp, struct bifrost_regs *next_regs)
|
|
|
|
{
|
|
|
|
struct bifrost_reg_ctrl next_ctrl = DecodeRegCtrl(fp, *next_regs);
|
|
|
|
if (next_ctrl.add_write_unit != REG_WRITE_NONE)
|
|
|
|
fprintf(fp, "r%u:t1", GetRegToWrite(next_ctrl.add_write_unit, *next_regs));
|
|
|
|
else
|
|
|
|
fprintf(fp, "t1");
|
|
|
|
}
|
|
|
|
|
2020-01-22 20:59:57 +00:00
|
|
|
static void dump_const_imm(FILE *fp, uint32_t imm)
|
2019-04-22 04:41:09 +01:00
|
|
|
{
|
|
|
|
union {
|
|
|
|
float f;
|
|
|
|
uint32_t i;
|
|
|
|
} fi;
|
|
|
|
fi.i = imm;
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "0x%08x /* %f */", imm, fi.f);
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static uint64_t get_const(uint64_t *consts, struct bifrost_regs srcs)
|
|
|
|
{
|
|
|
|
unsigned low_bits = srcs.uniform_const & 0xf;
|
|
|
|
uint64_t imm;
|
|
|
|
switch (srcs.uniform_const >> 4) {
|
2019-08-14 20:28:01 +01:00
|
|
|
case 4:
|
|
|
|
imm = consts[0];
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
imm = consts[1];
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
imm = consts[2];
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
imm = consts[3];
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
imm = consts[4];
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
imm = consts[5];
|
|
|
|
break;
|
|
|
|
default:
|
2020-08-25 18:15:27 +01:00
|
|
|
unreachable("bad imm");
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
return imm | low_bits;
|
|
|
|
}
|
|
|
|
|
2020-01-22 20:59:57 +00:00
|
|
|
static void dump_uniform_const_src(FILE *fp, struct bifrost_regs srcs, uint64_t *consts, bool high32)
|
2019-04-22 04:41:09 +01:00
|
|
|
{
|
|
|
|
if (srcs.uniform_const & 0x80) {
|
2020-09-08 15:47:13 +01:00
|
|
|
unsigned uniform = (srcs.uniform_const & 0x7f);
|
|
|
|
fprintf(fp, "u%d.w%d", uniform, high32);
|
2019-04-22 04:41:09 +01:00
|
|
|
} else if (srcs.uniform_const >= 0x20) {
|
|
|
|
uint64_t imm = get_const(consts, srcs);
|
|
|
|
if (high32)
|
2020-01-22 20:59:57 +00:00
|
|
|
dump_const_imm(fp, imm >> 32);
|
2019-04-22 04:41:09 +01:00
|
|
|
else
|
2020-01-22 20:59:57 +00:00
|
|
|
dump_const_imm(fp, imm);
|
2019-04-22 04:41:09 +01:00
|
|
|
} else {
|
|
|
|
switch (srcs.uniform_const) {
|
2019-08-14 20:28:01 +01:00
|
|
|
case 0:
|
2020-09-08 15:47:40 +01:00
|
|
|
fprintf(fp, "#0");
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
fprintf(fp, "lane_id");
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
fprintf(fp, "warp_id");
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
fprintf(fp, "core_id");
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
fprintf(fp, "framebuffer_size");
|
2019-08-14 20:28:01 +01:00
|
|
|
break;
|
|
|
|
case 5:
|
2020-09-08 15:47:40 +01:00
|
|
|
fprintf(fp, "atest_datum");
|
2019-08-14 20:28:01 +01:00
|
|
|
break;
|
|
|
|
case 6:
|
2020-09-08 15:47:40 +01:00
|
|
|
fprintf(fp, "sample");
|
2019-08-14 20:28:01 +01:00
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
case 9:
|
|
|
|
case 10:
|
|
|
|
case 11:
|
|
|
|
case 12:
|
|
|
|
case 13:
|
|
|
|
case 14:
|
|
|
|
case 15:
|
2020-09-08 15:47:40 +01:00
|
|
|
fprintf(fp, "blend_descriptor_%u", (unsigned) srcs.uniform_const - 8);
|
2019-08-14 20:28:01 +01:00
|
|
|
break;
|
|
|
|
default:
|
2020-09-08 15:47:40 +01:00
|
|
|
fprintf(fp, "XXX - reserved%u", (unsigned) srcs.uniform_const);
|
2019-08-14 20:28:01 +01:00
|
|
|
break;
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (high32)
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, ".y");
|
2019-04-22 04:41:09 +01:00
|
|
|
else
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, ".x");
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-14 18:09:53 +01:00
|
|
|
void
|
|
|
|
dump_src(FILE *fp, unsigned src, struct bifrost_regs srcs, uint64_t *consts, bool isFMA)
|
2019-04-22 04:41:09 +01:00
|
|
|
{
|
|
|
|
switch (src) {
|
2019-08-14 20:28:01 +01:00
|
|
|
case 0:
|
2020-09-08 15:47:13 +01:00
|
|
|
fprintf(fp, "r%d", get_reg0(srcs));
|
2019-08-14 20:28:01 +01:00
|
|
|
break;
|
|
|
|
case 1:
|
2020-09-08 15:47:13 +01:00
|
|
|
fprintf(fp, "r%d", get_reg1(srcs));
|
2019-08-14 20:28:01 +01:00
|
|
|
break;
|
|
|
|
case 2:
|
2020-09-08 15:47:13 +01:00
|
|
|
fprintf(fp, "r%d", srcs.reg3);
|
2019-08-14 20:28:01 +01:00
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
if (isFMA)
|
2020-09-08 15:47:13 +01:00
|
|
|
fprintf(fp, "#0");
|
2019-08-14 20:28:01 +01:00
|
|
|
else
|
2020-09-08 15:47:13 +01:00
|
|
|
fprintf(fp, "t"); // i.e. the output of FMA this cycle
|
2019-08-14 20:28:01 +01:00
|
|
|
break;
|
|
|
|
case 4:
|
2020-01-22 20:59:57 +00:00
|
|
|
dump_uniform_const_src(fp, srcs, consts, false);
|
2019-08-14 20:28:01 +01:00
|
|
|
break;
|
|
|
|
case 5:
|
2020-01-22 20:59:57 +00:00
|
|
|
dump_uniform_const_src(fp, srcs, consts, true);
|
2019-08-14 20:28:01 +01:00
|
|
|
break;
|
|
|
|
case 6:
|
2020-09-08 15:47:13 +01:00
|
|
|
fprintf(fp, "t0");
|
2019-08-14 20:28:01 +01:00
|
|
|
break;
|
|
|
|
case 7:
|
2020-09-08 15:47:13 +01:00
|
|
|
fprintf(fp, "t1");
|
2019-08-14 20:28:01 +01:00
|
|
|
break;
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-14 18:21:33 +01:00
|
|
|
static void dump_instr(FILE *fp, const struct bifrost_alu_inst *instr,
|
2020-01-22 20:59:57 +00:00
|
|
|
struct bifrost_regs next_regs, uint64_t *consts,
|
2019-04-22 04:41:09 +01:00
|
|
|
unsigned data_reg, unsigned offset, bool verbose)
|
|
|
|
{
|
|
|
|
struct bifrost_regs regs;
|
|
|
|
memcpy((char *) ®s, (char *) &instr->reg_bits, sizeof(regs));
|
|
|
|
|
|
|
|
if (verbose) {
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "# regs: %016" PRIx64 "\n", instr->reg_bits);
|
|
|
|
dump_regs(fp, regs);
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
2020-09-14 18:21:33 +01:00
|
|
|
bi_disasm_fma(fp, instr->fma_bits, ®s, &next_regs, data_reg, offset, consts);
|
|
|
|
bi_disasm_add(fp, instr->add_bits, ®s, &next_regs, data_reg, offset, consts);
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
|
2020-09-14 18:21:33 +01:00
|
|
|
static bool dump_clause(FILE *fp, uint32_t *words, unsigned *size, unsigned offset, bool verbose)
|
2019-08-14 20:28:01 +01:00
|
|
|
{
|
2019-04-22 04:41:09 +01:00
|
|
|
// State for a decoded clause
|
|
|
|
struct bifrost_alu_inst instrs[8] = {};
|
|
|
|
uint64_t consts[6] = {};
|
|
|
|
unsigned num_instrs = 0;
|
|
|
|
unsigned num_consts = 0;
|
|
|
|
uint64_t header_bits = 0;
|
|
|
|
bool stopbit = false;
|
|
|
|
|
|
|
|
unsigned i;
|
|
|
|
for (i = 0; ; i++, words += 4) {
|
|
|
|
if (verbose) {
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "# ");
|
2019-04-22 04:41:09 +01:00
|
|
|
for (int j = 0; j < 4; j++)
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "%08x ", words[3 - j]); // low bit on the right
|
|
|
|
fprintf(fp, "\n");
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
unsigned tag = bits(words[0], 0, 8);
|
|
|
|
|
|
|
|
// speculatively decode some things that are common between many formats, so we can share some code
|
|
|
|
struct bifrost_alu_inst main_instr = {};
|
|
|
|
// 20 bits
|
|
|
|
main_instr.add_bits = bits(words[2], 2, 32 - 13);
|
|
|
|
// 23 bits
|
|
|
|
main_instr.fma_bits = bits(words[1], 11, 32) | bits(words[2], 0, 2) << (32 - 11);
|
|
|
|
// 35 bits
|
|
|
|
main_instr.reg_bits = ((uint64_t) bits(words[1], 0, 11)) << 24 | (uint64_t) bits(words[0], 8, 32);
|
|
|
|
|
|
|
|
uint64_t const0 = bits(words[0], 8, 32) << 4 | (uint64_t) words[1] << 28 | bits(words[2], 0, 4) << 60;
|
|
|
|
uint64_t const1 = bits(words[2], 4, 32) << 4 | (uint64_t) words[3] << 32;
|
|
|
|
|
|
|
|
bool stop = tag & 0x40;
|
|
|
|
|
|
|
|
if (verbose) {
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "# tag: 0x%02x\n", tag);
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
if (tag & 0x80) {
|
|
|
|
unsigned idx = stop ? 5 : 2;
|
|
|
|
main_instr.add_bits |= ((tag >> 3) & 0x7) << 17;
|
|
|
|
instrs[idx + 1] = main_instr;
|
|
|
|
instrs[idx].add_bits = bits(words[3], 0, 17) | ((tag & 0x7) << 17);
|
|
|
|
instrs[idx].fma_bits |= bits(words[2], 19, 32) << 10;
|
|
|
|
consts[0] = bits(words[3], 17, 32) << 4;
|
|
|
|
} else {
|
|
|
|
bool done = false;
|
|
|
|
switch ((tag >> 3) & 0x7) {
|
2019-08-14 20:28:01 +01:00
|
|
|
case 0x0:
|
|
|
|
switch (tag & 0x7) {
|
|
|
|
case 0x3:
|
|
|
|
main_instr.add_bits |= bits(words[3], 29, 32) << 17;
|
|
|
|
instrs[1] = main_instr;
|
|
|
|
num_instrs = 2;
|
|
|
|
done = stop;
|
2019-04-22 04:41:09 +01:00
|
|
|
break;
|
2019-08-14 20:28:01 +01:00
|
|
|
case 0x4:
|
|
|
|
instrs[2].add_bits = bits(words[3], 0, 17) | bits(words[3], 29, 32) << 17;
|
|
|
|
instrs[2].fma_bits |= bits(words[2], 19, 32) << 10;
|
|
|
|
consts[0] = const0;
|
|
|
|
num_instrs = 3;
|
|
|
|
num_consts = 1;
|
2019-04-22 04:41:09 +01:00
|
|
|
done = stop;
|
2019-08-14 20:28:01 +01:00
|
|
|
break;
|
|
|
|
case 0x1:
|
2019-04-22 04:41:09 +01:00
|
|
|
case 0x5:
|
2019-08-14 20:28:01 +01:00
|
|
|
instrs[2].add_bits = bits(words[3], 0, 17) | bits(words[3], 29, 32) << 17;
|
|
|
|
instrs[2].fma_bits |= bits(words[2], 19, 32) << 10;
|
|
|
|
main_instr.add_bits |= bits(words[3], 26, 29) << 17;
|
|
|
|
instrs[3] = main_instr;
|
|
|
|
if ((tag & 0x7) == 0x5) {
|
|
|
|
num_instrs = 4;
|
|
|
|
done = stop;
|
|
|
|
}
|
2019-04-22 04:41:09 +01:00
|
|
|
break;
|
|
|
|
case 0x6:
|
2019-08-14 20:28:01 +01:00
|
|
|
instrs[5].add_bits = bits(words[3], 0, 17) | bits(words[3], 29, 32) << 17;
|
|
|
|
instrs[5].fma_bits |= bits(words[2], 19, 32) << 10;
|
|
|
|
consts[0] = const0;
|
|
|
|
num_instrs = 6;
|
|
|
|
num_consts = 1;
|
|
|
|
done = stop;
|
|
|
|
break;
|
|
|
|
case 0x7:
|
|
|
|
instrs[5].add_bits = bits(words[3], 0, 17) | bits(words[3], 29, 32) << 17;
|
|
|
|
instrs[5].fma_bits |= bits(words[2], 19, 32) << 10;
|
|
|
|
main_instr.add_bits |= bits(words[3], 26, 29) << 17;
|
|
|
|
instrs[6] = main_instr;
|
|
|
|
num_instrs = 7;
|
|
|
|
done = stop;
|
|
|
|
break;
|
|
|
|
default:
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "unknown tag bits 0x%02x\n", tag);
|
2019-08-14 20:28:01 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x2:
|
|
|
|
case 0x3: {
|
|
|
|
unsigned idx = ((tag >> 3) & 0x7) == 2 ? 4 : 7;
|
|
|
|
main_instr.add_bits |= (tag & 0x7) << 17;
|
|
|
|
instrs[idx] = main_instr;
|
|
|
|
consts[0] |= (bits(words[2], 19, 32) | ((uint64_t) words[3] << 13)) << 19;
|
|
|
|
num_consts = 1;
|
|
|
|
num_instrs = idx + 1;
|
|
|
|
done = stop;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0x4: {
|
|
|
|
unsigned idx = stop ? 4 : 1;
|
|
|
|
main_instr.add_bits |= (tag & 0x7) << 17;
|
|
|
|
instrs[idx] = main_instr;
|
|
|
|
instrs[idx + 1].fma_bits |= bits(words[3], 22, 32);
|
|
|
|
instrs[idx + 1].reg_bits = bits(words[2], 19, 32) | (bits(words[3], 0, 22) << (32 - 19));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0x1:
|
|
|
|
// only constants can come after this
|
|
|
|
num_instrs = 1;
|
|
|
|
done = stop;
|
2020-07-06 04:44:56 +01:00
|
|
|
/* fallthrough */
|
2019-08-14 20:28:01 +01:00
|
|
|
case 0x5:
|
|
|
|
header_bits = bits(words[2], 19, 32) | ((uint64_t) words[3] << (32 - 19));
|
|
|
|
main_instr.add_bits |= (tag & 0x7) << 17;
|
|
|
|
instrs[0] = main_instr;
|
|
|
|
break;
|
|
|
|
case 0x6:
|
|
|
|
case 0x7: {
|
|
|
|
unsigned pos = tag & 0xf;
|
|
|
|
// note that `pos' encodes both the total number of
|
|
|
|
// instructions and the position in the constant stream,
|
|
|
|
// presumably because decoded constants and instructions
|
|
|
|
// share a buffer in the decoder, but we only care about
|
|
|
|
// the position in the constant stream; the total number of
|
|
|
|
// instructions is redundant.
|
2019-08-21 17:02:40 +01:00
|
|
|
unsigned const_idx = 0;
|
2019-08-14 20:28:01 +01:00
|
|
|
switch (pos) {
|
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
case 6:
|
|
|
|
const_idx = 0;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
case 7:
|
|
|
|
case 9:
|
|
|
|
const_idx = 1;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
case 0xa:
|
|
|
|
const_idx = 2;
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
case 0xb:
|
|
|
|
case 0xc:
|
|
|
|
const_idx = 3;
|
|
|
|
break;
|
|
|
|
case 0xd:
|
|
|
|
const_idx = 4;
|
|
|
|
break;
|
2020-05-05 21:15:36 +01:00
|
|
|
case 0xe:
|
|
|
|
const_idx = 5;
|
|
|
|
break;
|
2019-04-22 04:41:09 +01:00
|
|
|
default:
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "# unknown pos 0x%x\n", pos);
|
2019-08-21 17:02:40 +01:00
|
|
|
break;
|
2019-08-14 20:28:01 +01:00
|
|
|
}
|
2019-08-21 17:02:40 +01:00
|
|
|
|
2019-08-14 20:28:01 +01:00
|
|
|
if (num_consts < const_idx + 2)
|
|
|
|
num_consts = const_idx + 2;
|
2019-08-21 17:02:40 +01:00
|
|
|
|
2019-08-14 20:28:01 +01:00
|
|
|
consts[const_idx] = const0;
|
|
|
|
consts[const_idx + 1] = const1;
|
|
|
|
done = stop;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (done)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*size = i + 1;
|
|
|
|
|
|
|
|
if (verbose) {
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "# header: %012" PRIx64 "\n", header_bits);
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
struct bifrost_header header;
|
|
|
|
memcpy((char *) &header, (char *) &header_bits, sizeof(struct bifrost_header));
|
2020-01-22 20:59:57 +00:00
|
|
|
dump_header(fp, header, verbose);
|
2019-04-22 04:41:09 +01:00
|
|
|
if (!header.no_end_of_shader)
|
|
|
|
stopbit = true;
|
|
|
|
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "{\n");
|
2019-04-22 04:41:09 +01:00
|
|
|
for (i = 0; i < num_instrs; i++) {
|
|
|
|
struct bifrost_regs next_regs;
|
|
|
|
if (i + 1 == num_instrs) {
|
|
|
|
memcpy((char *) &next_regs, (char *) &instrs[0].reg_bits,
|
2019-08-14 20:28:01 +01:00
|
|
|
sizeof(next_regs));
|
2019-04-22 04:41:09 +01:00
|
|
|
} else {
|
|
|
|
memcpy((char *) &next_regs, (char *) &instrs[i + 1].reg_bits,
|
2019-08-14 20:28:01 +01:00
|
|
|
sizeof(next_regs));
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
|
2020-01-22 20:59:57 +00:00
|
|
|
dump_instr(fp, &instrs[i], next_regs, consts, header.datareg, offset, verbose);
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "}\n");
|
2019-04-22 04:41:09 +01:00
|
|
|
|
|
|
|
if (verbose) {
|
|
|
|
for (unsigned i = 0; i < num_consts; i++) {
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "# const%d: %08" PRIx64 "\n", 2 * i, consts[i] & 0xffffffff);
|
|
|
|
fprintf(fp, "# const%d: %08" PRIx64 "\n", 2 * i + 1, consts[i] >> 32);
|
2019-04-22 04:41:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return stopbit;
|
|
|
|
}
|
|
|
|
|
2020-01-22 20:59:57 +00:00
|
|
|
void disassemble_bifrost(FILE *fp, uint8_t *code, size_t size, bool verbose)
|
2019-04-22 04:41:09 +01:00
|
|
|
{
|
|
|
|
uint32_t *words = (uint32_t *) code;
|
|
|
|
uint32_t *words_end = words + (size / 4);
|
|
|
|
// used for displaying branch targets
|
|
|
|
unsigned offset = 0;
|
2019-08-14 20:28:01 +01:00
|
|
|
while (words != words_end) {
|
2019-04-22 04:41:09 +01:00
|
|
|
// we don't know what the program-end bit is quite yet, so for now just
|
|
|
|
// assume that an all-0 quadword is padding
|
|
|
|
uint32_t zero[4] = {};
|
|
|
|
if (memcmp(words, zero, 4 * sizeof(uint32_t)) == 0)
|
|
|
|
break;
|
2020-01-22 20:59:57 +00:00
|
|
|
fprintf(fp, "clause_%d:\n", offset);
|
2019-04-22 04:41:09 +01:00
|
|
|
unsigned size;
|
2020-01-22 20:59:57 +00:00
|
|
|
if (dump_clause(fp, words, &size, offset, verbose) == true) {
|
2019-04-22 04:41:09 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
words += size * 4;
|
|
|
|
offset += size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|