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:
Vasily Khoruzhick 2025-02-25 22:25:36 -08:00 committed by Marge Bot
parent 69b119bc00
commit fa9ddbe82b

View file

@ -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,
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) {
if (node->op == ppir_op_branch) {
ppir_branch_node *branch = ppir_node_to_branch(node);
if (branch->target == old_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) {
/* We can only remove unconditional branches */
assert(ppir_node_get_src_num(node) == 0);
ppir_debug("ppir_block_update_successor: deleting branch %d\n", node->index);
ppir_node_delete(node);
}
}
@ -63,7 +71,7 @@ static void ppir_block_update_successor(ppir_block *pred,
pred->successors[i] = new_succ;
}
if (!new_succ)
if (!pred->successors[0] && !pred->successors[1])
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])) {
ppir_block *succ_block = block->successors[i];
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;
}
}
@ -83,6 +91,14 @@ static bool ppir_propagate_block_successors(ppir_compiler *comp)
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 */
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;
}
@ -251,10 +270,91 @@ static bool ppir_node_has_succ(ppir_compiler *comp, ppir_node *node)
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 */
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_safe(ppir_node, node, &block->node_list, list) {
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 progress;
@ -278,7 +410,13 @@ bool ppir_opt_prog(ppir_compiler *comp)
} while (progress);
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);
} while (progress);