From fa9ddbe82b369911cca654c21712add432f81657 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Tue, 25 Feb 2025 22:25:36 -0800 Subject: [PATCH] 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 Signed-off-by: Vasily Khoruzhick Part-of: --- src/gallium/drivers/lima/ir/pp/opt.c | 150 +++++++++++++++++++++++++-- 1 file changed, 144 insertions(+), 6 deletions(-) diff --git a/src/gallium/drivers/lima/ir/pp/opt.c b/src/gallium/drivers/lima/ir/pp/opt.c index 8ac2b26b565..d1768d97f77 100644 --- a/src/gallium/drivers/lima/ir/pp/opt.c +++ b/src/gallium/drivers/lima/ir/pp/opt.c @@ -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);