From: Connor Abbott Date: Mon, 17 Aug 2020 17:44:14 +0000 (+0200) Subject: freedreno/afuc: Handle xmov modifiers X-Git-Url: https://git.libre-soc.org/?p=mesa.git;a=commitdiff_plain;h=66dd248593b5fa54680b84032f382551ed7c3cf7 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: --- 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 T_OP_SETSECURE %token T_LSHIFT %token T_REP +%token T_XMOV %type reg %type 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: