Commit 19b38024 authored by Connor Abbott's avatar Connor Abbott

bifrost: Decode branch instructions

parent c1a00c40
......@@ -756,6 +756,7 @@ enum ADDSrcType {
ADDBlending,
ADDLoadAttr,
ADDVaryingAddress,
ADDBranch,
};
struct ADDOpInfo {
......@@ -859,6 +860,7 @@ static const ADDOpInfo ADDOpInfos[] = {
{ 0x0cf51, "COS_TABLE", ADDOneSrc },
{ 0x0cf60, "FLOG2_TABLE", ADDOneSrc },
{ 0x0cf64, "FLOGE_TABLE", ADDOneSrc },
{ 0x0d000, "BRANCH", ADDBranch },
{ 0x0ea60, "SEL.XX.i16", ADDTwoSrc },
{ 0x0ea70, "SEL.XY.i16", ADDTwoSrc },
{ 0x0ea68, "SEL.YX.i16", ADDTwoSrc },
......@@ -984,6 +986,9 @@ static ADDOpInfo findADDOpInfo(unsigned op)
case ADDLoadAttr:
opCmp = op & ~0x7f;
break;
case ADDBranch:
opCmp = op & ~0xfff;
break;
}
if (ADDOpInfos[i].op == opCmp)
return ADDOpInfos[i];
......@@ -1024,7 +1029,47 @@ struct DualTexCtrl {
unsigned unk1 : 22;
};
static void DumpADD(uint64_t word, Regs regs, Regs nextRegs, uint64_t *consts, unsigned dataReg)
enum BranchCond {
BrCondLT = 0,
BrCondLE = 1,
BrCondGE = 2,
BrCondGT = 3,
// Equal vs. not-equal determined by src0/src1 comparison
BrCondEQ = 4,
// floating-point comparisons
// Becomes UNE when you flip the arguments
BrCondOEQ = 5,
// TODO what happens when you flip the arguments?
BrCondOGT = 6,
BrCondOLT = 7,
};
enum BranchBitsize {
BrSize32 = 0,
BrSize16XX = 1,
BrSize16YY = 2,
// For the above combinations of bitsize and location, an extra bit is
// encoded via comparing the sources. The only possible source of ambiguity
// would be if the sources were the same, but then the branch condition
// would be always true or always false anyways, so we can ignore it. But
// this no longer works when comparing the y component to the x component,
// since it's valid to compare the y component of a source against its own
// x component. Instead, the extra bit is encoded via an extra bitsize.
BrSize16YX0 = 3,
BrSize16YX1 = 4,
BrSize32And16X = 5,
BrSize32And16Y = 6,
// Used for comparisons with zero and always-true, see below. I think this
// only works for integer comparisons.
BrSizeZero = 7,
};
enum BranchCode {
BrAlways = 63,
};
static void DumpADD(uint64_t word, Regs regs, Regs nextRegs, uint64_t *consts,
unsigned dataReg, unsigned offset)
{
printf("# ADD: %016" PRIx64 "\n", word);
ADD ADD;
......@@ -1043,6 +1088,105 @@ static void DumpADD(uint64_t word, Regs regs, Regs nextRegs, uint64_t *consts, u
printf(".f32");
else
printf(".v2f16");
} else if (info.srcType == ADDBranch) {
BranchCode branchCode = (BranchCode) ((ADD.op >> 6) & 0x3f);
if (branchCode == BrAlways) {
// unconditional branch
} else {
BranchCond cond = (BranchCond) ((ADD.op >> 6) & 0x7);
BranchBitsize size = (BranchBitsize) ((ADD.op >> 9) & 0x7);
bool portSwapped = (ADD.op & 0x7) < ADD.src0;
// See the comment in BranchBitsize
if (size == BrSize16YX0)
portSwapped = true;
if (size == BrSize16YX1)
portSwapped = false;
// These sizes are only for floating point comparisons, so the
// non-floating-point comparisons are reused to encode the flipped
// versions.
if (size == BrSize32And16X || size == BrSize32And16Y)
portSwapped = false;
// There's only one argument, so we reuse the extra argument to
// encode this.
if (size == BrSizeZero)
portSwapped = !(ADD.op & 1);
switch (cond) {
case BrCondLT:
if (portSwapped)
printf(".LT.u");
else
printf(".LT.i");
break;
case BrCondLE:
if (size == BrSize32And16X || size == BrSize32And16Y) {
printf(".UNE.f");
} else {
if (portSwapped)
printf(".LE.u");
else
printf(".LE.i");
}
break;
case BrCondGT:
if (portSwapped)
printf(".GT.u");
else
printf(".GT.i");
break;
case BrCondGE:
if (portSwapped)
printf(".GE.u");
else
printf(".GE.i");
break;
case BrCondEQ:
if (portSwapped)
printf(".NE.i");
else
printf(".EQ.i");
break;
case BrCondOEQ:
if (portSwapped)
printf(".UNE.f");
else
printf(".OEQ.f");
break;
case BrCondOGT:
if (portSwapped)
printf(".OGT.unk.f");
else
printf(".OGT.f");
break;
case BrCondOLT:
if (portSwapped)
printf(".OLT.unk.f");
else
printf(".OLT.f");
break;
}
switch (size) {
case BrSize32:
case BrSize32And16X:
case BrSize32And16Y:
printf("32");
break;
case BrSize16XX:
case BrSize16YY:
case BrSize16YX0:
case BrSize16YX1:
printf("16");
break;
case BrSizeZero: {
unsigned ctrl = (ADD.op >> 1) & 0x3;
if (ctrl == 0)
printf("32.Z");
else
printf("16.Z");
break;
}
}
}
}
printf(" ");
......@@ -1365,6 +1509,91 @@ static void DumpADD(uint64_t word, Regs regs, Regs nextRegs, uint64_t *consts, u
DumpSrc(ADD.op & 0x7, regs, consts, false);
Dump16Swizzle((ADD.op >> 8) & 0x3);
break;
case ADDBranch: {
BranchCode code = (BranchCode) ((ADD.op >> 6) & 0x3f);
BranchBitsize size = (BranchBitsize) ((ADD.op >> 9) & 0x7);
if (code != BrAlways) {
DumpSrc(ADD.src0, regs, consts, false);
switch (size) {
case BrSize16XX:
printf(".x");
break;
case BrSize16YY:
case BrSize16YX0:
case BrSize16YX1:
printf(".y");
break;
case BrSizeZero: {
unsigned ctrl = (ADD.op >> 1) & 0x3;
switch (ctrl) {
case 1:
printf(".y");
break;
case 2:
printf(".x");
break;
default:
break;
}
}
default:
break;
}
printf(", ");
}
if (code != BrAlways && size != BrSizeZero) {
DumpSrc(ADD.op & 0x7, regs, consts, false);
switch (size) {
case BrSize16XX:
case BrSize16YX0:
case BrSize16YX1:
case BrSize32And16X:
printf(".x");
break;
case BrSize16YY:
case BrSize32And16Y:
printf(".y");
break;
default:
break;
}
printf(", ");
}
// I haven't had the chance to test if this actually specifies the
// branch offset, since I couldn't get it to produce values other
// than 5 (uniform/const high), but these three bits are always
// consistent across branch instructions, so it makes sense...
int offsetSrc = (ADD.op >> 3) & 0x7;
if (offsetSrc == 4 || offsetSrc == 5) {
// If the offset is known/constant, we can decode it
uint32_t rawOffset;
if (offsetSrc == 4)
rawOffset = GetConst(consts, regs);
else
rawOffset = GetConst(consts, regs) >> 32;
// The high 4 bits are flags, while the rest is the
// twos-complement offset in bytes (here we convert to
// clauses).
int32_t branchOffset = ((int32_t) rawOffset << 4) >> 8;
// If high4 is the high 4 bits of the last 64-bit constant,
// this is calculated as (high4 + 4) & 0xf, or 0 if the branch
// offset itself is the last constant. Not sure if this is
// actually used, or just garbage in unused bits, but in any
// case, we can just ignore it here since it's redundant. Note
// that if there is any padding, this will be 4 since the
// padding counts as the last constant.
unsigned flags = rawOffset >> 28;
(void) flags;
// Note: the offset is in bytes, relative to the beginning of the
// current clause, so a zero offset would be a loop back to the
// same clause (annoyingly different from Midgard).
printf("clause_%d", offset + branchOffset);
} else {
DumpSrc(offsetSrc, regs, consts, false);
}
}
}
if (info.hasDataReg) {
printf(", R%d", dataReg);
......@@ -1381,14 +1610,15 @@ struct AluInstr {
uint64_t ADDBits;
};
void DumpInstr(const AluInstr &instr, Regs nextRegs, uint64_t *consts, unsigned dataReg)
void DumpInstr(const AluInstr &instr, Regs nextRegs, uint64_t *consts,
unsigned dataReg, unsigned offset)
{
printf("# regs: %016" PRIx64 "\n", instr.regBits);
Regs regs;
memcpy((char *) &regs, (char *) &instr.regBits, sizeof(regs));
DumpRegs(regs);
DumpFMA(instr.FMABits, regs, nextRegs, consts);
DumpADD(instr.ADDBits, regs, nextRegs, consts, dataReg);
DumpADD(instr.ADDBits, regs, nextRegs, consts, dataReg, offset);
}
struct Header {
......@@ -1481,7 +1711,7 @@ void DumpHeader(Header header)
header.clauseType, header.nextClauseType);
}
void DumpClause(uint32_t *words, unsigned *size)
void DumpClause(uint32_t *words, unsigned *size, unsigned offset)
{
// State for a decoded clause
AluInstr instrs[8] = {};
......@@ -1673,7 +1903,7 @@ void DumpClause(uint32_t *words, unsigned *size)
sizeof(nextRegs));
}
DumpInstr(instrs[i], nextRegs, consts, header.dataReg);
DumpInstr(instrs[i], nextRegs, consts, header.dataReg, offset);
}
printf("}\n");
......@@ -1687,6 +1917,8 @@ void DisassembleBifrost(uint8_t* instBlob, size_t size)
{
uint32_t *words = (uint32_t *) instBlob;
uint32_t *wordsEnd = words + (size / 4);
// used for displaying branch targets
unsigned offset = 0;
while (words != wordsEnd)
{
// we don't know what the program-end bit is quite yet, so for now just
......@@ -1694,9 +1926,11 @@ void DisassembleBifrost(uint8_t* instBlob, size_t size)
uint32_t zero[4] = {};
if (memcmp(words, zero, 4 * sizeof(uint32_t)) == 0)
break;
printf("clause_%d:\n", offset);
unsigned size;
DumpClause(words, &size);
DumpClause(words, &size, offset);
words += size * 4;
offset += size;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment