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;