replace IFLOOR with util_ifloor
[mesa.git] / src / mesa / program / prog_optimize.c
index 96971f2eda48fe9f6112c11a4b0c6fda2135684c..7efdcadc1220edd84a9b91ad2a5348883281f11d 100644 (file)
@@ -1,6 +1,5 @@
 /*
  * Mesa 3-D graphics library
- * Version:  7.5
  *
  * Copyright (C) 2009  VMware, Inc.  All Rights Reserved.
  *
@@ -58,44 +57,46 @@ get_src_arg_mask(const struct prog_instruction *inst,
    GLuint read_mask, channel_mask;
    GLuint comp;
 
-   ASSERT(arg < _mesa_num_inst_src_regs(inst->Opcode));
+   assert(arg < _mesa_num_inst_src_regs(inst->Opcode));
 
    /* Form the dst register, find the written channels */
-   if (inst->CondUpdate) {
+   switch (inst->Opcode) {
+   case OPCODE_MOV:
+   case OPCODE_MIN:
+   case OPCODE_MAX:
+   case OPCODE_ABS:
+   case OPCODE_ADD:
+   case OPCODE_MAD:
+   case OPCODE_MUL:
+   case OPCODE_SUB:
+   case OPCODE_CMP:
+   case OPCODE_FLR:
+   case OPCODE_FRC:
+   case OPCODE_LRP:
+   case OPCODE_SGE:
+   case OPCODE_SLT:
+   case OPCODE_SSG:
+      channel_mask = inst->DstReg.WriteMask & dst_mask;
+      break;
+   case OPCODE_RCP:
+   case OPCODE_SIN:
+   case OPCODE_COS:
+   case OPCODE_RSQ:
+   case OPCODE_POW:
+   case OPCODE_EX2:
+   case OPCODE_LOG:
+      channel_mask = WRITEMASK_X;
+      break;
+   case OPCODE_DP2:
+      channel_mask = WRITEMASK_XY;
+      break;
+   case OPCODE_DP3:
+   case OPCODE_XPD:
+      channel_mask = WRITEMASK_XYZ;
+      break;
+   default:
       channel_mask = WRITEMASK_XYZW;
-   }
-   else {
-      switch (inst->Opcode) {
-      case OPCODE_MOV:
-      case OPCODE_MIN:
-      case OPCODE_MAX:
-      case OPCODE_ABS:
-      case OPCODE_ADD:
-      case OPCODE_MAD:
-      case OPCODE_MUL:
-      case OPCODE_SUB:
-         channel_mask = inst->DstReg.WriteMask & dst_mask;
-         break;
-      case OPCODE_RCP:
-      case OPCODE_SIN:
-      case OPCODE_COS:
-      case OPCODE_RSQ:
-      case OPCODE_POW:
-      case OPCODE_EX2:
-      case OPCODE_LOG:
-         channel_mask = WRITEMASK_X;
-         break;
-      case OPCODE_DP2:
-         channel_mask = WRITEMASK_XY;
-         break;
-      case OPCODE_DP3:
-      case OPCODE_XPD:
-         channel_mask = WRITEMASK_XYZ;
-         break;
-      default:
-         channel_mask = WRITEMASK_XYZW;
-         break;
-      }
+      break;
    }
 
    /* Now, given the src swizzle and the written channels, find which
@@ -104,7 +105,6 @@ get_src_arg_mask(const struct prog_instruction *inst,
    read_mask = 0x0;
    for (comp = 0; comp < 4; ++comp) {
       const GLuint coord = GET_SWZ(inst->SrcReg[arg].Swizzle, comp);
-      ASSERT(coord < 4);
       if (channel_mask & (1 << comp) && coord <= SWIZZLE_W)
          read_mask |= 1 << coord;
    }
@@ -124,7 +124,7 @@ get_dst_mask_for_mov(const struct prog_instruction *mov, GLuint src_mask)
    GLuint comp;
    GLuint updated_mask = 0x0;
 
-   ASSERT(mov->Opcode == OPCODE_MOV);
+   assert(mov->Opcode == OPCODE_MOV);
 
    for (comp = 0; comp < 4; ++comp) {
       GLuint src_comp;
@@ -159,13 +159,14 @@ is_swizzle_regular(GLuint swz)
  * \return number of instructions removed
  */
 static GLuint
-remove_instructions(struct gl_program *prog, const GLboolean *removeFlags)
+remove_instructions(struct gl_program *prog, const GLboolean *removeFlags,
+                    void *mem_ctx)
 {
    GLint i, removeEnd = 0, removeCount = 0;
    GLuint totalRemoved = 0;
 
    /* go backward */
-   for (i = prog->NumInstructions - 1; i >= 0; i--) {
+   for (i = prog->arb.NumInstructions - 1; i >= 0; i--) {
       if (removeFlags[i]) {
          totalRemoved++;
          if (removeCount == 0) {
@@ -184,7 +185,7 @@ remove_instructions(struct gl_program *prog, const GLboolean *removeFlags)
           */
          if (removeCount > 0) {
             GLint removeStart = removeEnd - removeCount + 1;
-            _mesa_delete_instructions(prog, removeStart, removeCount);
+            _mesa_delete_instructions(prog, removeStart, removeCount, mem_ctx);
             removeStart = removeCount = 0; /* reset removal info */
          }
       }
@@ -192,7 +193,7 @@ remove_instructions(struct gl_program *prog, const GLboolean *removeFlags)
    /* Finish removing if the first instruction was to be removed. */
    if (removeCount > 0) {
       GLint removeStart = removeEnd - removeCount + 1;
-      _mesa_delete_instructions(prog, removeStart, removeCount);
+      _mesa_delete_instructions(prog, removeStart, removeCount, mem_ctx);
    }
    return totalRemoved;
 }
@@ -209,20 +210,20 @@ replace_regs(struct gl_program *prog, gl_register_file file, const GLint map[])
 {
    GLuint i;
 
-   for (i = 0; i < prog->NumInstructions; i++) {
-      struct prog_instruction *inst = prog->Instructions + i;
+   for (i = 0; i < prog->arb.NumInstructions; i++) {
+      struct prog_instruction *inst = prog->arb.Instructions + i;
       const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
       GLuint j;
       for (j = 0; j < numSrc; j++) {
          if (inst->SrcReg[j].File == file) {
             GLuint index = inst->SrcReg[j].Index;
-            ASSERT(map[index] >= 0);
+            assert(map[index] >= 0);
             inst->SrcReg[j].Index = map[index];
          }
       }
       if (inst->DstReg.File == file) {
          const GLuint index = inst->DstReg.Index;
-         ASSERT(map[index] >= 0);
+         assert(map[index] >= 0);
          inst->DstReg.Index = map[index];
       }
    }
@@ -236,7 +237,7 @@ replace_regs(struct gl_program *prog, gl_register_file file, const GLint map[])
  * write to such registers.  Be careful with condition code setters.
  */
 static GLboolean
-_mesa_remove_dead_code_global(struct gl_program *prog)
+_mesa_remove_dead_code_global(struct gl_program *prog, void *mem_ctx)
 {
    GLboolean tempRead[REG_ALLOCATE_MAX_PROGRAM_TEMPS][4];
    GLboolean *removeInst; /* per-instruction removal flag */
@@ -249,12 +250,12 @@ _mesa_remove_dead_code_global(struct gl_program *prog)
       /*_mesa_print_program(prog);*/
    }
 
-   removeInst = (GLboolean *)
-      calloc(1, prog->NumInstructions * sizeof(GLboolean));
+   removeInst =
+      calloc(prog->arb.NumInstructions, sizeof(GLboolean));
 
    /* Determine which temps are read and written */
-   for (i = 0; i < prog->NumInstructions; i++) {
-      const struct prog_instruction *inst = prog->Instructions + i;
+   for (i = 0; i < prog->arb.NumInstructions; i++) {
+      const struct prog_instruction *inst = prog->arb.Instructions + i;
       const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
       GLuint j;
 
@@ -263,7 +264,7 @@ _mesa_remove_dead_code_global(struct gl_program *prog)
          if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) {
             const GLuint index = inst->SrcReg[j].Index;
             GLuint read_mask;
-            ASSERT(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS);
+            assert(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS);
            read_mask = get_src_arg_mask(inst, j, NO_MASK);
 
             if (inst->SrcReg[j].RelAddr) {
@@ -274,42 +275,30 @@ _mesa_remove_dead_code_global(struct gl_program *prog)
 
            for (comp = 0; comp < 4; comp++) {
               const GLuint swz = GET_SWZ(inst->SrcReg[j].Swizzle, comp);
-              ASSERT(swz < 4);
-               if ((read_mask & (1 << swz)) == 0)
-                 continue;
-               if (swz <= SWIZZLE_W)
+               if (swz <= SWIZZLE_W) {
+                  if ((read_mask & (1 << swz)) == 0)
+                     continue;
                   tempRead[index][swz] = GL_TRUE;
+               }
            }
          }
       }
 
       /* check dst reg */
       if (inst->DstReg.File == PROGRAM_TEMPORARY) {
-         const GLuint index = inst->DstReg.Index;
-         ASSERT(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS);
+         assert(inst->DstReg.Index < REG_ALLOCATE_MAX_PROGRAM_TEMPS);
 
          if (inst->DstReg.RelAddr) {
             if (dbg)
                printf("abort remove dead code (indirect temp)\n");
             goto done;
          }
-
-         if (inst->CondUpdate) {
-            /* If we're writing to this register and setting condition
-             * codes we cannot remove the instruction.  Prevent removal
-             * by setting the 'read' flag.
-             */
-            tempRead[index][0] = GL_TRUE;
-            tempRead[index][1] = GL_TRUE;
-            tempRead[index][2] = GL_TRUE;
-            tempRead[index][3] = GL_TRUE;
-         }
       }
    }
 
    /* find instructions that write to dead registers, flag for removal */
-   for (i = 0; i < prog->NumInstructions; i++) {
-      struct prog_instruction *inst = prog->Instructions + i;
+   for (i = 0; i < prog->arb.NumInstructions; i++) {
+      struct prog_instruction *inst = prog->arb.Instructions + i;
       const GLuint numDst = _mesa_num_inst_dst_regs(inst->Opcode);
 
       if (numDst != 0 && inst->DstReg.File == PROGRAM_TEMPORARY) {
@@ -337,7 +326,7 @@ _mesa_remove_dead_code_global(struct gl_program *prog)
    }
 
    /* now remove the instructions which aren't needed */
-   rem = remove_instructions(prog, removeInst);
+   rem = remove_instructions(prog, removeInst, mem_ctx);
 
    if (dbg) {
       printf("Optimize: End dead code removal.\n");
@@ -376,12 +365,11 @@ find_next_use(const struct gl_program *prog,
 {
    GLuint i;
 
-   for (i = start; i < prog->NumInstructions; i++) {
-      const struct prog_instruction *inst = prog->Instructions + i;
+   for (i = start; i < prog->arb.NumInstructions; i++) {
+      const struct prog_instruction *inst = prog->arb.Instructions + i;
       switch (inst->Opcode) {
       case OPCODE_BGNLOOP:
       case OPCODE_BGNSUB:
-      case OPCODE_BRA:
       case OPCODE_CAL:
       case OPCODE_CONT:
       case OPCODE_IF:
@@ -400,7 +388,7 @@ find_next_use(const struct gl_program *prog,
             for (j = 0; j < numSrc; j++) {
                if (inst->SrcReg[j].RelAddr ||
                    (inst->SrcReg[j].File == PROGRAM_TEMPORARY &&
-                   inst->SrcReg[j].Index == index &&
+                   inst->SrcReg[j].Index == (GLint)index &&
                    (get_src_arg_mask(inst,j,NO_MASK) & mask)))
                   return READ;
             }
@@ -428,7 +416,6 @@ _mesa_is_flow_control_opcode(enum prog_opcode opcode)
    switch (opcode) {
    case OPCODE_BGNLOOP:
    case OPCODE_BGNSUB:
-   case OPCODE_BRA:
    case OPCODE_CAL:
    case OPCODE_CONT:
    case OPCODE_IF:
@@ -454,15 +441,9 @@ can_downward_mov_be_modifed(const struct prog_instruction *mov)
 {
    return
       mov->Opcode == OPCODE_MOV &&
-      mov->CondUpdate == GL_FALSE &&
       mov->SrcReg[0].RelAddr == 0 &&
       mov->SrcReg[0].Negate == 0 &&
-      mov->SrcReg[0].Abs == 0 &&
-      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.RelAddr == 0;
 }
 
 
@@ -471,7 +452,8 @@ can_upward_mov_be_modifed(const struct prog_instruction *mov)
 {
    return
       can_downward_mov_be_modifed(mov) &&
-      mov->DstReg.File == PROGRAM_TEMPORARY;
+      mov->DstReg.File == PROGRAM_TEMPORARY &&
+      !mov->Saturate;
 }
 
 
@@ -500,8 +482,8 @@ _mesa_remove_extra_move_use(struct gl_program *prog)
     *    FOO tmpY, arg0, arg1;
     */
 
-   for (i = 0; i + 1 < prog->NumInstructions; i++) {
-      const struct prog_instruction *mov = prog->Instructions + i;
+   for (i = 0; i + 1 < prog->arb.NumInstructions; i++) {
+      const struct prog_instruction *mov = prog->arb.Instructions + i;
       GLuint dst_mask, src_mask;
       if (can_upward_mov_be_modifed(mov) == GL_FALSE)
          continue;
@@ -516,8 +498,8 @@ _mesa_remove_extra_move_use(struct gl_program *prog)
        * rewritten or we get into some flow-control, eliminating the use of
        * this MOV.
        */
-      for (j = i + 1; j < prog->NumInstructions; j++) {
-        struct prog_instruction *inst2 = prog->Instructions + j;
+      for (j = i + 1; j < prog->arb.NumInstructions; j++) {
+         struct prog_instruction *inst2 = prog->arb.Instructions + j;
          GLuint arg;
 
         if (_mesa_is_flow_control_opcode(inst2->Opcode))
@@ -529,8 +511,7 @@ _mesa_remove_extra_move_use(struct gl_program *prog)
 
            if (inst2->SrcReg[arg].File != mov->DstReg.File ||
                inst2->SrcReg[arg].Index != mov->DstReg.Index ||
-               inst2->SrcReg[arg].RelAddr ||
-               inst2->SrcReg[arg].Abs)
+               inst2->SrcReg[arg].RelAddr)
               continue;
             read_mask = get_src_arg_mask(inst2, arg, NO_MASK);
 
@@ -588,16 +569,16 @@ _mesa_remove_extra_move_use(struct gl_program *prog)
  * with a proper control flow graph
  */
 static GLboolean
-_mesa_remove_dead_code_local(struct gl_program *prog)
+_mesa_remove_dead_code_local(struct gl_program *prog, void *mem_ctx)
 {
    GLboolean *removeInst;
    GLuint i, arg, rem = 0;
 
-   removeInst = (GLboolean *)
-      calloc(1, prog->NumInstructions * sizeof(GLboolean));
+   removeInst =
+      calloc(prog->arb.NumInstructions, sizeof(GLboolean));
 
-   for (i = 0; i < prog->NumInstructions; i++) {
-      const struct prog_instruction *inst = prog->Instructions + i;
+   for (i = 0; i < prog->arb.NumInstructions; i++) {
+      const struct prog_instruction *inst = prog->arb.Instructions + i;
       const GLuint index = inst->DstReg.Index;
       const GLuint mask = inst->DstReg.WriteMask;
       enum inst_use use;
@@ -620,7 +601,7 @@ _mesa_remove_dead_code_local(struct gl_program *prog)
          removeInst[i] = GL_TRUE;
    }
 
-   rem = remove_instructions(prog, removeInst);
+   rem = remove_instructions(prog, removeInst, mem_ctx);
 
 done:
    free(removeInst);
@@ -646,6 +627,8 @@ _mesa_merge_mov_into_inst(struct prog_instruction *inst,
    if (mask != (inst->DstReg.WriteMask & mask))
       return GL_FALSE;
 
+   inst->Saturate |= mov->Saturate;
+
    /* 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
@@ -667,7 +650,7 @@ _mesa_merge_mov_into_inst(struct prog_instruction *inst,
       for (dst_comp = 0; dst_comp < 4; ++dst_comp) {
          if (mov->DstReg.WriteMask & (1 << dst_comp)) {
             const GLuint src_comp = GET_SWZ(mov->SrcReg[0].Swizzle, dst_comp);
-            ASSERT(src_comp < 4);
+            assert(src_comp < 4);
             dst_to_src_comp[dst_comp] = src_comp;
          }
       }
@@ -683,9 +666,9 @@ _mesa_merge_mov_into_inst(struct prog_instruction *inst,
             if ((mov->DstReg.WriteMask & (1 << dst_comp)) == 0)
                continue;
             src_comp = dst_to_src_comp[dst_comp];
-            ASSERT(src_comp < 4);
+            assert(src_comp < 4);
             arg_comp = GET_SWZ(arg_swz, src_comp);
-            ASSERT(arg_comp < 4);
+            assert(arg_comp < 4);
             inst->SrcReg[arg].Swizzle |= arg_comp << (3*dst_comp);
          }
       }
@@ -722,7 +705,7 @@ _mesa_merge_mov_into_inst(struct prog_instruction *inst,
  * Try to remove extraneous MOV instructions from the given program.
  */
 static GLboolean
-_mesa_remove_extra_moves(struct gl_program *prog)
+_mesa_remove_extra_moves(struct gl_program *prog, void *mem_ctx)
 {
    GLboolean *removeInst; /* per-instruction removal flag */
    GLuint i, rem = 0, nesting = 0;
@@ -732,8 +715,8 @@ _mesa_remove_extra_moves(struct gl_program *prog)
       _mesa_print_program(prog);
    }
 
-   removeInst = (GLboolean *)
-      calloc(1, prog->NumInstructions * sizeof(GLboolean));
+   removeInst =
+      calloc(prog->arb.NumInstructions, sizeof(GLboolean));
 
    /*
     * Look for sequences such as this:
@@ -743,8 +726,8 @@ _mesa_remove_extra_moves(struct gl_program *prog)
     *    FOO tmpY, arg0, arg1;
     */
 
-   for (i = 0; i < prog->NumInstructions; i++) {
-      const struct prog_instruction *mov = prog->Instructions + i;
+   for (i = 0; i < prog->arb.NumInstructions; i++) {
+      const struct prog_instruction *mov = prog->arb.Instructions + i;
 
       switch (mov->Opcode) {
       case OPCODE_BGNLOOP:
@@ -773,13 +756,11 @@ _mesa_remove_extra_moves(struct gl_program *prog)
             prevI = i - 1;
             while (prevI > 0 && removeInst[prevI])
                prevI--;
-            prevInst = prog->Instructions + prevI;
+            prevInst = prog->arb.Instructions + prevI;
 
             if (prevInst->DstReg.File == PROGRAM_TEMPORARY &&
                 prevInst->DstReg.Index == id &&
-                prevInst->DstReg.RelAddr == 0 &&
-                prevInst->DstReg.CondSrc == 0 && 
-                prevInst->DstReg.CondMask == COND_TR) {
+                prevInst->DstReg.RelAddr == 0) {
 
                const GLuint dst_mask = prevInst->DstReg.WriteMask;
                enum inst_use next_use = find_next_use(prog, i+1, id, dst_mask);
@@ -810,7 +791,7 @@ _mesa_remove_extra_moves(struct gl_program *prog)
    }
 
    /* now remove the instructions which aren't needed */
-   rem = remove_instructions(prog, removeInst);
+   rem = remove_instructions(prog, removeInst, mem_ctx);
 
    free(removeInst);
 
@@ -863,7 +844,7 @@ insert_interval_by_end(struct interval_list *list, const struct interval *inv)
    {
       GLuint i;
       for (i = 0; i + 1 < list->Num; i++) {
-         ASSERT(list->Intervals[i].End <= list->Intervals[i + 1].End);
+         assert(list->Intervals[i].End <= list->Intervals[i + 1].End);
       }
    }
 #endif
@@ -879,8 +860,8 @@ remove_interval(struct interval_list *list, const struct interval *inv)
    for (k = 0; k < list->Num; k++) {
       if (list->Intervals[k].Reg == inv->Reg) {
          /* found, remove it */
-         ASSERT(list->Intervals[k].Start == inv->Start);
-         ASSERT(list->Intervals[k].End == inv->End);
+         assert(list->Intervals[k].Start == inv->Start);
+         assert(list->Intervals[k].End == inv->End);
          while (k < list->Num - 1) {
             list->Intervals[k] = list->Intervals[k + 1];
             k++;
@@ -916,7 +897,7 @@ sort_interval_list_by_start(struct interval_list *list)
    {
       GLuint i;
       for (i = 0; i + 1 < list->Num; i++) {
-         ASSERT(list->Intervals[i].Start <= list->Intervals[i + 1].Start);
+         assert(list->Intervals[i].Start <= list->Intervals[i + 1].Start);
       }
    }
 #endif
@@ -936,25 +917,36 @@ update_interval(GLint intBegin[], GLint intEnd[],
                struct loop_info *loopStack, GLuint loopStackDepth,
                GLuint index, GLuint ic)
 {
-   int i;
+   unsigned 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;
       }
    }
 
-   ASSERT(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS);
+   /* 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;
+      assert(intEnd[index] == -1);
+      intBegin[index] = begin;
+      intEnd[index] = end;
    }
    else {
-      intEnd[index] = ic;
+      intEnd[index] = end;
    }
 }
 
@@ -1042,7 +1034,8 @@ find_live_intervals(struct gl_program *prog,
    }
 
    /* build intermediate arrays */
-   if (!_mesa_find_temp_intervals(prog->Instructions, prog->NumInstructions,
+   if (!_mesa_find_temp_intervals(prog->arb.Instructions,
+                                  prog->arb.NumInstructions,
                                   intBegin, intEnd))
       return GL_FALSE;
 
@@ -1157,7 +1150,7 @@ _mesa_reallocate_registers(struct gl_program *prog)
                else {
                   /* Interval 'inv' has expired */
                   const GLint regNew = registerMap[inv->Reg];
-                  ASSERT(regNew >= 0);
+                  assert(regNew >= 0);
 
                   if (dbg)
                      printf("  expire interval for reg %u\n", inv->Reg);
@@ -1169,7 +1162,7 @@ _mesa_reallocate_registers(struct gl_program *prog)
                   /* return register regNew to the free pool */
                   if (dbg)
                      printf("  free reg %d\n", regNew);
-                  ASSERT(usedRegs[regNew] == GL_TRUE);
+                  assert(usedRegs[regNew] == GL_TRUE);
                   usedRegs[regNew] = GL_FALSE;
                }
             }
@@ -1202,7 +1195,7 @@ _mesa_reallocate_registers(struct gl_program *prog)
        */
       replace_regs(prog, PROGRAM_TEMPORARY, registerMap);
 
-      prog->NumTemporaries = maxTemp + 1;
+      prog->arb.NumTemporaries = maxTemp + 1;
    }
 
    if (dbg) {
@@ -1217,33 +1210,125 @@ _mesa_reallocate_registers(struct gl_program *prog)
 #if 0
 static void
 print_it(struct gl_context *ctx, struct gl_program *program, const char *txt) {
-   fprintf(stderr, "%s (%u inst):\n", txt, program->NumInstructions);
+   fprintf(stderr, "%s (%u inst):\n", txt, program->arb.NumInstructions);
    _mesa_print_program(program);
    _mesa_print_program_parameters(ctx, program);
    fprintf(stderr, "\n\n");
 }
 #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->arb.NumInstructions; i++) {
+      struct prog_instruction *inst = program->arb.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
  * instructions, temp regs, etc.
  */
 void
-_mesa_optimize_program(struct gl_context *ctx, struct gl_program *program)
+_mesa_optimize_program(struct gl_program *program, void *mem_ctx)
 {
    GLboolean any_change;
 
+   _mesa_simplify_cmp(program);
    /* Stop when no modifications were output */
    do {
       any_change = GL_FALSE;
       _mesa_remove_extra_move_use(program);
-      if (_mesa_remove_dead_code_global(program))
+      if (_mesa_remove_dead_code_global(program, mem_ctx))
          any_change = GL_TRUE;
-      if (_mesa_remove_extra_moves(program))
+      if (_mesa_remove_extra_moves(program, mem_ctx))
          any_change = GL_TRUE;
-      if (_mesa_remove_dead_code_local(program))
+      if (_mesa_remove_dead_code_local(program, mem_ctx))
          any_change = GL_TRUE;
+
+      any_change = _mesa_constant_fold(program) || any_change;
       _mesa_reallocate_registers(program);
    } while (any_change);
 }