summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorConnor Abbott <cwabbott0@gmail.com>2020-08-17 19:44:14 +0200
committerMarge Bot <eric+marge@anholt.net>2020-08-18 16:17:31 +0000
commit66dd248593b5fa54680b84032f382551ed7c3cf7 (patch)
treebb102f284bc1618d8769cec252fe491c9bf67548
parentb2b19234d81fe8fa47ad735c08049e1a6c0d2ce9 (diff)
freedreno/afuc: Handle xmov modifiers
Although it's kind-of similar to "(rptN)" in the shader ISA, I called it "xmov" to make it clear that it's completely orthogonal to "(rep)", although you certainly can use both modifiers on the same instruction. Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6368>
-rw-r--r--src/freedreno/afuc/afuc.h3
-rw-r--r--src/freedreno/afuc/asm.c10
-rw-r--r--src/freedreno/afuc/asm.h1
-rw-r--r--src/freedreno/afuc/disasm.c52
-rw-r--r--src/freedreno/afuc/lexer.l1
-rw-r--r--src/freedreno/afuc/parser.y2
6 files changed, 67 insertions, 2 deletions
diff --git a/src/freedreno/afuc/afuc.h b/src/freedreno/afuc/afuc.h
index 8233db04402..6ec268e3dcc 100644
--- a/src/freedreno/afuc/afuc.h
+++ b/src/freedreno/afuc/afuc.h
@@ -129,7 +129,8 @@ typedef union PACKED {
} movi;
struct PACKED {
uint32_t alu : 5;
- uint32_t pad : 6;
+ uint32_t pad : 4;
+ uint32_t xmov : 2; /* execute eXtra mov's based on $rem */
uint32_t dst : 5;
uint32_t src2 : 5;
uint32_t src1 : 5;
diff --git a/src/freedreno/afuc/asm.c b/src/freedreno/afuc/asm.c
index a03a89a31fa..cb11439b979 100644
--- a/src/freedreno/afuc/asm.c
+++ b/src/freedreno/afuc/asm.c
@@ -170,6 +170,10 @@ static void emit_instructions(int outfd)
if (ai->has_immed) {
/* MSB overlaps with STORE */
assert(ai->tok != T_OP_MSB);
+ if (ai->xmov) {
+ fprintf(stderr, "ALU instruction cannot have immediate and xmov\n");
+ exit(1);
+ }
opc = tok2alu(ai->tok);
instr.alui.dst = ai->dst;
instr.alui.src = ai->src1;
@@ -179,6 +183,7 @@ static void emit_instructions(int outfd)
instr.alu.dst = ai->dst;
instr.alu.src1 = ai->src1;
instr.alu.src2 = ai->src2;
+ instr.alu.xmov = ai->xmov;
instr.alu.alu = tok2alu(ai->tok);
}
break;
@@ -186,6 +191,10 @@ static void emit_instructions(int outfd)
/* move can either be encoded as movi (ie. move w/ immed) or
* an alu instruction
*/
+ if ((ai->has_immed || ai->label) && ai->xmov) {
+ fprintf(stderr, "ALU instruction cannot have immediate and xmov\n");
+ exit(1);
+ }
if (ai->has_immed) {
opc = OPC_MOVI;
instr.movi.dst = ai->dst;
@@ -206,6 +215,7 @@ static void emit_instructions(int outfd)
instr.alu.dst = ai->dst;
instr.alu.src1 = 0x00; /* $00 reads-back 0 */
instr.alu.src2 = ai->src1;
+ instr.alu.xmov = ai->xmov;
instr.alu.alu = OPC_OR;
}
break;
diff --git a/src/freedreno/afuc/asm.h b/src/freedreno/afuc/asm.h
index 03fb1508907..e0e6033e9e8 100644
--- a/src/freedreno/afuc/asm.h
+++ b/src/freedreno/afuc/asm.h
@@ -44,6 +44,7 @@ struct asm_instruction {
int immed;
int shift;
int bit;
+ int xmov;
uint32_t literal;
const char *label;
diff --git a/src/freedreno/afuc/disasm.c b/src/freedreno/afuc/disasm.c
index f687058f615..0e013f59a3a 100644
--- a/src/freedreno/afuc/disasm.c
+++ b/src/freedreno/afuc/disasm.c
@@ -506,6 +506,8 @@ static void disasm(uint32_t *buf, int sizedwords)
if (rep)
printf("(rep)");
+ if (instr->alu.xmov)
+ printf("(xmov%d)", instr->alu.xmov);
/* special case mnemonics:
* reading $00 seems to always yield zero, and so:
@@ -531,10 +533,58 @@ static void disasm(uint32_t *buf, int sizedwords)
/* print out unexpected bits: */
if (verbose) {
if (instr->alu.pad)
- printerr(" (pad=%03x)", instr->alu.pad);
+ printerr(" (pad=%01x)", instr->alu.pad);
if (instr->alu.src1 && !src1)
printerr(" (src1=%02x)", instr->alu.src1);
}
+
+ /* xmov is a modifier that makes the processor execute up to 3
+ * extra mov's after the current instruction. Given an ALU
+ * instruction:
+ *
+ * (xmovN) alu $dst, $src1, $src2
+ *
+ * In all of the uses in the firmware blob, $dst and $src2 are one
+ * of the "special" registers $data, $addr, $addr2. I've observed
+ * that if $dst isn't "special" then it's replaced with $00
+ * instead of $data, but I haven't checked what happens if $src2
+ * isn't "special". Anyway, in the usual case, the HW produces a
+ * count M = min(N, $rem) and then does the following:
+ *
+ * M = 1:
+ * mov $data, $src2
+ *
+ * M = 2:
+ * mov $data, $src2
+ * mov $data, $src2
+ *
+ * M = 3:
+ * mov $data, $src2
+ * mov $dst, $src2 (special case for CP_CONTEXT_REG_BUNCH)
+ * mov $data, $src2
+ *
+ * It seems to be frequently used in combination with (rep) to
+ * provide a kind of hardware-based loop unrolling, and there's
+ * even a special case in the ISA to be able to do this with
+ * CP_CONTEXT_REG_BUNCH. However (rep) isn't required.
+ *
+ * This dumps the expected extra instructions, assuming that $rem
+ * isn't too small.
+ */
+ if (verbose && instr->alu.xmov) {
+ for (int i = 0; i < instr->alu.xmov; i++) {
+ printf("\n ; mov ");
+ if (instr->alu.dst < 0x1d)
+ printf("$00");
+ else if (instr->alu.xmov == 3 && i == 1)
+ print_dst(instr->alu.dst);
+ else
+ printf("$data");
+ printf(", ");
+ print_src(instr->alu.src2);
+ }
+ }
+
break;
}
case OPC_CWRITE6:
diff --git a/src/freedreno/afuc/lexer.l b/src/freedreno/afuc/lexer.l
index 6b1ec07cade..8c3113d1c16 100644
--- a/src/freedreno/afuc/lexer.l
+++ b/src/freedreno/afuc/lexer.l
@@ -86,6 +86,7 @@ extern YYSTYPE yylval;
"setsecure" return TOKEN(T_OP_SETSECURE);
"<<" return TOKEN(T_LSHIFT);
"(rep)" return TOKEN(T_REP);
+"(xmov"[1-3]")" yylval.num = yytext[5] - '\0'; return T_XMOV;
"," return ',';
"[" return '[';
diff --git a/src/freedreno/afuc/parser.y b/src/freedreno/afuc/parser.y
index 657524a1f6c..5a8164e59fe 100644
--- a/src/freedreno/afuc/parser.y
+++ b/src/freedreno/afuc/parser.y
@@ -161,6 +161,7 @@ label(const char *str)
%token <tok> T_OP_SETSECURE
%token <tok> T_LSHIFT
%token <tok> T_REP
+%token <num> T_XMOV
%type <num> reg
%type <num> immediate
@@ -182,6 +183,7 @@ instr_or_label: instr_r
/* instructions that can optionally have (rep) flag: */
instr_r: alu_instr
+| T_XMOV alu_instr { instr->xmov = $1; }
| config_instr
/* need to special case: