i965/fs: Make resolve_source_modifiers consistent with the vec4 version
[mesa.git] / src / mesa / drivers / dri / i965 / brw_fs_peephole_predicated_break.cpp
index 3ba0b262451324ce938509d89120ae807b491fa1..b75f40ba5a1a222f5784e7804f37faba512f9018 100644 (file)
@@ -24,6 +24,8 @@
 #include "brw_fs.h"
 #include "brw_cfg.h"
 
+using namespace brw;
+
 /** @file brw_fs_peephole_predicated_break.cpp
  *
  * Loops are often structured as
  *
  * This peephole pass removes the IF and ENDIF instructions and predicates the
  * BREAK, dropping two instructions from the loop body.
+ *
+ * If the loop was a DO { ... } WHILE loop, it looks like
+ *
+ * loop:
+ *    ...
+ *    CMP.f0
+ *    (+f0) IF
+ *    BREAK
+ *    ENDIF
+ *    WHILE loop
+ *
+ * and we can remove the BREAK instruction and predicate the WHILE.
  */
 
 bool
@@ -45,46 +59,95 @@ fs_visitor::opt_peephole_predicated_break()
 {
    bool progress = false;
 
-   calculate_cfg();
-
-   for (int b = 0; b < cfg->num_blocks; b++) {
-      bblock_t *block = cfg->blocks[b];
+   foreach_block (block, cfg) {
+      if (block->start_ip != block->end_ip)
+         continue;
 
       /* BREAK and CONTINUE instructions, by definition, can only be found at
        * the ends of basic blocks.
        */
-      fs_inst *inst = (fs_inst *) block->end;
-      if (inst->opcode != BRW_OPCODE_BREAK && inst->opcode != BRW_OPCODE_CONTINUE)
+      fs_inst *jump_inst = (fs_inst *)block->end();
+      if (jump_inst->opcode != BRW_OPCODE_BREAK &&
+          jump_inst->opcode != BRW_OPCODE_CONTINUE)
          continue;
 
-      fs_inst *if_inst = (fs_inst *) inst->prev;
+      fs_inst *if_inst = (fs_inst *)block->prev()->end();
       if (if_inst->opcode != BRW_OPCODE_IF)
          continue;
 
-      fs_inst *endif_inst = (fs_inst *) inst->next;
+      fs_inst *endif_inst = (fs_inst *)block->next()->start();
       if (endif_inst->opcode != BRW_OPCODE_ENDIF)
          continue;
 
+      bblock_t *jump_block = block;
+      bblock_t *if_block = jump_block->prev();
+      bblock_t *endif_block = jump_block->next();
+
       /* For Sandybridge with IF with embedded comparison we need to emit an
        * instruction to set the flag register.
        */
-      if (brw->gen == 6 && if_inst->conditional_mod) {
-         fs_inst *cmp_inst = CMP(reg_null_d, if_inst->src[0], if_inst->src[1],
-                                 if_inst->conditional_mod);
-         if_inst->insert_before(cmp_inst);
-         inst->predicate = BRW_PREDICATE_NORMAL;
+      if (devinfo->gen == 6 && if_inst->conditional_mod) {
+         const fs_builder ibld(this, if_block, if_inst);
+         ibld.CMP(ibld.null_reg_d(), if_inst->src[0], if_inst->src[1],
+                  if_inst->conditional_mod);
+         jump_inst->predicate = BRW_PREDICATE_NORMAL;
       } else {
-         inst->predicate = if_inst->predicate;
-         inst->predicate_inverse = if_inst->predicate_inverse;
+         jump_inst->predicate = if_inst->predicate;
+         jump_inst->predicate_inverse = if_inst->predicate_inverse;
+      }
+
+      bblock_t *earlier_block = if_block;
+      if (if_block->start_ip == if_block->end_ip) {
+         earlier_block = if_block->prev();
+      }
+
+      if_inst->remove(if_block);
+
+      bblock_t *later_block = endif_block;
+      if (endif_block->start_ip == endif_block->end_ip) {
+         later_block = endif_block->next();
+      }
+      endif_inst->remove(endif_block);
+
+      if (!earlier_block->ends_with_control_flow()) {
+         earlier_block->children.make_empty();
+         earlier_block->add_successor(cfg->mem_ctx, jump_block);
+      }
+
+      if (!later_block->starts_with_control_flow()) {
+         later_block->parents.make_empty();
       }
+      jump_block->add_successor(cfg->mem_ctx, later_block);
 
-      if_inst->remove();
-      endif_inst->remove();
+      if (earlier_block->can_combine_with(jump_block)) {
+         earlier_block->combine_with(jump_block);
 
-      /* By removing the ENDIF instruction we removed a basic block. Skip over
-       * it for the next iteration.
+         block = earlier_block;
+      }
+
+      /* Now look at the first instruction of the block following the BREAK. If
+       * it's a WHILE, we can delete the break, predicate the WHILE, and join
+       * the two basic blocks.
        */
-      b++;
+      bblock_t *while_block = earlier_block->next();
+      fs_inst *while_inst = (fs_inst *)while_block->start();
+
+      if (jump_inst->opcode == BRW_OPCODE_BREAK &&
+          while_inst->opcode == BRW_OPCODE_WHILE &&
+          while_inst->predicate == BRW_PREDICATE_NONE) {
+         jump_inst->remove(earlier_block);
+         while_inst->predicate = jump_inst->predicate;
+         while_inst->predicate_inverse = !jump_inst->predicate_inverse;
+
+         earlier_block->children.make_empty();
+         earlier_block->add_successor(cfg->mem_ctx, while_block);
+
+         assert(earlier_block->can_combine_with(while_block));
+         earlier_block->combine_with(while_block);
+
+         earlier_block->next()->parents.make_empty();
+         earlier_block->add_successor(cfg->mem_ctx, earlier_block->next());
+      }
 
       progress = true;
    }