diff options
author | Connor Abbott <cwabbott0@gmail.com> | 2023-12-31 13:25:59 -0500 |
---|---|---|
committer | Marge Bot <emma+marge@anholt.net> | 2024-04-10 21:51:59 +0000 |
commit | 04af4cbfea7a226793c5ac145b41d6a3bd497ba5 (patch) | |
tree | 97b57240a6b70958fb3bc4b416b3eddb8d8f48e4 | |
parent | 9c0ba24c70a482daf1d02445d8f62c0bbef9cac0 (diff) |
freedreno/afuc: Add a7xx new-style branch instructions
It turns out that what I guessed was "preemptleave" was actually "bl"
which writes a return address to $1b which I've renamed $lr. On a750
this is combined with a new indirect jump instruction to create a more
"standard" function call ABI in the AQE firmware using $lr and $1a which
is $sp. This ABI is used for what appears to be compiled functions.
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/28628>
-rw-r--r-- | src/freedreno/.gitlab-ci/reference/afuc_test.asm | 4 | ||||
-rw-r--r-- | src/freedreno/.gitlab-ci/reference/afuc_test_a7xx.asm | 4 | ||||
-rw-r--r-- | src/freedreno/.gitlab-ci/traces/afuc_test.asm | 4 | ||||
-rw-r--r-- | src/freedreno/.gitlab-ci/traces/afuc_test_a7xx.asm | 4 | ||||
-rw-r--r-- | src/freedreno/afuc/afuc.h | 11 | ||||
-rw-r--r-- | src/freedreno/afuc/afuc.xml | 77 | ||||
-rw-r--r-- | src/freedreno/afuc/asm.c | 3 | ||||
-rw-r--r-- | src/freedreno/afuc/asm.h | 4 | ||||
-rw-r--r-- | src/freedreno/afuc/emu.c | 15 | ||||
-rw-r--r-- | src/freedreno/afuc/lexer.l | 4 | ||||
-rw-r--r-- | src/freedreno/afuc/parser.y | 9 |
11 files changed, 119 insertions, 20 deletions
diff --git a/src/freedreno/.gitlab-ci/reference/afuc_test.asm b/src/freedreno/.gitlab-ci/reference/afuc_test.asm index e764a0b83c3..fc2680ee3e7 100644 --- a/src/freedreno/.gitlab-ci/reference/afuc_test.asm +++ b/src/freedreno/.gitlab-ci/reference/afuc_test.asm @@ -72,6 +72,8 @@ mov $01, $data CP_SET_SECURE_MODE: mov $02, $data setsecure $02, #l61 + +fxn59: l59: jump #l59 nop @@ -149,7 +151,7 @@ IN_PREEMPT: cread $02, [$00 + 0x101] brne $02, 0x1, #l125 nop -preemptleave #l59 +bl #fxn59 nop nop nop diff --git a/src/freedreno/.gitlab-ci/reference/afuc_test_a7xx.asm b/src/freedreno/.gitlab-ci/reference/afuc_test_a7xx.asm index c684c9375e3..ee07fae703a 100644 --- a/src/freedreno/.gitlab-ci/reference/afuc_test_a7xx.asm +++ b/src/freedreno/.gitlab-ci/reference/afuc_test_a7xx.asm @@ -92,6 +92,8 @@ mov $01, $data CP_SET_SECURE_MODE: mov $02, $data setsecure $02, #l81 + +fxn79: l79: jump #l79 nop @@ -176,7 +178,7 @@ IN_PREEMPT: cread $02, [$00 + 0x101] brne $02, 0x1, #l152 nop -preemptleave #l79 +bl #fxn79 nop nop nop diff --git a/src/freedreno/.gitlab-ci/traces/afuc_test.asm b/src/freedreno/.gitlab-ci/traces/afuc_test.asm index 295f28df010..1463c548995 100644 --- a/src/freedreno/.gitlab-ci/traces/afuc_test.asm +++ b/src/freedreno/.gitlab-ci/traces/afuc_test.asm @@ -200,11 +200,11 @@ waitin mov $01, $data UNKN15: -; test preemptleave + iret + conditional branch w/ immed +; test bl + iret + conditional branch w/ immed cread $02, [$00 + 0x101] brne $02, 0x0001, #exit_iret nop -preemptleave #err +bl #err nop nop nop diff --git a/src/freedreno/.gitlab-ci/traces/afuc_test_a7xx.asm b/src/freedreno/.gitlab-ci/traces/afuc_test_a7xx.asm index 1879e370c96..0bc968ec41f 100644 --- a/src/freedreno/.gitlab-ci/traces/afuc_test_a7xx.asm +++ b/src/freedreno/.gitlab-ci/traces/afuc_test_a7xx.asm @@ -236,11 +236,11 @@ waitin mov $01, $data IN_PREEMPT: -; test preemptleave + iret + conditional branch w/ immed +; test bl + iret + conditional branch w/ immed cread $02, [$00 + 0x101] brne $02, 0x0001, #exit_iret nop -preemptleave #err +bl #err nop nop nop diff --git a/src/freedreno/afuc/afuc.h b/src/freedreno/afuc/afuc.h index 2791c72c6f0..7a9ce50b95e 100644 --- a/src/freedreno/afuc/afuc.h +++ b/src/freedreno/afuc/afuc.h @@ -118,8 +118,11 @@ typedef enum { OPC_IRET, /* return from preemption interrupt handler */ OPC_CALL, /* "function" call */ OPC_WAITIN, /* wait for input (ie. wait for WPTR to advance) */ - OPC_PREEMPTLEAVE, /* try to leave preemption */ + OPC_BL, /* Branch and Link (same as the MIPS/ARM instruction) */ OPC_SETSECURE, /* switch secure mode on/off */ + OPC_JUMPR, /* indirect jump with a register offset */ + OPC_SRET, /* Return instruction to use with "bl" */ + OPC_JUMPA, /* Absolute jump instruction */ /* pseudo-opcodes without an actual encoding */ OPC_BREQ, @@ -134,6 +137,10 @@ typedef enum { * * Notes: (applicable to a6xx, double check a5xx) * + * 0x1a: + * $sp + * 0x1b: + * $lr: written by bl * 0x1d: * $addr: writes configure GPU reg address to read/write * (does not respect CP_PROTECT) @@ -150,6 +157,8 @@ typedef enum { * or $usraddr */ typedef enum { + REG_SP = 0x1a, + REG_LR = 0x1b, REG_REM = 0x1c, REG_MEMDATA = 0x1d, /* when used as src */ REG_ADDR = 0x1d, /* when used as dst */ diff --git a/src/freedreno/afuc/afuc.xml b/src/freedreno/afuc/afuc.xml index 14426f379e3..457492c7e3f 100644 --- a/src/freedreno/afuc/afuc.xml +++ b/src/freedreno/afuc/afuc.xml @@ -86,8 +86,8 @@ SOFTWARE. <value val="0x17" display="$17"/> <value val="0x18" display="$18"/> <value val="0x19" display="$19"/> - <value val="0x1a" display="$1a"/> - <value val="0x1b" display="$1b"/> + <value val="0x1a" display="$sp"/> + <value val="0x1b" display="$lr"/> </enum> <expr name="#reg-special"> @@ -922,6 +922,21 @@ SOFTWARE. </encode> </bitset> +<bitset name="jumpa" extends="#instruction"> + <display> + jumpa #{TARGET} + </display> + + <gen min="7"/> + + <field name="TARGET" low="0" high="25" type="absbranch"/> + <pattern low="26" high="31">111001</pattern> + + <encode> + <map name="TARGET">src->literal</map> + </encode> +</bitset> + <bitset name="waitin" extends="#instruction-no-args"> <doc> A special branch instruction that parses the next PM4 packet @@ -936,17 +951,15 @@ SOFTWARE. <pattern low="26" high="31">110110</pattern> </bitset> -<bitset name="preemptleave" extends="#instruction"> +<bitset name="bl" extends="#instruction"> <doc> - Try to leave the preempt handler without jumping back to the - instruction that was interrupted. Jumps to the given destination - if this fails. + Jump to the target and store a return address in $lr. </doc> <display> - preemptleave #{TARGET} + bl #{TARGET} </display> - <field name="TARGET" low="0" high="25" type="absbranch"/> + <field name="TARGET" low="0" high="25" type="absbranch" call="true"/> <pattern low="26" high="31">111000</pattern> <encode> @@ -990,4 +1003,52 @@ SOFTWARE. <pattern low="27" high="31">00000</pattern> </bitset> +<bitset name="#jump-a7xx" extends="#instruction"> + <gen min="7"/> + <pattern low="26" high="31">110111</pattern> +</bitset> + +<bitset name="jumpr" extends="#jump-a7xx"> + <doc> + Indirect jump instruction. Jump to the given offset. + Used as a return instruction together with bl: + + bl #fxn + ... + + fxn: + ... + jump $lr + nop + + However this is only used by hand-crafted assembly + routines that make sure to never use any stack space. Otherwise + sret is used. + </doc> + + <display> + jump {SRC1} + </display> + + <pattern low="20" high="25">110111</pattern> + <pattern low="5" high="19">000000000000000</pattern> + <field name="SRC1" low="0" high="4" type="#src"/> +</bitset> + +<bitset name="sret" extends="#jump-a7xx"> + <doc> + Seems to be equivalent to "jump $lr". This seems to be used by + the compiled code, and is always immediately followed by a + "sub $sp, $sp, imm" instruction to restore the stack frame. It's + therefore possible this also includes a stack overflow check. + </doc> + + <display> + sret + </display> + + <pattern low="20" high="25">110110</pattern> + <pattern low="0" high="19">00000000000000000000</pattern> +</bitset> + </isa> diff --git a/src/freedreno/afuc/asm.c b/src/freedreno/afuc/asm.c index a7e75d154e4..0d04ddc8381 100644 --- a/src/freedreno/afuc/asm.c +++ b/src/freedreno/afuc/asm.c @@ -228,7 +228,8 @@ emit_instructions(int outfd) break; case OPC_CALL: - case OPC_PREEMPTLEAVE: + case OPC_BL: + case OPC_JUMPA: ai->literal = resolve_label(ai->label); break; diff --git a/src/freedreno/afuc/asm.h b/src/freedreno/afuc/asm.h index 6cdc2fa289f..ecadf0d3849 100644 --- a/src/freedreno/afuc/asm.h +++ b/src/freedreno/afuc/asm.h @@ -60,6 +60,10 @@ parse_reg(const char *str) return REG_USRADDR; else if (!strcmp(str, "$data")) return 0x1f; + else if (!strcmp(str, "$sp")) + return REG_SP; + else if (!strcmp(str, "$lr")) + return REG_LR; ret = strtol(str + 1, &retstr, 16); diff --git a/src/freedreno/afuc/emu.c b/src/freedreno/afuc/emu.c index f61ded93548..5b1de881744 100644 --- a/src/freedreno/afuc/emu.c +++ b/src/freedreno/afuc/emu.c @@ -349,7 +349,20 @@ emu_instr(struct emu *emu, struct afuc_instr *instr) emu->waitin = true; break; } - /* OPC_PREEMPTLEAVE6 */ + case OPC_BL: { + emu_set_gpr_reg(emu, REG_LR, emu->gpr_regs.pc + 2); + emu->branch_target = instr->literal; + break; + } + case OPC_JUMPR: { + emu->branch_target = emu_get_gpr_reg(emu, instr->src1); + break; + } + case OPC_SRET: { + emu->branch_target = emu_get_gpr_reg(emu, REG_LR); + /* TODO: read $sp and check for stack overflow? */ + break; + } case OPC_SETSECURE: { // TODO this acts like a conditional branch, but in which case // does it branch? diff --git a/src/freedreno/afuc/lexer.l b/src/freedreno/afuc/lexer.l index 25be4590f95..4e77d2556d7 100644 --- a/src/freedreno/afuc/lexer.l +++ b/src/freedreno/afuc/lexer.l @@ -88,8 +88,10 @@ extern YYSTYPE yylval; "iret" return TOKEN(T_OP_IRET); "call" return TOKEN(T_OP_CALL); "jump" return TOKEN(T_OP_JUMP); +"jumpa" return TOKEN(T_OP_JUMPA); +"sret" return TOKEN(T_OP_SRET); "waitin" return TOKEN(T_OP_WAITIN); -"preemptleave" return TOKEN(T_OP_PREEMPTLEAVE); +"bl" return TOKEN(T_OP_BL); "setsecure" return TOKEN(T_OP_SETSECURE); "<<" return TOKEN(T_LSHIFT); "(rep)" return TOKEN(T_REP); diff --git a/src/freedreno/afuc/parser.y b/src/freedreno/afuc/parser.y index 27894f83789..aeba1ed8a22 100644 --- a/src/freedreno/afuc/parser.y +++ b/src/freedreno/afuc/parser.y @@ -165,8 +165,10 @@ label(const char *str) %token <tok> T_OP_IRET %token <tok> T_OP_CALL %token <tok> T_OP_JUMP +%token <tok> T_OP_JUMPA +%token <tok> T_OP_SRET %token <tok> T_OP_WAITIN -%token <tok> T_OP_PREEMPTLEAVE +%token <tok> T_OP_BL %token <tok> T_OP_SETSECURE %token <tok> T_LSHIFT %token <tok> T_REP @@ -295,11 +297,14 @@ branch_instr: branch_op reg ',' T_BIT ',' T_LABEL_REF { src1($2); bit($ | branch_op reg ',' immediate ',' T_LABEL_REF { src1($2); immed($4); label($6); } other_instr: T_OP_CALL T_LABEL_REF { new_instr(OPC_CALL); label($2); } -| T_OP_PREEMPTLEAVE T_LABEL_REF { new_instr(OPC_PREEMPTLEAVE); label($2); } +| T_OP_BL T_LABEL_REF { new_instr(OPC_BL); label($2); } | T_OP_SETSECURE reg ',' T_LABEL_REF { new_instr(OPC_SETSECURE); src1($2); label($4); } | T_OP_RET { new_instr(OPC_RET); } | T_OP_IRET { new_instr(OPC_IRET); } | T_OP_JUMP T_LABEL_REF { new_instr(OPC_JUMP); label($2); } +| T_OP_JUMPA T_LABEL_REF { new_instr(OPC_JUMPA); label($2); } +| T_OP_JUMP reg { new_instr(OPC_JUMPR); src1($2); } +| T_OP_SRET { new_instr(OPC_SRET); } | T_OP_WAITIN { new_instr(OPC_WAITIN); } | T_OP_NOP { new_instr(OPC_NOP); } | T_LITERAL { new_instr(OPC_RAW_LITERAL); literal($1); } |