diff --git a/src/freedreno/.gitlab-ci/reference/afuc_test.asm b/src/freedreno/.gitlab-ci/reference/afuc_test.asm index 198b84331b8..09ec1b0f875 100644 --- a/src/freedreno/.gitlab-ci/reference/afuc_test.asm +++ b/src/freedreno/.gitlab-ci/reference/afuc_test.asm @@ -2,7 +2,7 @@ ; Version: 01000001 [01000001] -[01000081] +[#jumptbl] mov $01, 0x830 ; CP_SQE_INSTR_BASE mov $02, 0x2 cwrite $01, [$00 + @REG_READ_ADDR] @@ -279,131 +279,5 @@ UNKN96: UNKN97: waitin mov $01, $data -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[00000074] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[00000048] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[00000033] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[00000025] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[00000030] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[00000022] -[0000007f] -[0000007f] -[0000007f] -[0000002c] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[00000039] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000006b] -[0000007f] -[0000005e] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] -[0000007f] +jumptbl: +.jumptbl diff --git a/src/freedreno/.gitlab-ci/traces/afuc_test.asm b/src/freedreno/.gitlab-ci/traces/afuc_test.asm index e8d6f8b2b42..48203046b51 100644 --- a/src/freedreno/.gitlab-ci/traces/afuc_test.asm +++ b/src/freedreno/.gitlab-ci/traces/afuc_test.asm @@ -27,7 +27,7 @@ ; this to avoid having to host the actual firmware, especially the disassembled ; version, in Mesa. [01000001] -[01000000] +[#jumptbl] loc02: ; packet table loading: mov $01, 0x0830 ; CP_SQE_INSTR_BASE @@ -334,3 +334,6 @@ UNKN126: UNKN127: waitin mov $01, $data + +jumptbl: +.jumptbl diff --git a/src/freedreno/afuc/afuc.h b/src/freedreno/afuc/afuc.h index 4722526e33b..2791c72c6f0 100644 --- a/src/freedreno/afuc/afuc.h +++ b/src/freedreno/afuc/afuc.h @@ -126,6 +126,7 @@ typedef enum { OPC_BRNE, OPC_JUMP, OPC_RAW_LITERAL, + OPC_JUMPTBL, } afuc_opc; /** @@ -181,6 +182,16 @@ struct afuc_instr { bool peek : 1; }; +/* Literal offsets are sometimes encoded as NOP instructions, which on a6xx+ + * must have a high 8 bits of 0x01. + */ +static inline uint32_t +afuc_nop_literal(uint32_t x, unsigned gpuver) +{ + assert((x >> 24) == 0); + return gpuver < 6 ? x : x | (1 << 24); +} + void print_control_reg(uint32_t id); void print_sqe_reg(uint32_t id); void print_pipe_reg(uint32_t id); diff --git a/src/freedreno/afuc/asm.c b/src/freedreno/afuc/asm.c index e426905f49e..8c86fce061c 100644 --- a/src/freedreno/afuc/asm.c +++ b/src/freedreno/afuc/asm.c @@ -88,6 +88,8 @@ int gpuver; static struct afuc_instr instructions[0x4000]; static unsigned num_instructions; +static unsigned instr_offset; + static struct asm_label labels[0x512]; static unsigned num_labels; @@ -96,6 +98,7 @@ next_instr(afuc_opc opc) { struct afuc_instr *ai = &instructions[num_instructions++]; assert(num_instructions < ARRAY_SIZE(instructions)); + instr_offset++; ai->opc = opc; return ai; } @@ -107,10 +110,19 @@ decl_label(const char *str) assert(num_labels < ARRAY_SIZE(labels)); - label->offset = num_instructions; + label->offset = instr_offset; label->label = str; } +void +decl_jumptbl(void) +{ + struct afuc_instr *ai = &instructions[num_instructions++]; + assert(num_instructions < ARRAY_SIZE(instructions)); + ai->opc = OPC_JUMPTBL; + instr_offset += 0x80; +} + static int resolve_label(const char *str) { @@ -128,6 +140,30 @@ resolve_label(const char *str) exit(2); } +static void +emit_jumptable(int outfd) +{ + uint32_t jmptable[0x80] = {0}; + int i; + + for (i = 0; i < num_labels; i++) { + struct asm_label *label = &labels[i]; + int id = afuc_pm4_id(label->label); + + /* if it doesn't match a known PM4 packet-id, try to match UNKN%d: */ + if (id < 0) { + if (sscanf(label->label, "UNKN%d", &id) != 1) { + /* if still not found, must not belong in jump-table: */ + continue; + } + } + + jmptable[id] = label->offset; + } + + write(outfd, jmptable, sizeof(jmptable)); +} + static void emit_instructions(int outfd) { @@ -185,16 +221,15 @@ emit_instructions(int outfd) break; } - /* special case, 2nd dword is patched up w/ # of instructions - * (ie. offset of jmptbl) - */ - if (i == 1) { - assert(ai->opc == OPC_RAW_LITERAL); - ai->literal &= ~0xffff; - ai->literal |= num_instructions; + if (ai->opc == OPC_JUMPTBL) { + emit_jumptable(outfd); + continue; } if (ai->opc == OPC_RAW_LITERAL) { + if (ai->label) { + ai->literal = afuc_nop_literal(resolve_label(ai->label), gpuver); + } write(outfd, &ai->literal, 4); continue; } @@ -218,30 +253,6 @@ parse_sqe_reg(const char *name) return afuc_sqe_reg(name + 1); } -static void -emit_jumptable(int outfd) -{ - uint32_t jmptable[0x80] = {0}; - int i; - - for (i = 0; i < num_labels; i++) { - struct asm_label *label = &labels[i]; - int id = afuc_pm4_id(label->label); - - /* if it doesn't match a known PM4 packet-id, try to match UNKN%d: */ - if (id < 0) { - if (sscanf(label->label, "UNKN%d", &id) != 1) { - /* if still not found, must not belong in jump-table: */ - continue; - } - } - - jmptable[id] = label->offset; - } - - write(outfd, jmptable, sizeof(jmptable)); -} - static void usage(void) { @@ -314,7 +325,6 @@ main(int argc, char **argv) } emit_instructions(outfd); - emit_jumptable(outfd); close(outfd); diff --git a/src/freedreno/afuc/asm.h b/src/freedreno/afuc/asm.h index ced8c6a2546..ece309c19ab 100644 --- a/src/freedreno/afuc/asm.h +++ b/src/freedreno/afuc/asm.h @@ -37,6 +37,7 @@ struct asm_label { struct afuc_instr *next_instr(afuc_opc opc); void decl_label(const char *str); +void decl_jumptbl(void); static inline uint32_t parse_reg(const char *str) diff --git a/src/freedreno/afuc/disasm.c b/src/freedreno/afuc/disasm.c index 3a24eeaadb0..13dd2136ae4 100644 --- a/src/freedreno/afuc/disasm.c +++ b/src/freedreno/afuc/disasm.c @@ -191,13 +191,19 @@ post_instr_cb(void *data, unsigned n, void *instr) } } +uint32_t jumptbl_offset = ~0; + /* Assume that instructions that don't match are raw data */ static void no_match(FILE *out, const BITSET_WORD *bitset, size_t size) { - fprintf(out, "[%08x]", bitset[0]); - print_gpu_reg(out, bitset[0]); - fprintf(out, "\n"); + if (jumptbl_offset != ~0 && bitset[0] == afuc_nop_literal(jumptbl_offset, gpuver)) { + fprintf(out, "[#jumptbl]\n"); + } else { + fprintf(out, "[%08x]", bitset[0]); + print_gpu_reg(out, bitset[0]); + fprintf(out, "\n"); + } } static void @@ -241,6 +247,25 @@ setup_packet_table(struct isa_decode_options *options, options->entrypoint_count = sizedwords; } +static uint32_t +find_jump_table(uint32_t *instrs, uint32_t sizedwords, + uint32_t *jmptbl, uint32_t jmptbl_size) +{ + for (unsigned i = 0; i <= sizedwords - jmptbl_size; i++) { + bool found = true; + for (unsigned j = 0; j < jmptbl_size; j++) { + if (instrs[i + j] != jmptbl[j]) { + found = false; + break; + } + } + if (found) + return i; + } + + return ~0; +} + static void disasm(struct emu *emu) { @@ -289,6 +314,9 @@ disasm(struct emu *emu) setup_packet_table(&options, emu->jmptbl, ARRAY_SIZE(emu->jmptbl)); + jumptbl_offset = find_jump_table(emu->instrs, sizedwords, emu->jmptbl, + ARRAY_SIZE(emu->jmptbl)); + /* TODO add option to emulate LPAC SQE instead: */ if (emulator) { /* Start from clean slate: */ @@ -302,7 +330,18 @@ disasm(struct emu *emu) } /* print instructions: */ - isa_disasm(emu->instrs, sizedwords * 4, stdout, &options); + isa_disasm(emu->instrs, MIN2(sizedwords, jumptbl_offset) * 4, stdout, &options); + + /* print jump table */ + if (jumptbl_offset != ~0) { + printf("jumptbl:\n"); + printf(".jumptbl\n"); + + if (jumptbl_offset + ARRAY_SIZE(emu->jmptbl) != sizedwords) { + for (unsigned i = jumptbl_offset + ARRAY_SIZE(emu->jmptbl); i < sizedwords; i++) + printf("[%08x]\n", emu->instrs[i]); + } + } if (bv_offset) { printf(";\n"); @@ -322,7 +361,19 @@ disasm(struct emu *emu) uint32_t sizedwords = lpac_offset - bv_offset; - isa_disasm(emu->instrs, sizedwords * 4, stdout, &options); + jumptbl_offset = find_jump_table(emu->instrs, sizedwords, emu->jmptbl, + ARRAY_SIZE(emu->jmptbl)); + + isa_disasm(emu->instrs, MIN2(sizedwords, jumptbl_offset) * 4, stdout, &options); + + if (jumptbl_offset != ~0) { + printf("jumptbl:\n"); + printf(".jumptbl\n"); + if (jumptbl_offset + ARRAY_SIZE(emu->jmptbl) != sizedwords) { + for (unsigned i = jumptbl_offset + ARRAY_SIZE(emu->jmptbl); i < sizedwords; i++) + printf("[%08x]\n", emu->instrs[i]); + } + } emu->instrs -= bv_offset; emu->sizedwords += bv_offset; @@ -344,7 +395,19 @@ disasm(struct emu *emu) setup_packet_table(&options, emu->jmptbl, ARRAY_SIZE(emu->jmptbl)); - isa_disasm(emu->instrs, emu->sizedwords * 4, stdout, &options); + jumptbl_offset = find_jump_table(emu->instrs, emu->sizedwords, emu->jmptbl, + ARRAY_SIZE(emu->jmptbl)); + + isa_disasm(emu->instrs, MIN2(emu->sizedwords, jumptbl_offset) * 4, stdout, &options); + + if (jumptbl_offset != ~0) { + printf("jumptbl:\n"); + printf(".jumptbl\n"); + if (jumptbl_offset + ARRAY_SIZE(emu->jmptbl) != emu->sizedwords) { + for (unsigned i = jumptbl_offset + ARRAY_SIZE(emu->jmptbl); i < emu->sizedwords; i++) + printf("[%08x]\n", emu->instrs[i]); + } + } emu->instrs -= lpac_offset; emu->sizedwords += lpac_offset; diff --git a/src/freedreno/afuc/lexer.l b/src/freedreno/afuc/lexer.l index 333e732bb79..ba227044c64 100644 --- a/src/freedreno/afuc/lexer.l +++ b/src/freedreno/afuc/lexer.l @@ -98,6 +98,8 @@ extern YYSTYPE yylval; "(sds"[1-3]")" yylval.num = yytext[4] - '0'; return T_SDS; "(peek)" return TOKEN(T_PEEK); +".jumptbl" return TOKEN(T_JUMPTBL); + "," return ','; "[" return '['; "]" return ']'; diff --git a/src/freedreno/afuc/parser.y b/src/freedreno/afuc/parser.y index d1e1ff0ac63..1f9235d7463 100644 --- a/src/freedreno/afuc/parser.y +++ b/src/freedreno/afuc/parser.y @@ -173,6 +173,8 @@ label(const char *str) %token T_XMOV %token T_SDS +%token T_JUMPTBL + %type reg %type immediate %type xmov @@ -188,10 +190,11 @@ instrs: instrs instr_or_label | instr_or_label instr_or_label: instr_r -| T_REP instr_r { instr->rep = true; } +| T_REP instr_r { instr->rep = true; } | branch_instr | other_instr -| T_LABEL_DECL { decl_label($1); } +| T_LABEL_DECL { decl_label($1); } +| T_JUMPTBL { decl_jumptbl(); } xmov: T_XMOV { $$ = $1; } | { $$ = 0; } @@ -295,6 +298,7 @@ other_instr: T_OP_CALL T_LABEL_REF { new_instr(OPC_CALL); label($2); } | T_OP_WAITIN { new_instr(OPC_WAITIN); } | T_OP_NOP { new_instr(OPC_NOP); } | T_LITERAL { new_instr(OPC_RAW_LITERAL); literal($1); } +| '[' T_LABEL_REF ']' { new_instr(OPC_RAW_LITERAL); label($2); } reg: T_REGISTER