1 /*
2  * Copyright © 2014 Broadcom
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 
24 /**
25  * @file vc4_opt_dead_code.c
26  *
27  * This is a simple dead code eliminator for SSA values in QIR.
28  *
29  * It walks all the instructions finding what temps are used, then walks again
30  * to remove instructions writing unused temps.
31  *
32  * This is an inefficient implementation if you have long chains of
33  * instructions where the entire chain is dead, but we expect those to have
34  * been eliminated at the NIR level, and here we're just cleaning up small
35  * problems produced by NIR->QIR.
36  */
37 
38 #include "vc4_qir.h"
39 
40 static bool debug;
41 
42 static void
dce(struct vc4_compile * c,struct qinst * inst)43 dce(struct vc4_compile *c, struct qinst *inst)
44 {
45         if (debug) {
46                 fprintf(stderr, "Removing: ");
47                 qir_dump_inst(c, inst);
48                 fprintf(stderr, "\n");
49         }
50         assert(!inst->sf);
51         qir_remove_instruction(c, inst);
52 }
53 
54 static bool
has_nonremovable_reads(struct vc4_compile * c,struct qinst * inst)55 has_nonremovable_reads(struct vc4_compile *c, struct qinst *inst)
56 {
57         for (int i = 0; i < qir_get_nsrc(inst); i++) {
58                 if (inst->src[i].file == QFILE_VPM) {
59                         uint32_t attr = inst->src[i].index / 4;
60                         uint32_t offset = (inst->src[i].index % 4) * 4;
61 
62                         if (c->vattr_sizes[attr] != offset + 4)
63                                 return true;
64 
65                         /* Can't get rid of the last VPM read, or the
66                          * simulator (at least) throws an error.
67                          */
68                         uint32_t total_size = 0;
69                         for (uint32_t i = 0; i < ARRAY_SIZE(c->vattr_sizes); i++)
70                                 total_size += c->vattr_sizes[i];
71                         if (total_size == 4)
72                                 return true;
73                 }
74 
75                 if (inst->src[i].file == QFILE_VARY &&
76                     c->input_slots[inst->src[i].index].slot == 0xff) {
77                         return true;
78                 }
79         }
80 
81         return false;
82 }
83 
84 bool
qir_opt_dead_code(struct vc4_compile * c)85 qir_opt_dead_code(struct vc4_compile *c)
86 {
87         bool progress = false;
88         bool *used = calloc(c->num_temps, sizeof(bool));
89 
90         qir_for_each_inst_inorder(inst, c) {
91                 for (int i = 0; i < qir_get_nsrc(inst); i++) {
92                         if (inst->src[i].file == QFILE_TEMP)
93                                 used[inst->src[i].index] = true;
94                 }
95         }
96 
97         qir_for_each_block(block, c) {
98                 qir_for_each_inst_safe(inst, block) {
99                         if (inst->dst.file != QFILE_NULL &&
100                             !(inst->dst.file == QFILE_TEMP &&
101                               !used[inst->dst.index])) {
102                                 continue;
103                         }
104 
105                         if (qir_has_side_effects(c, inst))
106                                 continue;
107 
108                         if (inst->sf ||
109                             has_nonremovable_reads(c, inst)) {
110                                 /* If we can't remove the instruction, but we
111                                  * don't need its destination value, just
112                                  * remove the destination.  The register
113                                  * allocator would trivially color it and it
114                                  * wouldn't cause any register pressure, but
115                                  * it's nicer to read the QIR code without
116                                  * unused destination regs.
117                                  */
118                                 if (inst->dst.file == QFILE_TEMP) {
119                                         if (debug) {
120                                                 fprintf(stderr,
121                                                         "Removing dst from: ");
122                                                 qir_dump_inst(c, inst);
123                                                 fprintf(stderr, "\n");
124                                         }
125                                         c->defs[inst->dst.index] = NULL;
126                                         inst->dst.file = QFILE_NULL;
127                                         progress = true;
128                                 }
129                                 continue;
130                         }
131 
132                         for (int i = 0; i < qir_get_nsrc(inst); i++) {
133                                 if (inst->src[i].file != QFILE_VPM)
134                                         continue;
135                                 uint32_t attr = inst->src[i].index / 4;
136                                 uint32_t offset = (inst->src[i].index % 4) * 4;
137 
138                                 if (c->vattr_sizes[attr] == offset + 4) {
139                                         c->num_inputs--;
140                                         c->vattr_sizes[attr] -= 4;
141                                 }
142                         }
143 
144                         dce(c, inst);
145                         progress = true;
146                         continue;
147                 }
148         }
149 
150         free(used);
151 
152         return progress;
153 }
154