r300/compiler: Handle loops in rc_get_readers()
diff --git a/src/mesa/drivers/dri/r300/compiler/radeon_compiler_util.c b/src/mesa/drivers/dri/r300/compiler/radeon_compiler_util.c
index ae61d20..ce7b008 100644
--- a/src/mesa/drivers/dri/r300/compiler/radeon_compiler_util.c
+++ b/src/mesa/drivers/dri/r300/compiler/radeon_compiler_util.c
@@ -537,3 +537,49 @@
return 1;
}
+
+/**
+ * @return RC_OPCODE_NOOP if inst is not a flow control instruction.
+ * @return The opcode of inst if it is a flow control instruction.
+ */
+rc_opcode rc_get_flow_control_inst(struct rc_instruction * inst)
+{
+ const struct rc_opcode_info * info;
+ if (inst->Type == RC_INSTRUCTION_NORMAL) {
+ info = rc_get_opcode_info(inst->U.I.Opcode);
+ } else {
+ info = rc_get_opcode_info(inst->U.P.RGB.Opcode);
+ /*A flow control instruction shouldn't have an alpha
+ * instruction.*/
+ assert(!info->IsFlowControl ||
+ inst->U.P.Alpha.Opcode == RC_OPCODE_NOP);
+ }
+
+ if (info->IsFlowControl)
+ return info->Opcode;
+ else
+ return RC_OPCODE_NOP;
+
+}
+
+/**
+ * @return The BGNLOOP instruction that starts the loop ended by endloop.
+ */
+struct rc_instruction * rc_match_endloop(struct rc_instruction * endloop)
+{
+ unsigned int endloop_count = 0;
+ struct rc_instruction * inst;
+ for (inst = endloop->Prev; inst != endloop; inst = inst->Prev) {
+ rc_opcode op = rc_get_flow_control_inst(inst);
+ if (op == RC_OPCODE_ENDLOOP) {
+ endloop_count++;
+ } else if (op == RC_OPCODE_BGNLOOP) {
+ if (endloop_count == 0) {
+ return inst;
+ } else {
+ endloop_count--;
+ }
+ }
+ }
+ return NULL;
+}
diff --git a/src/mesa/drivers/dri/r300/compiler/radeon_compiler_util.h b/src/mesa/drivers/dri/r300/compiler/radeon_compiler_util.h
index 43ef877..0ba25aa 100644
--- a/src/mesa/drivers/dri/r300/compiler/radeon_compiler_util.h
+++ b/src/mesa/drivers/dri/r300/compiler/radeon_compiler_util.h
@@ -3,6 +3,8 @@
#ifndef RADEON_PROGRAM_UTIL_H
#define RADEON_PROGRAM_UTIL_H
+#include "radeon_opcodes.h"
+
struct radeon_compiler;
struct rc_instruction;
struct rc_pair_instruction;
@@ -71,4 +73,8 @@
unsigned int source,
unsigned int new_readmask);
+rc_opcode rc_get_flow_control_inst(struct rc_instruction * inst);
+
+struct rc_instruction * rc_match_endloop(struct rc_instruction * endloop);
+
#endif /* RADEON_PROGRAM_UTIL_H */
diff --git a/src/mesa/drivers/dri/r300/compiler/radeon_dataflow.c b/src/mesa/drivers/dri/r300/compiler/radeon_dataflow.c
index 966b7f8..b94838e 100644
--- a/src/mesa/drivers/dri/r300/compiler/radeon_dataflow.c
+++ b/src/mesa/drivers/dri/r300/compiler/radeon_dataflow.c
@@ -449,30 +449,6 @@
remap_pair_instruction(inst, cb, userdata);
}
-/**
- * @return RC_OPCODE_NOOP if inst is not a flow control instruction.
- * @return The opcode of inst if it is a flow control instruction.
- */
-static rc_opcode get_flow_control_inst(struct rc_instruction * inst)
-{
- const struct rc_opcode_info * info;
- if (inst->Type == RC_INSTRUCTION_NORMAL) {
- info = rc_get_opcode_info(inst->U.I.Opcode);
- } else {
- info = rc_get_opcode_info(inst->U.P.RGB.Opcode);
- /*A flow control instruction shouldn't have an alpha
- * instruction.*/
- assert(!info->IsFlowControl ||
- inst->U.P.Alpha.Opcode == RC_OPCODE_NOP);
- }
-
- if (info->IsFlowControl)
- return info->Opcode;
- else
- return RC_OPCODE_NOP;
-
-}
-
struct branch_write_mask {
unsigned int IfWriteMask:4;
unsigned int ElseWriteMask:4;
@@ -567,6 +543,11 @@
return shared_mask;
}
+ if (cb_data->ReaderData->LoopDepth > 0) {
+ cb_data->ReaderData->AbortOnWrite |=
+ (read_mask & cb_data->AliveWriteMask);
+ }
+
/* XXX The behavior in this case should be configurable. */
if ((read_mask & cb_data->AliveWriteMask) != read_mask) {
cb_data->ReaderData->Abort = 1;
@@ -649,12 +630,57 @@
unsigned int shared_mask = mask & d->DstMask;
d->ReaderData->AbortOnRead &= ~shared_mask;
d->AliveWriteMask &= ~shared_mask;
+ if (d->ReaderData->AbortOnWrite & shared_mask) {
+ d->ReaderData->Abort = 1;
+ }
}
if(d->WriteCB)
d->WriteCB(d->ReaderData, inst, file, index, mask);
}
+static void push_branch_mask(
+ struct get_readers_callback_data * d,
+ unsigned int * branch_depth)
+{
+ (*branch_depth)++;
+ if (*branch_depth > R500_PFS_MAX_BRANCH_DEPTH_FULL) {
+ d->ReaderData->Abort = 1;
+ return;
+ }
+ d->BranchMasks[*branch_depth].IfWriteMask =
+ d->AliveWriteMask;
+}
+
+static void pop_branch_mask(
+ struct get_readers_callback_data * d,
+ unsigned int * branch_depth)
+{
+ struct branch_write_mask * masks = &d->BranchMasks[*branch_depth];
+
+ if (masks->HasElse) {
+ /* Abort on read for components that were written in the IF
+ * block. */
+ d->ReaderData->AbortOnRead |=
+ masks->IfWriteMask & ~masks->ElseWriteMask;
+ /* Abort on read for components that were written in the ELSE
+ * block. */
+ d->ReaderData->AbortOnRead |=
+ masks->ElseWriteMask & ~d->AliveWriteMask;
+
+ d->AliveWriteMask = masks->IfWriteMask
+ ^ ((masks->IfWriteMask ^ masks->ElseWriteMask)
+ & (masks->IfWriteMask ^ d->AliveWriteMask));
+ } else {
+ d->ReaderData->AbortOnRead |=
+ masks->IfWriteMask & ~d->AliveWriteMask;
+ d->AliveWriteMask = masks->IfWriteMask;
+
+ }
+ memset(masks, 0, sizeof(struct branch_write_mask));
+ (*branch_depth)--;
+}
+
static void get_readers_for_single_write(
void * userdata,
struct rc_instruction * writer,
@@ -664,10 +690,14 @@
{
struct rc_instruction * tmp;
unsigned int branch_depth = 0;
+ struct rc_instruction * endloop = NULL;
+ unsigned int abort_on_read_at_endloop;
struct get_readers_callback_data * d = userdata;
d->ReaderData->Writer = writer;
d->ReaderData->AbortOnRead = 0;
+ d->ReaderData->AbortOnWrite = 0;
+ d->ReaderData->LoopDepth = 0;
d->ReaderData->InElse = 0;
d->DstFile = dst_file;
d->DstIndex = dst_index;
@@ -680,32 +710,43 @@
for(tmp = writer->Next; tmp != &d->C->Program.Instructions;
tmp = tmp->Next){
- rc_opcode opcode = get_flow_control_inst(tmp);
+ rc_opcode opcode = rc_get_flow_control_inst(tmp);
switch(opcode) {
case RC_OPCODE_BGNLOOP:
- /* XXX We can do better when we see a BGNLOOP if we
- * add a flag called AbortOnWrite to struct
- * rc_reader_data and leave it set until the next
- * ENDLOOP. */
+ d->ReaderData->LoopDepth++;
+ push_branch_mask(d, &branch_depth);
+ break;
case RC_OPCODE_ENDLOOP:
- /* XXX We can do better when we see an ENDLOOP by
- * searching backwards from writer and looking for
- * readers of writer's destination index. If we find a
- * reader before we get to the BGNLOOP, we must abort
- * unless there is another writer between that reader
- * and the BGNLOOP. */
- case RC_OPCODE_BRK:
- case RC_OPCODE_CONT:
- d->ReaderData->Abort = 1;
- return;
- case RC_OPCODE_IF:
- branch_depth++;
- if (branch_depth > R500_PFS_MAX_BRANCH_DEPTH_FULL) {
- d->ReaderData->Abort = 1;
- return;
+ if (d->ReaderData->LoopDepth > 0) {
+ d->ReaderData->LoopDepth--;
+ if (d->ReaderData->LoopDepth == 0) {
+ d->ReaderData->AbortOnWrite = 0;
+ }
+ pop_branch_mask(d, &branch_depth);
+ } else {
+ /* Here we have reached an ENDLOOP without
+ * seeing its BGNLOOP. These means that
+ * the writer was written inside of a loop,
+ * so it could have readers that are above it
+ * (i.e. they have a lower IP). To find these
+ * readers we jump to the BGNLOOP instruction
+ * and check each instruction until we get
+ * back to the writer.
+ */
+ endloop = tmp;
+ tmp = rc_match_endloop(tmp);
+ if (!tmp) {
+ rc_error(d->C, "Failed to match endloop.\n");
+ d->ReaderData->Abort = 1;
+ return;
+ }
+ abort_on_read_at_endloop = d->ReaderData->AbortOnRead;
+ d->ReaderData->AbortOnRead |= d->AliveWriteMask;
+ continue;
}
- d->BranchMasks[branch_depth].IfWriteMask =
- d->AliveWriteMask;
+ break;
+ case RC_OPCODE_IF:
+ push_branch_mask(d, &branch_depth);
break;
case RC_OPCODE_ELSE:
if (branch_depth == 0) {
@@ -725,35 +766,7 @@
d->ReaderData->InElse = 0;
}
else {
- struct branch_write_mask * masks =
- &d->BranchMasks[branch_depth];
-
- if (masks->HasElse) {
- /* Abort on read for components that
- * were written in the IF block. */
- d->ReaderData->AbortOnRead |=
- masks->IfWriteMask
- & ~masks->ElseWriteMask;
- /* Abort on read for components that
- * were written in the ELSE block. */
- d->ReaderData->AbortOnRead |=
- masks->ElseWriteMask
- & ~d->AliveWriteMask;
- d->AliveWriteMask = masks->IfWriteMask
- ^ ((masks->IfWriteMask ^
- masks->ElseWriteMask)
- & (masks->IfWriteMask
- ^ d->AliveWriteMask));
- } else {
- d->ReaderData->AbortOnRead |=
- masks->IfWriteMask
- & ~d->AliveWriteMask;
- d->AliveWriteMask = masks->IfWriteMask;
-
- }
- memset(masks, 0,
- sizeof(struct branch_write_mask));
- branch_depth--;
+ pop_branch_mask(d, &branch_depth);
}
break;
default:
@@ -770,6 +783,14 @@
rc_pair_for_all_reads_arg(tmp,
get_readers_pair_read_callback, d);
}
+
+ /* This can happen when we jump from an ENDLOOP to BGNLOOP */
+ if (tmp == writer) {
+ tmp = endloop;
+ endloop = NULL;
+ d->ReaderData->AbortOnRead = abort_on_read_at_endloop;
+ continue;
+ }
rc_for_all_writes_mask(tmp, get_readers_write_callback, d);
if (d->ReaderData->ExitOnAbort && d->ReaderData->Abort)
diff --git a/src/mesa/drivers/dri/r300/compiler/radeon_dataflow.h b/src/mesa/drivers/dri/r300/compiler/radeon_dataflow.h
index 6667ae1..d8a6272 100644
--- a/src/mesa/drivers/dri/r300/compiler/radeon_dataflow.h
+++ b/src/mesa/drivers/dri/r300/compiler/radeon_dataflow.h
@@ -88,6 +88,8 @@
struct rc_reader_data {
unsigned int Abort;
unsigned int AbortOnRead;
+ unsigned int AbortOnWrite;
+ unsigned int LoopDepth;
unsigned int InElse;
struct rc_instruction * Writer;