i965/cfg: Add a foreach_inst_in_block_reverse_safe macro.
[mesa.git] / src / mesa / drivers / dri / i965 / brw_dead_control_flow.cpp
index 7de27e3b22eca461d8ee7186f0ab1a724883786c..03f838dd9ae6ebc0ed89bc42844c882aca12d6d9 100644 (file)
@@ -32,6 +32,7 @@
 /* Look for and eliminate dead control flow:
  *
  *   - if/endif
+ *   . else in else/endif
  *   - if/else/endif
  */
 bool
@@ -39,39 +40,76 @@ dead_control_flow_eliminate(backend_visitor *v)
 {
    bool progress = false;
 
-   cfg_t cfg(v);
-
-   for (int b = 0; b < cfg.num_blocks; b++) {
-      bblock_t *block = cfg.blocks[b];
+   foreach_block_safe (block, v->cfg) {
+      bblock_t *if_block = NULL, *else_block = NULL, *endif_block = block;
       bool found = false;
 
       /* ENDIF instructions, by definition, can only be found at the start of
        * basic blocks.
        */
-      backend_instruction *endif_inst = block->start;
+      backend_instruction *endif_inst = endif_block->start();
       if (endif_inst->opcode != BRW_OPCODE_ENDIF)
          continue;
 
       backend_instruction *if_inst = NULL, *else_inst = NULL;
-      backend_instruction *prev_inst = (backend_instruction *) endif_inst->prev;
+      backend_instruction *prev_inst = endif_block->prev()->end();
+      if (prev_inst->opcode == BRW_OPCODE_ELSE) {
+         else_inst = prev_inst;
+         else_block = endif_block->prev();
+         found = true;
+
+         if (else_block->start_ip == else_block->end_ip)
+            prev_inst = else_block->prev()->end();
+      }
+
       if (prev_inst->opcode == BRW_OPCODE_IF) {
          if_inst = prev_inst;
+         if_block = else_block != NULL ? else_block->prev()
+                                       : endif_block->prev();
          found = true;
-      } else if (prev_inst->opcode == BRW_OPCODE_ELSE) {
-         else_inst = prev_inst;
-
-         prev_inst = (backend_instruction *) prev_inst->prev;
-         if (prev_inst->opcode == BRW_OPCODE_IF) {
-            if_inst = prev_inst;
-            found = true;
-         }
+      } else {
+         /* Don't remove the ENDIF if we didn't find a dead IF. */
+         endif_inst = NULL;
       }
 
       if (found) {
-         if_inst->remove();
-         if (else_inst)
-            else_inst->remove();
-         endif_inst->remove();
+         bblock_t *earlier_block = NULL, *later_block = NULL;
+
+         if (if_inst) {
+            if (if_block->start_ip == if_block->end_ip) {
+               earlier_block = if_block->prev();
+            } else {
+               earlier_block = if_block;
+            }
+            if_inst->remove(if_block);
+         }
+
+         if (else_inst) {
+            else_inst->remove(else_block);
+         }
+
+         if (endif_inst) {
+            if (endif_block->start_ip == endif_block->end_ip) {
+               later_block = endif_block->next();
+            } else {
+               later_block = endif_block;
+            }
+            endif_inst->remove(endif_block);
+         }
+
+         assert((earlier_block == NULL) == (later_block == NULL));
+         if (earlier_block && earlier_block->can_combine_with(later_block)) {
+            earlier_block->combine_with(later_block);
+
+            /* If ENDIF was in its own block, then we've now deleted it and
+             * merged the two surrounding blocks, the latter of which the
+             * __next block pointer was pointing to.
+             */
+            if (endif_block != later_block) {
+               __next = earlier_block->next();
+            }
+         }
+
          progress = true;
       }
    }