summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Turner <[email protected]>2014-07-16 15:29:41 -0700
committerMatt Turner <[email protected]>2014-08-22 10:23:34 -0700
commit81755bc67bd16bfeb1c4db817125dde85ba9b264 (patch)
treedd56c0498d126d0f8b43ad9b03667e14db118227
parent9cf06e27e1e42ad33f0c6f8dc201300d8d108169 (diff)
i965: Preserve CFG when deleting dead control flow.
This pass deletes an IF/ELSE/ENDIF or IF/ENDIF sequence, or the ELSE in an ELSE/ENDIF sequence. In the typical case (where IF and ENDIF) aren't the only instructions in their basic blocks, we can simply remove the instructions (implicitly deleting the block containing only the ELSE), and attempt to merge blocks B0 and B2 together. B0: ... (+f0) if(8) B1: else(8) B2: endif(8) ... If the IF or ENDIF instructions are the only instructions in their respective basic blocks (which are deleted by the removal of the instructions), we'll want to instead merge the next blocks. Both B0 and B2 are possibly removed by the removal of if & endif. Same situation for if/endif. E.g., in the following example we'd remove blocks B1 and B2, and then attempt to combine B0 and B3. B0: ... B1: (+f0) if(8) B2: endif(8) B3: ... Reviewed-by: Topi Pohjolainen <[email protected]>
-rw-r--r--src/mesa/drivers/dri/i965/brw_dead_control_flow.cpp54
1 files changed, 45 insertions, 9 deletions
diff --git a/src/mesa/drivers/dri/i965/brw_dead_control_flow.cpp b/src/mesa/drivers/dri/i965/brw_dead_control_flow.cpp
index e6ace5c444f..56884e64bf1 100644
--- a/src/mesa/drivers/dri/i965/brw_dead_control_flow.cpp
+++ b/src/mesa/drivers/dri/i965/brw_dead_control_flow.cpp
@@ -42,13 +42,14 @@ dead_control_flow_eliminate(backend_visitor *v)
v->calculate_cfg();
- foreach_block (block, v->cfg) {
+ 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;
@@ -56,6 +57,7 @@ dead_control_flow_eliminate(backend_visitor *v)
backend_instruction *prev_inst = (backend_instruction *) endif_inst->prev;
if (prev_inst->opcode == BRW_OPCODE_ELSE) {
else_inst = prev_inst;
+ else_block = (bblock_t *)endif_block->link.prev;
found = true;
prev_inst = (backend_instruction *) prev_inst->prev;
@@ -63,6 +65,8 @@ dead_control_flow_eliminate(backend_visitor *v)
if (prev_inst->opcode == BRW_OPCODE_IF) {
if_inst = prev_inst;
+ if_block = else_block != NULL ? (bblock_t *)else_block->link.prev
+ : (bblock_t *)endif_block->link.prev;
found = true;
} else {
/* Don't remove the ENDIF if we didn't find a dead IF. */
@@ -70,18 +74,50 @@ dead_control_flow_eliminate(backend_visitor *v)
}
if (found) {
- if (if_inst)
- if_inst->remove();
- if (else_inst)
- else_inst->remove();
- if (endif_inst)
- 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 = (bblock_t *)if_block->link.prev;
+ } else {
+ earlier_block = if_block;
+ }
+ if_inst->remove(if_block);
+ }
+
+ if (else_inst) {
+ else_block->if_block->else_block = NULL;
+ else_inst->remove(else_block);
+ }
+
+ if (endif_inst) {
+ if (endif_block->start_ip == endif_block->end_ip) {
+ later_block = (bblock_t *)endif_block->link.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 = (bblock_t *)earlier_block->link.next;
+ }
+ }
+
progress = true;
}
}
if (progress)
- v->invalidate_live_intervals();
+ v->invalidate_live_intervals(false);
return progress;
}