summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Stellard <tstellar@gmail.com>2012-02-13 21:27:28 -0500
committerTom Stellard <tstellar@gmail.com>2012-02-26 20:51:21 -0500
commit82043eb72dec18375d5ab0e49a6cc432b7413afc (patch)
treebdb32d6ffe1f5e891644345602ebcb29cb2feba0
parent2845a0be81853f3da59cf1180fd189a2236efa2b (diff)
r300/compiler: Fix bug when lowering KILP on r300 cards
KILP instruction inside IF blocks were being lowered to an unconditional KIL. Since r300 doesn't support branching, when the IF's were lowered to conditional moves, the KIL would always be executed. This is not a problem with the mesa state tracker, because the GLSL compiler handles lowering IF's, but this bug was appearing in the VDPAU state tracker, which does not use the GLSL compiler. (cherry picked from commit 342cac71669662abad3435fd13ecf28d073874c3)
-rw-r--r--src/gallium/drivers/r300/compiler/radeon_program_alu.c64
1 files changed, 54 insertions, 10 deletions
diff --git a/src/gallium/drivers/r300/compiler/radeon_program_alu.c b/src/gallium/drivers/r300/compiler/radeon_program_alu.c
index dd1dfb344d4..c48f936b82a 100644
--- a/src/gallium/drivers/r300/compiler/radeon_program_alu.c
+++ b/src/gallium/drivers/r300/compiler/radeon_program_alu.c
@@ -1165,35 +1165,79 @@ int radeonTransformDeriv(struct radeon_compiler* c,
}
/**
+ * IF Temp[0].x -> IF Temp[0].x
+ * ... -> ...
+ * KILP -> KIL -abs(Temp[0].x)
+ * ... -> ...
+ * ENDIF -> ENDIF
+ *
+ * === OR ===
+ *
* IF Temp[0].x -\
* KILP - > KIL -abs(Temp[0].x)
* ENDIF -/
*
- * This needs to be done in its own pass, because it modifies the instructions
- * before and after KILP.
+ * === OR ===
+ *
+ * IF Temp[0].x -> IF Temp[0].x
+ * ... -> ...
+ * ELSE -> ELSE
+ * ... -> ...
+ * KILP -> KIL -abs(Temp[0].x)
+ * ... -> ...
+ * ENDIF -> ENDIF
+ *
+ * === OR ===
+ *
+ * KILP -> KIL -none.1111
+ *
+ * This needs to be done in its own pass, because it might modify the
+ * instructions before and after KILP.
*/
void rc_transform_KILP(struct radeon_compiler * c, void *user)
{
struct rc_instruction * inst;
for (inst = c->Program.Instructions.Next;
inst != &c->Program.Instructions; inst = inst->Next) {
+ struct rc_instruction * if_inst;
+ unsigned in_if = 0;
if (inst->U.I.Opcode != RC_OPCODE_KILP)
continue;
+ for (if_inst = inst->Prev; if_inst != &c->Program.Instructions;
+ if_inst = if_inst->Prev) {
+
+ if (if_inst->U.I.Opcode == RC_OPCODE_IF) {
+ in_if = 1;
+ break;
+ }
+ }
+
inst->U.I.Opcode = RC_OPCODE_KIL;
- if (inst->Prev->U.I.Opcode != RC_OPCODE_IF
- || inst->Next->U.I.Opcode != RC_OPCODE_ENDIF) {
+ if (!in_if) {
inst->U.I.SrcReg[0] = negate(builtin_one);
} else {
-
+ /* This should work even if the KILP is inside the ELSE
+ * block, because -0.0 is considered negative. */
inst->U.I.SrcReg[0] =
- negate(absolute(inst->Prev->U.I.SrcReg[0]));
- /* Remove IF */
- rc_remove_instruction(inst->Prev);
- /* Remove ENDIF */
- rc_remove_instruction(inst->Next);
+ negate(absolute(if_inst->U.I.SrcReg[0]));
+
+ if (inst->Prev->U.I.Opcode != RC_OPCODE_IF
+ && inst->Next->U.I.Opcode != RC_OPCODE_ENDIF) {
+
+ /* Optimize the special case:
+ * IF Temp[0].x
+ * KILP
+ * ENDIF
+ */
+
+ /* Remove IF */
+ rc_remove_instruction(inst->Prev);
+ /* Remove ENDIF */
+ rc_remove_instruction(inst->Next);
+ }
}
}
}