summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorConnor Abbott <cwabbott0@gmail.com>2023-12-31 13:25:59 -0500
committerMarge Bot <emma+marge@anholt.net>2024-04-10 21:51:59 +0000
commit04af4cbfea7a226793c5ac145b41d6a3bd497ba5 (patch)
tree97b57240a6b70958fb3bc4b416b3eddb8d8f48e4
parent9c0ba24c70a482daf1d02445d8f62c0bbef9cac0 (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.asm4
-rw-r--r--src/freedreno/.gitlab-ci/reference/afuc_test_a7xx.asm4
-rw-r--r--src/freedreno/.gitlab-ci/traces/afuc_test.asm4
-rw-r--r--src/freedreno/.gitlab-ci/traces/afuc_test_a7xx.asm4
-rw-r--r--src/freedreno/afuc/afuc.h11
-rw-r--r--src/freedreno/afuc/afuc.xml77
-rw-r--r--src/freedreno/afuc/asm.c3
-rw-r--r--src/freedreno/afuc/asm.h4
-rw-r--r--src/freedreno/afuc/emu.c15
-rw-r--r--src/freedreno/afuc/lexer.l4
-rw-r--r--src/freedreno/afuc/parser.y9
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); }