r300/compiler: Use ALU Result for IF conditionals

This saves one instruction per IF.
This commit is contained in:
Tom Stellard
2011-05-14 21:47:26 -07:00
parent 8a4136f624
commit 6d539579ad
6 changed files with 211 additions and 19 deletions
@@ -109,8 +109,12 @@ void r3xx_compile_fragment_program(struct r300_fragment_program_compiler* c)
{ 0, 0 }
};
struct radeon_program_transformation native_rewrite_r500[] = {
struct radeon_program_transformation rewrite_if[] = {
{ &r500_transform_IF, 0 },
{0, 0}
};
struct radeon_program_transformation native_rewrite_r500[] = {
{ &radeonTransformALU, 0 },
{ &radeonTransformDeriv, 0 },
{ &radeonTransformTrigScale, 0 },
@@ -135,6 +139,7 @@ void r3xx_compile_fragment_program(struct r300_fragment_program_compiler* c)
{"emulate branches", 1, !is_r500, rc_emulate_branches, NULL},
{"saturate output writes", 1, sat_out, rc_local_transform, saturate_output},
{"transform TEX", 1, 1, rc_local_transform, rewrite_tex},
{"transform IF", 1, is_r500, rc_local_transform, rewrite_if},
{"native rewrite", 1, is_r500, rc_local_transform, native_rewrite_r500},
{"native rewrite", 1, !is_r500, rc_local_transform, native_rewrite_r300},
{"deadcode", 1, opt, rc_dataflow_deadcode, dataflow_outputs_mark_use},
@@ -30,6 +30,8 @@
#include <stdio.h>
#include "radeon_compiler_util.h"
#include "radeon_list.h"
#include "radeon_variable.h"
#include "../r300_reg.h"
/**
@@ -37,27 +39,143 @@
*/
int r500_transform_IF(
struct radeon_compiler * c,
struct rc_instruction * inst,
void* data)
struct rc_instruction * inst_if,
void *data)
{
struct rc_instruction * inst_mov;
struct rc_variable * writer;
struct rc_list * writer_list, * list_ptr;
struct rc_list * var_list = rc_get_variables(c);
unsigned int generic_if = 0;
unsigned int alu_chan;
if (inst->U.I.Opcode != RC_OPCODE_IF)
if (inst_if->U.I.Opcode != RC_OPCODE_IF) {
return 0;
}
inst_mov = rc_insert_new_instruction(c, inst->Prev);
inst_mov->U.I.Opcode = RC_OPCODE_MOV;
inst_mov->U.I.DstReg.WriteMask = 0;
inst_mov->U.I.WriteALUResult = RC_ALURESULT_W;
inst_mov->U.I.ALUResultCompare = RC_COMPARE_FUNC_NOTEQUAL;
inst_mov->U.I.SrcReg[0] = inst->U.I.SrcReg[0];
inst_mov->U.I.SrcReg[0].Swizzle = combine_swizzles4(inst_mov->U.I.SrcReg[0].Swizzle,
RC_SWIZZLE_UNUSED, RC_SWIZZLE_UNUSED, RC_SWIZZLE_UNUSED, RC_SWIZZLE_X);
writer_list = rc_variable_list_get_writers(
var_list, inst_if->Type, &inst_if->U.I.SrcReg[0]);
if (!writer_list) {
generic_if = 1;
} else {
inst->U.I.SrcReg[0].File = RC_FILE_SPECIAL;
inst->U.I.SrcReg[0].Index = RC_SPECIAL_ALU_RESULT;
inst->U.I.SrcReg[0].Swizzle = RC_SWIZZLE_XYZW;
inst->U.I.SrcReg[0].Negate = 0;
/* Make sure it is safe for the writers to write to
* ALU Result */
for (list_ptr = writer_list; list_ptr;
list_ptr = list_ptr->Next) {
struct rc_instruction * inst;
writer = list_ptr->Item;
/* We are going to modify the destination register
* of writer, so if it has a reader other than
* inst_if (aka ReaderCount > 1) we must fall back to
* our generic IF.
* If the writer has a lower IP than inst_if, this
* means that inst_if is above the writer in a loop.
* I'm not sure why this would ever happen, but
* if it does we want to make sure we fall back
* to our generic IF. */
if (writer->ReaderCount > 1 || writer->Inst->IP < inst_if->IP) {
generic_if = 1;
break;
}
/* The ALU Result is not preserved across IF
* instructions, so if there is another IF
* instruction between writer and inst_if, then
* we need to fall back to generic IF. */
for (inst = writer->Inst; inst != inst_if; inst = inst->Next) {
const struct rc_opcode_info * info =
rc_get_opcode_info(inst->U.I.Opcode);
if (info->IsFlowControl) {
generic_if = 1;
break;
}
}
if (generic_if) {
break;
}
}
}
if (GET_SWZ(inst_if->U.I.SrcReg[0].Swizzle, 0) == RC_SWIZZLE_X) {
alu_chan = RC_ALURESULT_X;
} else {
alu_chan = RC_ALURESULT_W;
}
if (generic_if) {
struct rc_instruction * inst_mov =
rc_insert_new_instruction(c, inst_if->Prev);
inst_mov->U.I.Opcode = RC_OPCODE_MOV;
inst_mov->U.I.DstReg.WriteMask = 0;
inst_mov->U.I.DstReg.File = RC_FILE_NONE;
inst_mov->U.I.ALUResultCompare = RC_COMPARE_FUNC_NOTEQUAL;
inst_mov->U.I.WriteALUResult = alu_chan;
inst_mov->U.I.SrcReg[0] = inst_if->U.I.SrcReg[0];
if (alu_chan == RC_ALURESULT_X) {
inst_mov->U.I.SrcReg[0].Swizzle = combine_swizzles4(
inst_mov->U.I.SrcReg[0].Swizzle,
RC_SWIZZLE_X, RC_SWIZZLE_UNUSED,
RC_SWIZZLE_UNUSED, RC_SWIZZLE_UNUSED);
} else {
inst_mov->U.I.SrcReg[0].Swizzle = combine_swizzles4(
inst_mov->U.I.SrcReg[0].Swizzle,
RC_SWIZZLE_UNUSED, RC_SWIZZLE_UNUSED,
RC_SWIZZLE_UNUSED, RC_SWIZZLE_Z);
}
} else {
rc_compare_func compare_func = RC_COMPARE_FUNC_NEVER;
unsigned int reverse_srcs = 0;
unsigned int preserve_opcode = 0;
for (list_ptr = writer_list; list_ptr;
list_ptr = list_ptr->Next) {
writer = list_ptr->Item;
switch(writer->Inst->U.I.Opcode) {
case RC_OPCODE_SEQ:
compare_func = RC_COMPARE_FUNC_EQUAL;
break;
case RC_OPCODE_SNE:
compare_func = RC_COMPARE_FUNC_NOTEQUAL;
break;
case RC_OPCODE_SLE:
reverse_srcs = 1;
/* Fall through */
case RC_OPCODE_SGE:
compare_func = RC_COMPARE_FUNC_GEQUAL;
break;
case RC_OPCODE_SGT:
reverse_srcs = 1;
/* Fall through */
case RC_OPCODE_SLT:
compare_func = RC_COMPARE_FUNC_LESS;
break;
default:
compare_func = RC_COMPARE_FUNC_NOTEQUAL;
preserve_opcode = 1;
break;
}
if (!preserve_opcode) {
writer->Inst->U.I.Opcode = RC_OPCODE_SUB;
}
writer->Inst->U.I.DstReg.WriteMask = 0;
writer->Inst->U.I.DstReg.File = RC_FILE_NONE;
writer->Inst->U.I.WriteALUResult = alu_chan;
writer->Inst->U.I.ALUResultCompare = compare_func;
if (reverse_srcs) {
struct rc_src_register temp_src;
temp_src = writer->Inst->U.I.SrcReg[0];
writer->Inst->U.I.SrcReg[0] =
writer->Inst->U.I.SrcReg[1];
writer->Inst->U.I.SrcReg[1] = temp_src;
}
}
}
inst_if->U.I.SrcReg[0].File = RC_FILE_SPECIAL;
inst_if->U.I.SrcReg[0].Index = RC_SPECIAL_ALU_RESULT;
inst_if->U.I.SrcReg[0].Swizzle = RC_MAKE_SWIZZLE(
RC_SWIZZLE_X, RC_SWIZZLE_UNUSED,
RC_SWIZZLE_UNUSED, RC_SWIZZLE_UNUSED);
inst_if->U.I.SrcReg[0].Negate = 0;
return 1;
}
@@ -44,7 +44,7 @@ extern struct rc_swizzle_caps r500_swizzle_caps;
extern int r500_transform_IF(
struct radeon_compiler * c,
struct rc_instruction * inst,
struct rc_instruction * inst_if,
void* data);
#endif
@@ -517,8 +517,11 @@ static int is_presub_candidate(
assert(inst->U.I.Opcode == RC_OPCODE_ADD);
if (inst->U.I.PreSub.Opcode != RC_PRESUB_NONE || inst->U.I.SaturateMode)
if (inst->U.I.PreSub.Opcode != RC_PRESUB_NONE
|| inst->U.I.SaturateMode
|| inst->U.I.WriteALUResult) {
return 0;
}
/* If both sources use a constant swizzle, then we can't convert it to
* a presubtract operation. In fact for the ADD and SUB presubtract
@@ -469,6 +469,67 @@ struct rc_list * rc_variable_readers_union(struct rc_variable * var)
return list;
}
static unsigned int reader_equals_src(
struct rc_reader reader,
unsigned int src_type,
void * src)
{
if (reader.Inst->Type != src_type) {
return 0;
}
if (src_type == RC_INSTRUCTION_NORMAL) {
return reader.U.I.Src == src;
} else {
return reader.U.P.Src == src;
}
}
static unsigned int variable_writes_src(
struct rc_variable * var,
unsigned int src_type,
void * src)
{
unsigned int i;
for (i = 0; i < var->ReaderCount; i++) {
if (reader_equals_src(var->Readers[i], src_type, src)) {
return 1;
}
}
return 0;
}
struct rc_list * rc_variable_list_get_writers(
struct rc_list * var_list,
unsigned int src_type,
void * src)
{
struct rc_list * list_ptr;
struct rc_list * writer_list = NULL;
for (list_ptr = var_list; list_ptr; list_ptr = list_ptr->Next) {
struct rc_variable * var = list_ptr->Item;
if (variable_writes_src(var, src_type, src)) {
struct rc_variable * friend;
rc_list_add(&writer_list, rc_list(&var->C->Pool, var));
for (friend = var->Friend; friend;
friend = friend->Friend) {
if (variable_writes_src(friend, src_type, src)) {
rc_list_add(&writer_list,
rc_list(&var->C->Pool, friend));
}
}
/* Once we have indentifed the variable and its
* friends that write this source, we can stop
* stop searching, because we know know of the
* other variables in the list will write this source.
* If they did they would be friends of var.
*/
break;
}
}
return writer_list;
}
void rc_variable_print(struct rc_variable * var)
{
unsigned int i;
@@ -79,6 +79,11 @@ unsigned int rc_variable_writemask_sum(struct rc_variable * var);
struct rc_list * rc_variable_readers_union(struct rc_variable * var);
struct rc_list * rc_variable_list_get_writers(
struct rc_list * var_list,
unsigned int src_type,
void * src);
void rc_variable_print(struct rc_variable * var);
#endif /* RADEON_VARIABLE_H */