r300: optimize single write scenarios in rc_copy_output

Right now we always write to a temp and than emit movs to both
POS and WPOS.

When the result is trivial, like:

MOV temp[0], in[0]
MOV output[0], temp[0]
MOV output[1], temp[0]

the dataflow analysis passes can take care of it, this is however
one of the last optimizations we need the pass for. Additionally
it fails even for some still trivial cases like:

 MAD temp[2], const[3], temp[0].wwww, temp[1];
 MOV output[0], temp[2];
 MOV output[2], temp[2];

This patch will just duplicate the output instuction when there
is only a single output write.

The long-term plan would be to just trust NIR, do as little in
the backend as possible and optimize the remaining backend passes
to not need any additional cleanups.

total instructions in shared programs: 105862 -> 105427 (-0.41%)
instructions in affected programs: 20527 -> 20092 (-2.12%)
total temps in shared programs: 14029 -> 13991 (-0.27%)
temps in affected programs: 152 -> 114 (-25.00%)

Signed-off-by: Pavel Ondračka <pavel.ondracka@gmail.com>
Reviewed-by: Filip Gawin <filip.gawin@zoho.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/15321>
This commit is contained in:
Pavel Ondračka
2022-03-09 22:41:23 +01:00
committed by Marge Bot
parent 5dcef1e7b8
commit d9449c5201
@@ -144,9 +144,11 @@ void rc_copy_output(struct radeon_compiler * c, unsigned output, unsigned dup_ou
unsigned tempreg = rc_find_free_temporary(c);
struct rc_instruction * inst;
struct rc_instruction * insert_pos = c->Program.Instructions.Prev;
struct rc_instruction * last_write_inst = NULL;
unsigned branch_depth = 0;
unsigned loop_depth = 0;
bool emit_after_control_flow = false;
unsigned num_writes = 0;
for(inst = c->Program.Instructions.Next; inst != &c->Program.Instructions; inst = inst->Next) {
const struct rc_opcode_info * opcode = rc_get_opcode_info(inst->U.I.Opcode);
@@ -164,32 +166,50 @@ void rc_copy_output(struct radeon_compiler * c, unsigned output, unsigned dup_ou
if (opcode->HasDstReg) {
if (inst->U.I.DstReg.File == RC_FILE_OUTPUT && inst->U.I.DstReg.Index == output) {
num_writes++;
inst->U.I.DstReg.File = RC_FILE_TEMPORARY;
inst->U.I.DstReg.Index = tempreg;
insert_pos = inst;
last_write_inst = inst;
if (loop_depth != 0 && branch_depth != 0)
emit_after_control_flow = true;
}
}
}
inst = rc_insert_new_instruction(c, insert_pos);
inst->U.I.Opcode = RC_OPCODE_MOV;
inst->U.I.DstReg.File = RC_FILE_OUTPUT;
inst->U.I.DstReg.Index = output;
/* If there is only a single write, just duplicate the whole instruction instead.
* We can do this even when the single write was is a control flow.
*/
if (num_writes == 1) {
last_write_inst->U.I.DstReg.File = RC_FILE_OUTPUT;
last_write_inst->U.I.DstReg.Index = output;
inst->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;
inst->U.I.SrcReg[0].Index = tempreg;
inst->U.I.SrcReg[0].Swizzle = RC_SWIZZLE_XYZW;
inst = rc_insert_new_instruction(c, last_write_inst);
struct rc_instruction * prev = inst->Prev;
struct rc_instruction * next = inst->Next;
memcpy(inst, last_write_inst, sizeof(struct rc_instruction));
inst->Prev = prev;
inst->Next = next;
inst->U.I.DstReg.Index = dup_output;
} else {
inst = rc_insert_new_instruction(c, insert_pos);
inst->U.I.Opcode = RC_OPCODE_MOV;
inst->U.I.DstReg.File = RC_FILE_OUTPUT;
inst->U.I.DstReg.Index = output;
inst = rc_insert_new_instruction(c, inst);
inst->U.I.Opcode = RC_OPCODE_MOV;
inst->U.I.DstReg.File = RC_FILE_OUTPUT;
inst->U.I.DstReg.Index = dup_output;
inst->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;
inst->U.I.SrcReg[0].Index = tempreg;
inst->U.I.SrcReg[0].Swizzle = RC_SWIZZLE_XYZW;
inst->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;
inst->U.I.SrcReg[0].Index = tempreg;
inst->U.I.SrcReg[0].Swizzle = RC_SWIZZLE_XYZW;
inst = rc_insert_new_instruction(c, inst);
inst->U.I.Opcode = RC_OPCODE_MOV;
inst->U.I.DstReg.File = RC_FILE_OUTPUT;
inst->U.I.DstReg.Index = dup_output;
inst->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;
inst->U.I.SrcReg[0].Index = tempreg;
inst->U.I.SrcReg[0].Swizzle = RC_SWIZZLE_XYZW;
}
c->Program.OutputsWritten |= 1U << dup_output;
}