/*
* Mesa 3-D graphics library
- * Version: 7.5
*
* Copyright (C) 2009 VMware, Inc. All Rights Reserved.
*
case OPCODE_MAD:
case OPCODE_MUL:
case OPCODE_SUB:
+ case OPCODE_CMP:
+ case OPCODE_FLR:
+ case OPCODE_FRC:
+ case OPCODE_LRP:
+ case OPCODE_SEQ:
+ case OPCODE_SGE:
+ case OPCODE_SGT:
+ case OPCODE_SLE:
+ case OPCODE_SLT:
+ case OPCODE_SNE:
+ case OPCODE_SSG:
channel_mask = inst->DstReg.WriteMask & dst_mask;
break;
case OPCODE_RCP:
/*_mesa_print_program(prog);*/
}
- removeInst = (GLboolean *)
- calloc(1, prog->NumInstructions * sizeof(GLboolean));
+ removeInst =
+ calloc(prog->NumInstructions, sizeof(GLboolean));
/* Determine which temps are read and written */
for (i = 0; i < prog->NumInstructions; i++) {
switch (inst->Opcode) {
case OPCODE_BGNLOOP:
case OPCODE_BGNSUB:
- case OPCODE_BRA:
case OPCODE_CAL:
case OPCODE_CONT:
case OPCODE_IF:
switch (opcode) {
case OPCODE_BGNLOOP:
case OPCODE_BGNSUB:
- case OPCODE_BRA:
case OPCODE_CAL:
case OPCODE_CONT:
case OPCODE_IF:
mov->SrcReg[0].HasIndex2 == 0 &&
mov->SrcReg[0].RelAddr2 == 0 &&
mov->DstReg.RelAddr == 0 &&
- mov->DstReg.CondMask == COND_TR &&
- mov->SaturateMode == SATURATE_OFF;
+ mov->DstReg.CondMask == COND_TR;
}
{
return
can_downward_mov_be_modifed(mov) &&
- mov->DstReg.File == PROGRAM_TEMPORARY;
+ mov->DstReg.File == PROGRAM_TEMPORARY &&
+ mov->SaturateMode == SATURATE_OFF;
}
GLboolean *removeInst;
GLuint i, arg, rem = 0;
- removeInst = (GLboolean *)
- calloc(1, prog->NumInstructions * sizeof(GLboolean));
+ removeInst =
+ calloc(prog->NumInstructions, sizeof(GLboolean));
for (i = 0; i < prog->NumInstructions; i++) {
const struct prog_instruction *inst = prog->Instructions + i;
if (mask != (inst->DstReg.WriteMask & mask))
return GL_FALSE;
+ inst->SaturateMode |= mov->SaturateMode;
+
/* Depending on the instruction, we may need to recompute the swizzles.
* Also, some other instructions (like TEX) are not linear. We will only
* consider completely active sources and destinations
_mesa_print_program(prog);
}
- removeInst = (GLboolean *)
- calloc(1, prog->NumInstructions * sizeof(GLboolean));
+ removeInst =
+ calloc(prog->NumInstructions, sizeof(GLboolean));
/*
* Look for sequences such as this:
if (prevInst->DstReg.File == PROGRAM_TEMPORARY &&
prevInst->DstReg.Index == id &&
prevInst->DstReg.RelAddr == 0 &&
- prevInst->DstReg.CondSrc == 0 &&
prevInst->DstReg.CondMask == COND_TR) {
const GLuint dst_mask = prevInst->DstReg.WriteMask;
GLuint index, GLuint ic)
{
int i;
+ GLuint begin = ic;
+ GLuint end = ic;
/* If the register is used in a loop, extend its lifetime through the end
* of the outermost loop that doesn't contain its definition.
*/
for (i = 0; i < loopStackDepth; i++) {
if (intBegin[index] < loopStack[i].Start) {
- ic = loopStack[i].End;
+ end = loopStack[i].End;
break;
}
}
+ /* Variables that are live at the end of a loop will also be live at the
+ * beginning, so an instruction inside of a loop should have its live
+ * interval begin at the start of the outermost loop.
+ */
+ if (loopStackDepth > 0 && ic > loopStack[0].Start && ic < loopStack[0].End) {
+ begin = loopStack[0].Start;
+ }
+
ASSERT(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS);
if (intBegin[index] == -1) {
ASSERT(intEnd[index] == -1);
- intBegin[index] = intEnd[index] = ic;
+ intBegin[index] = begin;
+ intEnd[index] = end;
}
else {
- intEnd[index] = ic;
+ intEnd[index] = end;
}
}
}
#endif
+/**
+ * This pass replaces CMP T0, T1 T2 T0 with MOV T0, T2 when the CMP
+ * instruction is the first instruction to write to register T0. The are
+ * several lowering passes done in GLSL IR (e.g. branches and
+ * relative addressing) that create a large number of conditional assignments
+ * that ir_to_mesa converts to CMP instructions like the one mentioned above.
+ *
+ * Here is why this conversion is safe:
+ * CMP T0, T1 T2 T0 can be expanded to:
+ * if (T1 < 0.0)
+ * MOV T0, T2;
+ * else
+ * MOV T0, T0;
+ *
+ * If (T1 < 0.0) evaluates to true then our replacement MOV T0, T2 is the same
+ * as the original program. If (T1 < 0.0) evaluates to false, executing
+ * MOV T0, T0 will store a garbage value in T0 since T0 is uninitialized.
+ * Therefore, it doesn't matter that we are replacing MOV T0, T0 with MOV T0, T2
+ * because any instruction that was going to read from T0 after this was going
+ * to read a garbage value anyway.
+ */
+static void
+_mesa_simplify_cmp(struct gl_program * program)
+{
+ GLuint tempWrites[REG_ALLOCATE_MAX_PROGRAM_TEMPS];
+ GLuint outputWrites[MAX_PROGRAM_OUTPUTS];
+ GLuint i;
+
+ if (dbg) {
+ printf("Optimize: Begin reads without writes\n");
+ _mesa_print_program(program);
+ }
+
+ for (i = 0; i < REG_ALLOCATE_MAX_PROGRAM_TEMPS; i++) {
+ tempWrites[i] = 0;
+ }
+
+ for (i = 0; i < MAX_PROGRAM_OUTPUTS; i++) {
+ outputWrites[i] = 0;
+ }
+
+ for (i = 0; i < program->NumInstructions; i++) {
+ struct prog_instruction *inst = program->Instructions + i;
+ GLuint prevWriteMask;
+
+ /* Give up if we encounter relative addressing or flow control. */
+ if (_mesa_is_flow_control_opcode(inst->Opcode) || inst->DstReg.RelAddr) {
+ return;
+ }
+
+ if (inst->DstReg.File == PROGRAM_OUTPUT) {
+ assert(inst->DstReg.Index < MAX_PROGRAM_OUTPUTS);
+ prevWriteMask = outputWrites[inst->DstReg.Index];
+ outputWrites[inst->DstReg.Index] |= inst->DstReg.WriteMask;
+ } else if (inst->DstReg.File == PROGRAM_TEMPORARY) {
+ assert(inst->DstReg.Index < REG_ALLOCATE_MAX_PROGRAM_TEMPS);
+ prevWriteMask = tempWrites[inst->DstReg.Index];
+ tempWrites[inst->DstReg.Index] |= inst->DstReg.WriteMask;
+ } else {
+ /* No other register type can be a destination register. */
+ continue;
+ }
+
+ /* For a CMP to be considered a conditional write, the destination
+ * register and source register two must be the same. */
+ if (inst->Opcode == OPCODE_CMP
+ && !(inst->DstReg.WriteMask & prevWriteMask)
+ && inst->SrcReg[2].File == inst->DstReg.File
+ && inst->SrcReg[2].Index == inst->DstReg.Index
+ && inst->DstReg.WriteMask == get_src_arg_mask(inst, 2, NO_MASK)) {
+
+ inst->Opcode = OPCODE_MOV;
+ inst->SrcReg[0] = inst->SrcReg[1];
+
+ /* Unused operands are expected to have the file set to
+ * PROGRAM_UNDEFINED. This is how _mesa_init_instructions initializes
+ * all of the sources.
+ */
+ inst->SrcReg[1].File = PROGRAM_UNDEFINED;
+ inst->SrcReg[1].Swizzle = SWIZZLE_NOOP;
+ inst->SrcReg[2].File = PROGRAM_UNDEFINED;
+ inst->SrcReg[2].Swizzle = SWIZZLE_NOOP;
+ }
+ }
+ if (dbg) {
+ printf("Optimize: End reads without writes\n");
+ _mesa_print_program(program);
+ }
+}
/**
* Apply optimizations to the given program to eliminate unnecessary
{
GLboolean any_change;
+ _mesa_simplify_cmp(program);
/* Stop when no modifications were output */
do {
any_change = GL_FALSE;
any_change = GL_TRUE;
if (_mesa_remove_dead_code_local(program))
any_change = GL_TRUE;
+
+ any_change = _mesa_constant_fold(program) || any_change;
_mesa_reallocate_registers(program);
} while (any_change);
}