lima: ppir: optimize branches
Implement 2 optimizations for branches: 1) if unconditional branch target is a block that only has unconditional branch, propagate the target 2) optimize following contruction: block 1: ... if (cond) block 3 block 2: branch block N block 3: ... into block 1: ... if (!cond) block N block 2: block 3: ... Note: optimization 1) significantly improves runtime of if ladders, but it is not visible in shader-db because we do not track shortest/longest path and it doesn't always create dead code (usually just a single instruction) Reviewed-by: Erico Nunes <nunes.erico@gmail.com> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33754>
This commit is contained in:
committed by
Marge Bot
parent
69b119bc00
commit
fa9ddbe82b
@@ -43,16 +43,24 @@ static bool ppir_block_succ_is_seq(ppir_block *pred, ppir_block *succ)
|
|||||||
|
|
||||||
static void ppir_block_update_successor(ppir_block *pred,
|
static void ppir_block_update_successor(ppir_block *pred,
|
||||||
ppir_block *old_succ,
|
ppir_block *old_succ,
|
||||||
ppir_block *new_succ)
|
ppir_block *new_succ,
|
||||||
|
bool invert_cond)
|
||||||
{
|
{
|
||||||
list_for_each_entry_safe(ppir_node, node, &pred->node_list, list) {
|
list_for_each_entry_safe(ppir_node, node, &pred->node_list, list) {
|
||||||
if (node->op == ppir_op_branch) {
|
if (node->op == ppir_op_branch) {
|
||||||
ppir_branch_node *branch = ppir_node_to_branch(node);
|
ppir_branch_node *branch = ppir_node_to_branch(node);
|
||||||
if (branch->target == old_succ)
|
if (branch->target == old_succ)
|
||||||
branch->target = new_succ;
|
branch->target = new_succ;
|
||||||
|
if (invert_cond) {
|
||||||
|
assert(ppir_node_get_src_num(node) == 2);
|
||||||
|
branch->cond_eq = !branch->cond_eq;
|
||||||
|
branch->cond_gt = !branch->cond_gt;
|
||||||
|
branch->cond_lt = !branch->cond_lt;
|
||||||
|
}
|
||||||
if (branch->target == NULL) {
|
if (branch->target == NULL) {
|
||||||
/* We can only remove unconditional branches */
|
/* We can only remove unconditional branches */
|
||||||
assert(ppir_node_get_src_num(node) == 0);
|
assert(ppir_node_get_src_num(node) == 0);
|
||||||
|
ppir_debug("ppir_block_update_successor: deleting branch %d\n", node->index);
|
||||||
ppir_node_delete(node);
|
ppir_node_delete(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,7 +71,7 @@ static void ppir_block_update_successor(ppir_block *pred,
|
|||||||
pred->successors[i] = new_succ;
|
pred->successors[i] = new_succ;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!new_succ)
|
if (!pred->successors[0] && !pred->successors[1])
|
||||||
pred->stop = true;
|
pred->stop = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +83,7 @@ static bool ppir_propagate_block_successors(ppir_compiler *comp)
|
|||||||
if (block->successors[i] && ppir_block_is_empty(block->successors[i])) {
|
if (block->successors[i] && ppir_block_is_empty(block->successors[i])) {
|
||||||
ppir_block *succ_block = block->successors[i];
|
ppir_block *succ_block = block->successors[i];
|
||||||
assert(!succ_block->successors[1]);
|
assert(!succ_block->successors[1]);
|
||||||
ppir_block_update_successor(block, block->successors[i], succ_block->successors[0]);
|
ppir_block_update_successor(block, block->successors[i], succ_block->successors[0], false);
|
||||||
progress = true;
|
progress = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,6 +91,14 @@ static bool ppir_propagate_block_successors(ppir_compiler *comp)
|
|||||||
return progress;
|
return progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ppir_renumber_blocks(ppir_compiler *comp)
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
list_for_each_entry(ppir_block, block, &comp->block_list, list) {
|
||||||
|
block->index = index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Removes empty blocks */
|
/* Removes empty blocks */
|
||||||
static bool ppir_remove_empty_blocks(ppir_compiler *comp)
|
static bool ppir_remove_empty_blocks(ppir_compiler *comp)
|
||||||
{
|
{
|
||||||
@@ -102,6 +118,9 @@ static bool ppir_remove_empty_blocks(ppir_compiler *comp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (progress)
|
||||||
|
ppir_renumber_blocks(comp);
|
||||||
|
|
||||||
return progress;
|
return progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,10 +270,91 @@ static bool ppir_node_has_succ(ppir_compiler *comp, ppir_node *node)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Must be run after ppir_renumber_blocks() */
|
||||||
|
static bool ppir_opt_cond_branch(ppir_compiler *comp)
|
||||||
|
{
|
||||||
|
/* Walk blocks in order, find a block with two successors. It's always a
|
||||||
|
* block with an conditional branch. One of the successors is a target of
|
||||||
|
* the branch, and the other is sequential successor. Let's call them
|
||||||
|
* seq_succ and branch_succ. If branch_succ->index == seq_succ->index + 1,
|
||||||
|
* and branch_succ contains just an unconditional branch (let's call it
|
||||||
|
* branch2), we can inverse condition of branch, update its target to
|
||||||
|
* target of branch2 and drop seq_succ block.
|
||||||
|
*/
|
||||||
|
bool progress = false;
|
||||||
|
list_for_each_entry(ppir_block, block, &comp->block_list, list) {
|
||||||
|
if (!block->successors[1] || !block->successors[0])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((block->index + 1) != block->successors[0]->index)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((block->successors[0]->index + 1) != block->successors[1]->index)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!list_is_singular(&block->successors[0]->node_list))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ppir_node *node = ppir_block_first_node(block->successors[0]);
|
||||||
|
if (node->op != ppir_op_branch)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (ppir_node_get_src_num(node) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ppir_block *seq_succ = block->successors[0];
|
||||||
|
ppir_block *branch_succ = block->successors[1];
|
||||||
|
ppir_branch_node *branch2 = ppir_node_to_branch(node);
|
||||||
|
ppir_block_update_successor(block, branch_succ, branch2->target, true);
|
||||||
|
ppir_block_update_successor(block, seq_succ, branch_succ, false);
|
||||||
|
ppir_debug("ppir_opt_cond_branch: deleting branch %d\n", node->index);
|
||||||
|
ppir_node_delete(node);
|
||||||
|
ppir_block *old_succ = branch2->target;
|
||||||
|
ppir_block_update_successor(seq_succ, old_succ, branch_succ, false);
|
||||||
|
|
||||||
|
progress = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ppir_opt_propagate_branch_target(ppir_compiler *comp)
|
||||||
|
{
|
||||||
|
bool progress = false;
|
||||||
|
|
||||||
|
/* Check if branch targets a block that has a single unconditional branch
|
||||||
|
* and update branch target in this case
|
||||||
|
*/
|
||||||
|
list_for_each_entry(ppir_block, block, &comp->block_list, list) {
|
||||||
|
list_for_each_entry(ppir_node, node, &block->node_list, list) {
|
||||||
|
if (node->op != ppir_op_branch)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ppir_branch_node *branch = ppir_node_to_branch(node);
|
||||||
|
ppir_block *target = branch->target;
|
||||||
|
|
||||||
|
if (!list_is_singular(&target->node_list))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ppir_node *node2 = ppir_block_first_node(target);
|
||||||
|
if (node2->op != ppir_op_branch)
|
||||||
|
continue;
|
||||||
|
if (ppir_node_get_src_num(node2))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ppir_branch_node *branch2 = ppir_node_to_branch(node2);
|
||||||
|
ppir_block_update_successor(block, branch->target, branch2->target, false);
|
||||||
|
progress = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
/* Dead code elimination */
|
/* Dead code elimination */
|
||||||
static bool ppir_dce(ppir_compiler *comp)
|
static bool ppir_dce(ppir_compiler *comp)
|
||||||
{
|
{
|
||||||
|
/* Delete root nodes with no successors */
|
||||||
list_for_each_entry(ppir_block, block, &comp->block_list, list) {
|
list_for_each_entry(ppir_block, block, &comp->block_list, list) {
|
||||||
list_for_each_entry_safe(ppir_node, node, &block->node_list, list) {
|
list_for_each_entry_safe(ppir_node, node, &block->node_list, list) {
|
||||||
if (ppir_node_is_root(node) && !ppir_node_has_succ(comp, node)) {
|
if (ppir_node_is_root(node) && !ppir_node_has_succ(comp, node)) {
|
||||||
@@ -265,9 +365,41 @@ static bool ppir_dce(ppir_compiler *comp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
if (list_is_singular(&comp->block_list))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Delete unreacheable blocks */
|
||||||
|
int num_blocks = list_length(&comp->block_list);
|
||||||
|
BITSET_WORD *reachable = rzalloc_array(comp, BITSET_WORD, num_blocks);
|
||||||
|
/* Block 0 is entry, it is always reachable */
|
||||||
|
BITSET_SET(reachable, 0);
|
||||||
|
/* Discard block is always reachable */
|
||||||
|
if (comp->uses_discard)
|
||||||
|
BITSET_SET(reachable, comp->discard_block->index);
|
||||||
|
list_for_each_entry(ppir_block, block, &comp->block_list, list) {
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
if (!block->successors[i])
|
||||||
|
continue;
|
||||||
|
BITSET_SET(reachable, block->successors[i]->index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool progress = false;
|
||||||
|
list_for_each_entry(ppir_block, block, &comp->block_list, list) {
|
||||||
|
if (BITSET_TEST(reachable, block->index))
|
||||||
|
continue;
|
||||||
|
list_for_each_entry_safe_rev(ppir_node, node, &block->node_list, list) {
|
||||||
|
ppir_debug("DCE: deleting node %d\n", node->index);
|
||||||
|
ppir_node_delete(node);
|
||||||
|
progress = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ralloc_free(reachable);
|
||||||
|
return progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool ppir_opt_prog(ppir_compiler *comp)
|
bool ppir_opt_prog(ppir_compiler *comp)
|
||||||
{
|
{
|
||||||
bool progress;
|
bool progress;
|
||||||
@@ -278,7 +410,13 @@ bool ppir_opt_prog(ppir_compiler *comp)
|
|||||||
} while (progress);
|
} while (progress);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
progress = ppir_remove_empty_blocks(comp);
|
progress = ppir_opt_propagate_branch_target(comp);
|
||||||
|
progress |= ppir_opt_cond_branch(comp);
|
||||||
|
progress |= ppir_remove_empty_blocks(comp);
|
||||||
|
} while (progress);
|
||||||
|
|
||||||
|
do {
|
||||||
|
progress = ppir_remove_empty_blocks(comp);
|
||||||
progress |= ppir_dce(comp);
|
progress |= ppir_dce(comp);
|
||||||
} while (progress);
|
} while (progress);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user