1 /*
2 * Copyright © 2021 Valve Corporation
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "ir3.h"
7
8 /* Sometimes we can get unreachable blocks from NIR. In particular this happens
9 * for blocks after an if where both sides end in a break/continue. These blocks
10 * are then reachable only via the physical CFG. This pass deletes these blocks
11 * and reroutes the physical edge past it.
12 */
13
14 static void
delete_block(struct ir3 * ir,struct ir3_block * block)15 delete_block(struct ir3 *ir, struct ir3_block *block)
16 {
17 struct ir3_instruction *end = NULL;
18 foreach_instr (instr, &block->instr_list) {
19 if (instr->opc == OPC_END) {
20 end = instr;
21 break;
22 }
23 }
24
25 /* The end block can be legitimately unreachable if the shader only exits via
26 * discarding. ir3_legalize will then insert a branch to the end. Keep the
27 * block around but delete all the other instructions and make the end not
28 * take any sources, so that we don't have any dangling references to other
29 * unreachable blocks.
30 */
31 if (end) {
32 foreach_instr_safe (instr, &block->instr_list) {
33 if (instr != end)
34 list_delinit(&instr->node);
35 }
36 end->srcs_count = 0;
37 return;
38 }
39
40 for (unsigned i = 0; i < 2; i++) {
41 struct ir3_block *succ = block->successors[i];
42 if (!succ)
43 continue;
44
45 unsigned pred_idx = ir3_block_get_pred_index(succ, block);
46
47 /* If this isn't the last predecessor, we swap it with the last before
48 * removing it.
49 */
50 bool swap_pred = pred_idx != succ->predecessors_count - 1;
51
52 foreach_instr (phi, &succ->instr_list) {
53 if (phi->opc != OPC_META_PHI)
54 break;
55
56 if (swap_pred)
57 phi->srcs[pred_idx] = phi->srcs[phi->srcs_count - 1];
58 phi->srcs_count--;
59 }
60 if (swap_pred) {
61 succ->predecessors[pred_idx] =
62 succ->predecessors[succ->predecessors_count - 1];
63 }
64 succ->predecessors_count--;
65 }
66 }
67
68 bool
ir3_remove_unreachable(struct ir3 * ir)69 ir3_remove_unreachable(struct ir3 *ir)
70 {
71 bool progress = false;
72 foreach_block_safe (block, &ir->block_list) {
73 if (block != ir3_start_block(ir) && block->predecessors_count == 0) {
74 delete_block(ir, block);
75 list_del(&block->node);
76 progress = true;
77 }
78 }
79
80 return progress;
81 }
82